Skip to content

test(metadata-protocol): measure the object registry write-through plural — dormant, and pinned - #9008

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-8862-object-registry-mutation-plural
Aug 16, 2026
Merged

test(metadata-protocol): measure the object registry write-through plural — dormant, and pinned#9008
os-zhuang merged 2 commits into
mainfrom
claude/issue-8862-object-registry-mutation-plural

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part of #8862

Part of, not Fixes, deliberately. The card's question is answered below; the card's
disposition — whether the now-proven-dead tolerance should be deleted — is the PM's call
and is not in this PR. #8862 remains open for that decision.

The question

applyObjectRegistryMutation does not merely admit a plural type key, it registers under it:

if (request.type !== 'object' && request.type !== 'objects') return;
this.engine.registry.registerItem(request.type, request.item, 'name');

The spelling that arrives is the spelling the registry entry is minted under — the shape
canonicalMetaType's header blames for a real prior bug (one plural read minted a plural
registry entry; the singular fallback supplying the code-authored items never ran again, so
one overlay row shadowed an entire code-authored listing and survived the DELETE meant to
lift it).

So the limb is either a live registry-shadowing defect or dead tolerance, and the card was
filed without that measurement on purpose: it came out of #8820, where an unmeasured
reachability claim was the whole defect.

The trace — all four producers fold. Verdict: DORMANT

applyObjectRegistryMutation has exactly one caller (applyRegistryWriteThrough), which has
exactly four. Every one of them delivers an already-folded type:

# caller where type is produced folded?
1 saveMetaItem (:12625) singularTypeForRepo, after canonicalizeMetaRequestType at :11869 yes
2 runPublishSideEffects (:13506) args.singularType from promoteDraftForPublish :13351 yes
3 revertCommit (:15673) PLURAL_TO_SINGULAR[it.type] ?? it.type, at the call site yes
4 rollbackMetaItem (:16014) singularType = request.type after canonicalizeMetaRequestType at :15932 (#8819) yes

Row 2 is the one #8820 flagged as the structural hazard: publishPackageDrafts feeds
runPublishSideEffects the draft row's stored type, which listDrafts does not fold. The
fold that saves it lives one level in, inside promoteDraftForPublish, so both of that
helper's callers are covered.

Both fold maps resolve the plural, measured rather than read:

PLURAL_TO_SINGULAR['objects']     = "object"
canonicalMetaUrlType('objects')   = "object"
metaUrlSpellingRefusal('objects') = null

No route can deliver 'objects'. The limb is dormant.

What this PR adds

packages/metadata-protocol/src/protocol.object-registry-write-through-spelling.test.ts
tests only, no behaviour change. It turns the trace above into a measurement and then into a
guard:

  • five route cases drive each of the four call sites with a plural at the only place that
    route accepts one (URL spelling for routes 1/2/4, a genuinely plural at-rest row for
    revertCommit, whose it.type is read from the stored commit item) and assert the spelling
    that actually reaches registerItem;
  • the fold-map pin — the verdict's external dependency, living in @objectstack/spec;
  • the call-site count pin — a fifth caller cannot be added without a human re-reading the
    trace, which is the guard the bare type: string parameter does not provide.

Ablations (predicted before running, per the file header)

Run from the committed state; protocol.ts restored with
git checkout claude/issue-8862-object-registry-mutation-plural -- packages/metadata-protocol/src/protocol.ts
and confirmed byte-identical (git diff --exit-code, exit 0).

# mutation predicted measured
1 ship state GREEN GREEN — 107 files / 1520 tests
2 'objects' limb deleted from both applyRegistryWriteThrough and applyObjectRegistryMutation GREEN GREEN — 107 files / 1520 tests
3 applyObjectRegistryMutation guard inverted to accept ONLY 'objects' RED RED — 1 file / 5 tests, and the 5 are all in the new file

Ablation 2 is the evidence that the limb is dead: removing it changes nothing observable.

Ablation 3 is the non-vacuity control, and it doubles as the coverage measurement — the
inverted guard breaks the object registry write-through on every write verb, and the other
106 test files in the package could not tell the two states apart. Only the new file notices.
That is the same gap #8820 measured one helper over.

Verification

All at merged head b33753634 (final commit, origin/main merged in first per the
concurrent-editor rule alongside the unmerged #8986).

  • pnpm --filter @objectstack/metadata-protocol test — 107 files passed
  • gate union, re-derived against the actual changed path with
    node scripts/pm/dispatch-gates.mjs (which added five convention-triggered gates the
    dispatch list did not name, because the change adds a test file):
    check:nul-bytes, check:cross-package-test-inputs (+ the ci.yml script form),
    check:durability-log-level, check:filter-alias-parity, check:engine-double-contract,
    check:where-matcher, check:query-options-erasure, check:type-check-coverage,
    check:meta-type-normalized, check:stack-collection-maps — all PASS
  • pnpm check:type-check-debt --re-measure on the fully built closure — OK, 33 ledger entries
    re-measured, none above its recorded number. The new test file raises no debt.

The whole union was re-run after the origin/main merge, because that merge changed the gate
scripts themselves and scripts/engine-double-contract.baseline.json.

No changeset: tests only, nothing user-visible.

Open question for the PM

The tolerance is dead. Removing it is a separate, now-provable step — and #8820's own landed
test header already anticipates it ("the limb is dead ONCE the producer folds — this is what
licenses its removal as a separate, provable follow-up"). My recommendation is in the dev
report; the call is the PM's, and this PR does not pre-empt it.

Generated by Claude Code


Generated by Claude Code

os-zhuang and others added 2 commits August 16, 2026 04:51
…ng (#8862)

Measure the reachability question #8862 was filed with deliberately
unanswered: can any `applyRegistryWriteThrough` caller deliver the plural
`'objects'` that `applyObjectRegistryMutation` registers under?

All four callers fold at the producer — `saveMetaItem` and
`rollbackMetaItem` via `canonicalizeMetaRequestType`, `runPublishSideEffects`
via `promoteDraftForPublish`'s `PLURAL_TO_SINGULAR` fold (which covers the
batch caller that hands it a stored, unfolded type), and `revertCommit` at
the call site. Both fold maps resolve `objects` to `object`, so the limb is
dormant.

The new file drives each of the four routes with a plural at the only place
that route accepts one and asserts the spelling that reaches `registerItem`,
pins the two fold maps the verdict depends on, and pins the call-site count
so a fifth caller cannot be added without re-running the trace.

No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTKPDRoynY8i3HmdSFUxFj
@vercel

vercel Bot commented Aug 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 16, 2026 5:34am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

@github-actions github-actions Bot added the tests label Aug 16, 2026
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 16, 2026 — with Claude
@os-zhuang
os-zhuang marked this pull request as ready for review August 16, 2026 05:57
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 16, 2026
Merged via the queue into main with commit 124a3ef Aug 16, 2026
30 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8862-object-registry-mutation-plural branch August 16, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant