docs(metadata-protocol): name each decline list's remit and add the JSDoc's missing flow-conflict bullet - #9556
Conversation
… JSDoc's flow-conflict gap (#9271) `migrateStoredMetadata` is described by two hand-written decline lists that were not the same enumeration, and nothing enforced either count. - `protocol.ts` JSDoc: add the missing "a flow whose rename the conflict guard refused" bullet. That path is internal to the function and reachable from every door, so its absence was a real omission rather than a scope choice. - `protocol.ts` JSDoc: the remit sentence PR #9270 landed closed with "that does not by itself account for every difference ... see #9271". With the bullet above added, it now does, so the clause is restated as the finished fact it has become. - `cli.mdx`: name the decline table's remit — the operator-observable surface — and say which single member the JSDoc carries that it does not, and why no operator door can reach it. The two lists now differ by exactly one member, in one direction, for a stated reason. Prose only; no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qYPmkKEsfbWY1yVg83p8F
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not seeCoarse fallback — 7 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 055a939f3f888712b9e00e253f639172936766a4 && git checkout 055a939f3f888712b9e00e253f639172936766a4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b348ac2c24100e26bf88486f704e3064a2e1fee6 0d3ea23920ad76033fabb7e39632ef3cfd85292b && git checkout -B drift-repro b348ac2c24100e26bf88486f704e3064a2e1fee6 && git merge --no-ff 0d3ea23920ad76033fabb7e39632ef3cfd85292b
node scripts/docs-audit/affected-docs.mjs --json b348ac2c24100e26bf88486f704e3064a2e1fee6
|
Fixes #9271
Option A as ruled on 2026-08-17: the two decline lists describing
migrateStoredMetadataare an intentional scope split, now made explicit.Two audiences, two documents, each self-describing — neither derived from nor
transcluded into the other.
Prose only. No behaviour change, no API change.
The ruling's falsifiable premise: verified, NOT falsified
The ruling carried a hard stop: verify the flow-conflict path is reachable from
a non-CLI caller, and if it turns out CLI-only, report the fork rather than
force the bullet. It is reachable, so the bullet was genuinely owed. What
settled it:
canonicalizeFlow:packages/rest/src/rest-server.ts—POST /meta/_migrate-storedpackages/runtime/src/domains/meta.ts—POST /metadata/_migrate-storedrequest.canonicalizeFlow ?? this.resolveFlowCanonicalizer()— andresolveFlowCanonicalizerreads theautomationservice off the servicesregistry.
flow,and the canonicalization returning a non-empty
conflictsarray. Nothing init discriminates on which caller supplied the engine.
automation engine. A live executor registry is precisely the condition
under which the guard can refuse a real rename; the CLI's deliberately inert
engine is the weaker case, not the only one.
Positive control for the caller sweep: the same sweep independently surfaced the
expected CLI caller (
packages/cli/src/commands/migrate/meta.ts) and 25 filesin total, so the non-CLI hits are a reading rather than an artefact of a query
that matches everything or nothing.
What changed
packages/metadata-protocol/src/protocol.ts— themigrateStoredMetadataJSDoc block only:
Placed immediately after the no-reachable-engine bullet so the two
flow-related declines sit together — and so the remit sentence's existing
"the first bullet below" reference keeps pointing at the bullet it always
meant.
the remit sentence, ending "That does not by itself account for every
difference between this list and cli.mdx's decline table — see [Decision]
migrateStoredMetadata's JSDoc decline list andcli.mdx's decline table document different sets — intentional scope split, or two lists that drifted? #9271". Addingthe bullet above makes that clause false: the difference is now fully
accounted for. It now states the finished fact, and names the split as ruled
rather than pointing at an open question. The sentence was improved in
place, never duplicated — no second remit sentence was added.
content/docs/deployment/cli.mdx— one sentence naming the decline table'sremit: the operator-observable surface, plus which single member the JSDoc
carries that it does not, and why no operator door can reach it.
Per the ruling,
cli.mdxdeliberately does not grow a"no reachable automation engine" row — an operator cannot reach that path, and
documenting it there would be its own small lie.
End state
cli.mdxflowrows with no reachable automation engineFive members against four, differing by exactly one member in exactly one
direction, for a reason each document now states itself — which is the end state
the ruling predicted.
Verified mechanically rather than by eye: parsing the file with the TypeScript
compiler API reports 0 parse diagnostics, attaches a 6481-char JSDoc to
migrateStoredMetadata, and counts 5 decline bullets in it.Changeset
None — the
skip-changesetlabel is applied instead. This repo'scheck-empty-changeset.mjsgate rejects a newly added empty-frontmatterchangeset, and AGENTS.md reserves changesets for feature or functional work. A
prose-only PR releases nothing, which is the label's textbook case as
lint.ymlitself describes it.Gates
Re-derived against the actual diff with
node scripts/pm/dispatch-gates.mjs(no path argument, so the script takes its own change set) — it returned the
same 12 families the dispatch named, no additions. All 12 run green on the
pushed tree, commit
0d3ea23, each exit status captured directly rather thanthrough a pipe:
check:cross-package-test-inputs·check-cross-package-test-inputs.mjs·check:docs-audit-scope·check:docs-redirects·check:role-word·check:durability-log-level·check:filter-alias-parity·spec
check:empty-state· speccheck:liveness·spec
check:strictness-ledger· speccheck:variant-docs·docs-audit/check-affected-docs.mjsThe affected-docs mapper flags three pages against this diff. Two are
content/docs/releases/, which is release-owned and untouched here. The third,content/docs/concepts/metadata-lifecycle.mdx, is a negative and staysunedited: it carries no decline list at all (zero occurrences of "decline"),
and its single mention of the function is one accurate clause about the
no-write-path skip, a decline this PR does not alter. So there is no third list
to fold into the split — the card's two-list framing is complete.
Generated by Claude Code