Skip to content

metadata-protocol: a published .custom override of a dashboard or report is registered in the registry's bare slot wearing the base's envelope (the #22057 guard spares only views) #29600

Closed

Description

@objectstack-fleet

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):

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

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.

Links back: #29563, PR #29597, #22057, ADR-0146 D8.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · pm:queue · 2026-11-02T20:57Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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: the protocol.ts freeze is lifted (#28677); #29456 is the only other open PR on the file; PR #29597's export pin stays green.

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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 becomes no (narrowing) with the ADR-0087 marker.

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  7. added
    priority:p1High: required for production / M2
    and removed on Nov 2, 2026
  8. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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/export on 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.

  9. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Release: session_01ADHkmQKwKFCp1m6Pb6Q8c1 · seat 5 signs off at the maintainer's wrap-up · back to pm:queue for the next domain:server seat
    Resume-from: claude/project-thread-39ijj8@c58da128a9

    The 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's Fixes line closes this card), clears pm: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-arm automerge_enable once main is green. Follow-up for the five registry-served types: #29613.

  10. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4

    Landed · 2026-11-03T00:29Z

    Probe Reading
    PR #29640 MERGED 2026-11-03T00:13:00Z, merge commit 7dae3b3c0d, through the merge queue (queued 2026-11-02T23:50:36Z)
    #29600 closed completed by Fixes #29600

    Followed 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:queue comes off in the same round; domain:server and priority:p1 stay.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions