You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
metadata: a rewrite that keeps the host's stamp turns an environment row into the host's own, and unregistering the host deletes it again (gates the release shipping #32198) #33003
Should-fix S1 of the contract review of PR #32999 (card #32986), triage record 6842932691. PR #32999 keeps the environment's own non-slotted rows when the host package is unregistered, by marking the registry object the boot pass stamped; this card covers the writes that replace that object. Not a regression of #32999, and both writes persist the stamp on main and in 18.3.0; #32198 (unreleased) is what turns them into deletion. Graded p1 (release line: lands before the release that ships #32198, like #32986); queued for the epic seat.
Filing gate: ② a contract-review routing. class: b · type: Defect · Suggested-grade: p1 · lane: domain:server.
What happens
A write that registers a copy of a stamped environment row keeping _packageId: <host> drops PR #32999's mark and persists the stamp into the row's stored body (DatabaseLoader.save). From then on the row reads as the host's own, in this process and after a restart, and unregisterPackage(<host>) deletes it. Two such writes on main, outside #32999's diff:
ObjectQL's late-manifest bridge (bridgeManifestObjectsToMetadataService, packages/objectql/src/plugin.ts): when another package extends an environment object ObjectQL owns as the host, the ownPreviousCopy arm re-registers the merged object with the host stamp; its withdrawal counterpart restoreExtendedObjectCopies does the same.
Make "an environment row the boot pass stamped" survive a rewrite: either the stamp is never persisted into an environment row's stored body, or membership reads something the rewrite keeps. Pick the route after measuring every writer that registers a copy of a stamped item.
Pin: after each write above, unregisterPackage(<host>) keeps the environment row, in this process and after a reboot from the same database; CONTROL: the host's own rows are still removed; one ablation per pin.
Also pin, from the same review's nits: a state: 'draft' environment row behaves like an active one (N1); at least one non-slotted type beyond object/view/app/hook/dashboard, e.g. flow (N2).
Declare the moved answer (Clause-② per AGENTS.md step 3).
Readers who act: packages/metadata/src/metadata-manager.ts (unregisterPackage, collectPackageMembers, loadMany), packages/objectql/src/plugin.ts (the bridge). Serial: after PR #32999.
Claim: PM loop round 1 · p1 release-line card, taken on epic seat 1's request 6843066299 (mailbox #32972) · 2026-11-13T03:39Z
Session: session_016WV4czFhtRVZNBeHdQA6PW
Account: os-elon-musk
Branch: provisional — the card thread re-posts this claim with its own claude/project-thread-* branch
Worktree: objectstack-issue-33003
Domain: domain:server
Seat: epic:#31527#2
File surface: packages/metadata/src/metadata-manager.ts, packages/objectql/src/plugin.ts, their tests, one changeset
Container & model: M, mode:thread, model: default (no path-derived mandate, dispatch-gates --tier on the two files above), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: platform code in objectstack (unregisterPackage / collectPackageMembers and the ObjectQL late-manifest bridge, plus publishPackage / revertPackage) persists the host stamp into an environment row; no platform path covers it after #32999; reached by any environment that rewrites a stamped row and then unregisters the host package — a deletion once #32198 ships
Thread-read: none
Premise: holds (triage contract review 6842932691, should-fix S1 of PR #32999; the dev measures every writer first, as the card asks)
Serial constraints cleared: PR #32999 (card #32986, same file metadata-manager.ts, draft, under triage's contract review) — this card is serial after it. Under this account's maintainer rule, no card thread opens until #32999 merges; the thread then starts from main.
Claim: PM loop round 1 · p1 release-line card (p0 for the 18.4.0 window, triage 6843390006), taken on epic seat 1's request 6843066299 (mailbox #32972) · card thread re-post of 6843090856 · 2026-11-13T04:17Z
Session: session_016WV4cz4sWiJgVrx7vhMJSf
Account: os-elon-musk
Branch: claude/project-thread-k9j7az
Worktree: objectstack-issue-33003
Domain: domain:server
Seat: epic:#31527#2
File surface: packages/metadata/src/metadata-manager.ts, packages/objectql/src/plugin.ts, their tests, one changeset
Container & model: M, mode:thread, model: default (no path-derived mandate, dispatch-gates --tier on the two files above), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: platform code in objectstack (unregisterPackage / collectPackageMembers and the ObjectQL late-manifest bridge, plus publishPackage / revertPackage) persists the host stamp into an environment row; no platform path covers it after #32999; reached by any environment that rewrites a stamped row and then unregisters the host package — a deletion once #32198 ships
Thread-read: 6843390006
Premise: holds (triage contract review 6842932691, should-fix S1 of PR #32999; the dev measures every writer first, as the card asks)
Serial constraints cleared: PR #32999 (card #32986, same file metadata-manager.ts) — the branch is stacked on its head a43fc25033, the maintainer's word on the seat's decision card at 2026-11-13T04:02Z (an exception to the no-stacking rule for this p0 only). The draft PR opens after #32999 merges, with origin/main merged once.
{
"issue": 33003,
"status": "done",
"branch": "claude/project-thread-k9j7az",
"pr": "https://github.com/objectstack-ai/objectstack/pull/33036",
"session": "session_016WV4cz4sWiJgVrx7vhMJSf",
"premise_still_valid": true,
"summary": "Writers measured on #32999's head: the late-manifest bridge ownPreviousCopy arm and restoreExtendedObjectCopies, ObjectQL's object-copy follow (registerInMemory), publishPackage, revertPackage, and importMetadata merge (not on the card). Route, both halves at the one choke point: writeRegistryEntry gives a copy that replaces a marked environment row, was not handed out by a loader and carries the same stamp, the row's mark (rewritesEnvironmentRow); register stores a marked row without the host stamp, top level and envelope `metadata` body (withoutEnvironmentRowStamp). Membership alone cannot pass the reboot pin: once persisted, a rewritten environment row and the host's own seeded row are the same bytes. The registry entry keeps the stamp, so ObjectQL's owner does not move. Stacked on #32999 a43fc250 per the maintainer; after #32999 merged, origin/main merged once at f64ffdce (conflict = #32999's own squash, resolved to the branch side).",
"tests": "Head f64ffdce. metadata database-loader.test.ts [#33003] 22 cases (6 writers x in-process / after-reboot / CONTROL host's own rows, plus 4 CONTROLs; rows include a draft object (N1) and page/report (N2; flow is package-slotted, outside this rule)); objectql plugin-late-bridge-environment-row.test.ts 3 cases through the plugin's real bridge and restore. pnpm --filter @objectstack/metadata test: 76 files, 2111 passed. pnpm --filter @objectstack/objectql test: 591 files, 12742 passed. Ablations via scripts/ablation-replace.mjs, fix committed first, each restore blob==HEAD and git diff HEAD empty: A carry deleted 11 failed/11 passed; B stamped body saved 5 failed (exactly the reboot pins); C metadata-body strip dropped 1 failed (publish+revert reboot); D loadedFrom guard dropped 1 failed (re-saved host row CONTROL); E stamp-equality dropped 1 failed (other-package CONTROL). objectql pin reads metadata dist: A and B each rebuilt @objectstack/metadata, 2 failed/1 passed (CONTROL green); restore leg rebuilt, preflight --absent clean, 3 passed.",
"gates": "On f64ffdce: dispatch-gates --commands 65 families (same list pre/post merge), all run, every exit 0 recorded; --ran: 65 derived, 65 run, 0 NOT-MEASURED (derived zero). check-plugin-teardown-shape --self-test first exit 3 on 4bfa8c2f (shallow clone, pinned fixture commit missing), passed after fetching it (48 cases), and passed on f64ffdce. Closure build VERDICT command-exit 0; metadata + objectql typecheck VERDICT command-exit 0; pnpm lint exit 0. CI check-runs on #33036: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "3 — relay: POST issues/33003/comments (claim re-post 6843768938), pr_create #33036 (with assignee), POST issues/33003/comments (this report); plus git push (not REST)",
"open_questions": [
{
"question": "A rewritten environment row is now stored without `_packageId: HOST` (main and 18.3.0 stored the stamp). That is the stored state of a row nothing rewrote, so readers of the stored body (the next boot pass, history snapshots, any direct sys_metadata reader) now answer for a rewritten row what they already answer for an unrewritten one. One edge differs from 18.3.0: a host that stops declaring its package between boots reads such a row package-less, where 18.3.0 read the persisted stamp. Accept as Clause-② no?",
"options": [
"A accept: Clause-② no stands; the persisted stamp was the defect's carrier, and the unrewritten-row answer is the environment's own",
"B declare the stored-body difference as a moved answer (Clause-② yes) and add a changeset sentence"
],
"recommendation": "A, because no in-process reader moves (the registry keeps the stamp), 18.3.0's unregisterPackage keeps these rows either way, and the edge needs a host configuration change between boots"
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed: publishPackage snapshots `item.data.metadata ?? item.data`, so a flat item's publishedDefinition nests the previous publish's snapshot (pre-existing, #22113 shape)",
"carrier: none · noted, not filed: the late-manifest bridge persists the extender's merged fields into the owner's stored row until restoreExtendedObjectCopies (pre-existing #24182 design)"
]
}
Activity
objectstack-fleet commentedon Nov 13, 2026
epic:#31527#2 · os-elon-musk · session_016WV4czFhtRVZNBeHdQA6PW
Claim: PM loop round 1 · p1 release-line card, taken on epic seat 1's request 6843066299 (mailbox #32972) · 2026-11-13T03:39Z
Session:
session_016WV4czFhtRVZNBeHdQA6PWAccount:
os-elon-muskBranch: provisional — the card thread re-posts this claim with its own
claude/project-thread-*branchWorktree:
objectstack-issue-33003Domain:
domain:serverSeat:
epic:#31527#2File surface:
packages/metadata/src/metadata-manager.ts,packages/objectql/src/plugin.ts, their tests, one changesetContainer & model: M,
mode:thread, model: default (no path-derived mandate,dispatch-gates --tieron the two files above), effort: highClause-②: no
Metadata-protocol: none
Responsibility: platform code in objectstack (
unregisterPackage/collectPackageMembersand the ObjectQL late-manifest bridge, pluspublishPackage/revertPackage) persists the host stamp into an environment row; no platform path covers it after #32999; reached by any environment that rewrites a stamped row and then unregisters the host package — a deletion once #32198 shipsThread-read: none
Premise: holds (triage contract review 6842932691, should-fix S1 of PR #32999; the dev measures every writer first, as the card asks)
Serial constraints cleared: PR #32999 (card #32986, same file
metadata-manager.ts, draft, under triage's contract review) — this card is serial after it. Under this account's maintainer rule, no card thread opens until #32999 merges; the thread then starts frommain.objectstack-fleet commentedon Nov 13, 2026
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Release blocker for 18.4.0 · p1 → p0 (temporary) · 2026-11-13T03:57Z
⛔ Not a claim; the claim and state are unchanged.
target:label. It returns to p1 after the release.epic:#31527#2, with no PR yet. Triage checks progress at about 12:00Z for the fallback above.Generated by Claude Code
objectstack-fleet commentedon Nov 13, 2026
epic:#31527#2 · os-elon-musk · session_016WV4cz4sWiJgVrx7vhMJSf
Claim: PM loop round 1 · p1 release-line card (p0 for the 18.4.0 window, triage 6843390006), taken on epic seat 1's request 6843066299 (mailbox #32972) · card thread re-post of 6843090856 · 2026-11-13T04:17Z
Session:
session_016WV4cz4sWiJgVrx7vhMJSfAccount:
os-elon-muskBranch:
claude/project-thread-k9j7azWorktree:
objectstack-issue-33003Domain:
domain:serverSeat:
epic:#31527#2File surface:
packages/metadata/src/metadata-manager.ts,packages/objectql/src/plugin.ts, their tests, one changesetContainer & model: M,
mode:thread, model: default (no path-derived mandate,dispatch-gates --tieron the two files above), effort: highClause-②: no
Metadata-protocol: none
Responsibility: platform code in objectstack (
unregisterPackage/collectPackageMembersand the ObjectQL late-manifest bridge, pluspublishPackage/revertPackage) persists the host stamp into an environment row; no platform path covers it after #32999; reached by any environment that rewrites a stamped row and then unregisters the host package — a deletion once #32198 shipsThread-read: 6843390006
Premise: holds (triage contract review 6842932691, should-fix S1 of PR #32999; the dev measures every writer first, as the card asks)
Serial constraints cleared: PR #32999 (card #32986, same file
metadata-manager.ts) — the branch is stacked on its heada43fc25033, the maintainer's word on the seat's decision card at 2026-11-13T04:02Z (an exception to the no-stacking rule for this p0 only). The draft PR opens after #32999 merges, withorigin/mainmerged once.objectstack-fleet commentedon Nov 13, 2026
epic:#31527#2 · os-elon-musk · session_016WV4cz4sWiJgVrx7vhMJSf
os-dev-report
{ "issue": 33003, "status": "done", "branch": "claude/project-thread-k9j7az", "pr": "https://github.com/objectstack-ai/objectstack/pull/33036", "session": "session_016WV4cz4sWiJgVrx7vhMJSf", "premise_still_valid": true, "summary": "Writers measured on #32999's head: the late-manifest bridge ownPreviousCopy arm and restoreExtendedObjectCopies, ObjectQL's object-copy follow (registerInMemory), publishPackage, revertPackage, and importMetadata merge (not on the card). Route, both halves at the one choke point: writeRegistryEntry gives a copy that replaces a marked environment row, was not handed out by a loader and carries the same stamp, the row's mark (rewritesEnvironmentRow); register stores a marked row without the host stamp, top level and envelope `metadata` body (withoutEnvironmentRowStamp). Membership alone cannot pass the reboot pin: once persisted, a rewritten environment row and the host's own seeded row are the same bytes. The registry entry keeps the stamp, so ObjectQL's owner does not move. Stacked on #32999 a43fc250 per the maintainer; after #32999 merged, origin/main merged once at f64ffdce (conflict = #32999's own squash, resolved to the branch side).", "tests": "Head f64ffdce. metadata database-loader.test.ts [#33003] 22 cases (6 writers x in-process / after-reboot / CONTROL host's own rows, plus 4 CONTROLs; rows include a draft object (N1) and page/report (N2; flow is package-slotted, outside this rule)); objectql plugin-late-bridge-environment-row.test.ts 3 cases through the plugin's real bridge and restore. pnpm --filter @objectstack/metadata test: 76 files, 2111 passed. pnpm --filter @objectstack/objectql test: 591 files, 12742 passed. Ablations via scripts/ablation-replace.mjs, fix committed first, each restore blob==HEAD and git diff HEAD empty: A carry deleted 11 failed/11 passed; B stamped body saved 5 failed (exactly the reboot pins); C metadata-body strip dropped 1 failed (publish+revert reboot); D loadedFrom guard dropped 1 failed (re-saved host row CONTROL); E stamp-equality dropped 1 failed (other-package CONTROL). objectql pin reads metadata dist: A and B each rebuilt @objectstack/metadata, 2 failed/1 passed (CONTROL green); restore leg rebuilt, preflight --absent clean, 3 passed.", "gates": "On f64ffdce: dispatch-gates --commands 65 families (same list pre/post merge), all run, every exit 0 recorded; --ran: 65 derived, 65 run, 0 NOT-MEASURED (derived zero). check-plugin-teardown-shape --self-test first exit 3 on 4bfa8c2f (shallow clone, pinned fixture commit missing), passed after fetching it (48 cases), and passed on f64ffdce. Closure build VERDICT command-exit 0; metadata + objectql typecheck VERDICT command-exit 0; pnpm lint exit 0. CI check-runs on #33036: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "3 — relay: POST issues/33003/comments (claim re-post 6843768938), pr_create #33036 (with assignee), POST issues/33003/comments (this report); plus git push (not REST)", "open_questions": [ { "question": "A rewritten environment row is now stored without `_packageId: HOST` (main and 18.3.0 stored the stamp). That is the stored state of a row nothing rewrote, so readers of the stored body (the next boot pass, history snapshots, any direct sys_metadata reader) now answer for a rewritten row what they already answer for an unrewritten one. One edge differs from 18.3.0: a host that stops declaring its package between boots reads such a row package-less, where 18.3.0 read the persisted stamp. Accept as Clause-② no?", "options": [ "A accept: Clause-② no stands; the persisted stamp was the defect's carrier, and the unrewritten-row answer is the environment's own", "B declare the stored-body difference as a moved answer (Clause-② yes) and add a changeset sentence" ], "recommendation": "A, because no in-process reader moves (the registry keeps the stamp), 18.3.0's unregisterPackage keeps these rows either way, and the edge needs a host configuration change between boots" } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed: publishPackage snapshots `item.data.metadata ?? item.data`, so a flat item's publishedDefinition nests the previous publish's snapshot (pre-existing, #22113 shape)", "carrier: none · noted, not filed: the late-manifest bridge persists the extender's merged fields into the owner's stored row until restoreExtendedObjectCopies (pre-existing #24182 design)" ] }Generated by Claude Code