Repository navigation
Description
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS
Filing gate: ① an out-of-scope finding of the #29563 dev (os-dev-report 6588016343, out_of_scope_findings class b), accepted by this seat on PR #29597. Filed by domain:server seat 4. ⛔ Not a claim. Suggested grade: p2 (PR #29597 closes the visible symptom at the export door; the registry state behind it stays wrong for every other reader of the per-package layer).
⛔ Hold: the fix is in packages/metadata-protocol/src/protocol.ts, frozen by server seat 5 until 2026-11-03T06:00Z at the latest. Dispatch after the freeze lifts.
What happens
Measured by the #29563 card thread over the real protocol and engine (the harness of PR #29597's new pin, reading engine.registry for dashboard and report):
- A published
.customrow of a name the base ships is hydrated byhydrateOverlayIntoRegistry(protocol.ts) and registered withregistry.registerItemin the bare slot, wearing the base's artifact envelope:_packageIdis the base's,_provenanceispackage, soisCodeArtifactBodyreads it as the base's own code document.lookupArtifactItemfalls back to the base's code artifact, which is where the envelope comes from. readFlattenedMetaItems' per-package layer then lists that copy under the base, beside the base's own item.- The finding(metadata-protocol): on an unscoped kernel, a package-bound stored row of a view name two packages ship is hydrated into the registry's bare slot, so a by-name read naming the other package serves that row's body under its own _packageId #22057 guard ("a VIEW row bound to one package, never under a name another ships",
protocol.ts~L11916 and ~L12089) sparesviewonly. Every other overlay type (dashboard, report, and any type the same hydrator handles) takes the bare slot this way.
Visible consequence before PR #29597: GET /packages/BASE/export carried two items per overridden dashboard or report, the second with the edited label and the overrides block (ADR-0146 D8 item 3 says the base export is unchanged). PR #29597 filters such items at assemblePackageManifest; this card removes the cause.
Wanted
- The hydrator registers a published
.customoverride under the pair's envelope, never the base's, for every overlay type, as finding(metadata-protocol): on an unscoped kernel, a package-bound stored row of a view name two packages ship is hydrated into the registry's bare slot, so a by-name read naming the other package serves that row's body under its own _packageId #22057 already does forview. The minimal change named by the dev: widen the finding(metadata-protocol): on an unscoped kernel, a package-bound stored row of a view name two packages ship is hydrated into the registry's bare slot, so a by-name read naming the other package serves that row's body under its own _packageId #22057 guard beyondview. - Pin per type (view, dashboard, report at least): after a published override, the per-package layer lists the base's own item only under the base, and the override only under the pair.
- Once it lands, PR fix(runtime): the base's package export leaves out its pair's dashboard and report overrides (#29563) #29597's export filter becomes a belt-and-braces guard; keep its pin green either way.
Serial: protocol.ts freeze (server seat 5), and any card holding the #22057 region at the time.
Dedupe: searched hydrateOverlayIntoRegistry bare slot, 22057 view guard envelope, override registered under base envelope; nearest #22057 (closed, view only), #29563 (the export symptom). New.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
pm:queue· 2026-11-02T20:57Z⛔ Not a claim.
.customoverride of a dashboard or report is registered in the registry's bare slot, wearing the base's envelope. The finding(metadata-protocol): on an unscoped kernel, a package-bound stored row of a view name two packages ship is hydrated into the registry's bare slot, so a by-name read naming the other package serves that row's body under its own _packageId #22057 guard spares only views. This is the registry face of the contract break packages: the base export carries a published dashboard or report override from its .custom pair, where ADR-0146 D8 item 3 says the base export is unchanged #29563 named. p2.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1
Claim: PM loop · card thread · #29600 · 2026-11-02T21:16Z
Seat: domain:server#5
Branch: pending (the card thread reports it; a second claim names it)
Order: measure each overlay type's registration on main, then fix at the hydrator (the #22057 guard beyond
view), with a pin per type. One PR, draft,Fixes #29600. Serial: theprotocol.tsfreeze is lifted (#28677); #29456 is the only other open PR on the file; PR #29597's export pin stays green.objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1
Claim: PM loop · card thread · #29600 · 2026-11-02T21:19Z (names the branch for the pending claim above)
Seat: domain:server#5
Branch: claude/project-thread-39ijj8
Clause-②: no
Metadata-protocol: none
Order: as in the pending claim. Measure each overlay type's registration on main, then fix at the hydrator (the #22057 guard beyond
view), with a pin per type. One PR, draft,Fixes #29600. If the measurement shows a reader loses a served reading, the declaration becomesno (narrowing)with the ADR-0087 marker.objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQWkCUmHFTi97EGNu1
os-dev-report
{ "issue": 29600, "status": "needs_decision", "branch": "claude/project-thread-39ijj8", "pr": null, "session": "session_01ADHkmQWkCUmHFTi97EGNu1", "premise_still_valid": true, "summary": "Measured on origin/main 0ab4a09e over the real ObjectQL engine and the real protocol (dist), per overridable type, after a draft save into the .custom pair declaring the override plus the per-item publish. The premise holds for every type but view: the hydrator puts the published override in the registry's bare slot wearing the base's envelope (_packageId = base, _provenance = package), and the per-package layer naming the base lists two items (the shipped one and the override). The stop in order step 2 is hit: for page, app, dataset, doc and book the bare-slot registration IS the served override. With the #22057 guard widened to those types, getMetaItem (no package, base, pair), getMetaItems (no package, base) and GET /me/apps (registry.getAllApps) all go back to the shipped body. No PR opened, as the order says. For dashboard, report and email_template the widened guard loses no protocol read (getMetaItem, effective list, layered read all still serve the override), so a partial fix is ready to land if option A is ruled.", "measurement": { "tree": "objectstack-ai/objectstack @ 0ab4a09e6525823c4b4b8dfa989f394b0a68b716; harness: ObjectQL + in-memory driver + ObjectStackProtocolImplementation from dist, base registered by registerApp, pair installed by installPackage, override = saveMetaItem(draft, packageId PAIR, overrides {package BASE}) then publishMetaItem", "legend": "S = shipped body, O = override body; slot/envelope = label/_packageId/_provenance", "view": "bare slot: not registered (#22057). per-package BASE [S/BASE], PAIR [O/BASE (#28831 stamp)]. getMetaItem none/BASE/PAIR = O. effective list none/BASE/PAIR = [O]. layered(BASE).effective = O.", "dashboard": "bare slot: O/BASE/package. per-package BASE [S/BASE, O/BASE] (the defect), PAIR [O/BASE]. getMetaItem = O everywhere. effective list = [O]. layered = O.", "report": "same as dashboard.", "email_template": "same as dashboard (key showcase_email@en-US).", "page": "bare slot: O/BASE/package. per-package BASE [S, O], PAIR []. getMetaItem = O everywhere. effective list none/BASE [O], PAIR []. layered(BASE).effective = S.", "app": "same as page; registry.getAllApps lists [S, O] and GET /me/apps keeps the last by name = O.", "dataset": "same as page.", "doc": "same as page.", "book": "same as page.", "translation": "not measurable through the pair: the save is refused NOT_OVERRIDABLE (the base ships no translation of that name in this harness; registerApp does not register `translations:`).", "flow": "not measured: the hydrator already registers a stored flow with no artifact envelope (Regime C, #20913), and the boot pull arms the declared flow override from the bare slot (#28717), so flow is outside this defect and must stay outside any widening.", "action": "not measured: the override save is refused (ADR-0146 D9 not yet available).", "widened_guard_readings": "Experiment A (skip the bare slot for a bound row whose own package ships no artifact of the name while another does, all types but flow): dashboard/report/email_template change only registry-direct reads (getItem bare/BASE/PAIR O → S, listItems(BASE) [S,O] → [S], per-package BASE [S,O] → [S], per-package unscoped [O,O] → [S,O]); every protocol served read unchanged. page/app/dataset/doc/book additionally lose getMetaItem (none/BASE/PAIR O → S), effective list none/BASE (O → S) and /me/apps (O → S).", "envelope_only_readings": "Experiment B (keep the bare slot, register it under the row's own binding when the artifact is another package's): per-package BASE fixed for all 8 types, getMetaItem and /me/apps keep O, but the effective list none/BASE for page/app/dataset/doc/book goes O → S. Also a loss, so not a way around the stop." }, "tests": "No committed test. Measurement file run under the lock (pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/zz-measure-29600.scratch.test.ts): main 1/1, experiment A 1/1, experiment B 1/1, each after OS_SKIP_DTS=1 pnpm --filter @objectstack/metadata-protocol build with the experiment marker counted in dist/index.js (1, then 0 after restore). Source restored with git checkout HEAD (git status clean), dist rebuilt from HEAD, scratch file removed (kept in the session scratchpad).", "gates": "n/a — no diff, no PR (needs_decision stop).", "dogfood_battery": "n/a — no PR.", "files_changed": [], "line_budget": "n/a", "deviations": ["Builds for the two experiments used OS_SKIP_DTS=1 (dist JS only); the worktree's metadata-protocol dist carries no d.ts until the next full build. Local worktree only."], "mcp_calls": "4 — mcp__github__issue_read (get, get_comments) on #29600, mcp__github__pull_request_read get_files on #29597 and #29456. No write tool.", "api_writes": "1 — this os-dev-report comment (POST issues/29600/comments via post-stamped relay).", "open_questions": [ { "question": "The five types page, app, dataset, doc and book serve a published pair override only through the bare-slot registration (getMetaItem, getMetaItems, GET /me/apps). How should #29600 proceed?", "options": [ "A — Land the cause fix now for the types whose served reads already come from the stored rows: widen the #22057 guard from view to view + dashboard + report + email_template, for a row whose bound package ships no artifact of the name while another package does (the #4624 two-shipper case keeps its bare entry). Measured: no protocol read changes; only registry-direct reads stop seeing the override under the base. PR says Part of #29600; page/app/dataset/doc/book move to a follow-up card that first gives those types the row-served override (getMetaItem, both lists, /me/apps, layered) and then widens the guard to them. Cost: one small PR now (+ per-type pins and ablation), one larger card later.", "B — One PR that does both: extend the row-served override path to page/app/dataset/doc/book and /me/apps, then widen the guard for all eight types. Cost: much larger diff across the by-name read, the list merge and a runtime/plugin reader outside this card's file surface; more risk of moving served reads.", "C — Keep the bare-slot registration and only stop it wearing the base's envelope (register under the row's own binding). Measured: still loses the effective list override for the five types (O → S), so it is a narrowing; not recommended." ], "recommendation": "A, because it removes the cause for the four row-served types with zero measured reader loss, keeps #29597's export pin and the #4624 pin green by construction, and the five registry-served types need their served path built before anything can be taken out of the bare slot." } ], "security_findings": "none", "out_of_scope_findings": [ "class: b · reach: protocol layer the export door reads (getMetaItemsPerPackage, scoped to the pair) measured on 0ab4a09e; ADR-0146 D8 item 3 says the pair's export carries the overriding item · evidence: for page, app, dataset, doc and book the per-package layer naming the pair lists nothing after a published declared override (dashboard/report/email_template/view list it) · dedupe words: pair export missing override, per-package layer pair empty, customization export page app dataset", "class: b · reach: getMetaItemLayered naming the base measured on 0ab4a09e · evidence: for page, app, dataset, doc and book the layered read's effective layer serves the shipped body while getMetaItem naming the base serves the published override (ADR-0146 D8 item 2 names the layered read's effective among the reads that serve the override) · dedupe words: layered effective override, getMetaItemLayered pair override, D8 item 2 layered" ] }Stamped 2026-11-02T21:35Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1
Seat ruling · open question 1 of the card report (6589050829) · 2026-11-02T21:38Z
A. The split is a scoping call inside the card's goal, so the seat makes it. It is not a product decision.
view(unchanged), plusdashboard,reportandemail_template. The finding(metadata-protocol): on an unscoped kernel, a package-bound stored row of a view name two packages ship is hydrated into the registry's bare slot, so a by-name read naming the other package serves that row's body under its own _packageId #22057 guard widens to them only for a row whose bound package ships no artifact of the name while another package does, so the loadMetaFromDb boot hydration keeps a third inline copy of the overlay→registry rule with an UNSCOPED artifact lookup (ADR-0048 gap) #4624 two-shipper case keeps its bare entry.Fixes #29600, with the card re-scoped by this ruling.Clause-②: no,Metadata-protocol: none.page,app,dataset,docandbookserve the override only from the bare slot, so taking them out first needs a row-served path. They move to follow-up metadata-protocol: page, app, dataset, doc and book serve a published override only from the registry bare slot; build the row-served path, then take them out of it #29613, together with the report's two class-b readings (the pair's per-package layer is empty, and the layered effective layer differs fromgetMetaItem).chore(changeset): #29600 pair override bare-slot envelope
test(objectql): email-template member doors read a pair override's se…
objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQWkCUmHFTi97EGNu1
os-dev-report
{ "issue": 29600, "status": "done", "branch": "claude/project-thread-39ijj8", "pr": "https://github.com/objectstack-ai/objectstack/pull/29640", "pr_state": "draft", "head": "c58da128a9b58e5d34788a9cabe48c1a87c1590f", "session": "session_01ADHkmQWkCUmHFTi97EGNu1", "premise_still_valid": true, "ruling": "seat ruling A (issuecomment-6589126548): widen the #22057 guard to dashboard, report, email_template only; no protocol read moves; page/app/dataset/doc/book to #29613", "summary": "hydrateOverlayIntoRegistry now declines the registry's bare slot for a dashboard, report or email_template row whose bound package ships no artifact of the name while another package does (declinesBorrowedEnvelope in protocol-package-bindings.ts, closed list of three types). View keeps its #22057 limb, the #4624 two-shipper case keeps its bare entry, flow and the five registry-served types are untouched. Measured before/after with the same harness: only registry-direct reads move for the three types (bare entry gone; getItem bare/BASE/PAIR O->S; listItems(BASE) [S,O]->[S]; per-package BASE [S,O]->[S]; unscoped per-package [O,O]->[S,O]). Every protocol served read (getMetaItem none/BASE/PAIR, getMetaItems none/BASE, getMetaItemLayered(BASE).effective, /me/apps) is unchanged for every type. Table in the PR body.", "files_changed": [ ".changeset/29600-pair-override-bare-slot-envelope.md (new, patch @objectstack/metadata-protocol)", "packages/metadata-protocol/src/protocol-package-bindings.ts", "packages/metadata-protocol/src/protocol.ts", "packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts (new, seat-approved; objectql because the pins need the real engine and a metadata-protocol test cannot import objectql)", "packages/objectql/src/environment-overlay-email-locale-restart.test.ts (4 seat-approved forced edits, named in the PR body)" ], "line_budget": "+424 / -17 over 5 files (441 changed lines, under the 3000 human-merge threshold); source change is the guard function plus one call line, the rest is docblocks and tests", "tests": "New pin file 9/9 (view, dashboard, report, email_template x per-package layer + served reads, plus the #4624 control). Ablation of the guard to view-only (marker in dist confirmed, restore proved by empty git diff HEAD and ablation-dist-preflight --absent exit 0): pin file alone 3 red / 6 green; pin file + email-locale file 6 red / 44 green. On head c58da128a, all under os-verify-lock with VERDICT command-exit 0: full build 79/79; metadata-protocol 308 files / 22257 tests; objectql 542 files / 10517 tests; typecheck metadata-protocol + 11 dependents 12/12; dogfood 17 files / 138 tests; runtime 6/59, cloud-connection 3/27, cli 2/118, plugin-email 48/785; eslint --no-inline-config on both packages exit 0.", "gates": "dispatch-gates --commands on c58da128a: 62 derived, 62 run, all exit 0; --ran reconciliation 0 NOT-MEASURED, 0 UNRUN.", "dogfood_battery": "n/a: no breaking marker in the title and no narrowing arm; the 17 touched dogfood files ran locally (listed in the PR body).", "mcp_calls": "6 reads, no write: issue_read get + get_comments on #29600 (x2 get_comments across the session), pull_request_read get_files on #29597 and #29456, pull_request_read get on #29640.", "api_writes": "3: the earlier needs_decision os-dev-report on #29600, the pr_create via the fleet-write relay (run 41545685778, read-back identical), and this os-dev-report. Git pushes to the branch are not REST writes.", "deviations": [ "The measurement experiments built metadata-protocol with OS_SKIP_DTS=1 (dist JS only); the full rebuild on the merged head restored the d.ts. Local worktree only.", "Ablation round 1 used a mutation that failed the metadata-protocol build, so it ran no tests; restored clean and not counted. Rounds 2 and 3 are the readings quoted.", "The relay pr_create carried no assignees key, as the order says; the pull lists no assignee." ], "security_findings": "none", "follow_up": "#29613 holds page, app, dataset, doc and book (row-served override path first, then the guard) and the two class-b findings below.", "out_of_scope_findings": [ "class: b · carried by #29613 · for page, app, dataset, doc and book the per-package layer naming the pair lists nothing after a published declared override (ADR-0146 D8 item 3)", "class: b · carried by #29613 · for the same five types getMetaItemLayered(BASE).effective serves the shipped body while getMetaItem serves the override (ADR-0146 D8 item 2)" ] }Stamped 2026-11-02T23:29Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
2026-11-02T23:52Z
Priority raised p2 → p1. Provenance: the maintainer, direct chat with the epic PM seat, 2026-11-03: 「相关卡 p1, 让车道处理」, naming the cards related to the run #29648.
Why it reaches this card: #29649 is the live finding that a published dashboard or report override leaves the item absent from
GET /packages/BASE/exporton a real showcase boot, since PR #29597's filter. That is the registry state this card describes. Please measure this card's fix on a real boot against #29649's reproduction, and say on #29649 whether it closes it. Assignee and state are unchanged.objectstack-fleet commentedon Nov 2, 2026
domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1
Release:
session_01ADHkmQKwKFCp1m6Pb6Q8c1· seat 5 signs off at the maintainer's wrap-up · back topm:queuefor the nextdomain:serverseatResume-from:
claude/project-thread-39ijj8@c58da128a9The maintainer's words, verbatim: 「server 已经提交了代码的,现在就可以下班。后续跟进交给下一任。」 (2026-11-02T23:54:00Z). This is the default hand-back; seat 5 keeps no stay-behind.
Next: PR #29640 is ACCEPTed (seat record on the PR, head
c58da128a9), every check-run is green, and it is ready with auto-merge armed (merge queue). The next seat watches it to MERGED, posts Landed here (the PR'sFixesline closes this card), clearspm:queue, and has the coordinator resolve the card thread. ⛔ Do not re-dispatch while #29640 is open. If the queue ejects it on a red that is not its own, re-armautomerge_enableonce main is green. Follow-up for the five registry-served types: #29613.objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Landed · 2026-11-03T00:29Z
7dae3b3c0d, through the merge queue (queued 2026-11-02T23:50:36Z)Fixes #29600Followed to MERGED by the seat 5 successor (
os-warren, opening mark 6591861808) per the previous holder's hand-off on the PR and brief 6591705744. Seat records: ACCEPT and hand-off note on PR #29640 (os-steve,session_01ADHkmQKwKFCp1m6Pb6Q8c1).pm:queuecomes off in the same round;domain:serverandpriority:p1stay.