FE-1322: Generate Petrinaut architecture docs from in-code annotations - #9204
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Dependency ReviewThe following issues were found:
Vulnerabilitieslibs/@local/petrinaut-arch-docs/package.json
License Issuesyarn.lock
OpenSSF ScorecardScorecard details
Scanned Files
|
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1002 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1527 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 108 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 27 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | 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
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |
69bc7b5 to
0b390a8
Compare
0b390a8 to
1ba0b4c
Compare
1ba0b4c to
76514eb
Compare
76514eb to
82427e7
Compare
PR SummaryCursor Bugbot is generating a summary for commit bb21633. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds an annotation-driven Petrinaut architecture documentation generator, replacing the manually maintained dependency diagrams.
Changes:
- Extracts layers from source annotations and README frontmatter, validating them against imports and architecture rules.
- Generates Markdown/MDX, JSON manifests, and D2/SVG diagrams with comprehensive tests.
- Annotates Petrinaut packages and removes the legacy diagram generator.
Reviewed changes
Copilot reviewed 79 out of 82 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
yarn.lock |
Moves dependency-cruiser to the new package. |
libs/@local/petrinaut-arch-docs/turbo.json |
Defines documentation tasks and outputs. |
libs/@local/petrinaut-arch-docs/tsconfig.json |
Configures TypeScript compilation. |
libs/@local/petrinaut-arch-docs/src/tags.ts |
Scans source annotations. |
libs/@local/petrinaut-arch-docs/src/tags.test.ts |
Tests annotation parsing. |
libs/@local/petrinaut-arch-docs/src/scope.ts |
Defines source inclusion rules. |
libs/@local/petrinaut-arch-docs/src/scope.test.ts |
Tests source scope patterns. |
libs/@local/petrinaut-arch-docs/src/paths.ts |
Normalizes repository paths. |
libs/@local/petrinaut-arch-docs/src/model.ts |
Defines the architecture schema. |
libs/@local/petrinaut-arch-docs/src/index.ts |
Exports the bundle manifest type. |
libs/@local/petrinaut-arch-docs/src/graph.ts |
Builds layer dependency edges. |
libs/@local/petrinaut-arch-docs/src/frontmatter.ts |
Parses README declarations. |
libs/@local/petrinaut-arch-docs/src/frontmatter.test.ts |
Tests declaration frontmatter. |
libs/@local/petrinaut-arch-docs/src/extract.ts |
Assigns source files to layers. |
libs/@local/petrinaut-arch-docs/src/extract.test.ts |
Tests layer extraction. |
libs/@local/petrinaut-arch-docs/src/emit/mdx.ts |
Generates architecture pages. |
libs/@local/petrinaut-arch-docs/src/emit/mdx.test.ts |
Tests generated link resolution. |
libs/@local/petrinaut-arch-docs/src/emit/d2.ts |
Generates and renders diagrams. |
libs/@local/petrinaut-arch-docs/src/emit/d2.test.ts |
Tests neighborhood diagrams. |
libs/@local/petrinaut-arch-docs/src/emit/bundle-outputs.ts |
Generates bundle indexes. |
libs/@local/petrinaut-arch-docs/src/diagnostics.ts |
Defines diagnostics. |
libs/@local/petrinaut-arch-docs/src/content.ts |
Collects authored documentation. |
libs/@local/petrinaut-arch-docs/src/cli.ts |
Implements build and check commands. |
libs/@local/petrinaut-arch-docs/src/check.ts |
Enforces architecture invariants. |
libs/@local/petrinaut-arch-docs/src/check.test.ts |
Tests architecture checks. |
libs/@local/petrinaut-arch-docs/src/build.ts |
Orchestrates bundle generation. |
libs/@local/petrinaut-arch-docs/README.md |
Documents the generator. |
libs/@local/petrinaut-arch-docs/package.json |
Declares package scripts and dependencies. |
libs/@local/petrinaut-arch-docs/LICENSE.md |
Adds license summary. |
libs/@local/petrinaut-arch-docs/LICENSE-MIT.md |
Adds MIT license. |
libs/@local/petrinaut-arch-docs/LICENSE-APACHE.md |
Adds Apache license. |
libs/@local/petrinaut-arch-docs/dependency-cruiser.tsconfig.json |
Configures import resolution. |
libs/@local/petrinaut-arch-docs/architecture.config.ts |
Configures packages and rules. |
libs/@local/petrinaut-arch-docs/.oxlintrc.json |
Configures linting. |
libs/@local/petrinaut-arch-docs/.gitignore |
Ignores generated bundles. |
libs/@hashintel/petrinaut/src/ui/views/SDCPN/sdcpn-view.tsx |
Declares the canvas layer. |
libs/@hashintel/petrinaut/src/ui/views/README.md |
Declares the views layer. |
libs/@hashintel/petrinaut/src/ui/views/Editor/editor-view.tsx |
Declares the editor layer. |
libs/@hashintel/petrinaut/src/ui/monaco/provider.tsx |
Declares the Monaco layer. |
libs/@hashintel/petrinaut/src/ui/index.ts |
Declares the UI root layer. |
libs/@hashintel/petrinaut/src/react/state/README.md |
Declares the React state layer. |
libs/@hashintel/petrinaut/src/react/simulation/provider.tsx |
Declares the simulation provider layer. |
libs/@hashintel/petrinaut/src/react/playback/README.md |
Declares the playback layer. |
libs/@hashintel/petrinaut/src/react/lsp/provider.tsx |
Declares the React LSP layer. |
libs/@hashintel/petrinaut/src/react/index.ts |
Declares the React root layer. |
libs/@hashintel/petrinaut/src/react/hooks/index.ts |
Declares the hooks layer. |
libs/@hashintel/petrinaut/src/react/experiments/provider.tsx |
Declares the experiments layer. |
libs/@hashintel/petrinaut/src/react/execution-frame/provider.tsx |
Declares the execution-frame layer. |
libs/@hashintel/petrinaut/src/main.ts |
Declares the host-facing layer. |
libs/@hashintel/petrinaut/ARCHITECTURE.md |
Updates architecture documentation links. |
libs/@hashintel/petrinaut-core/src/workers/README.md |
Declares the worker-entry layer. |
libs/@hashintel/petrinaut-core/src/validation/README.md |
Declares the validation layer. |
libs/@hashintel/petrinaut-core/src/types/sdcpn.ts |
Declares the types layer. |
libs/@hashintel/petrinaut-core/src/store/index.ts |
Declares the store layer. |
libs/@hashintel/petrinaut-core/src/simulation/worker/README.md |
Declares the simulation worker layer. |
libs/@hashintel/petrinaut-core/src/simulation/runtime/simulation.ts |
Declares the runtime layer. |
libs/@hashintel/petrinaut-core/src/simulation/README.md |
Declares and documents simulation. |
libs/@hashintel/petrinaut-core/src/simulation/monte-carlo/README.md |
Declares the Monte Carlo layer. |
libs/@hashintel/petrinaut-core/src/simulation/frames/frame-reader.ts |
Declares the frames layer. |
libs/@hashintel/petrinaut-core/src/simulation/engine/README.md |
Declares the engine layer. |
libs/@hashintel/petrinaut-core/src/simulation/authoring/sandbox.ts |
Declares the authoring layer. |
libs/@hashintel/petrinaut-core/src/simulation/ARCHITECTURE.md |
Updates deep-dive documentation links. |
libs/@hashintel/petrinaut-core/src/schemas/entity-schemas.ts |
Declares the schemas layer. |
libs/@hashintel/petrinaut-core/src/playback/index.ts |
Declares core playback. |
libs/@hashintel/petrinaut-core/src/lsp/worker/language-server.worker.ts |
Declares the LSP worker layer. |
libs/@hashintel/petrinaut-core/src/lsp/index.ts |
Declares the LSP layer. |
libs/@hashintel/petrinaut-core/src/layout/index.ts |
Declares the layout layer. |
libs/@hashintel/petrinaut-core/src/index.ts |
Declares the core root layer. |
libs/@hashintel/petrinaut-core/src/hir/README.md |
Declares the HIR layer. |
libs/@hashintel/petrinaut-core/src/handle/index.ts |
Declares the handle layer. |
libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.ts |
Declares the file-format layer. |
libs/@hashintel/petrinaut-core/src/examples/index.ts |
Declares the examples layer. |
libs/@hashintel/petrinaut-core/src/clipboard/paste.ts |
Declares the clipboard layer. |
libs/@hashintel/petrinaut-core/src/actual-mode/README.md |
Declares the actual-mode layer. |
libs/@hashintel/petrinaut-core/scripts/generate-dependency-diagrams.mjs |
Removes the legacy generator. |
libs/@hashintel/petrinaut-core/package.json |
Removes the legacy script and dependency. |
libs/@hashintel/petrinaut-core/docs/architecture/petrinaut-dependencies.d2 |
Removes an obsolete generated diagram. |
libs/@hashintel/petrinaut-core/docs/architecture/petrinaut-compilation-dependencies.d2 |
Removes an obsolete compilation diagram. |
libs/@hashintel/petrinaut-core/docs/architecture/dependency-diagrams.md |
Removes obsolete diagram instructions. |
AGENTS.md |
Documents architecture annotation requirements. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
bb21633 to
f056856
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f056856. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 79 out of 82 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
libs/@local/petrinaut-arch-docs/src/extract.ts:261
- A known
@rolewithout@layerRootis currently ignored. In a folder already covered by an ancestor, a half-written child declaration therefore passes all checks and silently leaves those files in the parent layer—the same coverage-loss case thatparseFrontmatterexplicitly rejects for README declarations. Report the orphaned role before handling a complete declaration.
libs/@local/petrinaut-arch-docs/src/extract.ts:230 - This parses every Markdown file as a layer declaration, although the declared contract limits folder declarations to
README.md. For example, adding ordinary frontmatter withlayer/roletoBUFFER_ABI.mdwould unexpectedly claim its whole folder (or conflict with the real README) instead of remaining a reference. Restrict declaration parsing to files namedREADME.md; pass 2 can continue collecting other Markdown files as references.
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.
f056856 to
490efd4
Compare

🌟 What is the purpose of this PR?
Generates the Petrinaut architecture docs from annotations in the source, and fails the build when an annotation stops matching the code.
This replaces
petrinaut-core/scripts/generate-dependency-diagrams.mjs. That script held the layer mapping as ~180 lines ofif (path.startsWith(...)), and put anything it did not match into a default bucket without reporting it. It also hard-coded 7 ofpetrinaut-core's 10 entry points, so imports through./ai,./optimizationand./compiled-modelnever appeared in the diagrams.The output is a bundle of files, not a website, so it can be rendered locally, embedded in
hash.dev, or read as plain text.First of three PRs. #9205 moves the hand-written architecture prose into the bundle. #9206 adds a site that renders it.
🔍 What does this change?
Declaring a layer
Two tags on a folder's main file:
A folder
README.mdcan declare the same two things in frontmatter, and its prose becomes that layer's page.Files with no annotation belong to the nearest ancestor folder that declares one. 37 declarations cover 413 files. The layer sizes, the 178 edges between layers, and the parent/child tree are read from the TypeScript import graph using dependency-cruiser.
There are only two tags. Both are needed to put a layer in the graph and label it. A third would state something the generator cannot check against the code.
Output
Written to
bundle/, which is git-ignored:architecture.jsonarchitecture.mdmanifest.jsonpages/**.mdxdiagrams/**.{d2,svg}Generated MDX is YAML frontmatter plus CommonMark, with no JSX and no imports.
Diagrams
The build generates 44 D2 files from the model and renders them to SVG. A box exists because a layer does, an arrow because an import does:
Arrow labels are sums of file-level imports. A neighbourhood draws at most 12 neighbours:
core.typeshas 18, so 12 are drawn and the other 6 become one node carrying their combined count.What fails the build
doc:architecturerefuses to write, andlint:arch-docsexits 1, on:exportssubpath with no source file behind itarchitecture.config.tsFour rules are configured. The main one is that
reactmust not importui. It holds today: 0 imports, against 235 in the other direction.Four of these checks exist because the failure would otherwise remove coverage instead of reporting an error. An
exportssubpath that stops resolving, or a rule with a typo, leaves a build that passes while checking less than before.🔗 Related links
hash.dev/docs/petrinaut. This PR produces the files that work needs. It publishes nothing.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
@hashintel/petrinautandpetrinaut-corechange only in comments, READMEs, and the removal of a private script with itsdependency-cruiserdevDependency. No runtime code, types or exports change.📜 Does this require a change to the docs?
The user-facing guide (
libs/@hashintel/petrinaut/docs/) is untouched, since no UI or behaviour changed.AGENTS.mdgains a section on declaring layers and what the build enforces.🕸️ Does this require a change to the Turbo Graph?
turbo.json's have been updated to reflect thisAdds
@local/petrinaut-arch-docs#doc:architecture. It is uncached: Turborepo hashes a package plus its dependencies' task outputs, and this task reads source comments inpetrinautandpetrinaut-core, which are nobody's output. A cached bundle would survive an annotation change and stop matching the code. The task declaresoutputs, so consumers depend on the task rather than on the directory existing.Removes
doc:dependency-diagramfrompetrinaut-core.core.typesis the example: four separate parents across two packages depend on it, so it sits undercorewhile being used everywhere.@roleis not verified. The structure is checked against the import graph. The one-line description is prose, and nothing confirms it is accurate.uirenders as "Ui". Change the id if a name reads badly. A separate display-name tag was considered and left out.petrinaut-cli,petrinaut-website,petrinaut-opt. A TypeScript package needs a config entry and one root declaration. The Python app needs an extractor that does not exist yet, and configuring it without one is an error rather than a silent skip.lint:tscandbuildboth depend ondoc:architecture. Until then, runlint:arch-docslocally. It takes about 2.5 seconds and does not needd2.mise run fix:package-jsonneeds a nightly Cargo feature and could not run locally, sopackage.jsonkey ordering was checked by hand against the sorter's field list.🐾 Next steps
🛡 What tests cover this?
75 tests across 7 files in
@local/petrinaut-arch-docs:tags.test.ts: tag grammar, wrapped values, duplicates, miscased tags, tags named in prose.frontmatter.test.ts: declarations, malformed YAML, half-written declarations, CRLF, unknown keys, and the YAML cases that a line-based parser got wrong.extract.test.ts: inheritance through folders that declare nothing, uncovered files, ordering.check.test.ts: each check in both directions, so it fires when broken and stays quiet when not.scope.test.ts: source roots, path escaping, and the exclusion pattern the extractor and the graph share.emit/d2.test.ts: neighbourhood diagrams, edge directions, and the 12-neighbour cap.emit/mdx.test.ts: link resolution at different depths, fragments, unresolved targets.Existing suites are unaffected: 842 (
petrinaut-core), 187 (petrinaut).❓ How to test this?
yarn workspace @local/petrinaut-arch-docs lint:arch-docs # 0 errors, ~2.5s turbo run doc:architecture --filter @local/petrinaut-arch-docsRead
bundle/architecture.mdfor the whole model, and openbundle/diagrams/around/core.types.svg, which is the diagram that hits the 12-neighbour cap.To see the checks fire: empty a
role:in a declaring README, add a source file in a folder no declaration covers, or add a rule toarchitecture.config.tsnaming a layer that does not exist. Each failslint:arch-docsand names the file.