Skip to content

docs(spec): four conversion summaries state their decision in words instead of a deleted tracker number (stage 9) - #20718

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20234-conversion-summaries-form-d
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20234-conversion-summaries-form-d

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20234

Clause-②: no

Stage 9 of the dead-citation sweep. Four conversion summary literals in packages/spec/src/conversions/registry.ts cited tracker numbers that no longer exist on the board. A summary is author-shown: os migrate meta prints it, and gen:spec-changes / gen:upgrade-guide project it into packages/spec/spec-changes.json and docs/protocol-upgrade-guide.md. So each now takes ruling C+D form D: the decision in words, no number to look up. One stale comment clause at :2100 is rewritten to be true (form C). No conversion id, surface, retirement state, transform or code token moves.

What changes

conversion citation removed decision now stated in words decided by
datasource-driver-mongo-to-mongodb (#6345) appended: "so the id that selects a driver and the id that selects its config contract are one string with no mapping between them" commit e2798fa (the entry's own docblock states this rationale)
translation-component-submit-label-removed #10926 — "retired rather than re-anchored", and appended: "and re-anchoring the key there would only have added a second place to translate one word" commit d173125 (the maintainer chose retire over re-anchor)
mapping-lookup-params-removed #10329, appended: "Implementing them instead would have added a second reference-resolution dialect to the import path" commit 15d58db
connector-error-mapping-removed #14676, appended: "deleting the block resolves that collision without a rename" commit 13c48c2

The other number in the submitLabel literal, #9249, is live (200) and stays. ADR-0049 stays in the two literals that carried it.

The comment at :2100 said the flag "does not mean what its name and every docblock around it say it means". That has been false since commit 29dd1a6, which rewrote the flag's own docblock to state its authoring-only reach. It now reads: "does not mean what its name says: its own docblock (RetiredConversionState in types.ts) has stated the authoring-only reach since commit 29dd1a6." Evidence: git log -S"its reach is the authoring surface" -- packages/spec/src/conversions/types.ts answers 29dd1a6 only; rev-parse --disambiguate count 1; merge-base --is-ancestor exit 0 against the base and against HEAD; GET /repos/objectstack-ai/objectstack/commits/29dd1a6dd answers 200 with the full sha, and the 4-character control 29dd answers 422.

Verification

Base e4e5222b7b; head 7ec0ef4c84 (the base plus three commits, then origin/main 5757463712 merged through scripts/pm/os-regen-merge.sh; that merge touched no path under packages/spec or scripts, and the post-merge regeneration produced no diff).

Deadness. REST GET issues/N, no redirects, with lit control #20234 and dead control #8710 at the start and end of each pass. At base, registry.ts has 109 string sites holding 79 distinct unqualified numbers of 100 or more: 75 answer 200 and 4 answer 404, exactly #6345, #10926, #10329, #14676. At head: 105 string sites, 75 numbers, all 200. Controls 4/4 and 4/4 each pass.

The gate's census (node scripts/check-issue-citations.mjs --census --json, board enumerated in full):

base e4e5222 (19:45Z, frontier #20708) head 7ec0ef4 (20:54Z, frontier #20717)
registry.ts findings 0 0
packages/spec/src findings 235 (22 numbers: migrations/ 233, data/api-derivation.ts 1, identity/identity.zod.ts 1) 235, the identical site set
sites of the four numbers elsewhere #6345 1, #10926 1, #10329 1, #14676 11 (release pages and migrations/) the same

Repo-wide allocated-but-absent goes 1222 to 1195. That moved with main's merged commits, not with this diff.

Hypothesis falsified: neither count moves, by construction. The citation gate reads source through the comment-prose projection, which blanks string literals, so these four sites were never in its census. scripts/doc-authoring-prose-id.baseline.json excludes packages/spec (PACKAGES_PROSE_EXCLUDED), and check:doc-authoring's spec leg sweeps message, strictObject, tombstone, describe and function-built positions, not a conversion summary: it was green at base with all four numbers present. At head it prints "16827 customer-facing string(s) across 1186 spec sources clean" and "808 pinned site(s) across 229 file(s) ... no growth, no burn-down unrecorded".

Residue. A scratch instrument over TypeScript 6.0.3 builds (a) the file with every comment token cut and everything else kept byte for byte and (b) the leaf-token stream, with the four summary initializers masked by conversion id. Base vs head: IDENTICAL (residue sha256 prefix 3352f29d13665120 on both sides; 47,810 leaf tokens and 842 comment tokens cut on both). Controls mutate the head text in memory. A comment insertion stays IDENTICAL. Each of these DIFFERS: another summary literal edited, a template literal, a regex literal, an appended declaration, a masked conversion's id, and its surface (7/7). Unmasking the mongo summary alone DIFFERS (exit 1). The instrument evaluates each literal's value at base and at head: only the citation and the added clause differ. Line balance for registry.ts: +16/-11 (12,678 to 12,683 lines).

Regeneration. gen:spec-changes and gen:upgrade-guide, never by hand. Exactly 3 lines move, all #6345's copy: spec-changes.json :229 and :1121, docs/protocol-upgrade-guide.md :188. Word diff on each: used (#6345) becomes used, so the id that selects a driver and the id that selects its config contract are one string with no mapping between them. check:spec-changes and check:upgrade-guide exit 1 before regeneration and 0 after.

Tests. Spec sources at 31a296909f are identical to head.

  • Build under os-verify-lock: turbo run build --concurrency=2 over ./packages/* and ./packages/*/*, 71 of 71 (pre-merge and again at head).
  • check:generated: all 15 generated artifacts are up to date.
  • Targeted run (src/conversions src/migrations src/integration src/data, the translation and cron retirement files, the two step-18 merge scripts): 132 files, 4,732 passed.
  • The whole local project: 575 files, 16,960 passed and 1 todo.
  • repo project: 40 of 44 files, 645 passed. The other four (build-schemas-check-mode, def-key-collisions, publish-smoke-boot-failure, publish-smoke-port-collision) hit timeout 560 (exit 124). NOT MEASURED, left to CI. None reads summary text.
  • pnpm --filter @objectstack/spec typecheck: exit 0.

Gates at 7ec0ef4. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derives 78, the same list as before the merge. All 78 exit 0, check:issue-citations, check:doc-authoring, check:generated and check:upgrade-guide among them. --ran reports 78 derived, 78 run, 0 NOT-MEASURED, 0 UNRUN. Also run: check:spec-changes and check:authorable-surface, both exit 0.

Lint, a proven narrowing. eslint --no-inline-config --format json on registry.ts: 1 file, 0 errors, 0 warnings. isPathIgnored is false. eslint.config.mjs:327-328 states that type-aware linting is never enabled, so a text edit cannot move an untouched file's verdict. The repo-wide pnpm lint is CI's run.

Changeset. patch, with a standalone Clause-②: no line. files[] ships dist and spec-changes.json. Each new phrase is in 6 dist files, and the four old citation spellings are in 0. Control: the unchanged rateLimitConfig summary is in 6.

Merge probes. From a bare --shared clone with no merge driver, deleted afterwards: merge-tree of this head with #20637's branch head a2abb8c78a exits 0, and with #20662's head 3fcedfb564 exits 0.

Acceptance notes


Generated by Claude Code

…that cited deleted tracker numbers (stage 9)

Ruling C+D form D: a conversion `summary` is author-shown (`os migrate meta`,
spec-changes.json, the protocol upgrade guide), so it carries the lesson, not a
number to look up. The four numbers removed answer 404 on the board; the lit
reference in the submitLabel summary stays. No id, surface, verdict or code
token moves.

The comment clause at the withdrawn field-required-notnull-explicit entry said
every docblock around retiredFromLoadPath misstates it; the flag's own docblock
has stated the authoring-only reach since commit 29dd1a6, so the clause now
says that.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
…ide for the reworded mongo conversion summary

Generated by gen:spec-changes and gen:upgrade-guide; only the one summary's
copy moves (two JSON lines, one guide table row).

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 6 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

⛔ 1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17/17-3.mdx (via submitLabel (literal, a string literal in summary))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0126aaf052db44fd67aade978d1de9ebe4fc3c86 — the merge of head 7ec0ef4c84bf4a4e0ca7e51c780da6d46cfa0708 into base dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0126aaf052db44fd67aade978d1de9ebe4fc3c86 && git checkout 0126aaf052db44fd67aade978d1de9ebe4fc3c86
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19 7ec0ef4c84bf4a4e0ca7e51c780da6d46cfa0708 && git checkout -B drift-repro dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19 && git merge --no-ff 7ec0ef4c84bf4a4e0ca7e51c780da6d46cfa0708

node scripts/docs-audit/affected-docs.mjs --json dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs dfc8547c4b4fd2e5e5780409b2b0a0e02a220f19 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7ec0ef4c84bf4a4e0ca7e51c780da6d46cfa0708
Local-runs: none

Read-only review of PR #20718 (card #20234, stage 9) against ruling C+D on #19123 (5749154545): a comment takes form C (anchored on the commit that decided it), anything an author is shown takes form D (the lesson in words, no number to look up). Inputs: the card body, stage 8's report 5895704120 and landing record 5896689039, the stage-9 claim 5897370057 and os-dev-report 5898878385, the PR body, comments, file list and net diff, and one read of the head's check-runs. Every reading below is from git objects (git show / git diff / git log -S) or REST GETs; nothing was built, run or re-run.

① Derived judgments

Diff shape. Net diff against main at the head: 4 files, +36/-14 (registry.ts +16/-11, spec-changes.json +2/-2, docs/protocol-upgrade-guide.md +1/-1, one new changeset). The head is a merge of origin/main 5757463712; git diff 5757463712..7ec0ef4c84 is byte-identical to the merge-base diff, and the merge commit changes nothing under packages/spec, scripts, docs or .changeset relative to its branch parent 31a296909f, so no regeneration commit was owed. main has since moved by three commits that touch only their own changeset files: no overlap with this diff. Right.

registry.ts, hunk by hunk. Five hunks: four lie entirely inside a summary: string expression, one inside a block comment. No conversion id, toMajor, surface, retiredFromLoadPath / retiredAfter, apply body, fixture, ordering or export line moves. The only tracker number on an added line is #9249 (answers 200), on a rewritten line of the submitLabel literal, so nothing is net-added. Right.

The four summary literals (each concatenation evaluated at base and head from the git objects, word-token diff; each removed number probed with GET /repos/objectstack-ai/objectstack/issues/N, lit control #20234 answers 200, dead control #8710 answers 404):

  • datasource-driver-mongo-to-mongodb: removed (#6345) (404); added , so the id that selects a driver and the id that selects its config contract are one string with no mapping between them; every other token kept. Writing commit e2798fab7 (git log -S answers only it); its own docblock for this entry says the rename was made "so that the id which selects a driver and the id which selects its config contract are one string with no mapping layer between them". TRUE statement of that commit's decision. Right.
  • translation-component-submit-label-removed: removed #10926 — (404); #9249 kept (200); added retired rather than re-anchored — and , and re-anchoring the key there would only have added a second place to translate one word; every other token kept. Writing commit d173125fb: message "Option A per the maintainer ruling on i18n: component-translation submitLabel copy key lost its only declared carrier when element:form retired (#9249) — decide retire vs re-anchor #10926 (2026-08-22): drop the key"; docblock and changeset "The maintainer ruled retire over re-anchor ... re-anchoring would have widened the face for one word". A faithful paraphrase (widening the copy face for one word is a second place to translate it). TRUE. Right.
  • mapping-lookup-params-removed: removed #10329, (404); ADR-0049 kept; added . Implementing them instead would have added a second reference-resolution dialect to the import path (one splice: way) becomes way. plus the clause). Writing commit 15d58dbf1: docblock "Implementing them (a second reference-resolution dialect on the import path) is what the code comment in packages/rest/src/import-mapping.ts declines to build, and the spec(data): mapping lookup transform params (object / fromField / toField / autoCreate) are authorable and read by nothing #10329 triage ruling confirms that posture"; changeset says the same. TRUE. Right.
  • connector-error-mapping-removed: removed #14676, (404); ADR-0049 kept; added ; deleting the block resolves that collision without a rename (one splice: the period after shown becomes a semicolon). Writing commit 13c48c2a5: docblock "Deletion resolves the collision without a rename." TRUE, near verbatim. Right.

The :2100 comment clause. At base the comment read that the flag "does not mean what its name and every docblock around it say it means". False at base: conversions/types.ts at the merge-base (and at head, :232, on RetiredConversionState.retiredFromLoadPath) states "The flag's name says load path, but its reach is the authoring surface only — data-at-rest load paths replay retired entries on purpose". That text was written by 29dd1a6dd (git log -S "its reach is the authoring surface" answers only that commit; its diff rewrites the retiredFromLoadPath field doc; message "state the authoring-surface jurisdiction retiredFromLoadPath actually has", Fixes #16864). e956924e17 (2026-09-28) later split the interface and moved that docblock onto RetiredConversionState, so the head sentence's parenthetical names the docblock's current home and "since commit 29dd1a6" names the commit that wrote the statement. The rewrite is TRUE, and 29dd1a6dd is the form-C anchor that decided it: unique at 9 hex, an ancestor of origin/main. Right.

Generated artifacts. spec-changes.json :229 and :1121 and docs/protocol-upgrade-guide.md :188 each change exactly the mongo entry's to / Change cell, byte-equal to the new summary. Both files are generator output: composeSpecChanges (packages/spec/src/migrations/spec-changes.ts:250, to: conversion.summary) and build-upgrade-guide.ts:101 (${c.summary}); both are merge=os-regen in .gitattributes; the guide carries its GENERATED banner. The three protocol-18 summaries are absent from both artifacts by construction, not by staleness: both generators walk majors up to PROTOCOL_MAJOR (17) and those entries are toMajor: 18. The guide's :68 still carries #6345; that sentence is rendered from step.rationale at migrations/registry.ts:343, which is the migrations/ stage's, so leaving it is right and stage 8's "hand-written" label for it was wrong. Right.

Hypothesis 5 (neither gate counts a conversion summary), confirmed from the scripts. scripts/check-issue-citations.mjs reads sources through commentProse (:111-112, :364), which blanks string literals, so a summary was never in its census and its packages/spec/src count cannot move. scripts/check-doc-authoring.mjs sets PACKAGES_PROSE_EXCLUDED = 'packages/spec' (:714), and its spec leg sinks only message: / error:, positional zod messages, the strictObject option keys (surface, history, aliases, guidance, guidanceSets, retiredForms), retiredKey() tombstones, .describe() and hoisted text consts: no summary sink. Noted-not-filed is the right disposition: a gate's coverage boundary is none of Prime Directive #10's three filing classes, the ruling on #19123 already assigns widening the gate's reach to a sibling card (#19124), and a new gate defaults to no.

Public surface and accept set. No export, schema, accept-set, reject-set or runtime behaviour changes; what changes is author-shown text (os migrate meta output, spec-changes.json, the upgrade guide) plus one source comment. The Docs Drift Check names the release-owned page content/docs/releases/v17/17-3.mdx via submitLabel; it is read-only and correctly untouched. Not a governed diff: none of the four paths is in the register, head repo equals base repo, 50 changed lines. Right.

② Semver level

@objectstack/spec patch: right. registry.ts compiles into dist and files[] ships dist and spec-changes.json, so the four new sentences publish; nothing is added, removed or narrowed, so minor is not owed and skip-changeset would be wrong. The changeset front-matter is '@objectstack/spec': patch, it carries no tracker number and no model identifier, and no ADR-0087 disposition marker is owed (not breaking). Check Changeset on the head: success.

Clause-②: no — right; the PR body and the changeset each carry it as a standalone line.

③ Boundary flags

Dev flags (the report's deviations), each answered:

  • Base e4e5222b7b instead of the dispatch's 735594bea9: immaterial; the net diff is judged against the merge-base and origin/main, with no overlap either way.
  • Hypothesis 5 falsified rather than forced: confirmed from the two scripts (above).
  • Guide :68 is generated, not hand-written: confirmed (migrations/registry.ts:343-346 is that sentence); the migrations/ stage carries it.
  • Merge through os-regen-merge.sh with no regeneration commit: confirmed; the merge changed nothing under packages/spec, scripts, docs or .changeset.
  • Four heavy repo-project test files NOT MEASURED locally: CI's Test Core shards are the reading; none of the four reads summary text.
  • Labels set by another actor, worktree kept, no force-push, model-free trailers with the session-URL PR footer: nothing to act on; the three branch commits carry Claude-Session plus Co-authored-by: Claude and no model identifier.

open_questions: none declared.

Out-of-scope findings, judged:

Check-runs on the head (one read of 32 runs, 0 failed, taken after the newest run's started_at 2026-09-29T21:02:17Z and before this act's next clock read 2026-09-29T21:09:06Z; not re-read; this record posted 2026-09-29T21:17Z):

  • success (14): Build Core, Check Changeset, Check PR Size, Auto Label, Governed Surface Queue Guard, Type Check · source gates (this job carries check:spec-changes, check:upgrade-guide, check:generated --reconcile-only, check:authorable-surface and check:docs, so the generated-artifact family is answered green), Type Check · debt ledger, Flag docs affected by code changes, Check Documentation Links, Spec property liveness, Part-of PR must not also close its card, The card this PR closes must claim this branch, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, filter.
  • skipped (3): Console Pin Gate, Build Docs, Packed-tarball smoke (opt-in); path or opt-in skips, not this diff's.
  • not concluded at my read (15), named, not presumed green: Lint & Repo Gates (carries check:issue-citations, diff-scoped over the one added citation #9249, check:doc-authoring, check:adr-0087-registration and pnpm lint), Type Check · workspace, Type Check · consumer gates (check:api-surface), Test Core 1/6 to 6/6, Dogfood Regression Gate 1/3 to 3/3, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL). The required aggregate TypeScript Type Check had no run listed on the head at my read.
  • Of the seven required contexts: Build Core and Governed Surface Queue Guard are green; Lint & Repo Gates, Test Core, Dogfood Regression Gate and Temporal Conformance (live PG + MySQL) were in progress; TypeScript Type Check was not yet listed. Enqueue waits on those concluding green; that is the seat's read at enqueue time, not this record's presumption.

Implemented-by: claude/issue-20234-conversion-summaries-form-d
Reviewed-by: session_014EJ1ED8X4MMrT18BhVx4tx

VERDICT: PASS


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants