Repository navigation
fix(metadata-protocol): an orphan adoption rebinds a draft view in storage only, never registering it live - #23724
Conversation
…orage only, never registering it live reassignOrphanedMetadata's view write-through admitted a row by its type alone, so an adopted package-less draft view was served as live until the next boot. One active-only gate now runs before both the view and object limbs (the object limb's behaviour is unchanged). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVE8bSorrxjRHx7edWAdKm
…opt-orphans-draft-view
📓 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 see
Coarse fallback — 11 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 08b6fc7eeb4f4a197e1215ba3bcb4555aa26c0fb && git checkout 08b6fc7eeb4f4a197e1215ba3bcb4555aa26c0fb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f40d877f8115ea77270d41d6224818b132c1e9f8 f60a676d6d8ca327edbf43839ebf3be5ddc4127f && git checkout -B drift-repro f40d877f8115ea77270d41d6224818b132c1e9f8 && git merge --no-ff f60a676d6d8ca327edbf43839ebf3be5ddc4127f
node scripts/docs-audit/affected-docs.mjs --json f40d877f8115ea77270d41d6224818b132c1e9f8
|
席位核对 ·
|
| Item | Reading |
|---|---|
| Shape | PR → main. Line 1 is Fixes #23690, the only closing keyword. |
| Scope | 3 files (+264 / −5): packages/metadata-protocol/src/protocol.ts (reassignOrphanedMetadata only: one row.state !== 'active' gate before both limbs, replacing the object limb's inline copy), a new real-kernel pin in packages/objectql, and the changeset. That is the claim's surface (6200629773). The object limb's lines moved; its behaviour did not. The seat read the hunk. Before, a non-active object row ran the rebound hydration and then continued before any registry write. Now it continues first. The only difference is that a hydration failure on a draft object row no longer logs its warn. No governed path (check-governed-merges.mjs --pr 23724: 0 of 3). Under the 3,000-line threshold. |
Clause-② |
no, in the claim, the PR body and the changeset; @objectstack/metadata-protocol patch. The adoption's response and storage rebind are unchanged. The registry stops serving an unpublished view, which brings the write-through back to its declared rule (a draft never reaches the runtime). No accept set moves, so no contract review is owed. This table is the seat's review of record. |
| Measured | Negative control on main: 2 of the 3 new pins red. The draft view was registered live, and a draft adopted beside an active row replaced the served published body. HTTP walk on dev:crm: after the adoption the draft view answers 404, and the active view is served at Q. The same walk with the ablated build served the draft (200, 'Draft gadgets'). |
| Pins and controls | 3 new pins (a draft view adopted alone, a draft beside an active row, CONTROL an adopted active view served at Q). PR #23688's 5 object pins stay green. Ablation (the gate narrowed to objects): 2 red / 7 green; restore 3/3. |
| Gates | The dev's derived union: 67 derived, 67 exit 0, --ran reconciled. PR #23706 is merged in (f60a676d6, no conflict), and its REST pin is green beside these. CI on this head at this read: 27 success, 6 skipped (the path-filtered roster), 6 Test Core shards in progress. The seat arms the queue only once they conclude green. |
Disposition:
- open_questions: [0] A (one gate before both limbs; the object behaviour is unchanged, as read above). [1] and [2]: process notes, accepted. [3] A (
mainmerged; PR fix(metadata-protocol)!: a save naming a package of an object its package-less definition owns is refused, with a way out into that package (ADR-0029 D9.9) #23704merge-treeclean). out_of_scope_findings: none.
Generated by Claude Code
Fixes #23690
Clause-②: no
Dev session
session_01SVE8bSorrxjRHx7edWAdKm, dispatched bydomain:engineseat 1 (session_01GP3comALHRcRBixS2TEnLF), claim 6200629773.What this does
reassignOrphanedMetadata(the methodPOST /api/v1/packages/:id/adopt-orphanscalls, ADR-0070 D5) sends each rebound view row through the registry write-through (#23198). The view limb admitted a row by its type alone, so adopting a package-less draft view registered its draft body as live, stamped_packageId= the target package, until the next boot (which registers active rows only,loadMetaFromDb).applyRegistryWriteThrough's own header says drafts "are a staging buffer and must never leak into the runtime" (ADR-0048).row.state !== 'active'gate now runs before both limbs (view and object). It replaces the object limb's own copy of the same check (PR fix(metadata-protocol): an orphan adoption hands each adopted object's registry owner and definition to the target package in the same request (#23646) #23688), so the object limb's behaviour is byte-for-byte the same (its pins green, below). An adopted draft view is rebound in storage only.state,reassignedCount/reassigned, and an adopted ACTIVE view going live at its new binding.main, below).docs/adr/grepped for the surface (ADR-0070 D5 rules on the storage rebind only; ADR-0048 on draft / active). No ADR is reversed.Verification (final head
f60a676d6, a merge commit oforigin/main9e0945cf4that brought in PR #23706)main(4c21e57169), before the fix, the new pins on a realObjectKernel+ObjectQLPlugin:Tests 2 failed | 1 passed (3). The draft case:registerItemwas called with['view', {…}, 'name']. The draft-beside-active case: served{ label: 'Draft gadgets', … }where'Published gadgets'was expected. The CONTROL (active view adopted) passed.packages/objectql/src/protocol-adopt-orphans-draft-view-registry.test.ts, beside the object-registry pin):registerItem('view', …)call, the stored row is Q's draft,reassignedCount: 1, and a boot afterwards serves nothing;reassignedCount: 2, the active body is served at Q, and a boot agrees;protocol-package-lifecycle.test.ts:Test Files 3 passed (3),Tests 19 passed (19)(before and after the main merge).scripts/ablation-replace.mjs, the gate narrowed to objects only,metadata-protocolrebuilt,ablation-dist-preflightmarker present indist/index.cjsanddist/index.js):Tests 2 failed | 7 passed (9), the two draft-view pins red; the CONTROL and all five object pins green. Restored: blob == HEAD07e2e03b650b,git diff HEADempty; rebuilt,--absentclean across 24 built files, pins 3/3.pnpm dev:crm -- --fresh -p 38690(better-sqlite3): packagecom.qa.qcreated (201), a package-less draft view and a package-less active view seeded intosys_metadata(no door writes package-less rows today). Before:GET /api/v1/meta/view/qa_draft_grid404; the active one200.POST /api/v1/packages/com.qa.q/adopt-orphans200,reassignedCount: 2. After: draft404 RESOURCE_NOT_FOUND; active200,label: 'Published gadgets',_packageId: 'com.qa.q'; both stored rows bound tocom.qa.q, states unchanged.GET /api/v1/meta/view/qa_draft_grid200with"label":"Draft gadgets","_packageId":"com.qa.q"after the adoption. So the leak was reachable at the public door, and the fix closes it there.@objectstack/metadata-protocolfull suiteTest Files 236 passed | 3 skipped (239),Tests 20566 passed | 19 skippedonf60a676d6. PR fix(metadata-protocol)!: a draft save naming no package, of an item never published into a package, takes the draft doors' address (#23657) #23706's REST pinmeta-draft-head-package-inheritance.test.tsre-run after the merge: 21/21.@objectstack/metadata-protocoland@objectstack/objectqlexit 0 (objectql's includescheck:test-typecheck: OK, debt held).f60a676d6:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(3 paths, 67 commands), all run; reconciled with--ran:67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN(a derived zero, every line carrying its exit code). Two families first refused on the shallow clone (check-engine-split-ratio.mjs --days 90exit 2,check-plugin-teardown-shape.mjs --self-testexit 3); aftergit fetch --shallow-since=2026-07-10both re-ran at exit 0.Neighbours
Open PRs touching
packages/metadata-protocol/src/protocol.ts: PR #23704 (#23514, the D9.9 /saveMetaItemregion) only;git merge-treeof this head with its branch is clean. PR #23418 touches ametadata-protocoltest file, notprotocol.ts. PR #23706 (#23657) landed onmainwhile this was in flight and is merged in with a merge commit (no conflict); its pins above.Acceptance notes
Generated by Claude Code