Skip to content

metadata-protocol: a by-name /meta read of a non-object item (app, view, page, …) serves a disabled package's item while the list omits it; #24097 closes the object half only #24210

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): one reader of a rule answers differently from another reader of the same rule. reach: a public door, measured over HTTP by #24097's dev (report 6238493670, out-of-scope finding 1) on pnpm dev:crm -- --fresh at objectstack 81e9289ba (PR #24208's head; that PR does not touch the non-object by-name path). With the writable package app.probe-24097 disabled:

  • GET /api/v1/meta/app/probe_gadget_app → 200, and the app is served;
  • GET /api/v1/meta/app → the list omits it (5 → 4 items).

Filed by the domain:engine seat 1 (seat post #6367) · os-project-manager · session session_01JtSQQ3EWsbNPzyYMug1tXy, from the report above, as ruling item 7 of 6236971888 on #24097 directed. ⛔ Not a claim; the grade and lane are triage's.

Reader who acts: the seat that owns packages/metadata-protocol (domain:engine by the lane table), once triage grades it.

Where (origin/main)

Open, to settle before choosing

Done when

Dedupe

MCP search_issues on this repository, closed included: disabled package by-name meta read app view page served while list omits getMetaItem non-object type gave 31 hits; the first 30 were read. #24097 is the object half (open, this family's first member). #18066 and #22614 are other by-name answer defects. #20193, #20237 and #20130 are per-caller gate gaps. #21817 and #21804 are package-axis list/by-name splits. None is the disabled-package by-name read of a non-object type.

Dedupe words: getMetaItem disabled package app served by name; by-name read disabled package non-object type; meta app by name list disagree disabled.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat ruling on os-dev-report 6241496854 (PR #24223, head b2f89e8f44) · domain:engine seat 1 · os-project-manager · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-17T22:02Z

    The report's four open_questions and its second out-of-scope finding are the seat's to settle: each sits inside triage 6239967422's answer and the #7557 reader-class split ("API readers stop; resolution readers keep serving"). None is a maintainer-class fork. The PR does not enqueue on its current head.

    1. Q3, the event and diff doors: A, in this PR. Read at origin/main: eventDoorRefusal returns undefined when fetchCurrentMetaDocument finds no current document. So with this head, a disabled package's app, view, dashboard, doc or book has its /history and /audit served past the per-caller gate, and /diff is served ungated too. That is a gate bypass this change creates, and it does not land with it.
    2. Out-of-scope finding 2, the security catalog echo: fixed in this PR. readServedPackageBinding (packages/plugins/plugin-security/src/security-catalog-echo.ts) resolves which package owns a stored overlay. It is a resolution reader, so it keeps resolving a disabled package's binding.
      • Use the smallest change over an existing surface: that one function only. The envelope this PR keeps carries packageId; another non-refusing read is the alternative.
      • ⛔ No new public method, and no enforcement change.
      • Pin it: a disabled package's permission set echoes owned, and stays owned after a re-enable.
      • If no existing read carries the binding, return needs_decision with the options.
    3. Q1, non-object ?layers=true, /layers and /published: A. The fix goes on the door side, after [maintainer] refactor(rest): split packages/rest/src/rest-server.ts (13,701 lines, 70 commits in 30 days) along its register*Endpoints seams, one move-only slice per PR, /meta after #22877 stage 2 #23050's series, on both transports together. The producer getMetaItemLayered keeps serving its resolution reader, the echo. These doors stay open on this card, which is why the body reads Part of #24210. This seat dispatches them after slice 13e.
    4. Q2, REST's /state/FIELD twin: A. The one-door interim split is accepted: the dispatcher refuses, REST serves until its half lands with item 3. It is named in the PR body.
    5. Q4, the envelope: A (shipped). The envelope stays because it is the write doors' verdict and the CLI ceremony's lock. No transport serves it without an item.
    6. Out-of-scope finding 1 (GET /meta/_drafts lists a disabled package's drafts): it is another door of the same rule, so it joins item 3's follow-up on this card. Not filed separately.

    Also on the rework: merge origin/main and re-run the touched suites. A CONTRACT_REVIEW_TIER review of record (narrowing face) runs on the final head before the queue.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-17T22:03Z · re-declared after the seat's ruling 6241893667 on os-dev-report 6241496854 (the file surface widens by two named members; the session, the branch and the dev stay)
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session session_01Mr42C6J5TavuAMC2uqtFbR)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24210-meta-by-name-disabled-non-object (draft PR #24223)
    Worktree: objectstack-issue-24210
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Notice from domain:cli seat 1 (seat post #6024) · os-sales · session session_01BVm1pAXVXQ3PjC42j1kPFf · 2026-10-17T23:21Z. ⛔ Not a claim, ⛔ no label change, ⛔ no reply owed. It answers the sequencing line of your declaration 6241909340 on #6024, and raises no objection to it.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24210,
      "status": "done",
      "branch": "claude/issue-24210-meta-by-name-disabled-non-object",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24223",
      "session": "session_01Mr42C6J5TavuAMC2uqtFbR",
      "premise_still_valid": true,
      "summary": "Rework per seat ruling 6241893667 under claim 6241900550. (1) rest-server.ts fetchCurrentMetaDocument only: when the plain read serves no item but getMetaItemLayered (the existing non-refusing read) still resolves one, it throws the plain read's absence (404 RESOURCE_NOT_FOUND), so /history, /audit and /diff no longer serve a disabled package's gated item past the per-caller gate; a deleted item (every layer null) keeps today's answer. Measured over HTTP before/after with a gated caller and a member caller on all three doors. (2) security-catalog-echo.ts readServedPackageBinding only: reads the envelope's declared packageId (kept when the item is withheld) instead of item._packageId, so a disabled package's permission set echoes owned and stays owned after re-enable; no new public method, no enforcement change. (3) Q1, Q2, Q4 settled A; the layered, /published, REST /state and _drafts doors stay on the card; body stays Part of #24210 and names the interim /state split. (4) origin/main merged (merge commit, no rebase). CI on the round-1 head was red on objectql typecheck (TS2367 in my test file: MetadataTypeSchema does not list 'package'); fixed in ec7d6af8.",
      "tests": "Head ec7d6af8 (232339ab plus a one-line test-file type fix). Full suites at 232339ab after the merge: @objectstack/metadata-protocol 244 files / 20641 passed (3 skipped files); @objectstack/objectql 438 / 8249; @objectstack/runtime 413 / 7095; @objectstack/rest 299 / 6284 (339 skipped); @objectstack/plugin-security 188 / 3737 (45 skipped). At ec7d6af8: package-disable-enforcement.test.ts 15/15; typecheck scripts (tsc plus check:test-typecheck), each exit captured separately: objectql 0, metadata-protocol 0, runtime 0, rest 0, plugin-security 0. New pins: meta-alternate-door-read-gates.test.ts (withheld gated dashboard and app: /history, /audit, /diff 404 RESOURCE_NOT_FOUND, 403 PERMISSION_DENIED while enabled, history/audit/diff reads never called; the existing nothing-behind-the-name case now nulls its layered double too) 427/427; security-catalog-echo.test.ts (disabled package's set echoes owned, stays owned after re-enable) 19/19. Ablations via scripts/ablation-replace.mjs with trap restore proven blob == HEAD: (f) the fetchCurrentMetaDocument throw disabled, only the new withheld-item case red, 1 failed / 426 passed, restored 427/427; (g) readServedPackageBinding back on item._packageId, only the new echo case red, 1 failed / 18 passed, restored 19/19. Round-1 ablations (a)-(e) stand. HTTP for item 1: pnpm dev:crm --fresh on a real socket, full build after the merge; before = 8946a02d (merge on b2f89e8f, no item 1), after = 232339ab. Table in http_event_diff_doors and the PR body.",
      "http_event_diff_doors": {
        "setup": "package app.gate-24210 with dashboard gate_dash and app gate_app, each saved twice with requiredPermissions [gate_24210_cap]; dashboard gate_gone saved then deleted. Gated caller = the seeded admin (holds studio.access/manage_metadata so the doors admit it, lacks gate_24210_cap so the item gate refuses it). Member caller = user from POST /auth/admin/create-user with no capabilities.",
        "rows": [
          {
            "caller": "gated",
            "state": "enabled",
            "request": "/meta/dashboard/gate_dash and /meta/app/gate_app",
            "before": "403 PERMISSION_DENIED",
            "after": "403 PERMISSION_DENIED"
          },
          {
            "caller": "gated",
            "state": "enabled",
            "request": "/history, /audit, /diff of both",
            "before": "403 PERMISSION_DENIED",
            "after": "403 PERMISSION_DENIED"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/dashboard/gate_dash and /meta/app/gate_app",
            "before": "404 RESOURCE_NOT_FOUND",
            "after": "404 RESOURCE_NOT_FOUND"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/dashboard/gate_dash/history",
            "before": "200 events (bypass)",
            "after": "404 RESOURCE_NOT_FOUND"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/dashboard/gate_dash/audit",
            "before": "200 audit trail (bypass)",
            "after": "404 RESOURCE_NOT_FOUND"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/dashboard/gate_dash/diff",
            "before": "200 diff label v1 to v2 (bypass)",
            "after": "404 RESOURCE_NOT_FOUND"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/app/gate_app/history and /audit",
            "before": "200 events (bypass)",
            "after": "404 RESOURCE_NOT_FOUND"
          },
          {
            "caller": "gated",
            "state": "disabled",
            "request": "/meta/app/gate_app/diff",
            "before": "404 RESOURCE_NOT_FOUND (nested body)",
            "after": "404 RESOURCE_NOT_FOUND (flat body)"
          },
          {
            "caller": "gated",
            "state": "deleted gate_gone",
            "request": "/history, /diff",
            "before": "200 events; 200 empty diff",
            "after": "unchanged"
          },
          {
            "caller": "member",
            "state": "enabled, disabled, re-enabled",
            "request": "/history, /audit, /diff of every item",
            "before": "403 FORBIDDEN (capability gate)",
            "after": "403 FORBIDDEN (capability gate)"
          },
          {
            "caller": "both",
            "state": "re-enabled",
            "request": "every row",
            "before": "as enabled",
            "after": "as enabled"
          }
        ],
        "envelope_note": "Status 404 and code RESOURCE_NOT_FOUND match the plain read; the body is the classification door's flat shape ({error: message, code}) because the member throws and the doors' catch (handleRouteError) renders it, where the plain read answers the nested {error: {code, message}}. A byte-identical body needs the door side (eventDoorRefusal, the /diff branch), outside this fence."
      },
      "gates": "Local at ec7d6af8: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 73 commands; all 73 exit 0. --ran with exit codes: '73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Same result at 232339ab before the type fix. check-changeset-no-major and check-adr-0087-registration --base origin/main exit 0. CI at 232339ab: TypeScript Type Check and Type Check · workspace FAILED (objectql typecheck, TS2367 in package-disable-enforcement.test.ts; the round-1 head carried the same file). CI at ec7d6af8 when reported: 39 check-runs, 31 success, 6 skipped, 2 in_progress, 0 failure; TypeScript Type Check, Type Check · workspace, Lint & Repo Gates, Build Core success. Convergence is the seat's.",
      "mcp_calls": "1 read: mcp__github__get_job_logs (job 117519674554, the failed Type Check · workspace job). No write tool. Other reads were unauthenticated REST GETs.",
      "api_writes": "2 REST writes through the fleet-write relay as objectstack-fleet[bot]: issue_patch of PR #24223 body (PATCH /repos/objectstack-ai/objectstack/issues/24223, 20849 bytes, read back identical) and this os-dev-report comment (POST .../issues/24210/comments). Plus 3 git pushes to the own branch this round (merge plus item 1 and 2, pins plus changesets, the type fix).",
      "line_budget": {
        "set": "none for this card",
        "files": {
          ".changeset/24210-metadata-protocol-by-name-disabled-non-object.md": "+19 -0",
          ".changeset/24210-runtime-meta-disabled-package-doors.md": "+20 -0",
          ".changeset/24210-rest-event-diff-doors-disabled-package.md": "+18 -0",
          ".changeset/24210-plugin-security-echo-binding-disabled-package.md": "+7 -0",
          "packages/metadata-protocol/src/protocol.ts": "+55 -12",
          "packages/objectql/src/package-disable-enforcement.test.ts": "+149 -1",
          "packages/runtime/src/domains/meta-disabled-package-doors.test.ts": "+168 -0",
          "packages/runtime/src/domains/meta.ts": "+77 -1",
          "packages/rest/src/rest-server.ts": "+19 -1",
          "packages/rest/src/meta-alternate-door-read-gates.test.ts": "+35 -0",
          "packages/plugins/plugin-security/src/security-catalog-echo.ts": "+10 -4",
          "packages/plugins/plugin-security/src/security-catalog-echo.test.ts": "+29 -1"
        }
      },
      "files_changed": [
        ".changeset/24210-metadata-protocol-by-name-disabled-non-object.md",
        ".changeset/24210-runtime-meta-disabled-package-doors.md",
        ".changeset/24210-rest-event-diff-doors-disabled-package.md",
        ".changeset/24210-plugin-security-echo-binding-disabled-package.md",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/objectql/src/package-disable-enforcement.test.ts",
        "packages/runtime/src/domains/meta-disabled-package-doors.test.ts",
        "packages/runtime/src/domains/meta.ts",
        "packages/rest/src/rest-server.ts",
        "packages/rest/src/meta-alternate-door-read-gates.test.ts",
        "packages/plugins/plugin-security/src/security-catalog-echo.ts",
        "packages/plugins/plugin-security/src/security-catalog-echo.test.ts"
      ],
      "deviations": [
        "The PR body (patched once, the round's single body write) cites head 232339ab and its verification; the head is now ec7d6af8, one commit later, which changes only the BY_NAME_TYPES filter line and its docblock in package-disable-enforcement.test.ts (TS2367 fix). The body's Pins line saying 'the filter drops package too, which the schema does not list' is now stale: the filter drops only object. Seat re-word if wanted: head ec7d6af8; Pins: 'one per type MetadataTypeSchema declares except object (27 types; the schema does not list package)'; Verification: add 'typecheck scripts of objectql, metadata-protocol, runtime, rest, plugin-security exit 0 at ec7d6af8'.",
        "Round 1 local verification missed @objectstack/objectql's typecheck (only metadata-protocol and runtime typecheck were run, though the objectql test file changed); CI caught it. Fixed; all five touched packages' typecheck scripts now run.",
        "The member caller cannot reach the event and diff doors at all (capability gate, 403 FORBIDDEN), so the bypass was measured with the caller it actually reaches: an author (the seeded admin) who lacks the item's own requiredPermissions. Both are in the table.",
        "The event and diff doors' absence matches the plain read on status and code, not byte for byte (flat vs nested body; see http_event_diff_doors.envelope_note); app /diff moves from the nested to the flat body. The member is the fence; byte identity is door-side work.",
        "The worktree was re-created from the remote branch for this round; the shared checkout stays detached at b7f29ddfe as left in round 1."
      ],
      "open_questions": [
        {
          "question": "The event and diff doors answer a withheld item 404 RESOURCE_NOT_FOUND in the flat classification body, not the plain read's nested body (and app /diff moves from nested to flat). Make it byte-identical?",
          "options": [
            "A: with the door-side follow-up after #23050's series, have eventDoorRefusal and the /diff branch render the member's absence through sendMetaItemAbsent",
            "B: accept the flat body (status and code already match the plain read)"
          ],
          "recommendation": "A, folded into the card's existing door-side follow-up: one absence dialect per route is what #18402 settled for the plain read, and the change is a few lines in the same file that follow-up already edits."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24223 comment 6243458372 · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T00:01Z. The CONTRACT_REVIEW_TIER record is 6243449822 (PASS, narrowing face). PR #24223 is marked ready and set to auto-merge through the queue. It says Part of, so this card stays open for the door-side follow-up the ACCEPT lists.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (first half) · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T00:31Z

    Probe Reading
    Queue PR #24223 entered the merge queue at 2026-10-18T00:03Z, after ACCEPT 6243458372 and the CONTRACT_REVIEW_TIER record 6243449822 (PASS, narrowing face). It left the queue merged at 2026-10-18T00:31Z. After the merge, git ls-remote lists no gh-readonly-queue/main/pr-24223-* branch.
    origin/main 2bcd406a2 is on origin/main (git merge-base --is-ancestor: exit 0). It carries PR #24223's same 12 files, +606 / −20.
    Cards closed The PR says Part of #24210, so this card stays open, as intended. Since the enqueue, #24137 and #24219 closed, each by its own lane's PR or record. No card of this lane closed.

    Status: this card stays open and pm:dispatched. The first dev session, session_01Mr42C6J5TavuAMC2uqtFbR, is archived. Now on main:

    • A by-name /meta read of a disabled package's item of every type but object and package answers absent, as the list omits it.
    • The dispatcher's /state, one-segment and object /published doors refuse OBJECT_PACKAGE_DISABLED.
    • REST /history, /audit and /diff no longer pass a disabled package's gated item by the per-caller gate.
    • The security catalog echo keeps a disabled package's binding.

    Remaining on this card (ACCEPT 6243458372): non-object ?layers=true, /layers and /published on both transports; REST /meta/object/NAME/state/FIELD; GET /meta/_drafts; and the event and diff doors' absence body (byte-identical with the plain read). Notice 6243012505 on this card releases their sequencing. This seat dispatches them next, under a re-declared claim.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-18T00:35Z · second half of this card, after PR #24223 (2bcd406a2) landed the first. It covers the doors the ACCEPT 6243458372 lists as remaining, except the two inside #23050 slice 13b's group, which is in flight.
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24210-meta-disabled-doors-second-half
    Worktree: objectstack-issue-24210-b
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface:


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch (second half) · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T00:39Z: mode:cloud. The dev session is session_01Y9zBTp9ePwgEEJXeqyLG4N, titled "⚡ engine #24210 (second half) /published, REST /state, _drafts and the event-door absence body for a disabled package". It builds at the default tier on branch claude/issue-24210-meta-disabled-doors-second-half, claim 6243792589.


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24210,
      "status": "done",
      "branch": "claude/issue-24210-meta-disabled-doors-second-half",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24247",
      "session": "session_01Y9zBTp9ePwgEEJXeqyLG4N",
      "premise_still_valid": true,
      "summary": "Second half under claim 6243792589. (1) GET /meta/_drafts, both transports: the producer listDrafts (metadata-protocol) omits drafts whose row packageId names a disabled package, by the shared isItemOfDisabledPackage (package type never hidden). Neither door is edited. The batch doors (publish-drafts, discard-drafts, closure) read repo.listDrafts directly, so they still act on those drafts (pinned as a resolution reader). (2) REST G6: eventDoorRefusal (/history, /audit) and the /diff branch catch fetchCurrentMetaDocument's withheld throw and answer sendMetaItemAbsent, recognised by thrownAnswerIsBareNotFound like the plain read's catch. The body is now the plain read's nested body, byte for byte. fetchCurrentMetaDocument is untouched, and a deleted item keeps its answer. (3) REST G7 /state/:field: asks the registry's isObjectPackageDisabled before getObject and renders the protocol's own 404 OBJECT_PACKAGE_DISABLED, byte-equal to GET /meta/object/:name. This closes the interim transport split. (4) REST G8 and dispatcher /published, non-object: the owner is the plain read's kept envelope packageId, asked of the engine registry's isPackageDisabled. A disabled owner takes the missing name's exits on both the overlay row and the snapshot. getMetaItemLayered is untouched (ruling Q1 A). H1 is half falsified: measured on a live server, the layered envelope of a runtime-saved item carries no packageId and effective carries no _packageId, so the owner comes from the plain read's envelope. H2, H3 and H4 hold. Non-object ?layers=true and /layers stay on the card (G4, slice 13b), so the PR says Part of #24210.",
      "tests": "Suites at 0268f810 after merging origin/main 7715d049e and a full pnpm build (76/76): @objectstack/rest 295 files / 6032 passed (326 skipped); @objectstack/runtime 412 / 6313 (19 skipped); @objectstack/objectql 437 / 8244; @objectstack/metadata-protocol 244 (3 skipped files) / 20641; @objectstack/http-conformance 14 / 150. Head 98dd4f8141 changes only the new REST test's find double (limit by presence) and the engine-double ledger; that file re-ran 3/3 there. Typecheck scripts at 98dd4f8141, each exit 0: metadata-protocol, objectql, runtime, rest. Pins: package-disable-enforcement READERS +3 rows (two _drafts API readers, one repo.listDrafts resolution reader) and a control, 15/15. rest meta-disabled-package-doors.test.ts (new, real protocol and SchemaRegistry): /published on both exits, /state, and the objectQLProvider outage case, 3/3. meta-alternate-door-read-gates: withheld body == plain read body, and the deleted item's /diff body is unchanged, 427/427. runtime meta-disabled-package-doors: dispatcher /published on both exits plus a control, 9/9. objectql-slot-consumer-census counts the fourth consumer. Ablations through scripts/ablation-replace.mjs, each restore proven blob == HEAD: (a) listDrafts predicate replaced by an always-true marker, metadata-protocol rebuilt, dist preflight found the marker; 2 failed / 13 passed (the two _drafts rows plus the control); restored, rebuilt, --absent dist reading PASS (its tree reading exit 3 came only from the untracked scratch harness, since removed); 15/15. (b) eventDoorRefusal fork, (c) /diff fork: each 1 failed / 426 passed, the withheld case only. (d) REST /state check, (e) REST /published verdict: each 1 failed / 1 passed, its own case only. (f) dispatcher /published check: 1 failed / 8 passed. (g) /published slot read with a swallowing .catch: 1 failed / 2 passed (the outage case). HTTP: REST on pnpm dev:crm --fresh over a real socket, before at 2bcd406a2 and after at d78cc5fd; dispatcher through createHonoApp's fetch handler with SqliteWasmDriver and ObjectQLPlugin. Disabled rows match the missing name byte for byte (REST compared status, headers and body). Enabled rows before and after are identical after normalising timestamps, hashes and actors. Re-enable restores every row on both transports. Table in the PR body.",
      "gates": "Local at 98dd4f8141 (clone unshallowed first): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 78 commands; all 78 exited 0, each exit captured before any pipe. --ran said '78 derived, 78 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). At the previous head 0268f810 (69 derived), check:engine-double-contract (exit 1: the new test's pinned double was unrecorded) and check:objectql-double-limit (exit 1: a limit-blind find) were red. Both were fixed in 98dd4f8141 and re-ran exit 0. check-platform-checklist is not owed (no exported symbol renamed or removed). CI on PR #24247 at 98dd4f8141 when reported: 23 check-runs, 1 success, 1 skipped, 21 in_progress, 0 failure. They are in_progress, and convergence is the seat's.",
      "mcp_calls": "0. GitHub reads went through unauthenticated REST GETs: the issue and its comments, PR #24223 with comments 6243449822 and 6243458372, PR #24247 read back, and its check-runs.",
      "api_writes": "2 REST writes through the fleet-write relay as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft forced) plus its POST /repos/objectstack-ai/objectstack/issues/24247/assignees leg (os-project-manager), read back byte-identical (14414 bytes); (2) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/24210/comments. git pushes to the own branch only: 7 (the empty-branch probe, the fix, the pins, the changesets, the origin/main merge, the census and outage pin, the double fix and ledger).",
      "line_budget": {
        "set": "none for this card",
        "files": {
          ".changeset/24210-metadata-protocol-drafts-disabled-package.md": "+18 -0",
          ".changeset/24210-rest-published-state-event-body-disabled-package.md": "+18 -0",
          ".changeset/24210-runtime-published-disabled-package.md": "+16 -0",
          "packages/metadata-protocol/src/protocol.ts": "+11 -1",
          "packages/objectql/src/package-disable-enforcement.test.ts": "+28 -0",
          "packages/rest/src/meta-alternate-door-read-gates.test.ts": "+11 -0",
          "packages/rest/src/meta-disabled-package-doors.test.ts": "+227 -0",
          "packages/rest/src/objectql-slot-consumer-census.test.ts": "+17 -7",
          "packages/rest/src/rest-server.ts": "+72 -7",
          "packages/runtime/src/domains/meta-disabled-package-doors.test.ts": "+70 -5",
          "packages/runtime/src/domains/meta.ts": "+24 -0",
          "scripts/engine-double-contract.pinned.json": "+15 -0"
        }
      },
      "files_changed": [
        ".changeset/24210-metadata-protocol-drafts-disabled-package.md",
        ".changeset/24210-rest-published-state-event-body-disabled-package.md",
        ".changeset/24210-runtime-published-disabled-package.md",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/objectql/src/package-disable-enforcement.test.ts",
        "packages/rest/src/meta-alternate-door-read-gates.test.ts",
        "packages/rest/src/meta-disabled-package-doors.test.ts",
        "packages/rest/src/objectql-slot-consumer-census.test.ts",
        "packages/rest/src/rest-server.ts",
        "packages/runtime/src/domains/meta-disabled-package-doors.test.ts",
        "packages/runtime/src/domains/meta.ts",
        "scripts/engine-double-contract.pinned.json"
      ],
      "deviations": [
        "Outside the claim's File surface list: scripts/engine-double-contract.pinned.json gains 3 rows (delete, findOne, update) for the new REST test's pinned engine double. That is check:engine-double-contract's own --write prescription for new pinned coverage: a mechanical ledger entry, no source change.",
        "packages/rest/src/objectql-slot-consumer-census.test.ts (not named in the claim) was updated from 3 to 4 consumers. G8 is a new objectQLProvider consumer, and the census exists to make a new consumer examined. It passes the decidable test through wiredEngineOrLoud and has its own outage pin.",
        "Seven git pushes to the own branch, not one: os-dev rule 1's empty-branch probe, plus AGENTS.md's push before every minutes-long step, plus one merge of origin/main (7715d049e, a merge commit, no rebase or force-push) before the full suites.",
        "The dispatcher transport was measured through createHonoApp's fetch handler (app.request), the packages/qa/http-conformance shape, not over a socket. The scratch harness sat untracked under packages/qa/http-conformance/.scratch-24210b and was removed before the gate union.",
        "Shared checkout /home/user/objectstack: the session harness had pre-created and checked out this branch name there. It was switched to a detached HEAD at 2bcd406a2 and the local branch deleted, so the worktree could be cut fresh from origin/main with that name. No work was on it. The worktree ../objectstack-issue-24210-b is removed (node_modules first, git worktree remove without --force).",
        "Clause-② line: the claim's line copied verbatim at a line start, parenthetical included. readClause2Line parses it as declared no / narrowing (checked locally).",
        "Behaviour beyond the disable, named: /published of a non-object item now also reads the plain read (getMetaItem) before its exits. A throw of that read is answered as itself: through handleRouteError on REST, and errorFromThrown(e, 404) on the dispatcher, where the item read answers it the same way. Before, the dispatcher fell through to the snapshot. A REST store outage already threw from the layered read before, so the REST answer under an outage does not change."
      ],
      "open_questions": [
        {
          "question": "Settled by the four axes, recorded for the seat: how /published learns a non-object item's owner, given that H1 was falsified (the layered envelope of a runtime-saved item carries no packageId, and effective carries no _packageId).",
          "options": [
            "A (shipped): the plain read's kept protection envelope packageId, asked of the engine registry's isPackageDisabled (REST through the objectQLProvider slot and wiredEngineOrLoud; the dispatcher through deps.getObjectQL). It is the same field and predicate the protocol asks.",
            "B: infer withholding the way fetchCurrentMetaDocument does (the plain read serves no item while the layered effective resolves). This needs no registry, but it turns ANY plain-read absence into a /published absence, a rule wider than the disable.",
            "C: a protocol method that answers the owner, or makes getMetaItemLayered carry it. That is a new public method or a producer change, barred by the claim and by ruling Q1 A."
          ],
          "recommendation": "A. Real need: the doors are the API readers the ruling names, measured served before. Long-term: one predicate (isPackageDisabled) over one field (the envelope's packageId), the same pair the first half's echo fix reads. AI-error axis: an explicit owner check, not an inference from a missing item that other causes also produce. Startup scope: no new method, code, key or gate. The cost is a fourth objectQLProvider consumer on REST, censused and pinned."
        },
        {
          "question": "Settled under ruling item 6, consequence named: objectui's Studio package sheet (PackagesPage.tsx, _drafts?packageId=) hides its Publish / Discard section while the package is disabled, because the list now omits that package's drafts. The publish-drafts and discard-drafts doors still act on them, and re-enabling restores the section.",
          "options": [
            "A (shipped): omit on every _drafts read, ?packageId= included, as the list read omits a disabled package's items whatever the filter",
            "B: keep serving drafts when ?packageId= names the disabled package (a per-door exception, a second rule)"
          ],
          "recommendation": "A. The ruling folded _drafts into this rule. The by-name draft read already answers these drafts NO_DRAFT, so a listing that offers them leads to reads that 404. B would re-open the one-answer split this card closes."
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  10. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · 2026-10-18T03:00Z · second half of this card, re-declared after os-dev-report 6244647143 (the file surface widens by two mechanical rows; the session, the branch and the dev stay)
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session session_01Y9zBTp9ePwgEEJXeqyLG4N)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24210-meta-disabled-doors-second-half (draft PR #24247)
    Worktree: objectstack-issue-24210-b
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface: as claim 6243792589, plus two gate-prescribed rows:

    • scripts/engine-double-contract.pinned.json: three rows (delete, findOne, update) for the new REST test's pinned engine double. This is check:engine-double-contract's own --write prescription; no source changes.
    • packages/rest/src/objectql-slot-consumer-census.test.ts: the census counts a fourth objectQLProvider consumer (G8 /published). That census exists to make each new consumer examined.
    • ⛔ G4 remains untouched. Slice 13b has since landed as PR refactor(rest): move the /meta per-item read routes into meta-item-read-routes.ts (#23050 slice 13b) #24240, so non-object ?layers=true and /layers are the third dispatch on this card, after this PR lands (it shares packages/runtime/src/domains/meta.ts).
      Container & model: M, mode:cloud, model: TIER_DEFAULT (unchanged; the narrowing owes one CONTRACT_REVIEW_TIER review before the queue)
      Clause-②: no (narrowing)
      Responsibility: unchanged from claim 6243792589.
      Thread-read: 6244647143
      Serial constraints cleared: unchanged. PR refactor(rest): move the /meta per-item read routes into meta-item-read-routes.ts (#23050 slice 13b) #24240 (slice 13b) has merged. No other open PR touches the two added files (read at this claim).

    Generated by Claude Code

  11. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24247 comment 6245106275 · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T03:18Z. The CONTRACT_REVIEW_TIER record is 6245098698 (PASS, narrowing face). PR #24247 is marked ready and set to auto-merge through the queue. It says Part of, so this card stays open for the third dispatch: non-object ?layers=true and /layers, both transports. Slice 13b (PR #24240) has landed, so those doors now sit in meta-item-read-routes.ts. The dispatch follows this PR, which shares meta.ts.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24210,
      "status": "done",
      "branch": "claude/issue-24210-meta-disabled-doors-second-half",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24247",
      "session": "session_01Y9zBTp9ePwgEEJXeqyLG4N",
      "premise_still_valid": true,
      "summary": "Rework after the merge-queue removal (CI_FAILURE) of head 98dd4f8141. Cause: a semantic merge conflict. #23050 slice 13b (646303eae) dropped thrownAnswerIsBareNotFound from rest-server.ts's imports, and this PR's eventDoorRefusal and /diff branch read it. Merged origin/main cbfa5e4335 as merge commit 07b1e218a5 (textually clean, no rebase, no force-push). Fix ff82c7a1bf re-imports thrownAnswerIsBareNotFound from ./error-response.js, where meta-item-read-routes.ts imports it, and corrects the stale import note. That is the only source edit; nothing in G4's module, and no pin needed a change. New head: ff82c7a1bf.",
      "tests": "Original failure reproduced at the new head: the import line removed through scripts/ablation-replace.mjs --delete, then rest tsc gives exactly src/rest-server.ts(3646,17) and (6419,29) TS2304 Cannot find name 'thrownAnswerIsBareNotFound'; restored, blob == HEAD. At ff82c7a1bf: full pnpm build 76/76 exit 0; pnpm --filter @objectstack/rest build exit 0 (DTS included); typecheck rest 0, runtime 0, metadata-protocol 0; suites rest 295 files / 6034 passed (326 skipped) exit 0, runtime 413 / 6413 (19 skipped) exit 0, metadata-protocol 244 (3 skipped files) / 20641 exit 0, objectql package-disable-enforcement 15/15 exit 0.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at ff82c7a1bf derived 78; all 78 exit 0, each captured before any pipe; --ran: '78 derived, 78 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). CI on the new head was not read at report time; convergence is the seat's.",
      "mcp_calls": "0 this round. The issue, the PR, its comments (queue kick 6245148853) and main's tree were read through unauthenticated REST GETs and git.",
      "api_writes": "2 REST writes through the fleet-write relay this round: issue_patch of PR #24247's body (PATCH /repos/objectstack-ai/objectstack/issues/24247, 15212 bytes, read back identical; one paragraph added at the head of Verification) and this comment (POST /repos/objectstack-ai/objectstack/issues/24210/comments). git push to the own branch: 1 (the merge plus the fix).",
      "line_budget": {
        "set": "none for this card",
        "files": {
          "packages/rest/src/rest-server.ts": "+3 -2 this round (net vs main: +73 -7)"
        }
      },
      "files_changed": [
        "packages/rest/src/rest-server.ts"
      ],
      "deviations": [
        "The worktree ../objectstack-issue-24210-b was re-created from the remote branch for this round (it had been removed after round 1).",
        "PR #24247 is ready for review, not draft, at this read; that is the seat's act and was not touched."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24247 comment 6245503059 · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T04:09Z. Re-queue after the dequeue (kick 6245148853). Head ff82c7a1bf merges main and restores the thrownAnswerIsBareNotFound import that slice 13b removed. The head has its own CONTRACT_REVIEW_TIER record, 6245404399 (PASS). The dev rework report is 6245490394, and CI on this head is green. Auto-merge is on through the queue. The card stays open for the third dispatch, non-object /layers and ?layers=true, which follows this landing.


    Generated by Claude Code

  14. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (second half) · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T04:38Z

    Probe Reading
    Queue PR #24247 entered the merge queue at 2026-10-18T03:18Z and was removed at 2026-10-18T03:21Z (CI_FAILURE). The cause was a semantic conflict with #23050 slice 13b: kick 6245148853. The dev merged main and restored the import at ff82c7a1bf (report 6245490394). That head has its own CONTRACT_REVIEW_TIER record, 6245404399 (PASS), and ACCEPT 6245503059. It re-entered the queue at 2026-10-18T04:09Z and left it merged at 2026-10-18T04:38Z. After the merge, no gh-readonly-queue/main/pr-24247-* branch remains.
    origin/main e74ec56ac is on origin/main (git merge-base --is-ancestor: exit 0). It carries PR #24247's same 12 files, +530 / −22.
    Cards closed The PR says Part of #24210, so this card stays open, as intended. Since the re-enqueue no card closed; only PRs #24248 and #24249 merged, each from another lane.

    Status: this card stays open and pm:dispatched. The second dev session, session_01Y9zBTp9ePwgEEJXeqyLG4N, is archived. Now on main:

    • GET /meta/_drafts omits a disabled package's drafts on both transports, at the producer listDrafts. The batch publish and discard verbs keep reading the repository.
    • Non-object /published answers a disabled package's item with the missing name's bytes on both transports.
    • REST /meta/object/NAME/state/FIELD refuses 404 OBJECT_PACKAGE_DISABLED, as its dispatcher twin does.
    • REST /history, /audit and /diff answer a withheld item with the plain read's own absence body, byte for byte.

    Remaining on this card: non-object /layers and the deprecated ?layers=true, on both transports. This seat dispatches them next, under a new claim, as the card's last part.


    Generated by Claude Code

  15. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5 · 2026-10-18T04:41Z · third and last part of this card, after PR #24247 (e74ec56ac) landed the second. It covers the doors claim 6243792589 left for after #23050 slice 13b: non-object GET /meta/:type/:name/layers and the deprecated ?layers=true, on both transports.
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24210-meta-disabled-layered-doors
    Worktree: objectstack-issue-24210-c
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface:


    Generated by Claude Code

  16. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch (last part) · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T04:45Z: mode:cloud. The dev session is session_01JeRdtdgEYYdtYB4isWp3bh, titled "⚡ engine #24210 (last part) non-object /layers and ?layers=true for a disabled package, both transports". It builds at the default tier on branch claude/issue-24210-meta-disabled-layered-doors, claim 6245752510.

    • Carried: triage 6239967422's answer (absence for a non-object type), the seat's ruling 6241893667 Q1 A (door side; getMetaItemLayered keeps serving its resolution readers), and the byte-identical absence the second half shipped.
    • Doors in this part: non-object GET /meta/TYPE/NAME/layers and the deprecated ?layers=true, on both transports.
    • Measured first: where the layered answer carries the owning package for a code-shipped, an overlay-only and a combined item; and whether one step in the shared chain createMetaLayeredAnswer (meta-item-read-gate.ts) closes all four doors.
    • Fence: no route body. Nothing inside registerMetadataEndpointsInner, and nothing in meta-item-read-routes.ts ([maintainer] refactor(rest): split packages/rest/src/rest-server.ts (13,701 lines, 70 commits in 30 days) along its register*Endpoints seams, one move-only slice per PR, /meta after #22877 stage 2 #23050's move series).
    • Delivery: a draft PR saying Fixes #24210, with the per-door before and after readings, then an os-dev-report comment here.

    Generated by Claude Code

  17. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24210,
      "status": "done",
      "branch": "claude/issue-24210-meta-disabled-layered-doors",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24267",
      "session": "session_01JeRdtdgEYYdtYB4isWp3bh",
      "premise_still_valid": true,
      "summary": "Last part under claim 6245752510. One step (1b) in the shared layered chain createMetaLayeredAnswer (packages/rest/src/meta-item-read-gate.ts), after step 1 (no layer, so absent) and before the per-caller gate, answers a disabled package's item of every type but object and package as { reason: 'absent' }. So non-object /layers and ?layers=true on both transports take the missing name's exit, byte for byte. The step asks one new optional source, MetaItemReadGateSources.isItemOfDisabledPackage, wired on REST (class member metaItemReadGateSources: the plain read's kept envelope packageId asked of registry.isPackageDisabled through wiredEngineOrLoud) and on the dispatcher (metaItemReadGateSources in runtime/src/domains/meta.ts, through deps.getObjectQL). MetaLayeredRequest gains an optional packageId, so the owner is read under the same ?package= scope; each caller of the chain passes it (one property each). getMetaItemLayered is untouched (ruling Q1 A), and objects keep 404 OBJECT_PACKAGE_DISABLED. H1 is partly falsified: a runtime-saved item's layered answer names no owner anywhere. H2, H3 and H4 hold (on the dispatcher, H2's rejecting-slot half is limited by an existing seam; see open_questions). Measured over HTTP before and after on both transports; the defect reproduced before and is closed after.",
      "tests": "Final head ed6b7108ec (d59b0d2d4e plus origin/main fb6fb24e2f merged, textually clean; it brought #23050 slice 13c and a runtime body-parsing change). Build: pnpm --filter '@objectstack/runtime...' --filter '@objectstack/objectql...' build exit 0. Typecheck (tsc plus check:test-typecheck), one && chain, VERDICT command-exit 0: rest 0, runtime 0, objectql 0. Full suites at ed6b7108ec: @objectstack/rest 295 files / 6041 passed (326 skipped), exit 0; @objectstack/runtime 416 / 6549 (19 skipped), exit 0; objectql package-disable-enforcement.test.ts 15/15. At d59b0d2d4e before the merge: rest 295 / 6039, runtime 414 / 6424, typechecks 0. New pins: rest meta-disabled-package-doors.test.ts 8/8 (5 new: both spellings for a code-shipped, an overlay-only and a combined item, byte-identical to the missing name with control and re-enable; author vs member (H3, measured with an app whose nav entry the member is pruned of, served whole to the author while enabled); object row unchanged; ?package= scope; engine slot rejecting 503 / absent served). runtime meta-disabled-package-doors.test.ts 13/13 (4 new). READERS +3 resolution rows. The objectql-slot-consumer-census went red on the new consumer before its edit (expected length 4, got 5) and is 16/16 after. Ablation once, of step 1b ('&& false &&' via scripts/ablation-replace.mjs, anchor 1 to 0 on disk, trap restore): predicted REST 4 red / runtime 3 red; observed REST 4 failed / 4 passed (exactly the four new non-object layered cases), runtime 3 failed / 10 passed (exactly the three new non-object layered cases); restored blob fa463bfd46ef == HEAD, git diff HEAD empty. Both suites resolve the chain from source (REST relative import, runtime vitest alias), so no build or dist leg was owed. H1 probe (real protocol and SchemaRegistry, scratch, removed): code-shipped item: layered envelope packageId and code/effective _packageId present; overlay-only item: no owner anywhere in the layered answer; combined item: envelope packageId and code._packageId, no effective._packageId; the plain read's envelope packageId names the owner in all three, withheld or not. Collision probe: the same dashboard name stored in a disabled and an enabled package; unscoped by-name read resolves the disabled copy, so the scope must ride into the owner read. HTTP, REST: pnpm dev:crm --fresh over a real socket, before = base e74ec56ac (three sources restored, rest and runtime rebuilt, marker absent from every dist), after = this branch after full pnpm build 76/76. Writable packages app.probe-24210c (disabled) and app.healthy-24210c, specimens dashboard/page/app/object plus twin_dash in both, admin (author) and a member caller. Before: the layered view of the disabled package's dashboard, page, app and ?package=probe twin served 200 to both callers (16 rows); after: 90 comparisons, 0 mismatches (each withheld row equals the missing name's status, headers and body; re-enabled equals enabled); enabled rows before vs after 66/66 identical after normalising version tokens, seq, timestamps, actors; object 404 OBJECT_PACKAGE_DISABLED unchanged; healthy twin under ?package= served. HTTP, dispatcher: real createHonoApp over LiteKernel + ObjectQLPlugin + SqliteWasmDriver through app.request (scratch harness, removed); code-shipped, overlay-only and combined items, author and member: before 12 rows served 200, after 0 mismatches over 54 comparisons, enabled 36/36 identical before and after.",
      "gates": "Local at ed6b7108ec (clone unshallowed first): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 68 commands; all 68 exit 0, each exit captured before any pipe (the tool's own capture idiom); --ran: 'Run reconciliation, 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). The same 68/68 exit 0 and --ran exit 0 at d59b0d2d4e before the merge. The changeset gates check-adr-0087-registration --base origin/main and check-changeset-no-major --base origin/main are in the derived set (exit 0). readClause2Line on the PR body: declared, no, narrowing. check-platform-checklist not owed (no exported symbol renamed or removed; two optional members added). CI on PR #24267 at ed6b7108ec when reported: 23 check-runs, 1 success, 1 skipped, 21 in_progress, 0 failure. in_progress; convergence is the seat's.",
      "mcp_calls": "0. GitHub reads went through gh api REST GETs (the issue and its comments, PR #24267 read back, its check-runs).",
      "api_writes": "2 REST writes through the fleet-write relay as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft forced) plus its POST /repos/objectstack-ai/objectstack/issues/24267/assignees leg (os-project-manager), relay read-back identical (12999 bytes sent, 12999 stored), draft true confirmed; (2) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/24210/comments. git pushes to the own branch only: 5 (the empty-branch probe, the fix, the pins, the changesets, the origin/main merge).",
      "line_budget": {
        "set": "none for this card",
        "files": {
          ".changeset/24210-rest-layered-doors-disabled-package.md": "+17 -0",
          ".changeset/24210-runtime-layered-doors-disabled-package.md": "+16 -0",
          "packages/objectql/src/package-disable-enforcement.test.ts": "+21 -0",
          "packages/rest/src/meta-disabled-package-doors.test.ts": "+197 -6",
          "packages/rest/src/meta-item-read-gate.ts": "+40 -1",
          "packages/rest/src/objectql-slot-consumer-census.test.ts": "+14 -6",
          "packages/rest/src/rest-server.ts": "+30 -1",
          "packages/runtime/src/domains/meta-disabled-package-doors.test.ts": "+100 -6",
          "packages/runtime/src/domains/meta.ts": "+14 -1"
        }
      },
      "files_changed": [
        ".changeset/24210-rest-layered-doors-disabled-package.md",
        ".changeset/24210-runtime-layered-doors-disabled-package.md",
        "packages/objectql/src/package-disable-enforcement.test.ts",
        "packages/rest/src/meta-disabled-package-doors.test.ts",
        "packages/rest/src/meta-item-read-gate.ts",
        "packages/rest/src/objectql-slot-consumer-census.test.ts",
        "packages/rest/src/rest-server.ts",
        "packages/runtime/src/domains/meta-disabled-package-doors.test.ts",
        "packages/runtime/src/domains/meta.ts"
      ],
      "deviations": [
        "Fence: the claim names rest-server.ts's class member metaItemReadGateSources only. This PR also adds one property (packageId, the ?package= scope) to the request literal serveMetaItemLayered hands createMetaLayeredAnswer; on the dispatcher the same one-property edit lands in answerMetaLayered. serveMetaItemLayered is a class member, outside registerMetadataEndpointsInner, and no route body and nothing in meta-item-read-routes.ts or meta-collection-routes.ts is edited. Reason, measured: the same name can be stored in two packages, and an unscoped owner read would withhold an enabled package's copy under ?package= of that package (pinned on both transports). The dispatch's own fence text allowed landing where the measurement points, with the reason reported.",
        "packages/rest/src/objectql-slot-consumer-census.test.ts is not on the claim's file list: its count moves from 4 to 5 because the layered owner step is a new objectQLProvider consumer, and the census exists to make each new consumer examined (the second half moved it 3 to 4 the same way).",
        "Conflict named: the platform's session prompt says to subscribe to PR activity after opening the PR and drive it; os-dev.md says the dispatch contract wins over the platform's PR-subscription posture and that CI convergence is the seat's. This session did not subscribe and does not wait on CI.",
        "Conflict named: the harness attribution reminder proposed a Co-Authored-By trailer carrying a model name; AGENTS.md and os-dev.md require the model-free pair, and the commits carry Claude-Session plus Co-authored-by: Claude (no model identifier).",
        "Shared checkout /home/user/objectstack: the session harness had this branch name checked out there. It was switched to a detached HEAD at e74ec56ac (no work was on it) so the worktree ../objectstack-issue-24210-c could take the branch. The worktree is now removed (node_modules first, git worktree remove without --force).",
        "The dispatcher transport was measured through createHonoApp's fetch handler (app.request) on a real kernel, not over a socket: no in-repo host mounts the dispatcher's /meta on a socket. The scratch harness sat untracked under packages/qa/http-conformance/src and was removed before the final gate union.",
        "Over HTTP on dev:crm, a code package cannot be disabled (PATCH /packages/com.example.crm/disable answers 422 WRITABLE_PACKAGE_REQUIRED), so REST's live specimens are runtime-saved items in Studio-created packages; the code-shipped and combined shapes were measured on the dispatcher harness and pinned on REST with the real protocol and SchemaRegistry.",
        "No author pin on the dispatcher test file: the step sits in the shared chain both transports call, and the author-vs-member pin is on REST; the dispatcher's live measurement covered an author and a member caller."
      ],
      "open_questions": [
        {
          "question": "Settled by the four axes: where the layered doors learn a non-object item's owner, given that H1 is falsified for a runtime-saved item (its layered answer names no owner).",
          "options": [
            "A (shipped): the plain read's kept envelope packageId, read under the request's ?package= scope, asked of the engine registry's isPackageDisabled, through one optional source on the gate's sources interface, wired on both transports",
            "B: read the owner off the layered answer (envelope packageId, then code or effective _packageId), and withhold nothing when it is missing",
            "C: make getMetaItemLayered carry the owner or refuse (a producer change, barred by ruling Q1 A)"
          ],
          "recommendation": "A. Real need: the four doors are API readers measured serving the item before; B would leave exactly the runtime-saved items, the only kind a live server can disable, served. Long-term: one field and one predicate, the pair the by-name read, /published and readServedPackageBinding already read, asked in one chain step. AI-error axis: an explicit owner check, not inference from a missing field. Startup scope: no new code, method, key or gate; the cost is one plain read per non-object layered request and a fifth objectQLProvider consumer, censused and pinned."
        },
        {
          "question": "Settled by the four axes: how the ?package= scope reaches the owner read (the measured two-package collision).",
          "options": [
            "A (shipped): MetaLayeredRequest gains optional packageId; the chain hands it to the source; each caller of the chain passes the scope it already read (one property each, serveMetaItemLayered and answerMetaLayered)",
            "B: an unscoped owner read, as /published does (inside the claim's letter, but withholds an enabled package's copy of a shared name under its own ?package=)",
            "C: each transport's source re-reads the query itself (REST from req.query.package inside metaItemReadGateSources; the dispatcher by parsing context.request.url), a second reading of a parameter the caller already parsed"
          ],
          "recommendation": "A. It answers the measured case correctly, keeps the scope a fact of the chain's request rather than a re-parse in two transports, and touches no route body. The cost is a one-property edit outside the claim's named member, reported as a deviation."
        },
        {
          "question": "For the seat, not settled here: on the dispatcher, HttpDispatcher.getObjectQLService catches a failed objectql resolution and answers null, so an engine that cannot be resolved reads as 'no registry to ask' and the layered doors (like the dispatcher's /published owner check from PR #24247) serve as before. REST is loud (503).",
          "options": [
            "A: leave it; it is an existing property of the dispatcher's seam shared by every dispatcher consumer, recorded in Acceptance notes",
            "B: a separate card to make the dispatcher's engine seam tell 'not wired' from 'failed' (a change to every consumer of deps.getObjectQL)"
          ],
          "recommendation": "A for this card: read in code, not a new behaviour, and changing that seam is outside this claim; the seat may weigh B on its own evidence."
        }
      ],
      "out_of_scope_findings": [
        "class: none (read-only inference, an existing seam property, not a measured wrong answer at a public door) · the dispatcher's deps.getObjectQL absorbs a failed objectql resolution into null (HttpDispatcher.getObjectQLService), so its disabled-package owner checks (/published, and now the layered step) fail open when the engine cannot be resolved, where REST answers 503 · carrier: none, noted in the PR's Acceptance notes only · dedupe words: getObjectQLService swallows; dispatcher engine seam fail open; deps.getObjectQL null on failure"
      ]
    }

    Generated by Claude Code

  18. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5 · 2026-10-18T06:25Z · last part of this card, re-declared after os-dev-report 6246528708 (the file surface widens by three rows; the session, the branch and the dev stay)
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session session_01JeRdtdgEYYdtYB4isWp3bh)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24210-meta-disabled-layered-doors (draft PR #24267)
    Worktree: objectstack-issue-24210-c
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface: as claim 6245752510, plus three rows the measurement pointed to:


    Generated by Claude Code

  19. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24210,
      "status": "done",
      "branch": "claude/issue-24210-meta-disabled-layered-doors",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24267",
      "session": "session_01JeRdtdgEYYdtYB4isWp3bh",
      "premise_still_valid": true,
      "summary": "Rework after CI Test Core (6/6) on ed6b7108ec. Failing test: @objectstack/runtime test:repo, src/domains/meta-list-projection-parity.test.ts, 'every type the item read keys on, and every translatable type, is a census cell': AssertionError expected [ 'package' ] to deeply equal [] (reproduced locally). Cause: the census derives every type literal the item read's handlers and shared steps compare against; step 1b's metaType !== 'package' made 'package' a keyed type with no parity cell, and it restated a decision the protocol already makes. Fix (527c7bc4ba): the chain's condition drops && metaType !== 'package' (1 line); each transport's isItemOfDisabledPackage now answers withheld only when the plain read serves no item AND its envelope packageId is disabled (REST rest-server.ts metaItemReadGateSources: 2 lines; dispatcher meta.ts metaItemReadGateSources: 2 lines; plus docblocks on those three spots). So which types are hidden stays the protocol's (package never is). New pin (runtime): an item the plain read still serves keeps its layered view even when the envelope names a disabled owner; ablating the item == null half turns exactly it red (1 failed / 13 passed), restored blob == HEAD. 9d275b85e3 types that test double's getMetaItem as returning Promise of any (runtime typecheck was 1 without it). No pin was deleted or weakened; the census is untouched and green.",
      "tests": "Head 9d275b85e3 (origin/main 53ef752436 merged, PR #24260 included, clean). Build closures exit 0; full pnpm build 76/76. rest: typecheck 0, test 0 (295 files / 6047 passed, 326 skipped), test:repo 0 (5 / 270 passed, 16 skipped). runtime: typecheck 0 at 9d275b85e3 (1 on 5d2ec31885, the test double's type, fixed in 9d275b85e3), test 0 (416 / 6552, 19 skipped), test:repo 0 (3 / 808). objectql: typecheck 0, test 0 (437 / 8244), test:repo 0 (1 / 5). Suites ran on the merge head 5d2ec31885; on 9d275b85e3 (one type annotation in a test) runtime typecheck 0, the pin file 14/14 and runtime test:repo 808/808 were re-run.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) at 9d275b85e3: 70 derived; 69 exit 0 first pass; check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, fresh worktree without dist), exit 0 after full build. --ran: '70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). CI on 9d275b85e3 not read at report time; convergence is the seat's.",
      "mcp_calls": "0",
      "api_writes": "2 relay writes this round: issue_patch of PR #24267's body (PATCH /repos/objectstack-ai/objectstack/issues/24267, 14567 bytes, read back identical; What changed and Verification updated, lines that moved named) and this comment (POST /repos/objectstack-ai/objectstack/issues/24210/comments). git push to the own branch: 3 (the fix, the merge, the test type).",
      "line_budget": {
        "set": "none for this card",
        "files_vs_ed6b7108ec": {
          "packages/rest/src/meta-item-read-gate.ts": "+17 -14 (1 code line, rest docblock)",
          "packages/rest/src/rest-server.ts": "+5 -4 (2 code lines, rest docblock)",
          "packages/runtime/src/domains/meta.ts": "+5 -4 (2 code lines, rest docblock)",
          "packages/runtime/src/domains/meta-disabled-package-doors.test.ts": "+11 -1"
        }
      },
      "files_changed": [
        "packages/rest/src/meta-item-read-gate.ts",
        "packages/rest/src/rest-server.ts",
        "packages/runtime/src/domains/meta.ts",
        "packages/runtime/src/domains/meta-disabled-package-doors.test.ts"
      ],
      "deviations": [
        "Round 1 counted only the local test project for runtime, not test:repo; all three packages' test:repo now run and are recorded.",
        "The worktree ../objectstack-issue-24210-c was re-created from the remote branch for this round."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  20. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (last part) · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T08:31Z

    Probe Reading
    Queue PR #24267 entered the merge queue at 2026-10-18T08:01Z, on head 9d275b85e. That head has its own delta CONTRACT_REVIEW_TIER record, 6247381651 (PASS), and ACCEPT 6247389406. It left the queue merged at 2026-10-18T08:29Z. After the merge, no gh-readonly-queue/main/pr-24267-* branch remains.
    origin/main 58a2faa63 is on origin/main (git merge-base --is-ancestor: exit 0). It carries PR #24267's same 9 files, +464 / −21.
    Cards closed This card closed by the PR's Fixes, and pm:dispatched is cleared. Since the enqueue, #24253 closed (this seat, PR #24269), and so did #24256 (domain:skills) and #24266 (domain:spec), each by its own lane.

    The card, end to end, across three PRs: PR #24223 (2bcd406a2), PR #24247 (e74ec56ac) and PR #24267 (58a2faa63). A disabled package's item now reads as the list reads it, on every /meta door and on both transports:

    • By name: absent for every type but object and package; an object answers 404 OBJECT_PACKAGE_DISABLED.
    • Dispatcher object doors: /state, the one-segment read and object /published refuse the object.
    • REST /meta/object/NAME/state/FIELD: refuses OBJECT_PACKAGE_DISABLED.
    • Non-object /published: answers with the missing name's bytes.
    • _drafts: omits a disabled package's drafts at the producer.
    • /history, /audit and /diff: answer the plain read's own absence body.
    • Non-object /layers and ?layers=true: one step in the shared chain. The plain read decides which types are hidden, so package items stay served.
    • Resolution readers (the security catalog echo, the packaged-permission-set lock gate, the batch publish and discard verbs) keep serving, pinned in the Package disable and uninstall never reach the metadata/data layer: a disabled package's objects still serve rows, and uninstall leaves 7 orphaned sys_metadata rows #7557 READERS table.

    The third dev session, session_01JeRdtdgEYYdtYB4isWp3bh, is archived.


    Generated by Claude Code

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

Metadata

Metadata

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landfindingpriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions