Skip to content

feat(spec): a semantic migration names the D2 conversions whose applied edits it judges (SemanticMigration.conversionIds) - #20716

Merged
os-justin merged 3 commits into
mainfrom
claude/issue-20697-semantic-conversion-ids
Sep 29, 2026
Merged

os-justin merged 3 commits into
mainfrom
claude/issue-20697-semantic-conversion-ids

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Closes #20697

SemanticMigration gains an optional conversionIds, naming the D2 conversions whose applied edits the entry judges. A test refuses an id that no registered conversion at or below the entry's step replays. One link is added after reading both sides: flow-decision-edge-branching-first-match → flow-decision-mode-inclusive-explicit.

Clause-②: yes (widening)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1

…ed edits it judges

SemanticMigration gains an optional conversionIds list, the same ids
MigrationApplication.conversionId carries, so a printer of a chain
result can show an entry beside the applied edits it judges.
flow-decision-edge-branching-first-match links
flow-decision-mode-inclusive-explicit, and migrations.test.ts refuses
a link that no step at or below the entry's own replays.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <[email protected]>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 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
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 679f95ec5c7c4b0797d10afb60efa8220d92a98a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from bf4e11d967d3c0f561227a098113ffab03b60a25 — the merge of head 32e8411a6ba96f7681120e7c5973724cdcdf8221 into base 679f95ec5c7c4b0797d10afb60efa8220d92a98a, 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 bf4e11d967d3c0f561227a098113ffab03b60a25 && git checkout bf4e11d967d3c0f561227a098113ffab03b60a25
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 679f95ec5c7c4b0797d10afb60efa8220d92a98a 32e8411a6ba96f7681120e7c5973724cdcdf8221 && git checkout -B drift-repro 679f95ec5c7c4b0797d10afb60efa8220d92a98a && git merge --no-ff 32e8411a6ba96f7681120e7c5973724cdcdf8221

node scripts/docs-audit/affected-docs.mjs --json 679f95ec5c7c4b0797d10afb60efa8220d92a98a

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 32e8411a6ba96f7681120e7c5973724cdcdf8221
Local-runs: none

Inputs read: card #20697 (body and all 4 comments: triage 5897646840, claim 5897876723, os-dev-report 5899359294, ruling 1 5899392601); PR #20716 (body, 5-file list, net diff main...head, 97 insertions, 0 deletions); the 46 check-runs on the head; parent #20620's body and its triage direction 2 (5888087153) to test sentences. Read-only git against the fetched refs; nothing built, run or re-run.

① Derived judgments

Accept-set and public-surface changes the diff implies — each named.

  1. SemanticMigration (exported: src/migrations/index.ts:19 via src/index.ts:227) gains conversionIds?: readonly string[]. The set of valid entry literals widens: an entry may now carry the list; every existing entry stays valid. Right — triage's option A, verbatim.
  2. MigrationTodo extends SemanticMigration, so the chain result's todos[] and each hops[].todos[] may carry the key. chain.ts:102 builds a todo by spreading the entry, so the copy needs no chain change. Right.
  3. os migrate meta --json passes result.todos and h.todos through untouched (packages/cli/src/commands/migrate/meta.ts:600 and :606; emitJson is JSON.stringify, packages/cli/src/utils/format.ts:113). The machine-readable output gains one optional key on one todo. Additive; no CLI code moves, and no pin holds a todo's key set (migrate-meta.e2e.test.ts:204-208 asserts presence of fields; the Object.keys pin at :557 is the top-level payload). Right, and no CLI changeset is owed for a key that rides through the spec's exported type.
  4. The human printer (meta.ts:379-383) prints surface, replacement, reason, acceptanceCriteria and does not print the link. Right — the printer's pairing is [finding][devx] os migrate meta --from 17 buries a project's real findings under 240 generic protocol-18 notices, and marks default-flip conversions as "Applied" when the right action is usually no edit #20620's, and this PR claims nothing about it.
  5. The --stored path is untouched: StoredMigrationTodo is an explicit-field projection of a different todo (metadata-protocol/src/protocol.ts:17234-17241). Right.
  6. Generated artifacts: registry.ts gains the 3 lines the generator concatenates from the entry file (comment carried, 6-space indent, inside step 18's generated region at :11453-11455) — a regeneration, not a hand edit. api-surface/root.json:146 and export-origins/root.json:145 record name and kind only. spec-changes.ts:256-263 and build-upgrade-guide.ts:105-113 project named members only, so spec-changes.json and the upgrade guide do not move. Right that nothing else was regenerated.
  7. Test-time narrowing (not a runtime accept-set): every conversionIds id must be registered AND replayed by a step at or below the entry's major; plus an anti-vacuity floor of at least one link repo-wide. Right — the card's option A sentence, adopted by triage and confirmed by ruling 1; a registered-but-unreplayed id (10 exist: majors 11, 13, 14, 15 in CONVERSIONS_BY_MAJOR, with steps only for 17 and 18) can never meet a MigrationApplication. The floor is a small ratchet (the field can never be dropped from every entry without touching the test); disclosed by the dev, and fitting for a field whose reason to exist is the one link.
  8. One link, on 18.flow-decision-edge-branching-first-match.ts. Tested against both sides: the entry's acceptanceCriteria opens with "Review every flow-decision-mode-inclusive-explicit line the chain replay lists" and gives the three per-edit verdicts; the conversion's docblock (packages/spec/src/conversions/registry.ts, above :12258) calls it "the paired D3 entry"; the conversion is toMajor: 18, in step 18's derived conversionIds. Right, and not prose-derived.
  9. No new check:* gate; no other entry touched; the file surface matches the claim's fence exactly. Right.
  10. The member name conversionIds is reused from MigrationStep with a different meaning (judged vs graduated). As triaged ("the same id name the printer joins on"); the TSDoc's explicit cross-reference to MigrationStep.conversionIds carries the distinction. Right.

Every author-shown or AI-facing sentence, tested against the tree.

  • TSDoc, types.ts:53-72:
    • "Each id names a conversion that this entry's step or an earlier one replays, and migrations.test.ts refuses one that does not" — true on main when this PR lands (the test is in this diff).
    • "an id here is the conversionId of every MigrationApplication that conversion produces" — true: chain.ts:85-94 pushes the step's id string as conversionId.
    • "the chain copies this field onto the entry's MigrationTodo like every other field" — true, chain.ts:102.
    • "a printer of a chain result can show the entry beside each applied edit it judges" — true as a capability ("can"), now; the shipped printer does not yet, and the sentence does not say it does.
    • "the entry is reported as a TODO of its hop whether or not any edit it names was applied" — true, chain.ts:102 maps every step.semantic entry.
    • "never derive one from the entry's prose naming the id" — sourced: triage 5897646840.
    • The parenthetical "(keep the written value, delete it, or narrow the source instead)" lists the shipped link's three verdicts as if they were the general shape of a judgment. True for the one link; a future link over a rename conversion would read past it. Not false; the rule is the clause before it.
  • Entry comment, 18.flow-decision-edge-branching-first-match.ts:87-88: "every mode: 'inclusive' that conversion writes is one decision to keep, delete or narrow, per the criteria above" — true: the conversion's summary says what it writes; criteria (1) delete, (2) keep, (3) narrow.
  • Test comments and messages, migrations.test.ts:75-121: the join "keyed on MigrationApplication.conversionId" — true; the three ways a link pairs nothing (typo, later major, step below the floor) — each true against CONVERSION_IDS and MIGRATIONS_BY_MAJOR; the remedy's gen:migration-registry — a real script (packages/spec/package.json:286); expect(conversion.toMajor).toBe(18) — true; applyMetaMigrations(before, 17, 18) — matches the signature (chain.ts:68-72), and 17 is above the floor of 16.
  • Changeset .changeset/20697-semantic-migration-conversion-ids.md: "exported by @objectstack/spec" — true; "the same conversionId that the conversion's MigrationApplication rows carry" — true; "so objectstack migrate meta --json shows it on that todo" — true on main when this PR lands (the workspace CLI reads the spec's type; objectstack is a declared bin, packages/cli/package.json:23), and for a published CLI at the release that publishes this spec minor, with no CLI change needed; "Nothing is removed or renamed, and every entry is still reported as a todo of its hop" — true; "One link ships" — true.
  • PR body: "Closes spec(migrations): a semantic entry names the D2 conversions whose applied edits it judges (SemanticMigration.conversionIds), so os migrate meta can pair them (the spec half of #20620) #20697" — right (the "Part-of PR must not also close its card" and "The card this PR closes must claim this branch" checks both pass). "a dangling-id test" under-counts: the diff adds two tests (the dangling-id assertion and the end-to-end join). Incomplete, not false. "Clause-②: yes (widening)" — matches the changeset and the claim.
  • Not changed, noted: entries/README.md:53-60 still shows the five-member template and is silent on the new optional member. No sentence there is false; the TSDoc on the type the template imports is the authority. Non-blocking.

② Semver level

.changeset/20697-semantic-migration-conversion-ids.md: '@objectstack/spec': minor, body Clause-②: yes (widening). Matches the diff: one optional member added to an exported interface, nothing removed or renamed, no breaking marker owed (AGENTS.md: yes takes at least minor; (widening) is the arm for it). No other released package publishes a code change. The PR body carries the identical Clause-②: yes (widening) line; the claim's Clause-②: yes (widening) agrees. Check Changeset on the head: success.

③ Boundary flags

Check-runs on 32e8411a6b (read last; 46 runs, deduped by name keeping the newest started_at → 35 names; none still running): 30 completed success, 5 completed skipped (Auto Label, Build Docs, Check PR Size, Console Pin Gate, Packed-tarball smoke (opt-in)). Success includes Lint & Repo Gates, Check Changeset, Governed Surface Queue Guard, Spec property liveness, Test Core (1/6..6/6 and roll-up), TypeScript Type Check and the four Type Check · gates, Dogfood Regression Gate (1/3..3/3 and roll-up), Dogfood Verify CLI, Temporal Conformance, Build Core, Check Documentation Links, Flag docs affected by code changes, and the three card-claim checks. Legacy status roll-up: success (1 context).

Implemented-by: claude/issue-20697-semantic-conversion-ids
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-29T21:41Z · rendered by the seat's at-tier review subagent on this head. The seat read its served tier family from the subagent transcript before posting.

  • The two wording notes are not false, so they stay as written: the TSDoc parenthetical is true for the one shipped link, and the PR body names one of the two added tests. The entry README's template stays silent on the optional member.
  • ③ The 56 remaining link candidates: the review is right that the entries live under packages/spec/**, so a spec follow-up carries them if more links are wanted. Triage made them optional, so the seat files nothing now.
  • Landing follows once needs:contract-review is stripped and the checks are green again.

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 tests tooling

Projects

None yet

2 participants