Skip to content

fix(metadata-protocol,metadata): the runtime save door refuses a view container whose name disagrees with its save name, through the one judge every door calls (#21412) - #21483

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-21412-save-door-container-name
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-21412-save-door-container-name

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21412
Clause-②: yes (narrowing)

The runtime save door (saveMetaItem, which REST PUT /api/v1/meta/view/:name and the dispatcher's metadata save both call) now refuses an aggregated view container whose body name disagrees with the name it is saved under. It answers VALIDATION_ERROR / 400 before anything is stored or registered, and it refuses through the same judge the source registrars call. Before this change, the card's probe (row crm_lead, body name lead_views) was accepted, stored under crm_lead, and registered under lead_views plus crm_lead.default, so one document answered under two names.

This round follows the seat's answer on the card (comment 5961930912) to the dev's needs_decision report (5961864645): Q1 A, Q2 A, Q3 A and Q4 A.

What changed

  • One judge, in @objectstack/metadata. New subpath @objectstack/metadata/view-container-name (packages/metadata/src/view-container-name.ts). The judgement: a container's own name, when set, equals the key the door files the container under. It has two entries, which share one gate, one envelope and one message template:
    • viewContainerNameRefusal(container, sourceLabel, ownerId) is the source registrars' entry. Its key is DERIVED from the binding (deriveViewContainerObject). It is the function that used to live in packages/objectql, moved, and its words are byte for byte the same (proof below).
    • savedViewContainerNameRefusal(container, saveName) is the save door's entry. Its key is the save name.
  • Why the save door's key is the save name, not the binding. The door keeps a container saved under a name other than its object (the rest/meta: getViewsByObject / GET /meta/view?object= omits a runtime-authored view CONTAINER — #7163/#7736 expansion fix does not cover this path #13407 pin) and expands one on another package's object under its own name (metadata: a view container with a bare list on another package's object silently replaces that object's packaged default view on GET /meta/view?object= — while the by-name read still serves the original #21334's ruled arm). The dev's probes measured that the derived key would refuse the body the door itself stores for that shape when it is sent back, and would pass two bodies whose name disagrees with the row (P3, P4).
  • Why @objectstack/metadata. @objectstack/core cannot host the judge: the judge needs deriveViewContainerObject, which lives in @objectstack/metadata, and @objectstack/metadata lists core. @objectstack/metadata is the one layer all three doors already depend on. The judge is a subpath of its own, not the ./view-container leaf, because that leaf imports nothing and the judge needs isAggregatedViewContainer from @objectstack/spec. It is not on the root entry either, because the root loads the manager and the filesystem machinery that objectql's ADR-0076 lean entry must not reach. check:lean-entry-closure holds: @objectstack/objectql/core is 15 packages, the admitted set held exactly.
  • The save door (packages/metadata-protocol/src/protocol.ts, saveMetaItem) calls savedViewContainerNameRefusal(request.item, request.name) for view first, before normalizeViewMetadata can keep an authored name. Containers only. normalizeViewMetadata's docblock says so.
  • The artifact/HMR door's container branch (packages/metadata/src/plugin.ts) calls viewContainerNameRefusal(item, 'artifact', packageId) after it derives the key and before memLoader.save / manager.register. The probe document is now refused through the judge, in the judge's words. Row 1 (assertMetadataRegisterContract) is unchanged for every type; on this shape it is simply no longer reached.
  • @objectstack/objectql keeps viewContainerNameRefusal and the ViewContainerNameRefusal type as a re-export (src/view-container-name-refusal.ts). Its module header is rewritten: the judge's home, why it moved, why the source registrars' entry derives the key and the save door's takes it. engine.ts and packages/cli are untouched.
  • Rider 5954896314 (comment only): the comment above ViewSchema's guidance: in packages/spec/src/ui/view.zod.ts no longer says saveMetaItem sends the name, artifact-shipped containers do, and the sweep injects it. It says the door's own stamp (normalizeViewMetadata) is the only platform writer of the key and states the one rule, worded to Q1 A. No schema change.

The message: one template, two renderings

The per-door words are one value, the door's key origin. It fills four slots: the subject, the key clause, the text after the key, and the cross-reference after the shared reason. Everything else is shared.

  • The source registrars' rendering is byte-identical to the old objectql function. This was proven by a temporary test that compared old and new outputs over 10 fixtures, 3 of them refusals: message, code, status and httpStatus were all equal, Tests 1 passed. The test was deleted afterwards.
  • The save door's rendering differs in three places, and each has a reason:
    • Its subject is view container and not `views:` container from SOURCE 'OWNER': the save door has no views: collection and no owning manifest.
    • Its key clause is the name it is saved under.
    • It drops the cross-reference the artifact/HMR loader refuses this same document, which would be false at this door for P3 and P4: the artifact loader accepts a body whose name equals its binding.
  • The ruling said the renderings differ "only in the clause that names where the key came from". Read literally, that is one slot. All four slots carry that one fact, so this is reported as a measured reading, not chosen silently.

Pins (triage pins, restated to Q1 A)

  • packages/metadata-protocol/src/view-container-runtime-expansion.test.ts, the filing's own stub engine:
    • P1, the card's probe, is refused VALIDATION_ERROR / 400. No row is stored and nothing is registered.
    • P1's message is exactly savedViewContainerNameRefusal's, so the refusal goes through the judge.
    • P1's code / status equal the artifact/HMR registrar's for the same document.
    • P3 and P4 are refused, with nothing stored or registered.
    • P2 (name equal to the row, bound elsewhere) passes. The container is registered under exactly one key, its row key (Q4 A), and the object door serves crm_lead.default.
    • P2b (no name) passes, is stamped with the row name, and the stamped body passes when it is read and sent back.
    • A control with name, row and binding all equal passes, under one key.
  • packages/metadata/src/view-container-name.test.ts covers:
  • The boot loop's and os validate's refusal words are unchanged, and their pins are unedited: packages/objectql/src/view-container-name-refusal.test.ts (green) and packages/cli/test/validate-view-container-name.test.ts (CI; see Tests).
  • Edited because the ruling moves them: in packages/objectql/src/view-container-divergent-name-registrars.test.ts, the artifact door's two message assertions now read the judge's words (binds to, 'crm_lead', `name` is 'lead_views') instead of row 1's. The envelope-equality pin is unchanged.

Reverse verification (both from committed HEAD, through scripts/ablation-replace.mjs)

  • Save door. The call if (nameRefusal) throw nameRefusal; in protocol.ts was disabled (anchor 1 to 0, blob 478daa416f90 to 6f9ae10ffb7b), and view-container-runtime-expansion.test.ts went 5 failed | 65 passed (70): P1 three times, P3 and P4. P2, P2b and the control stayed green. The tool then restored the file to HEAD (blob == HEAD (478daa416f90), git diff HEAD empty). Both test and subject resolve the protocol from src, so no build was involved.
  • Artifact door. The same call in plugin.ts was disabled (blob 6924156b5fda to cef5ec76af37), and view-container-name.test.ts went 1 failed | 10 passed (11). The refusal came from row 1 instead (no httpStatus), so the assertion failed. The file was restored to HEAD. The test imports ./plugin.js from src.

Tests

Code at a70d0d61a4 is identical to 7d4ee0ac46 outside .changeset/. All runs went through scripts/pm/os-verify-lock.sh, and each gave VERDICT command-exit 0:

  • @objectstack/metadata: pnpm test gave Test Files 57 passed (57), Tests 847 passed (847), and pnpm typecheck exited 0. Its tsconfig includes src/**/*, so the tests are type-checked.
  • @objectstack/metadata-protocol: pnpm test gave Test Files 205 passed | 3 skipped (208), Tests 3092 passed | 19 skipped (3111), and pnpm typecheck exited 0 (tests included).
  • @objectstack/objectql: the local project gave Test Files 366 passed (366), Tests 7383 passed (7383), test:repo gave 1 passed, and typecheck exited 0. That covers tsc, the scripts project, and check:test-typecheck OK, held in the debt ledger.
  • Build: turbo run build --filter=@objectstack/objectql^... --filter=@objectstack/objectql gave 14 successful. The new subpath loads under both conditions (require and import each return both entries, VALIDATION_ERROR 400), and dist/view-container-name.d.ts / .d.cts are emitted.
  • Not run here: packages/cli, the os validate pins. @objectstack/objectql's export keeps its name, signature and bytes of output, and the cli imports it unchanged. Building the cli closure is 60 tasks, 11 of them cached. CI runs them. The byte-identity proof above is the local evidence.
  • One count control moved: packages/metadata/src/serializers/typescript-serializer-annotation.test.ts pins how many exports entries it visits (5 to 6), so the new entry is checked too.

Gates

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was derived at a70d0d61a4 and gave 99 commands. All 99 were run at a70d0d61a4:

  • 98 exited 0.
  • 1 is NOT MEASURED: pnpm check:dual-build-cjs-loads exited 3 with PREREQUISITE NOT MET, because it needs every package's dist/ (the direct load check above stands in, but is not the gate).
  • Of note: check-adr-0087-registration --base origin/main: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. check-changeset-no-major: no major; the level axis needs a PR payload. check:lean-entry-closure: admitted set held. check:published-files, check:dts-closure, check:issue-citations (22 resolve), check:doc-authoring, check:spec-docblock-symbol-anchors, check:nul-bytes, check:test-source-alias, check:cross-package-test-inputs: all green.

Lint, a proven narrowing at a70d0d61a4: eslint --no-inline-config --format json over the 10 touched .ts files reports 10 files, 0 errors, 0 warnings, and none ignored. Every one is in eslint's population (--print-config resolves each). eslint.config.mjs never enables type-aware linting (its own note near line 327: no parserOptions.project, no typed rules), so this diff cannot move the verdict on any untouched file. The repo-wide pnpm lint is CI's.

Changesets

  • @objectstack/metadata minor: the new subpath, and the artifact door's refusal speaking through the judge.
  • @objectstack/metadata-protocol minor with a BREAKING banner: an accept-set narrowing at the save door, graded like the boot loop's refusal of the same divergence. Its ADR-0087 disposition is not-required (no-migration-prescription).
  • @objectstack/objectql patch: the re-export.
  • @objectstack/spec patch: comment only. src/**/*.zod.ts ships as source, and the comment ships in the ui JavaScript output (measured: the new sentence is in 20 dist files; the old one is in none).

The Clause-② line above is the claim's, copied as dispatched. By scripts/pm/clause2-line.mjs's own definitions, this diff both widens a public surface (the new @objectstack/metadata subpath, with two functions and a type) and narrows an accept set (the save door). That reads as yes (narrowing). This is raised to the seat in the dev report; the line here is not changed by the dev.

Acceptance notes


Generated by Claude Code

claude added 5 commits October 2, 2026 21:47
…es a container

The judge moves into @objectstack/metadata as the ./view-container-name
subpath: a container's own name, when set, must equal the key the door
files it under. The source registrars derive that key from the binding;
the runtime save door files under the save name. The artifact door's
container branch now refuses through the judge before the loader write.

Claude-Session: https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3
Co-authored-by: Claude <noreply@anthropic.com>
…name disagrees with its save name

saveMetaItem now calls the one judge before normalizeViewMetadata can
keep an authored container name, so a container is never stored under
its row name and registered under its body's. objectql re-exports the
judge from @objectstack/metadata; the ViewSchema comment states who
writes a container's name and the rule every door applies to it.

Claude-Session: https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3
Co-authored-by: Claude <noreply@anthropic.com>
…l, the re-export and the ViewSchema comment

Claude-Session: https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:ui tests tooling labels Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/metadata-protocol, @objectstack/metadata, @objectstack/objectql, @objectstack/spec, touching 12 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/metadata/package.json, packages/metadata/tsup.config.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

24 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 aa4632235ba571ef800b95e6bc18d00a30aa1d57.

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

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/metadata/package.json, packages/metadata/tsup.config.ts) — pages documenting those are invisible to this run
  • 3 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 — 142 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 aa4632235ba571ef800b95e6bc18d00a30aa1d57 → packageMentionDocs.

Which tree this was computed on

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

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0e7d0bdb71fd3b4a2c5249414fff6e2b6866556b
Local-runs: none

Inputs: card #21412 (body and all 7 comments: triage 5954055712, rider 5954896314, claim 5961640434 with revisions 2 and 3, needs_decision 5961864645, seat answer 5961930912, reports 5962619241 and 5962685002), PR #21483 (body, 15-file list, net diff against main at merge base 49524f69), and the check-runs on the head. Read-only; nothing built, run or re-run.

① Derived judgments

Accept-set changes the diff implies:

  1. Runtime save door (saveMetaItem, packages/metadata-protocol/src/protocol.ts, types view / views): an aggregated container (list / form / listViews / formViews, no viewKind) whose own name is a non-empty string that differs from request.name is now refused VALIDATION_ERROR / 400 through savedViewContainerNameRefusal, before normalizeViewMetadata can keep the authored name and before any store or register. RIGHT: this is Q1 A (key = the save name), Q2 A (containers only) and triage pins 1 to 3. Placement checked against main: saveMetaItem opens at line 17270 and carries no return ahead of the normalize block, so every writer that reaches the door (REST PUT, the dispatcher, migrateStoredMetadata at 18829, duplicatePackage at 21755, draft mode) passes the judge; request.name is MetadataItemNameSchema, a non-empty string, so the judge's falsy-key precondition cannot stand down at this door. P1, P3, P4 refused with nothing stored and nothing registered; P2, P2b and the equal-everywhere control pass under exactly one container key (Q4 A); the stamped body round-trips. A standalone ViewItem and every non-view type stay unjudged, which is right: the every-type half is finding(metadata-protocol): the runtime save door accepts any metadata body whose name differs from its row name, and registers it under the body name (the every-type half of #21412) #21470.

  2. Artifact/HMR door, container branch (packages/metadata/src/plugin.ts, _registerArtifactBodyCollections): the judge runs after deriveViewContainerObject and slots.skip, before applyProtection, memLoader.save and manager.register. Accept set UNCHANGED: the judge's refusal set is a subset of row 1's (assertMetadataRegisterContract refused the same document after the loader write and still runs after the judge, unedited for every type). What moves is the refusal's words (the judge's, source label artifact) and its moment (before the loader write). RIGHT: Q3 A as the seat corrected triage's literal form. The views: subject is accurate at this door, since ARTIFACT_FIELD_TO_TYPE maps the views field to view.

  3. Boot loop and os validate / os compile: unchanged. I compared the old objectql template against the new refusal() filled with the derived entry's KeyOrigin slot by slot: for every (sourceLabel, ownerId, name, key) the bytes are identical. The predicate order moved (the key is derived before the name test), but deriveViewContainerObject is total on unknown and judge() keeps the same four tests (container, non-empty string name, truthy key, inequality), so the boot precondition (a falsy derived key refuses nothing) is carried. The unedited pins (packages/objectql/src/view-container-name-refusal.test.ts, packages/cli/test/validate-view-container-name.test.ts) hold this. RIGHT.

Public-surface changes the diff implies:

  1. @objectstack/metadata gains the subpath ./view-container-name (package.json exports plus a tsup entry) exporting viewContainerNameRefusal, savedViewContainerNameRefusal and the type ViewContainerNameRefusal. A widening. RIGHT: Q3 A, and the home is correctly reasoned on measured edges (core cannot host it because metadata lists core; objectql and metadata-protocol both list metadata; metadata lists spec). Its own entry rather than the zero-import ./view-container leaf or the root is right too: the judge needs isAggregatedViewContainer from @objectstack/spec, and the root loads the manager and filesystem machinery objectql's ADR-0076 lean entry must not reach. The lean closure's package set does not move: spec and metadata were already in objectql's closure through the old module.

  2. @objectstack/objectql: viewContainerNameRefusal and its type are kept as re-exports at the same module path; src/index.ts (112 to 113) and engine.ts (260) are untouched, as the claim required. No surface change. RIGHT.

  3. @objectstack/spec: comment-only edit above ViewSchema's guidance: (the rider 5954896314, worded to Q1 A). No schema, parse, export or accept-set change; a comment inside a schema literal cannot move the d.ts surface. RIGHT.

  4. packages/metadata/src/serializers/typescript-serializer-annotation.test.ts: the exports-entry control is derived from package.json exports, which go from 5 entries to 6. RIGHT.

  5. packages/objectql/src/view-container-divergent-name-registrars.test.ts: the artifact door's two message assertions now read the judge's words; the convergence pin (boot code and status equal the artifact door's) is unedited and still holds, both now coming from the judge. RIGHT.

Nothing judged wrong.

② Semver level

Four changesets, each judged against what its package publishes:

  • @objectstack/metadata minor, Clause-②: yes: the new subpath is an additive widening; minor is the floor. Right. The artifact door's message change is not an accept-set change (the envelope is the contract), so no banner is owed there.
  • @objectstack/metadata-protocol minor with a BREAKING banner, Clause-②: no (narrowing), ADR-0087 disposition not-required (no-migration-prescription): an accept-set narrowing at a write door is breaking and ships minor under the launch-window convention (check-changeset-no-major.mjs's own header). The category is right: no key is removed, renamed or re-shaped, there is no tombstone and nothing mechanical for migrate meta to rewrite, and which of the two names a divergent container meant is authoring intent no conversion entry can decide; the other four categories are closed in the marker on facts. The body states what was accepted before, what is refused now and the remedy, which is the migration text the rule asks for; its remedy sentence is imperative prose, not a FROM-to-TO rewrite, so it does not contradict the category. Right.
  • @objectstack/objectql patch, Clause-②: no: a re-export with the same name, signature and output bytes. Right.
  • @objectstack/spec patch, Clause-②: no: the comment ships in the ui JavaScript output, so a patch is the honest grade for bytes that ship; not skip-changeset. Right.

The PR body's declaration reads Clause-②: yes (narrowing), which is right by scripts/pm/clause2-line.mjs's definitions: the diff widens one published surface (the subpath) AND narrows an accept set (the save door). no (narrowing) would deny the widening; a bare yes would hide the break. The claim's revision 3 carries the same line. The per-changeset lines are each truthful for their own package and union to the PR's line; a yes (narrowing) copied onto the objectql or spec patch changesets would be false for those packages, and the ADR-0087 gate reads the arm (narrowing) and the banner, both of which the breaking changeset carries.

Gate verdicts on the head: Check Changeset (the job that runs check-empty-changeset, check-adr-0087-registration and check-changeset-no-major) completed success on 0e7d0bdb.

③ Boundary flags

Dev flags from the PR body and the reports 5962619241 and 5962685002:

  1. Clause-② copied as no, measured as yes (narrowing) (report open question 1): answered. The seat revised the claim (revision 3), the PR body reads yes (narrowing), and round 2 gave each changeset its own package's arm. Closed; judged right in ②.
  2. The per-door words fill four slots, not one clause (open question 2): answered, accepted. The governing text requires one judge, no second rule, no copy of the message, and the boot loop's and os validate's words byte for byte. All four hold: one judge(), one refusal() template, a KeyOrigin whose slots carry only where the key came from, and a source-registrar rendering identical to the old bytes. The views: manifest subject and the artifact cross-reference cannot be true at the save door, so the save-door rendering drops them. The seat's "only the clause naming where the key came from" is met in substance; recorded as a measured reading, not escalated.
  3. Edits outside the four pin files the ruling named (registrars test, serializer count test, package.json, tsup.config.ts): answered, accepted as forced consequences, and the claim's revision 3 already covers them.
  4. The artifact door's judge sits after the residual-sweep slots.skip: answered, accepted. A skipped duplicate files nothing either way, and the judge judges what the branch would file.
  5. Not run locally: the packages/cli validate pins and check:dual-build-cjs-loads: answered. These are CI's. At read time Build Core, Test Core 1 to 6 and Lint & Repo Gates are in progress on this head (list below); a check still running is read as running, not as a pass.
  6. Carrier finding(metadata-protocol): the runtime save door accepts any metadata body whose name differs from its row name, and registers it under the body name (the every-type half of #21412) #21470, noted not filed (PR acceptance notes; report out-of-scope finding): at-rest container rows that already carry a divergent name keep registering under it at boot, and revertCommit / rollbackMetaItem re-persist stored versions without the save seam; the row count is not measured. Answered: carried by finding(metadata-protocol): the runtime save door accepts any metadata body whose name differs from its row name, and registers it under the body name (the every-type half of #21412) #21470 per the seat (5961930912, "Carried"), outside this card by Q2 A. Not escalated. One observation added for that card's census, not a defect of this diff: migrateStoredMetadata (protocol.ts 18829) and duplicatePackage (21755) re-enter saveMetaItem, so such a pre-existing divergent row now refuses loudly on a stored-migration or duplicate pass instead of re-persisting; the row keeps its bytes, which is what the changeset promises.
  7. origin/main moved during the round (the branch is 2 behind at read): no overlap on the touched files was claimed or found; the queue's rebase lap carries it.

Open questions on the final report (5962685002): none. The earlier Q1 to Q4 (5961864645) were answered by the seat (5961930912) and the diff implements A, A, A, A as judged in ①.

CI on the head at read time: 14 check-runs completed success (filter, Check Changeset, Type Check source gates, Spec property liveness, Governed Surface Queue Guard, Validate Package Dependencies, Check PR Size, Check Documentation Links, Flag docs affected by code changes, Auto Label, and the four claim and single-writer guards); 3 skipped (Packed-tarball smoke, Console Pin Gate, Build Docs); 16 in progress (Build Core; Test Core 1 to 6; Type Check workspace, debt ledger and consumer gates; Lint & Repo Gates; Temporal Conformance; Dogfood Verify CLI; Dogfood Regression Gate 1 to 3). This record's PASS is on the diff, the card and the completed checks; landing still waits for every check green.

Implemented-by: claude/issue-21412-save-door-container-name
Reviewed-by: session_01DDZNkDVwPQnevTFcYE47H3

VERDICT: PASS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:ui size/l tests tooling

Projects

None yet

2 participants