Skip to content

FE-1322: Generate Petrinaut architecture docs from in-code annotations - #9204

Draft
kube wants to merge 1 commit into
mainfrom
cf/fe-1322-arch-docs-generator
Draft

FE-1322: Generate Petrinaut architecture docs from in-code annotations#9204
kube wants to merge 1 commit into
mainfrom
cf/fe-1322-arch-docs-generator

Conversation

@kube

@kube kube commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Petrinaut's architecture documentation had no mechanism keeping it true. This makes the architecture something you declare next to the code it describes, generates the docs from those declarations, and fails CI when a declaration stops matching the code.

The output is a portable bundle, not a website — the same artefact renders locally, embeds into hash.dev/docs/petrinaut, or gets handed to an AI agent.

First of three. This PR is the generator and the annotations. #9205 migrates the hand-written architecture prose into it; #9206 adds a site that renders it. Each is independently correct; this one is the only one that needs reviewing to judge whether the architecture model is right.

🔍 What does this change?

1. Two annotations describe the whole architecture

/**
 * @layerRoot core.simulation.monte-carlo
 * @role Runs many simulations with bounded frame memory
 */

A folder README.md declares the same pair in frontmatter, and its prose becomes that layer's page.

Files with no annotation inherit from the nearest declaring ancestor, which keeps this proportional to the architecture rather than the file count: 37 declarations cover 412 files. Layer sizes, the 177 dependency edges and the parent/child tree are all derived from the real TypeScript import graph.

The vocabulary stops there deliberately. Both tags are needed to place a node in the graph and label it, which is the whole of what these docs assert. Anything further would be prose the generator cannot check — and a docs system that cannot check its own claims is the thing being replaced.

2. Every layer gets a diagram

D2, rendered to SVG at build time. 44 diagrams in three kinds, each bounding node count differently:

  • Overview (1) — the top-level layers.
  • Neighbourhood (37) — one per layer: what it depends on, what depends on it. Leaves included; they are where readers land.
  • Drill-down (6) — for a layer with sub-layers, its direct children.

Aggregation never invents a dependency: edges exist because imports exist, and counts sum real fileDependencies. Neighbours are capped at twelve — core.types has 18, so the rest becomes a dashed "+6 further layers" node carrying its 8 dependencies rather than being dropped.

3. CI enforces the structure

lint:arch-docs fails on: an unannotated source file, a layer id implying an undeclared ancestor, a duplicate declaration, a malformed tag, a package configured for a language with no extractor, and any dependency violating a rule in architecture.config.ts.

Every check is a statement about the graph. Four rules are enforced; the substantive one — react must not depend on ui — already held (0 imports against 235 the other way), so it locks in an existing property.

4. What this replaces

petrinaut-core/scripts/generate-dependency-diagrams.mjs held the architecture as ~180 lines of if (path.startsWith(...)) far from the code, with a fallback that silently mis-bucketed anything renamed. It hard-coded 7 of petrinaut-core's 10 entry points, so imports through ./ai, ./optimization and ./compiled-model were absent from the diagrams entirely.

🔗 Related links

  • FE-1322 (this PR)
  • FE-1157 — moving Petrinaut docs to hash.dev/docs/petrinaut. This produces the exportable artefact that work needs; it publishes nothing itself.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

@hashintel/petrinaut and petrinaut-core are touched only by comments, READMEs, and removal of a private script and its dependency-cruiser devDependency. No runtime code, types or exports change.

📜 Does this require a change to the docs?

  • require changes to docs which are made as part of this PR

The user-facing guide (libs/@hashintel/petrinaut/docs/) is untouched — no UI or behaviour changed. AGENTS.md gains a section on declaring layers and what CI enforces.

🕸️ Does this require a change to the Turbo Graph?

  • affected the execution graph, and the turbo.json's have been updated to reflect this

Adds @local/petrinaut-arch-docs#doc:architecture, deliberately not cached: Turborepo hashes a package plus its dependencies' task outputs, and the annotations this reads are source comments in petrinaut and petrinaut-core — nobody's output. A cached bundle would survive an annotation change and go quietly stale, which is the rot this package exists to catch. It declares outputs, so consumers depend on the task rather than on the directory existing.

Removes doc:dependency-diagram from petrinaut-core.

⚠️ Known issues

  • The taxonomy is a tree, so cross-cutting concerns have no home. Every layer has exactly one parent. core.types is the symptom already in the model: four distinct parents across two packages depend on it, so it is filed under core while behaving like a shared foundation under everything.
  • Nothing verifies @role. The structure is checked against the import graph; the one-line description of each layer is prose. Declaring it beside the code makes it likelier to be corrected when that code changes, but CI does not hold it to account.
  • Richer annotations are written but unread. @boundary, @invariant, @entryPoint and @layerName remain in the Petrinaut source and are ignored. Unknown tags are skipped by design and README frontmatter is tolerant, so re-reading one is a schema change in the generator, not a rewrite of the packages. Layer names are derived from the id's last segment, so a few read plainly (ui renders as "Ui").
  • Three packages are not covered. petrinaut-cli, petrinaut-website and petrinaut-opt have no declarations. A TypeScript package is a config entry plus one root declaration; the Python app needs docstring extraction, which is not written — configuring a package for a language with no extractor is a hard error rather than a silent no-op.
  • mise run fix:package-json could not run locally (needs a nightly Cargo feature), so package.json key ordering was verified by reading the sorter's field list.

🐾 Next steps

🛡 What tests cover this?

48 tests in @local/petrinaut-arch-docs:

  • tags.test.ts — tag grammar: multi-line continuation, duplicates, typo suggestions, that a tag named in prose is not a declaration, and that an unread annotation is ignored rather than rejected.
  • frontmatter.test.ts — declarations, malformed YAML, half-written declarations, CRLF, unknown keys ignored.
  • extract.test.ts — inheritance through undeclared folders, uncovered files, stable ordering.
  • check.test.ts — each CI check in both directions: fires when broken, silent when not.
  • emit/mdx.test.ts — link resolution at varying depths, fragments, unresolved targets.

Existing suites unaffected: 842 (petrinaut-core), 187 (petrinaut).

❓ How to test this?

yarn workspace @local/petrinaut-arch-docs lint:arch-docs   # expect 0 errors
turbo run doc:architecture --filter @local/petrinaut-arch-docs

Then read libs/@local/petrinaut-arch-docs/bundle/architecture.md — the whole architecture in one file — and open a diagram from bundle/diagrams/. around/core.types.svg exercises the neighbour cap: 12 drawn, plus a dashed "+6 further layers" node.

Break something and confirm it is caught: change a role: in any layer-declaring README to empty, add a source file in a folder no declaration covers, or point a rule in architecture.config.ts at a dependency that exists — each fails lint:arch-docs with the offending file named.

The architecture was described in a script that sat nowhere near the code it
described: ~180 lines of `if (path.startsWith(...))` in
`generate-dependency-diagrams.mjs`, with a fallback that silently mis-bucketed
anything renamed. It also hard-coded 7 of petrinaut-core's 10 entry points, so
imports through `./ai`, `./optimization` and `./compiled-model` were absent
from the diagrams entirely.

This replaces it with declarations that live beside the code, and a generator
that joins them with the real import graph.

A declaration is two tags. `@layerRoot <id>` names the layer a folder and its
descendants form; `@role <one line>` says what it is for. A folder README's
frontmatter declares the same pair, and its prose becomes that layer's page.
Files with no annotation inherit from the nearest declaring ancestor, which is
what keeps this proportional to the architecture rather than the file count:
37 declarations cover 412 files, producing 37 layers and 177 edges.

The vocabulary stops there deliberately. Both tags are needed to place a node
in the graph and label it, which is the whole of what these docs assert.
Anything further would be prose the generator cannot check, and a docs system
that cannot check its own claims is the thing being replaced.

Output is a portable bundle, not a website: `architecture.json` for consumers,
`architecture.md` for a single-pass read, generated pages, and 44 D2 diagrams
— an overview, a neighbourhood per layer showing what it depends on and what
depends on it, and a drill-down for each layer with children. Leaves get a
diagram too; they are where readers land.

`bundle/` is git-ignored build output. Committing it would mean reviewing every
change twice and resolving conflicts in generated files, and a stored copy
could go stale against the annotations that produced it.

CI runs `lint:arch-docs`, which fails on an unannotated source file, a layer id
implying an ancestor nobody declared, a duplicate declaration, a malformed tag,
a package configured for a language with no extractor, and any dependency
violating a rule in `architecture.config.ts`. Every check is a statement about
the graph. Four rules are enforced; the substantive one — `react` must not
depend on `ui` — already held, 0 imports against 235 the other way, so it locks
in a property the code already has.

`doc:architecture` is deliberately uncached: Turborepo hashes a package plus
its dependencies' task outputs, and the annotations this reads are source
comments in petrinaut and petrinaut-core, which are nobody's output. A cached
bundle would survive an annotation change and go quietly stale.

The authored-content pipeline is here and exercised by tests, but this branch
ships no `content/` directory and no renderer; both follow separately.
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview Aug 12, 2026 1:00am
hashdotdesign-tokens Ready Ready Preview Aug 12, 2026 1:00am
petrinaut Ready Ready Preview Aug 12, 2026 1:00am

@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team type/eng > backend Owned by the @backend team type/legal Owned by the @legal team labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

The following issues were found:
  • ❌ 1 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

Vulnerabilities

libs/@local/petrinaut-arch-docs/package.json

NameVersionVulnerabilitySeverityPatched Version
js-yaml4.3.0JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026-59870 fix not backportedhigh4.3.1

License Issues

yarn.lock

PackageVersionLicenseIssue Type
@local/petrinaut-arch-docs@workspace:libs/0.0.0-use.localNullUnknown License

OpenSSF Scorecard

Scorecard details
PackageVersionScoreDetails
npm/js-yaml 4.3.0 🟢 6.1
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1030 commit(s) and 28 issue activity found in the last 90 days -- score normalized to 10
Code-Review⚠️ 1Found 3/30 approved changesets -- score normalized to 1
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Fuzzing🟢 10project is fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Security-Policy🟢 4security policy file detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@hashintel/petrinaut workspace:* UnknownUnknown
npm/@hashintel/petrinaut-core workspace:* UnknownUnknown
npm/@local/tsconfig workspace:* UnknownUnknown
npm/@types/js-yaml ^4 UnknownUnknown
npm/@types/node 22.18.13 🟢 6.6
Details
CheckScoreReason
Code-Review🟢 9Found 29/30 approved changesets -- score normalized to 9
Maintained🟢 1030 commit(s) and 4 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy🟢 10security policy file detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
License🟢 9license file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Fuzzing⚠️ 0project is not fuzzed
npm/dependency-cruiser 18.0.0 🟢 7
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review⚠️ 0Found 0/30 approved changesets -- score normalized to 0
Security-Policy🟢 10security policy file detected
Token-Permissions🟢 8detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
SAST🟢 10SAST tool is run on all commits
Packaging🟢 10packaging workflow detected
npm/oxlint 1.63.0 UnknownUnknown
npm/tsx 4.20.6 UnknownUnknown
npm/typescript 5.9.3 🟢 7.9
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 1016 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Dependency-Update-Tool🟢 10update tool detected
Security-Policy🟢 10security policy file detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Vulnerabilities⚠️ 012 existing vulnerabilities detected
Binary-Artifacts🟢 10no binaries found in the repo
License🟢 10license file detected
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Pinned-Dependencies🟢 7dependency not pinned by hash detected -- score normalized to 7
SAST🟢 10SAST tool is run on all commits
Branch-Protection⚠️ -1internal error: error during GetBranch(release-5.9): error during branchesHandler.query: internal error: githubv4.Query: Resource not accessible by integration
Fuzzing🟢 10project is fuzzed
CI-Tests🟢 1030 out of 30 merged PRs checked by a CI test -- score normalized to 10
Contributors🟢 10project has 38 contributing companies or organizations
npm/vitest 4.1.10 UnknownUnknown
npm/zod 4.4.3 🟢 5.1
Details
CheckScoreReason
Code-Review⚠️ 1Found 5/29 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Security-Policy🟢 10security policy file detected
Maintained🟢 1013 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@local/petrinaut-arch-docs@workspace:libs/ 0.0.0-use.local UnknownUnknown

Scanned Files

  • libs/@hashintel/petrinaut-core/package.json
  • libs/@local/petrinaut-arch-docs/package.json
  • yarn.lock

export const GENERATED_ORDER_BASE = 1000;

const escapeTableCell = (text: string): string =>
text.replace(/\|/gu, "\\|").replace(/\n/gu, " ");
let parsed: unknown;

try {
parsed = load(match[1] ?? "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity and reachable issue identified in your code:
Line 64 has a vulnerable usage of js-yaml, introducing a high severity vulnerability.

ℹ️ Why this is reachable

A reachable issue is a real security risk because your project actually executes the vulnerable code. This issue is reachable because your code uses a certain version of js-yaml.
Affected versions of js-yaml are vulnerable to Inefficient Algorithmic Complexity. An attacker can supply a YAML document containing a large !!omap sequence, which js-yaml resolves with a linear duplicate-key scan inside its per-element loop. Resolution is therefore quadratic in the number of entries, so a modestly sized document consumes disproportionate CPU inside the load call and blocks the event loop, resulting in a denial of service.

References: GHSA

To resolve this comment:
Upgrade this dependency to at least version 4.3.1 at yarn.lock.

💬 Ignore this finding

To ignore this, reply with:

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

You can view more details on this finding in the Semgrep AppSec Platform here.

let parsed: unknown;

try {
parsed = load(match[1] ?? "");
let parsed: unknown;

try {
parsed = load(match[1] ?? "");

const fields = match[1] ?? "";
const read = (key: string): string | null => {
const found = new RegExp(`^${key}\\s*:\\s*(.+)$`, "mu").exec(fields);
@codspeed-hq

codspeed-hq Bot commented Aug 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 98 untouched benchmarks


Comparing cf/fe-1322-arch-docs-generator (69bc7b5) with main (3c6dc3d)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (866383e) during the generation of this report, so 3c6dc3d was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark results

@rust/hash-graph-benches – Integrations

policy_resolution_large

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2002 $$28.1 \mathrm{ms} \pm 176 \mathrm{μs}\left({\color{gray}0.164 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.54 \mathrm{ms} \pm 22.0 \mathrm{μs}\left({\color{gray}-2.010 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1002 $$13.7 \mathrm{ms} \pm 113 \mathrm{μs}\left({\color{gray}-1.824 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 3314 $$44.7 \mathrm{ms} \pm 405 \mathrm{μs}\left({\color{gray}-1.059 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$15.0 \mathrm{ms} \pm 136 \mathrm{μs}\left({\color{gray}-4.387 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 1527 $$25.2 \mathrm{ms} \pm 175 \mathrm{μs}\left({\color{gray}-1.958 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 2078 $$29.2 \mathrm{ms} \pm 194 \mathrm{μs}\left({\color{gray}-0.844 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.88 \mathrm{ms} \pm 29.2 \mathrm{μs}\left({\color{gray}-1.397 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 1033 $$15.1 \mathrm{ms} \pm 119 \mathrm{μs}\left({\color{gray}-0.614 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_medium

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 102 $$3.89 \mathrm{ms} \pm 22.6 \mathrm{μs}\left({\color{gray}-0.493 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.09 \mathrm{ms} \pm 19.9 \mathrm{μs}\left({\color{gray}-0.328 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 52 $$3.49 \mathrm{ms} \pm 24.4 \mathrm{μs}\left({\color{gray}0.884 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 269 $$5.33 \mathrm{ms} \pm 47.1 \mathrm{μs}\left({\color{gray}-1.260 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.64 \mathrm{ms} \pm 21.3 \mathrm{μs}\left({\color{gray}-0.278 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 108 $$4.28 \mathrm{ms} \pm 27.9 \mathrm{μs}\left({\color{gray}-1.124 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 133 $$4.59 \mathrm{ms} \pm 37.1 \mathrm{μs}\left({\color{gray}0.227 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.58 \mathrm{ms} \pm 24.7 \mathrm{μs}\left({\color{gray}-0.618 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 63 $$4.24 \mathrm{ms} \pm 35.4 \mathrm{μs}\left({\color{gray}-0.773 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_none

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2 $$2.76 \mathrm{ms} \pm 16.9 \mathrm{μs}\left({\color{gray}-2.082 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.57 \mathrm{ms} \pm 13.3 \mathrm{μs}\left({\color{gray}-2.186 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 2 $$2.76 \mathrm{ms} \pm 18.5 \mathrm{μs}\left({\color{gray}-0.634 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 8 $$3.02 \mathrm{ms} \pm 24.7 \mathrm{μs}\left({\color{gray}-1.164 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.80 \mathrm{ms} \pm 17.8 \mathrm{μs}\left({\color{gray}-1.427 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 3 $$2.99 \mathrm{ms} \pm 17.3 \mathrm{μs}\left({\color{gray}-1.682 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_small

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 52 $$3.14 \mathrm{ms} \pm 22.8 \mathrm{μs}\left({\color{gray}-2.846 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.86 \mathrm{ms} \pm 20.9 \mathrm{μs}\left({\color{gray}-2.865 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 26 $$3.06 \mathrm{ms} \pm 24.8 \mathrm{μs}\left({\color{gray}-3.247 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 94 $$3.58 \mathrm{ms} \pm 24.4 \mathrm{μs}\left({\color{gray}0.023 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.09 \mathrm{ms} \pm 21.1 \mathrm{μs}\left({\color{gray}0.264 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 27 $$3.43 \mathrm{ms} \pm 25.5 \mathrm{μs}\left({\color{gray}0.121 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 66 $$3.50 \mathrm{ms} \pm 27.6 \mathrm{μs}\left({\color{gray}-1.877 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.03 \mathrm{ms} \pm 16.7 \mathrm{μs}\left({\color{gray}-3.891 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 29 $$3.40 \mathrm{ms} \pm 23.5 \mathrm{μs}\left({\color{gray}-4.049 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_complete

Function Value Mean Flame graphs
entity_by_id;one_depth 1 entities $$44.9 \mathrm{ms} \pm 235 \mathrm{μs}\left({\color{gray}-0.251 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 10 entities $$35.1 \mathrm{ms} \pm 213 \mathrm{μs}\left({\color{gray}-0.860 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 25 entities $$38.7 \mathrm{ms} \pm 301 \mathrm{μs}\left({\color{gray}2.08 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 5 entities $$33.9 \mathrm{ms} \pm 245 \mathrm{μs}\left({\color{gray}0.039 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 50 entities $$44.3 \mathrm{ms} \pm 295 \mathrm{μs}\left({\color{gray}-1.560 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 1 entities $$52.4 \mathrm{ms} \pm 337 \mathrm{μs}\left({\color{gray}0.466 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 10 entities $$42.5 \mathrm{ms} \pm 278 \mathrm{μs}\left({\color{gray}-0.701 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 25 entities $$95.3 \mathrm{ms} \pm 672 \mathrm{μs}\left({\color{gray}-0.682 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 5 entities $$36.3 \mathrm{ms} \pm 214 \mathrm{μs}\left({\color{gray}-0.214 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 50 entities $$284 \mathrm{ms} \pm 897 \mathrm{μs}\left({\color{gray}-0.148 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 1 entities $$11.7 \mathrm{ms} \pm 75.5 \mathrm{μs}\left({\color{gray}-2.368 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 10 entities $$11.8 \mathrm{ms} \pm 59.4 \mathrm{μs}\left({\color{gray}-1.674 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 25 entities $$11.8 \mathrm{ms} \pm 71.7 \mathrm{μs}\left({\color{gray}-1.237 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 5 entities $$11.7 \mathrm{ms} \pm 76.6 \mathrm{μs}\left({\color{gray}-2.626 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 50 entities $$11.7 \mathrm{ms} \pm 76.2 \mathrm{μs}\left({\color{gray}-1.800 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_linkless

Function Value Mean Flame graphs
entity_by_id 1 entities $$11.6 \mathrm{ms} \pm 78.0 \mathrm{μs}\left({\color{gray}-1.938 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$11.8 \mathrm{ms} \pm 65.2 \mathrm{μs}\left({\color{gray}-1.544 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$11.9 \mathrm{ms} \pm 80.6 \mathrm{μs}\left({\color{gray}-0.131 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$11.8 \mathrm{ms} \pm 84.3 \mathrm{μs}\left({\color{gray}-3.046 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10000 entities $$12.0 \mathrm{ms} \pm 69.0 \mathrm{μs}\left({\color{gray}-1.714 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity

Function Value Mean Flame graphs
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1 $$12.2 \mathrm{ms} \pm 83.4 \mathrm{μs}\left({\color{gray}0.328 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$12.4 \mathrm{ms} \pm 90.5 \mathrm{μs}\left({\color{gray}-0.174 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$12.1 \mathrm{ms} \pm 71.5 \mathrm{μs}\left({\color{gray}-3.791 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$12.4 \mathrm{ms} \pm 76.7 \mathrm{μs}\left({\color{gray}1.44 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$12.2 \mathrm{ms} \pm 73.7 \mathrm{μs}\left({\color{gray}-1.092 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$12.4 \mathrm{ms} \pm 80.7 \mathrm{μs}\left({\color{gray}0.863 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$12.2 \mathrm{ms} \pm 71.7 \mathrm{μs}\left({\color{gray}-0.748 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$12.2 \mathrm{ms} \pm 74.0 \mathrm{μs}\left({\color{gray}-0.217 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$12.4 \mathrm{ms} \pm 72.6 \mathrm{μs}\left({\color{gray}0.327 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity_type

Function Value Mean Flame graphs
get_entity_type_by_id Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba $$9.11 \mathrm{ms} \pm 74.8 \mathrm{μs}\left({\color{gray}0.552 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
entity_by_property traversal_paths=0 0 $$61.4 \mathrm{ms} \pm 417 \mathrm{μs}\left({\color{gray}4.30 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$118 \mathrm{ms} \pm 771 \mathrm{μs}\left({\color{gray}4.57 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$69.7 \mathrm{ms} \pm 559 \mathrm{μs}\left({\color{gray}3.63 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$79.5 \mathrm{ms} \pm 501 \mathrm{μs}\left({\color{gray}3.66 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$89.4 \mathrm{ms} \pm 498 \mathrm{μs}\left({\color{gray}4.53 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$95.4 \mathrm{ms} \pm 716 \mathrm{μs}\left({\color{gray}4.81 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=0 0 $$46.5 \mathrm{ms} \pm 287 \mathrm{μs}\left({\color{gray}3.12 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$74.7 \mathrm{ms} \pm 492 \mathrm{μs}\left({\color{gray}-0.060 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$52.1 \mathrm{ms} \pm 391 \mathrm{μs}\left({\color{gray}0.567 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$61.2 \mathrm{ms} \pm 349 \mathrm{μs}\left({\color{gray}0.664 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$64.0 \mathrm{ms} \pm 396 \mathrm{μs}\left({\color{gray}0.258 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$63.5 \mathrm{ms} \pm 482 \mathrm{μs}\left({\color{gray}-0.050 \mathrm{\%}}\right) $$

scenarios

Function Value Mean Flame graphs
full_test query-limited $$123 \mathrm{ms} \pm 843 \mathrm{μs}\left({\color{gray}2.52 \mathrm{\%}}\right) $$ Flame Graph
full_test query-unlimited $$134 \mathrm{ms} \pm 778 \mathrm{μs}\left({\color{gray}2.36 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-limited $$20.5 \mathrm{ms} \pm 170 \mathrm{μs}\left({\color{gray}2.18 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-unlimited $$546 \mathrm{ms} \pm 1.38 \mathrm{ms}\left({\color{gray}0.884 \mathrm{\%}}\right) $$ Flame Graph

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team type/legal Owned by the @legal team

Development

Successfully merging this pull request may close these issues.

2 participants