Skip to content

feat(spec)!: retire the ADR-0078 conversion-conflict channel — onConflict / reservedNodeTypes, the notice types, the conflicts arrays and the FLOW_CONVERSION_CONFLICT 409 - #24045

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24001-conversion-conflict-retire
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-24001-conversion-conflict-retire

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24001
Clause-②: yes (narrowing)

What this does

Retires ADR-0078's conversion-conflict channel, which has had no producer since #23543 F5-http (PR #23954) reserved the three old callout node types (FLOW_RESERVED_NODE_TYPES). Per the card's "Reader who acts" and triage 6221525633:

  • @objectstack/spec: the root exports CONVERSION_CONFLICT_CODE, ConversionConflictNotice, ConversionConflictDetail leave; ApplyConversionsOptions loses onConflict / reservedNodeTypes (and so StoredConversionOptions), ConversionContext loses reservedNodeTypes / reportConflict, IAutomationService.canonicalizeStoredFlow's result loses conflicts. api-surface/root.json and export-origins/root.json regenerated (−3 each); declaration-map/ did not move (check:generated: all 14 current).
  • Error code: FLOW_CONVERSION_CONFLICT comes out of ERROR_CODE_LEDGER by the ledger's own "Retiring a code" rule ("A row whose last EMITTER is deleted comes out with it"; precedent OVERLAY_PERSISTENCE_FAILED). retired-error-codes.ts is NOT used: that table is for StandardErrorCode catalogue members only (finding: QUOTA_EXCEEDED and CONCURRENT_LIMIT_EXCEEDED are registered StandardErrorCode members with zero producers — an ADR-0049 enforce-or-remove decision #17707's CONCURRENT_LIMIT_EXCEEDED), and this is a ledger row. The error catalog's 409 row is the generated content/docs/references/api/{contract,error-code-ledger}.mdx entry (regenerated by gen:docs); content/docs/api/error-catalog.mdx never listed it.
  • Dispatcher vocabulary: the OS_METADATA_CONVERSION_CONFLICT foreign-vocabulary row (its stamp site in apply.ts is gone) is deleted; the running-log comments no longer spell the retired code.
  • @objectstack/service-automation: canonicalizeFlow / canonicalizeStoredFlow no longer build the live-name set or collect conflicts; plugin.ts's inert-mode rationale is restated for what still needs the registry (the registration preview's descriptor checks).
  • @objectstack/metadata-protocol: StoredFlowCanonicalization loses conflicts; four consumer branches go — the save door's 409, the stored-migration pass's failed row, duplicatePackage's failed item and importUnmanagedPackage's 409 — with their docblocks.
  • D3 semantic entry conversion-conflict-channel-removed (no D2 conversion, no tombstone: no metadata document ever carried any of it). One BREAKING minor changeset with FROM → TO, marker adr-0087: registered conversion-conflict-channel-removed.

FLOW_RESERVED_NODE_TYPES and the F5-http reservation are untouched.

Hypothesis 1 — zero producers (measured on origin/main fd9664ffac, before any edit)

git grep -n -E 'reportConflict\(|onConflict|reservedNodeTypes|ConversionConflictNotice|ConversionConflictDetail|FLOW_CONVERSION_CONFLICT|ConversionConflict' (excluding CHANGELOG.md), plus CONVERSION_CONFLICT_CODE|OS_METADATA_CONVERSION_CONFLICT and reportConflict|\.conflicts\b:

site role
packages/spec/src/conversions/types.ts:37 CONVERSION_CONFLICT_CODE, :96 ConversionConflictDetail, :110 ConversionConflictNotice, :178-:180 context members declaration
packages/spec/src/conversions/apply.ts:70 / :96 options, :139, :158-:168 the reportConflict bridge bridge (sink wiring), not a producer
packages/spec/src/conversions/index.ts:13,17,18 re-export
packages/spec/src/contracts/automation-service.ts:20,857 declaration (contract field)
packages/spec/src/conversions/registry.ts:84 comment (the guard's own retirement note)
packages/services/service-automation/src/engine.ts:70,4528,4575,4609-4628 consumer (builds the set, collects into an array)
packages/metadata-protocol/src/protocol.ts:23077 (save door 409), :24168 (stored pass), :27555 (duplicatePackage), :27829/:27853 (unmanaged import 409); stored-migration.ts:60 consumer
packages/runtime/src/dispatcher-error-vocabulary.ts:292,598,672 gate ledger
conversions / stored / metadata-protocol / service-automation tests test doubles that CONSTRUCT a conflict (not producers)
onConflict hits in automation/execution.zod.ts, data/data-engine.zod.ts, driver-sql, types/unbacked-conflict-target.ts, check-tenant-chokepoint.mjs unrelated members of the same name

Producer of a conflict = a conversion entry calling context.reportConflict: zero. No MetadataConversion.apply reads context for it; flow-node-http-callout-rename's apply(stack, emit) takes no context at all. No fork, so the hard delete proceeded.

Cross-repo: objectui at the pinned .objectui-sha f8267df9 and at its HEAD 360e27a — 0 hits for all seven spellings, control defineStack lit (38 / 37 files). cloud is private and was NOT MEASURED (the ledger rule asks for it; see the report's open question).

Pins (one per retired export / code), all in packages/spec/src/conversions/conversion-conflict-channel-retirement.test.ts

  • §1 … every retired name has ZERO holders on any public entry, and the survivors still stand on the root — CONVERSION_CONFLICT_CODE, ConversionConflictNotice, ConversionConflictDetail by resolved symbol identity (export-origins/), survivors CONVERSION_TODO_CODE, ConversionTodoNotice, ConversionContext, … present.
  • §1 … the runtime barrel resolves without the retired constant and keeps its TODO twin.
  • §1 … the D3 semantic entry is registered on step 18.
  • §2 … the retired members are absent at the type level, the surviving ones present — onConflict, reservedNodeTypes (options and context), reportConflict, the result's conflicts; compiled by check:test-typecheck.
  • §3 … ErrorCode no longer parses it, and no owner lists it — FLOW_CONVERSION_CONFLICT, with DEFAULT_RECORD_PAGE_CONFLICT / CONCURRENT_UPDATE as lit controls.
  • tree-scoped absence … no retired spelling survives inside the declared radius outside the retirement kit — all seven spellings over packages / examples / skills / content / scripts (the spec: the rest of the incident-response, training and change-management families — every remaining key and all fifteen defs — has zero readers; whole-def enforce-or-remove is the open question left after #14477 #15513 radius, this file added as a witness on each heldBy row in scripts/cross-package-test-inputs.mjs, and in vitest.repo-tests.json). Excluded: the pin itself, packages/spec/src/migrations/ (the D3 entry), the git-ignored generated content/docs/protocol-upgrade/ (the D3 registry's projection), content/docs/releases/, .changeset/, CHANGELOG.md.

Ablation (one, on the pin file)

Restored the deleted export in packages/spec/src/conversions/index.ts through scripts/ablation-replace.mjs (anchor 1 → 0, blob 630ed35f → 2bec0047), ran the pin file: 2 failed / 4 passed — the runtime barrel resolves without the retired constant… and the tree-scoped absence (offenders index.ts spells CONVERSION_CONFLICT_CODE / OS_METADATA_CONVERSION_CONFLICT). The export-origins holder pin stayed green, as it must: it reads the committed artifact, which the source mutation does not regenerate (check:export-origins is that half's gate). Restored: blob == HEAD 630ed35f, git diff HEAD empty; re-run 6 / 6 passed. A first attempt was a no-op — its replacement contained its own anchor, so the tool refused (count 1 → 1) and ran nothing; it is not counted.

The ablation also surfaced a real gap, fixed before push: the git-ignored upgrade-guide page content/docs/protocol-upgrade/18.md, once generated locally, prints the D3 entry verbatim, so the scan now excludes that projection like the registry it projects.

Serial constraint (hypothesis 5)

PR #23986 (#23903) also edits protocol.ts: its hunks sit at the flowSaveReplaysRetired docblock/body (~6612–6700) and the save door's comment and fallback lines (~23025–23115). This PR's save-door hunk is the result.conflicts branch below them (~23077–23094 at the base) plus three other doors. Disjoint: each lands on its own, and whichever lands second merges main. #23986's new test builds its canonicalizer stub with conflicts: [] as a standalone arrow (no contextual type, so no excess-property check): it should still compile after this lands, as a dead key — whichever lands second re-runs that suite on the merge. #24010's three lines are in the package door, not touched here.

Acceptance notes

  • File surface beyond the claim, comment/doc only: packages/cli/src/commands/migrate/meta.ts, packages/cli/src/utils/data-migration-plugins.ts (comments that justified the inert engine by the conflict guard) and content/docs/deployment/cli.mdx (the "flow whose rename the conflict guard refused" decline row and two sentences) — each described a failure mode this PR removes. packages/metadata-protocol/src/stored-migration.ts is the StoredFlowCanonicalization declaration the claim's "the contract's conflicts arrays" names. scripts/cross-package-test-inputs.mjs and packages/spec/vitest.repo-tests.json declare the new pin's radius.
  • Four tests that pinned the removed branches are deleted with them (a refused rename fails the item…, a refused rename fails the save loudly…, the conflict path stays silent — the 409 IS the signal, fails the row loudly when the guard refuses a rename over a live name): each constructed a conflict through a test double and asserted the branch this PR deletes.
  • The stored pass's skipped reason for a flow with no engine is reworded (it named the conflict guard); no test pins its prose.
  • ADR-0078's status note and the ADR-0087 addendum note are in a separate governed PR (claude/issue-24001-adr-0078-note).

Verification

Reported in full in the os-dev-report comment on #24001 (local gate union with exit codes, reconciled --ran 139/139; per-package pnpm test).

🤖 Generated with Claude Code

https://claude.ai/code/session_01WdZxJadrrYettvjrqiWeAV


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/cli, @objectstack/metadata-protocol, @objectstack/runtime, @objectstack/service-automation, @objectstack/spec, touching 28 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/spec/api-surface/root.json, packages/spec/export-origins/root.json, packages/spec/src/conversions/registry.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

27 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json c4111c676aed66f12b7af06e73c2ce46899f1ae5.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/api-surface/root.json, packages/spec/export-origins/root.json, packages/spec/src/conversions/registry.ts, …) — pages documenting those are invisible to this run
  • 10 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 — 152 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 c4111c676aed66f12b7af06e73c2ce46899f1ae5 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 c4111c676aed66f12b7af06e73c2ce46899f1ae5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6f0bca9671b5139dfa750979304ce2a553388731
Local-runs: none
Scope: packages/spec/src/api/error-code-ledger.zod.ts, packages/spec/src/contracts/automation-service.ts, packages/spec/src/conversions/apply.ts, packages/spec/src/conversions/index.ts, packages/spec/src/conversions/registry.ts, packages/spec/src/conversions/types.ts, packages/spec/src/migrations/entries/semantic/18.conversion-conflict-channel-removed.ts + packages/spec/src/conversions/stored.ts, packages/spec/api-surface/root.json, packages/spec/export-origins/root.json, packages/spec/src/conversions/conversion-conflict-channel-retirement.test.ts, packages/spec/vitest.repo-tests.json, scripts/cross-package-test-inputs.mjs, packages/services/service-automation/src/engine.ts, packages/metadata-protocol/src/protocol.ts, packages/metadata-protocol/src/stored-migration.ts, packages/runtime/src/dispatcher-error-vocabulary.ts, .changeset/24001-conversion-conflict-channel-retired.md

Inputs: card #24001 (body; comments 6221525633, 6222477086, 6224904559); PR #24045 (body, 32-file list, net diff against the merge-base 2b274600b1 with main, +486 / -436); the check-runs on the head, re-read before rendering; PR #24046 read only to judge dependency. Read-only throughout: git show / git grep on fetched refs, no worktree, no build, no test, no gate run. Face read off the 32 paths by record-recognisers --paths: the schema face (seven packages/spec/src/** hits, no governed path).

① Derived judgments

Independently measured on origin/main and the head tree: no conversion entry calls reportConflict( (the only hits are the bridge's own property in apply.ts and a docblock), and flow-node-http-callout-rename's apply takes no context — zero producers, so the hard delete is the right route. No D2 conversion and no retiredKey() tombstone are owed: nothing retired here is a metadata key an author writes.

  1. @objectstack/spec root loses CONVERSION_CONFLICT_CODE, ConversionConflictNotice, ConversionConflictDetail — RIGHT. api-surface/root.json and export-origins/root.json each lose exactly those three; declaration-map/ carries no spelling of them on the head, so it had nothing to regenerate; check:api-surface / check:generated green in TypeScript Type Check.
  2. ApplyConversionsOptions loses onConflict and reservedNodeTypes, and through Omit so does StoredConversionOptions (stored.ts:77, unchanged) — RIGHT. The apply.ts bridge was the only reader of either and is deleted whole.
  3. ConversionContext loses reservedNodeTypes and reportConflict; MetadataConversionBody.apply's docblock no longer promises them — RIGHT.
  4. IAutomationService.canonicalizeStoredFlow's result loses conflicts — RIGHT. Its one implementation (AutomationEngine) drops the member and the live-name set it built for a context member nothing read; FLOW_STRUCTURAL_NODE_TYPES stays imported for its other uses (engine.ts:10524). Behaviour unchanged: the onConflict warn sink could never be called.
  5. @objectstack/metadata-protocol: StoredFlowCanonicalization loses conflicts, and four consumer branches go — the save door's 409 (hunk at 23074), the stored pass's failed row (24165), duplicatePackage's failed item (27552) and importUnmanagedPackage's 409 (27826 / 27845) — RIGHT; the card's "two" undercounted and the dev's four is the measured count. No untyped .conflicts reader survives in any packages/*/src non-test file on the head. Each branch was unreachable since f901746ac8; the stored pass's reworded skipped reason is CLI-visible prose with no tracker number, pinned by no test, and still true.
  6. Wire vocabulary: FLOW_CONVERSION_CONFLICT leaves ERROR_CODE_LEDGER['@objectstack/metadata-protocol'], so ErrorCode narrows by one (the generated contract.mdx enum count 333 to 332) — RIGHT by the ledger header's first ground (a row whose last emitter is deleted comes out with it; the four branches above were the emitters). retired-error-codes.ts is correctly not used: its header scopes it to StandardErrorCode catalogue members, and the code never appears in errors.zod.ts. content/docs/api/error-catalog.mdx never listed it; on main only the two generated references pages did, and both are regenerated here.
  7. packages/runtime/src/dispatcher-error-vocabulary.ts: the OS_METADATA_CONVERSION_CONFLICT foreign-vocabulary row goes with its stamp site, and the running-log comments are reworded so the pin's radius holds — RIGHT; check:dispatcher-error-vocabulary reconciles sites and rows in both directions and is green. The file is not reached from packages/runtime/src/index.ts (the tsup entry), so it ships in no dist.
  8. D3 conversion-conflict-channel-removed on step 18: every SemanticMigration field present, no backticks in surface, no tracker number in the author-facing strings; it registers through the gitignored registry's concatenation of entries/semantic/, and the changeset's single marker adr-0087: registered conversion-conflict-channel-removed names it — RIGHT (check:adr-0087-registration green).
  9. The pin conversion-conflict-channel-retirement.test.ts — §1 zero holders by resolved symbol identity plus eight survivors, the barrel, the D3 registration; §2 type-level absence compiled by check:test-typecheck; §3 ErrorCode refuses the code with two lit controls; §4 tree-scoped absence of seven spellings over packages / examples / skills / content / scripts, excluding only the kit and its projections — RIGHT, with its radius declared as the third heldBy witness on every cross-package-test-inputs.mjs row and in vitest.repo-tests.json. Independently, git grep on the head tree for the seven spellings outside the kit finds only docs/adr/0087-metadata-protocol-upgrade-contract.md lines 558 and 701: historical addenda, outside the pin's radius and outside this PR. The dev's ablation (barrel export restored, 2 of 6 red, export-origins pin green by design) is read as reported, not re-run.
  10. Docs: content/docs/deployment/cli.mdx (hand-written tree) loses the decline row for a refusal that no longer exists and restates two sentences; content/docs/references/api/contract.mdx and error-code-ledger.mdx are AUTO-GEN and move only by the generator — RIGHT. No content/docs/releases/**, CHANGELOG.md, skills/** or .claude/** is touched.
  11. FLOW_RESERVED_NODE_TYPES and the F5-http reservation are untouched, as the claim forbids — RIGHT.

Governed surfaces: none in the file list (Governed Surface Queue Guard green). PR #24046 (docs/adr/0078, docs/adr/0087, three added lines) is prose only; no gate on this head reads it and nothing here imports it, so PR #24045 does not depend on it and the ADR text simply lags until it lands. Serial constraint: PR #23986 is still open; its protocol.ts hunks (base 177, 6612 to 6636, 23025 to 23042) and this PR's (6461, 6531, 22988 to 23020, 23074 to 23099, 23847, 24105, 24165, 27509, 27669, 27755, 27826, 27845) are disjoint, as are the two PRs' hunks in protocol.save-flow-canonicalization.test.ts; whichever lands second merges main. #24010 landed before this head's merge of origin/main.

Remainder of the diff, the lane seat's ACCEPT at TIER_DEFAULT: packages/cli/src/commands/migrate/meta.ts and packages/cli/src/utils/data-migration-plugins.ts (comment-only, verified line by line), content/docs/deployment/cli.mdx, the two generated references pages, packages/services/service-automation/src/plugin.ts (comment-only), and the eight test files (four tests deleted with the branches they pinned, stubs lose a dead key, one assertion strengthened to count the two OS_METADATA_CONVERTED notices).

② Semver level

  • .changeset/24001-conversion-conflict-channel-retired.md: @objectstack/spec, @objectstack/service-automation, @objectstack/metadata-protocol at minor, BREAKING, with the FROM to TO table, the one-line fix, what an upgrader sees (TS2305, excess property, ErrorCode parse) and exactly one ADR-0087 marker — RIGHT. minor is the launch-window level for a breaking change (check-changeset-no-major green), and those three are exactly the packages whose published source moves.
  • No changeset for @objectstack/runtime (the vocabulary file is gate-only and not in dist) or @objectstack/cli (comments only); no skip-changeset — RIGHT.
  • Clause-②: line: yes (narrowing) on the PR body (line 2) and in the changeset, one spelling in both. The (narrowing) arm is RIGHT and load-bearing: it is what makes the changeset BREAKING and owes the D3 marker. The value yes over-declares: clause2-line.mjs reads it as "widens one surface and narrows another", and this diff adds no export, key, field, option or code (the D3 row is the narrowing's own declaration, which 23650 and 23634 spell no (narrowing), while 23877, a pure unexport like this one, and the spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543 family spell yes (narrowing)). The exact spelling is no (narrowing). It moves nothing — the level is minor either way, the face is the schema face on the path hits either way, and the over-declaration is in the safe direction — so it is noted and not a FAIL ground; correct it on the next push if one happens.

③ Boundary flags

Dev deviations (report 6224904559), each answered:

  1. File surface beyond the claim (the cli comments, cli.mdx, stored-migration.ts, cross-package-test-inputs.mjs, vitest.repo-tests.json) — accepted: each is the removal's own consequence or the pin's declared radius; none is governed or release-owned.
  2. Four conflicts consumer branches in protocol.ts, not two — accepted; measured, see ① item 5.
  3. Ledger row deleted outright, not tombstoned; the catalog's 409 row is the generated references entry — accepted, see ① item 6.
  4. declaration-map/ did not move — accepted; no spelling of the retired names on the head.
  5. ADR-0087 registries live in code, so the D3 entry rides PR A and PR B holds only docs/adr lines — accepted.
  6. feat(metadata-protocol)!: a flow re-save that newly introduces a retired spelling is refused like a new flow's #23986's hunks are disjoint — accepted, re-measured on feat(metadata-protocol)!: a flow re-save that newly introduces a retired spelling is refused like a new flow's #23986's current file list.
  7. No runtime changeset; cli comment-only — accepted, see ②.
  8. Four tests deleted with their branches — accepted; each constructed a conflict through a double and asserted a branch this PR deletes.
  9. Zero label writes, model-free commit trailers, objectui fetched read-only — outside this record's contract scope; nothing to answer.

Open question 1 — the ledger's "Retiring a code" precondition (no consumer in objectui or cloud reads the literal), objectui measured clean, cloud NOT MEASURED:

  • objectui half — answered: the dev's measurement (0 hits for the seven spellings at pin f8267df9 and HEAD 360e27a, control lit 38 / 37) is accepted; the Console Pin Gate is skipped on this head by its filter, so that grep is the evidence. A read-only grep of a local objectui checkout at df05784a, not one of this record's inputs and not load-bearing, also finds 0 hits.
  • cloud half — NOT MEASURED by the dev, and not measurable from this seat's session either (no such repository is attached). Answered on the merits and escalated for the measurement. The row's deletion reaches cloud only when cloud bumps its @objectstack/spec or @objectstack/metadata-protocol pin past this changeset, and every way cloud could hold a spelling fails loudly there: a type import is TS2305, a returned conflicts member is an excess-property error, an ErrorCode parse of the literal fails, each carrying this changeset's FROM to TO. A bare string branch on the wire code has been dead since f901746ac8 landed (no producer), and this PR changes nothing about it. So the precondition's purpose — never retire a code a consumer still reads live — holds in substance, and the ruling-ref's startup-stage axis carries the retirement. ESCALATION: whoever holds objectstack-ai/cloud greps it for FLOW_CONVERSION_CONFLICT, OS_METADATA_CONVERSION_CONFLICT, ConversionConflictNotice, ConversionConflictDetail, CONVERSION_CONFLICT_CODE, reservedNodeTypes and reportConflict before cloud's next spec pin bump; a hit is a cloud-side delete per the FROM to TO, never a reason to restore the row. This record does not wait on it.

Out-of-scope finding (#23986's conflicts: [] stub) — noted; it becomes a dead key on whichever PR lands second and compiles either way.

Check-runs on the head, re-read before rendering: 41, all completed — 36 success, 5 skipped by filter or opt-in (Temporal Conformance, Dogfood Regression Gate matrix, Dogfood Verify CLI, Console Pin Gate, Packed-tarball smoke), 0 failure, 0 in progress. Nothing NOT MEASURED.

Implemented-by: claude/issue-24001-conversion-conflict-retire
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Review: ACCEPT · #24001 · PR #24045 @ 6f0bca9671 · domain:spec seat 2 (#18549) · 2026-10-16T21:55Z

Session session_01LFqdRsuM5nkqoJdHd5ztVk · os-bill. The reviewer of record, checked against GitHub rather than the report. The chain: claim 6222477086, report 6224904559, and contract review PASS 6225384741 on this head.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 21:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit a65a59b Oct 16, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24001-conversion-conflict-retire branch October 16, 2026 22:28
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/l tests tooling

Projects

None yet

2 participants