docs: the Extending-the-canvas contract — how it loads, what's required, what you may reach (goal 0211) - #433
Merged
Conversation
…ed, what you may reach (goal 0211) One reference page, userdocs/reference/extending-the-canvas.md: how a canvas noun's tools/<id>Tool.ts loads (glob discovery + the declaration-vs-code split, VS Code's contributes/API precedent named directly), the AtlasToolShape field table (generated), the conformance suite that IS the contract test, and which platform APIs a noun's runtime code may use vs. what it may not reach — with the actual enforcement (dependency-cruiser boundary, review-only, or "no RPC exists to call") named per line rather than implying a sandbox compiled-in TypeScript doesn't have. Generation mechanism: Go docsgen has no TypeScript parser, so extracting the field table straight from AtlasToolShape's own .ts type was disproportionate. Instead: a small committed JSON (atlasNounDeclarationFields.json) is the generation source docsgen reads to render the table into one marked region of the page (go generate ./internal/docsgen, freshness-checked by TestExtendingCanvasPage_NounFieldTableMatchesCommitted); a `satisfies Record<keyof AtlasToolShape, true>`-checked literal inside atlasNounDeclarationFields.test.ts enforces on the TypeScript side that the JSON's field set can never drift from the real registry type (TypeScript's own excess/missing-property checks only apply to a fresh literal, so the check lives in the test file, not on the JSON import itself). The rest of the page is hand-authored prose, honestly not claimed as generated. Named-tax audit (goal 0211's own teeth): five places today's board-object rendering path already reaches past the documented surface, all clustered around AtlasBoardObjectNode.tsx/ atlasBuildBoardObjectNodes.ts/locales/en/atlas/shared.json -- diagram's missing registry entry, the unregistered object.Kind if/else content dispatch, per-Kind aria-labels leaking into the shared locale namespace, a hand-maintained z-order table with a silent default, and diagram's own dragBand/resizable gap. Full detail and the deferral (0211 slice 2, triggered by the first diagram-shaped noun or v1 planning) recorded in docs/goals/0211-extension-tiers.md's new "Named tax" section; SPEC.md §9.5 now points at the page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012im1JxQQV2ahnXzZDdVmZq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
One reference page —
userdocs/reference/extending-the-canvas.md— the goal 0211 slice 1 deliverable: "here is how you load it, what is required, and what platform API and services exist." Three sections plus a stability paragraph, exactly per the goal's Sharpened scope.Generation mechanism
Go's
internal/docsgenhas no TypeScript parser, so extracting the "What is required" field table straight fromAtlasToolShape's own.tstype was disproportionate. Chosen instead: a small committed JSON (frontend/src/atlas/atlasNounDeclarationFields.json) is the generation sourcedocsgen.GenerateNounFieldTablereads to render the table into one marked region of the page (go generate ./internal/docsgen, freshness-checked byTestExtendingCanvasPage_NounFieldTableMatchesCommitted, mirroring the existingTestUserDocs_MatchCommittedshape). Asatisfies Record<keyof AtlasToolShape, true>-checked literal insideatlasNounDeclarationFields.test.tscloses the other half of the freshness chain on the TypeScript side — the JSON's field set can never silently drift from the real registry type, because TypeScript's excess/missing-property checks only apply to a fresh object literal (not an imported JSON binding), so the check lives in that literal, and a vitest assertion compares the JSON's keys against it. The rest of the page (sections 1 and 3, the stability paragraph) is hand-authored prose, not claimed as generated.Named-tax audit (the goal's teeth)
Found while writing section 3 against the actual code, all clustered around the shared
atlas-objectrendering path:diagramhas no registered descriptor at all (notools/diagramTool.ts, no identity entry) — the entry the goal named first.AtlasBoardObjectNode.tsx's content dispatch is an unregisteredobject.Kindif/else chain — the registry has norenderContenthook.locales/en/atlas/shared.json, not each noun's own locale file — breaks the "each noun owns its own locale file" model section 1 documents.atlasBuildBoardObjectNodes.ts'sOBJECT_Z_INDEXis a hand-maintained string-keyed table with a silent?? 0default for an omitted noun.dragBand/resizablegap, already partly documented inatlasNounRegistry.ts's own comments — same root cause as build(deps): Bump golang.org/x/oauth2 from 0.35.0 to 0.36.0 in the go-dependencies group #1.Full detail appended to
docs/goals/0211-extension-tiers.md's new "Named tax" section (committed separately to the nested docs repo, ahead of this PR). Deferred to a future 0211 slice, triggered by the first diagram-shaped noun or v1 planning — not built here.What stays hand-authored
Sections 1 ("How it loads") and 3 ("What platform APIs exist / may not reach") plus the stability paragraph — reviewed prose, not generated, and the page says so nowhere implies otherwise. Only the field table in section 2 carries the generated-region markers and the doc-freshness promise.
Test plan
go test ./internal/docsgen/...— green, including the new freshness testgo generate ./internal/docsgenproduces a byte-identical committed filenpx vitest run(targeted + full suite via lefthook) — greennpx tsc --noEmit,npm run boundaries(dependency-cruiser) — green🤖 Generated with Claude Code
https://claude.ai/code/session_012im1JxQQV2ahnXzZDdVmZq