Repository navigation
refactor(metadata-protocol): the two overlay-write closes ask one fix-can-be-taken predicate - #29874
Conversation
…base close where it was untested Claude-Session: https://claude.ai/code/session_01JQwxbZz2XXsYuaJz9sPuGU Co-authored-by: Claude <noreply@anthropic.com>
…an-be-taken steps into one predicate namedBaseCloseSubject and packagelessCloseSubject repeated the same four steps (the pair id, the ensure door, the base's version, the override write gate on the declared body). overlayPairFixOf / overlayPairFixAdmits now hold them once in protocol-packaged-base-refusal.ts; each close keeps only its own difference (which base, which body, the package-less close's row read between the two halves). Move-only: no refusal and no call order changes. Claude-Session: https://claude.ai/code/session_01JQwxbZz2XXsYuaJz9sPuGU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbZz2XXsYuaJz9sPuGU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 10 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 310c570e597c9954309000fba49de881c6412a84 && git checkout 310c570e597c9954309000fba49de881c6412a84
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3a856f680d7df622d3226f0a76e5f3f576a5327b ba4aaf11cb445d87aee70203fe00788df34302c7 && git checkout -B drift-repro 3a856f680d7df622d3226f0a76e5f3f576a5327b && git merge --no-ff ba4aaf11cb445d87aee70203fe00788df34302c7
node scripts/docs-audit/affected-docs.mjs --json 3a856f680d7df622d3226f0a76e5f3f576a5327b
|
|
domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf 维护者速读
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Warren · project thread
Fixes #29397
Clause-②: no
Metadata-protocol: none
维护者速读
namedBaseCloseSubject、packagelessCloseSubject)里逐步重复的「修复能否走通」四步(配对 id、ensure 门、基包版本、D2 门放行声明体)收成一个共享判定overlayPairFixOf+overlayPairFixAdmits,各自只保留本该不同的那部分。packages/metadata-protocol/src/protocol-packaged-base-refusal.ts(新判定与具名关闭)、packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts(无包关闭,行读取仍夹在两半之间)、packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts(补的三条控制用例)。pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.named-base-overlay-write.test.ts,预期 31/31 全绿,含三条新的「某一步失败则写入保持开放」控制用例。Why
The contract review of #29270 (record 6577747588, should-fix S6) found that the two overlay-write closes judge "can the fix the refusal names be taken" with the same four steps, written twice. A change to one copy could drift from the other with every pin still green. This PR keeps one copy.
What changes
protocol-packaged-base-refusal.ts, the module that owns the override write gate and both refusal sentences):overlayPairFixOf(host, base): the registry half. The base has a pair id, the ensure door would answer or create the pair (customizationPairEnsureRefusal), and the base records amanifest.version. It answers{ base, pair, basedOn }ornull.overlayPairFixAdmits(host, fix, { type, name, body, member }): the gate half. The override write gate admits the declared body (overrides: { package: base, basedOn }) into the pair.null).namedBaseCloseSubjectkeeps only its own steps: a named package, a body carryingoverridesstays the D2 gate's, the named package among the shippers, and the declared body from the save (no row read).packagelessCloseSubjectkeeps only its own steps: exactly one shipper, the item's row read, andclassifyPackagelessRow.closedOverlayWriteClauses, both refusal sentences,protocol.ts(no line touched; its two host methods and their eight call sites are as onmain), and the return shape{ base, pair }. The new functions are internal:src/index.tsnames its exports, and neither is added there.@objectstack/metadata-protocolpatch (see Acceptance notes for why it is notskip-changeset).Measured first, on
mainat 42ff86fEvery step of the two predicates, common or intended-different (line numbers are
main's):namedBaseCloseSubject(protocol-packaged-base-refusal.ts)packagelessCloseSubject(protocol-packageless-overlay-migration.ts):394-395a non-empty package:729no packageoverridesin the body:399left to the D2 gate:764,foreign_declaration):400the named one among them:731-733exactly one:401-403:734-736:404-405:737-738:406:739:407-408readsregistry.getPackage(base):740-741baseVersionOfre-readsengine.registryObjectQL#registryis a getter over one never-reassigned field, andgetPackagewas already proved a function at the step above:743-768:411-415keyed onisPlainRecord(item):754-756keyed onitem !== undefined:416-423:770-778No step differs by accident, so the card's stop condition did not fire.
Tests
No existing pin was edited.
protocol.named-base-overlay-write.test.tsgained fixtures (three packages and two shipped dashboards that no existing case addresses) and one new top-leveldescribe. The package-less close's pins (packages/objectql/src/protocol-packageless-overlay-migration.test.ts,environment-overlay-email-locale-restart.test.ts,environment-overlay-five-types.test.ts) were not touched.Step coverage before this PR:
no_paircasea read-only base whose pair cannot be ensuredan unensurable paircasesemail_templateunshipped-locale resetThree new pins, all through the named close, all green on
mainbefore the fold (commit 0dc436c on 42ff86f), and each shown able to fail: a write naming a read-only base whose id forms no pair, a base that records no version while its pair is installed, and anemail_templateat a locale the base does not ship. Each stays open, as onmain. OnebasedOnpin covers both closes now, because the step lives once.Acceptance notes
basedOncheck and the no-version pin stays green: the gate then seesoverrideswith nobasedOn, whichOverrideDeclarationSchemarejects, so the gate judges the body as an undeclared same name in the pair and refuses it. The pin still pins the outcome: a fallback version (?? '0.0.0') reds it (Verification, ablation 4). The check is kept as it was.skip-changeset. No published behaviour changes, but the shippeddist/of@objectstack/metadata-protocoldoes (the build carries the new functions). AGENTS.md keepsskip-changesetfor a diff that publishes nothing, so this is apatch. The order allowedskip-changesetif no behaviour changed; AGENTS.md wins.registry present with a getPackage functionis a type guard reached only by an engine without a registry. It is not one of the card's four steps and is not pinned. That is unchanged.Verification
Unit tests at ba4aaf1 (the head, main merged in at 3a856f6, after #29797's 5f99582): @objectstack/metadata-protocol typecheck and full suite, and the package-less close's three objectql files against the rebuilt dist
Earlier heads: at e1c8a10 the suite read 22314/22314 and the objectql files 105/105; at 408cc35, 22308/22308 and 105/105.
New pins on main first: 25/25 at 0dc436c (the pins on 42ff86f, before the fold)
Ablation on main: four mutations of namedBaseCloseSubject through scripts/ablation-replace.mjs, each landed on disk and was restored to the HEAD blob
The file was
protocol-packaged-base-refusal.ts. The blob was 410bf525 before, and it was 410bf525 after every restore, equal to HEAD.if (!pairId.ok) return null;replaced with a no-op: the no-pair-id pin goes red, 1 failed | 24 passed.basedOncheck deleted: 25 passed. The step is masked by the gate (see Acceptance notes).refusereturn deleted: the unshipped-locale pin goes red, 1 failed | 24 passed.basedOngiven the fallback?? '0.0.0': the no-version pin goes red, 1 failed | 24 passed.Derived gates at e1c8a10: 59 derived, 58 run green, 1 NOT MEASURED; pnpm lint exit 0. Re-run at ba4aaf1: the ratchet and text families, all exit 0
At ba4aaf1 (the merge brings no change to this PR's four files;
git diff --stat origin/main HEADstill reads 4 files, +177 / -53), a declared narrowing to the ratchet and text families:check:type-check-coverage,check:type-check-debt,check:durability-log-level,check:doc-authoring,check:closed-ceremony-text,check:nul-bytes,check:issue-citations,check-empty-changeset.mjs --base origin/main, and eslint (--no-inline-config) on the three changed source files: every one exit 0. CI runs the full set on this head.The full union, at e1c8a10:
node scripts/pm/dispatch-gates.mjs --commands(change set: the four files above, vs merge base onorigin/main), each run with its exit code captured before any pipe, then--ran:NOT MEASURED, reason: that self-test's fixture is pinned to a commit outside this shallow clone (it asks for
git fetch --unshallow). CI runs it on a full checkout. Every other derived family exited 0, includingcheck:durability-log-level,check:doc-authoring,check:closed-ceremony-text,check:type-check-coverageandcheck:type-check-debt.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01JQwxbZz2XXsYuaJz9sPuGU
Generated by Claude Code