Repository navigation
docs(spec): RuntimeAuthoringIssueSchema.path states the name-keying rule for every collection-resident write type - #22784
Conversation
The path describe named object / permission / book as the only collection-resident write types and called every other write type positional; a dataset write's findings are name-keyed as well. State the rule (a write type whose collection the gate's per-write snapshot fills with the tenant's other stored items is keyed by name) and name where the derived set lives, instead of a closed list that drifts. Describe text only; the accept set does not move. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
Generated by `pnpm --filter @objectstack/spec gen:docs`, the one artifact `check:generated` proved stale. Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4a7f92f9982ba78ea95d439e6161626bb17ce1f8 && git checkout 4a7f92f9982ba78ea95d439e6161626bb17ce1f8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e84aeb36ce14169a633670f14ce8280fc998e2b9 498abe794e4e7f51645c75737cc9716e6d67c126 && git checkout -B drift-repro e84aeb36ce14169a633670f14ce8280fc998e2b9 && git merge --no-ff 498abe794e4e7f51645c75737cc9716e6d67c126
node scripts/docs-audit/affected-docs.mjs --json e84aeb36ce14169a633670f14ce8280fc998e2b9 |
Contract reviewServed-tier: Read-only review of PR #22784 at the head above, for card #22759 (filed under ruling ① Derived judgments1. Describe only — holds. The net diff is 3 files, +35 / −12. In 2. The text is true against the gate on
No sentence is false and none claims more than the code does for any path a caller can receive. The old text's unqualified "never by an array index" was false (the fallback existed and was undocumented); the new text states the fallback. 3. No new closed list — holds. The four members are never enumerated. The only write types named are 4. The changeset — holds. 5. CI on the head. Read at 2026-10-11T06:47:57Z: 32 check-runs, none failed, none cancelled. Against the seven required contexts of the ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Landing pre-checks at
|
Fixes #22759
Clause-②: no
What changed
RuntimeAuthoringIssueSchema.path(packages/spec/src/api/protocol.zod.ts) is the describe of thepathon a runtime publish-gate finding, on both the 422issues[]and the 2xxadvisories[]. It namedobject/permission/bookas the only collection-resident write types and called every other write type positional. Adatasetwrite's findings have been name-keyed too since #19143.The describe now states the rule instead of a list, per triage's grade
6105640249under ruling6104584601on #22636:[0]stays positional;@objectstack/lint's runtime gate,NAME_KEYED_STACK_KEYS, the collections it carries as resolution context that a gated write type also lands in. There is no import of lint into spec. The only members the text names are two examples markedfor example(object,dataset).The true sentences are kept: the submitted-body framing, the empty path, and nested positions staying positional. One sentence is added because "never by an array index" was not true without it:
nameKeyFindingPathkeeps the index when the entry's name cannot be spliced into a dotted path.Describe text only. No schema shape, accepted value, default or type moves.
content/docs/references/api/protocol.mdxis regenerated bygen:docs, the one artifactcheck:generatedproved stale. The changeset.changeset/22759-runtime-authoring-issue-path-rule.mdis apatchfor@objectstack/spec.Readings (at
origin/maine84aeb36ce, the merge base; origin/main has not moved since)The old text.
protocol.zod.ts:560–:571: "For the collection-resident write types (object/permission/book) the TOP-LEVEL collection entry is keyed by NAME … Every other write type is the sole member of its own collection, so its[0]is trivially stable and stays positional (flows[0]...)".The derived set.
packages/lint/src/runtime-gate.ts:905derivesNAME_KEYED_STACK_KEYS=deriveNameKeyedStackKeys(CONTEXT_STACK_KEYS, WRITTEN_STACK_KEYS)(:796,:550,:845). Ate84aeb36ceit isobjects,permissions,books,datasets. The write types that map into those keys (TYPE_TO_STACK_KEY,:94;dataset: 'datasets'at:146) areobject,permission,bookanddataset.pageis NOT in the set today. Thepackages/lintchangelog (CHANGELOG.md:5287) recordspagesjoining it, but it left again whenpagesleft the context withvalidateViewPageRefs.runtime-gate.derived-name-keys.test.ts:53–:68pins['objects', 'permissions', 'books', 'datasets'], and its comment records thepagesexit.runtime-gate.dataset-writes.test.ts:310expectsdatasets.acme_invoice_metrics.dimensions[0].field, and:313refusesdatasets[N].runtime-gate.derived-name-keys.test.ts:85asks, for each context collection, that it is name-keyed exactly when a write type maps into it. Both files ran green here: 2 files, 29 tests.Is "every other write type is the sole member of its own collection" still true? Yes, for all 17 gated write types. They are the union of
runtimeTypesoverAUTHORING_RULES. Measured by drivingbuildRuntimeWriteSnapshotsandnameKeyFindingPathfrompackages/lint/src, with every context collection filled:objectobjectspermissionpermissionsbookbooksdatasetdatasetsaction,app,dashboard,datasource,email_template,flow,hook,mapping,page,position,report,seed(keydata),viewagentis mapped inTYPE_TO_STACK_KEYbut no rule declares it inruntimeTypes, so the gate dispatches nothing for it. Its snapshot collection would also hold one member.The family (Zone 2 item 4)
git grepoverpackages/specandcontent/docsfor the closed list (object/permission/book), the "sole member" sentence, "collection-resident", "trivially stable" and theobjects.acme_invoiceexample:packages/spec, edited: one hit, this describe (protocol.zod.ts:560–:571).packages/spec, not edited:packages/spec/CHANGELOG.md:31982, the released entry for commit def0d3e. It is release-owned and accurate about what shipped then.content/docs/references/api/protocol.mdx. The describe appears three times (:2371,:2597,:2651), all regenerated.packages/spec, reported only, nothing wrong:packages/lint/src/runtime-gate.ts:612–:617already namesdatasetbeside the other three.packages/lint/src/validate-security-posture.runtime-surface.test.ts:168is a historical note about the card that declared those three types for that rule block.authoring-rules.ts:202,data-model-rules.ts:66andscripts/bench/runtime-publish-gate.bench.mts:495useobjects.…as an example, not a list.The pin: not added
No spec test holds the describe free of a closed list. The new text carries no enumeration whose membership can drift. Its two members are marked examples, and both stay true while
objectsanddatasetsare context collections a write lands in. The set itself is already pinned where it is derived (runtime-gate.derived-name-keys.test.ts). A spec-side test could only assert that one particular string is absent, which guards against re-adding exactly this list and nothing else. A cross-package pin would have to enumerate the members, which is the closed list the ruling refuses.Verification (head
498abe794e)pnpm --filter @objectstack/spec build: exit 0. Thencheck:generatednamedcheck:docsstale.gen:docsrewrote onlyapi/protocol.mdx(+3/−3).pnpm --filter @objectstack/spec check:generated: "✓ All 14 generated artifacts are up to date".check:docs: "✅ 225 generated files in sync with packages/spec".vitest run --project localon@objectstack/spec: 645 files passed, 19222 tests passed, 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0.check:test-typecheckOK.dispatch-gates --commands --repo objectstack-ai/objectstack(3 paths) printed 103 commands. The full tally is in the report on the card.check:dual-build-cjs-loads, by dispatch (no whole-workspace build).check:skill-examplesneedsclient-reactbuilt;check:lean-entry-closureneedsobjectqlbuilt.Acceptance notes
.describe()string, its generated rendering and a changeset. No governed surface is touched.Generated by Claude Code