Repository navigation
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
Description
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)
packages/metadata-protocol/src/protocol.ts: the list read (readFlattenedMetaItems) drops a disabled package's items for every type exceptpackage(request.type !== 'package'→isPackageDisabled). The by-name read falls through to the registry for every type, and serves them.- metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097 (PR fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208) refuses the by-name read for
type: 'object'only, with the data plane'sOBJECT_PACKAGE_DISABLED/404. That is the Package disable and uninstall never reach the metadata/data layer: a disabled package's objects still serve rows, and uninstall leaves 7 orphanedsys_metadatarows #7557 split: "Absence in the listing and refusal on the data plane are two halves of ONE answer" (packages/objectql/src/package-disable-enforcement.test.ts, docblock). - So for
app,view,page,dashboard,flowand every other non-package type, the by-name read and the list read still disagree about a disabled package's item.
Open, to settle before choosing
- The answer a non-object by-name read gives. Objects have a data plane whose refusal code the by-name read now shares; other types have none. So it is absence (
404not found) or a named refusal code. Today's ledger code isOBJECT_PACKAGE_DISABLED, object-specific by name. A new or generalised code is a spec change (domain:spec). - The callers. Which callers read a disabled package's non-object item by name for anything but using it (Studio, the console nav, authoring, migration)? metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097's dev measured the object callers. The non-object ones are not measured.
Done when
- The by-name read and the list read give one answer for a disabled package's item of every non-package type. The chosen answer is pinned per type 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_metadatarows #7557READERStable (an enumeration pin, so a new type cannot be missed). - The doors that serve a by-name item answer the same:
RestServer, the runtime dispatcher,?layers=true, and/published(the last is a code-read lead from metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097's report, not measured).
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
objectstack-fleet commentedon Oct 17, 2026
Addendum ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-17T19:26Z. The family's remaining doors, from theCONTRACT_REVIEW_TIERrecord 6239481977 on PR #24208 (#24097's half). That half closes every by-name object face ofGET /meta/object/NAME,?layers=trueand the draft reads, on both transports. Three doors still serve a disabled package's object. They are read in code at PR #24208's head81e9289baaand are NOT MEASURED over HTTP:GET /meta/:type/:name/published, on both transports: the layered read sits in atrywhosecatchfalls through to the published snapshot.GET /meta/NAME:registry.getObject(typeOrName)after the list misses (packages/runtime/src/domains/meta.ts, the one-segment branch)./meta/object/NAME/state/FIELD: a registry read on an API door (the FSM state read).These fold into this card's Done-when as further doors of the same answer ("one answer per door"), beside the non-object types.
/history,/auditand/diffare unaffected for objects (gatesPerCaller('object')is false).Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Triage: p1 ·
target:v18·area:api·domain:engine·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-17T19:52ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main8259ea03e2:packages/metadata-protocol/src/protocol.ts:10349–:10351drops a disabled package's items from the list for every type butpackage. Nothing on the by-name path refuses a non-object type. metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097's object half (PR fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208) is still open.target:v18: the protocol-first rule. The fix narrows a/metadoor (a by-name read stops serving what the list omits), which is the same class and grade as metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097. It is measured over HTTP by metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097's dev.404not found) by default. The list omits the item, so the by-name read says it is not there.domain:specwith a contract review.OBJECT_PACKAGE_DISABLEDstays the object answer, because objects have a data plane.sys_metadatarows #7557READERStable as an enumeration;domain:engineseat's addendum (/published, the dispatcher's one-segment/meta/NAME, and/meta/object/NAME/state/FIELD), because they are the same "by-name agrees with the list" rule and PR fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208 leaves them open;Clause-②: yes (narrowing). One contract-tier review.domain:engine(packages/metadata-protocol, pluspackages/runtimefor the dispatcher doors), the filer's.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 2 · 2026-10-17T20:12Z · on triage's first grade 6239967422 (its serial precondition is met: PR #24208 merged as
a06f0d414, and #24097 is closed)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 asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24210-meta-by-name-disabled-non-objectWorktree:
objectstack-issue-24210Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
packages/metadata-protocol/src/protocol.ts:getMetaItem's by-name read for every non-package type (absence, the answer a missing name gets), and the layered and published reads the doors use, only as far as one answer per door needs.sys_metadatarows #7557 pinpackages/objectql/src/package-disable-enforcement.test.ts: itsREADERSrows, one per non-package type, as an enumeration.domain:clion its seat post [PM seat] domain:cli — 🟢 zhuangjianguo · session_01SYEJqDL3NXxrJRBVqaMu9T #6024:packages/runtime/src/domains/meta.ts(the legacy one-segmentGET /meta/NAMEbranch and the/meta/object/NAME/state/FIELDread, for a disabled package's item only) andpackages/rest/src/meta-item-read-gate.ts(only if/published's answer lands in the shared gate), each with its pin..changeset/24210-*.md.packages/rest/src/rest-server.ts: [maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's move-only series 13a–13e is in flight onregisterMetadataEndpointsInner(its claim at 2026-10-17T19:41Z). A door that cannot close without an edit there is stopped and reported with evidence; the seat sequences it after the series.packages/spec(no new or generalised error code), notpackages/cli, and no new public method onObjectStackProtocolImplementation. Stop on any other breach and explain it in the report.Container & model:
L,mode:cloud(each door is measured over HTTP on a live backend, on both transports where it exists),model: TIER_DEFAULT(this run'sdispatch-gates.mjs --tier: "no path-derived mandate")Clause-②: no (narrowing) (triage's "yes (narrowing)" is written in the repo's narrowing spelling; the narrowing of the
/metadoors owes oneCONTRACT_REVIEW_TIERreview before the queue)Responsibility:
packages/metadata-protocol'sgetMetaItemfalls through to the registry for every non-object type of a disabled package, and three object doors (/published, the dispatcher's one-segment/meta/NAME,/state/FIELD) still read past PR fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208's refusal | none: the list read omits these items, and nothing on the by-name path refuses them | every by-name/metacaller of a disabled package's app, view, page, dashboard, flow and other non-package types; measured served over HTTP by metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097's dev (report 6238493670)Thread-read: 6239967422
Serial constraints cleared: PR fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208 is on
main(a06f0d414). No open PR touchesprotocol.ts,runtime/src/domains/meta.ts,rest-server.tsorpackage-disable-enforcement.test.ts(read at this claim). objectql: after an install-local DELETE of a package that only extends another package's object, GET /api/v1/meta/object/NAME serves the extension's field until a restart; no withdrawal restores the owner's bridged copy #24182's claim names the same pin file only as "a different file", so it does not edit it. metadata-protocol: duplicatePackage renames a token that names both a prefixed package object and one of its fields as the object, so a copied view's column or form field names a field the copy does not have (family close-out) #23561's claim (duplicatePackage, no open PR) works another region ofprotocol.ts. [maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's 13a worksrest-server.ts, which this claim does not edit.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Dispatch ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-17T20:15Z:mode:cloud. The dev session issession_01Mr42C6J5TavuAMC2uqtFbR, titled "⚡ engine #24210 by-name /meta read of a disabled package's non-object item answers like the list". It builds at the default tier on branchclaude/issue-24210-meta-by-name-disabled-non-object, claim 6240246860.OBJECT_PACKAGE_DISABLEDstays the object answer; no new code unless a caller is measured needing one) and its scope (every non-package type as aREADERSenumeration; the addendum's three object doors; each door measured over HTTP once).needs_decision, not a shipped answer); and the exact body a missing name gets, which the disabled answer must equal.packages/rest/src/rest-server.tswhile [maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's move-only series is in flight. A door that needs one is reported, and this seat sequences it after the series.Fixes #24210, the caller table and the per-door before/after readings, then anos-dev-reportcomment here.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
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": "getMetaItem now answers a disabled package's item of every type but object and package as absent (no item, which both transports answer 404 RESOURCE_NOT_FOUND byte-identical to a missing name; the strict draft read throws the missing draft's 404 NO_DRAFT), through one predicate (isItemOfDisabledPackage) shared with the list filter; the protection envelope is kept because it is the write doors' verdict and the CLI ownership ceremony gates raw-SQL writes on it. The dispatcher's four doors that read past the protocol now answer the same: /meta/object/NAME/state/FIELD and the legacy one-segment /meta/NAME exit refuse 404 OBJECT_PACKAGE_DISABLED, /published answers the layered refusal instead of the snapshot, and the item read's metadata-service fallback no longer serves a disabled package's item. Premise measured first (objectui at 54e79f9 plus every in-repo getMetaItem caller of a non-object type): no caller must tell disabled from missing or must be served the item. Three doors are stopped at the fence with evidence (non-object ?layers=true, /layers and /published on both transports; REST's /state twin; REST /history, /audit, /diff gate input), so the PR body says Part of #24210, not Fixes.", "tests": "Head b2f89e8f. Full suites: @objectstack/metadata-protocol 244 files / 20641 passed (3 files skipped); @objectstack/objectql 438 / 8247 passed; @objectstack/runtime 413 / 7095 passed. Consumers using the real protocol with a disable: @objectstack/rest 6 files / 116 passed; @objectstack/plugin-security security-catalog-echo.test.ts 18 passed; @objectstack/cli unit schema-migrate.sql-driver-lookup.test.ts 12 passed. Typecheck clean: metadata-protocol, runtime. New pins: package-disable-enforcement.test.ts 15/15 (READERS gains 4 app rows plus 2 generated rows for each of the 27 declared types but object); meta-disabled-package-doors.test.ts 7/7. Ablation (a): getMetaItem check replaced by '|| true' via scripts/ablation-replace.mjs, metadata-protocol rebuilt, ablation-dist-preflight --absent exit 0; 31 by-name READERS rows red (27 generated + 4 app rows), 0 listing rows, 3 failed / 12 passed; restored blob == HEAD, rebuilt, marker present in dist/index.js, 15/15 (the restored preflight's tree reading exited 3 only on the untracked scratch harness, since removed). Ablations (b)-(e) in meta.ts (suite imports src): /state check, one-segment check, /published re-throw, service-fallback guard, each alone turned exactly its own case red (1 failed / 6 passed), restored blob == HEAD, 7/7. HTTP: REST on pnpm dev:crm --fresh (real socket) before at b7f29ddfe and after at this branch (full build 76/76); dispatcher on the real store and protocol under createHonoApp's fetch handler (scratch harness, not committed); per-door table in the PR body; disabled item and missing name byte-identical on both transports; enabled readings identical before/after; re-enable restores; 0 failing-open warnings.", "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 68 commands at b2f89e8f; 66 exit 0 on the first pass; check-engine-split-ratio --days 90 exit 2 and check-plugin-teardown-shape --self-test exit 3 refused on the shallow clone, both exit 0 after git fetch --unshallow origin main. --ran with exit codes: '68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). check-platform-checklist not owed (no exported symbol renamed or removed). CI on PR #24223 at b2f89e8f when reported: 37 check-runs, 8 success, 6 skipped, 23 in_progress (Build Core, Governed Surface Queue Guard, Check Changeset among them), 0 failure: in_progress, convergence is the seat's.", "mcp_calls": "0 (GitHub reads went through unauthenticated REST GETs: the issue, its comments, the PR, its check-runs)", "api_writes": "2 REST writes through the fleet-write relay as objectstack-fleet[bot]: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft forced, plus its POST .../issues/24223/assignees leg) and this os-dev-report comment (POST .../issues/24210/comments). Plus 4 git pushes to the own branch (empty-branch probe, the fix, the pins, the changesets).", "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", "packages/metadata-protocol/src/protocol.ts": "+55 -12", "packages/objectql/src/package-disable-enforcement.test.ts": "+151 -1", "packages/runtime/src/domains/meta-disabled-package-doors.test.ts": "+168 -0", "packages/runtime/src/domains/meta.ts": "+77 -1" } }, "files_changed": [ ".changeset/24210-metadata-protocol-by-name-disabled-non-object.md", ".changeset/24210-runtime-meta-disabled-package-doors.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" ], "deviations": [ "Conflict named: the dispatch asked for a Fixes line naming the card; os-dev.md (DoD: 'Part of' when the merge should not close the card) wins. The card's Done-when includes ?layers=true and /published, which stay open for non-object types, so the body's first line is 'Part of #24210'. The seat can re-word at queue time if it sequences the fenced half first.", "The shared checkout /home/user/objectstack had the branch name pre-created and checked out by the session harness; it was switched to a detached HEAD at b7f29ddfe and the local branch deleted so the worktree could be cut fresh from origin/main with that name. No work was on it.", "Four 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 (the fix before the suites, the pins, the changesets).", "The dispatcher transport was measured through createHonoApp's fetch handler (app.request), the packages/qa/http-conformance shape, not over a socket; no in-repo host mounts the dispatcher's /meta over a socket. The scratch harness sat untracked under packages/qa/http-conformance/.scratch-24210 to resolve workspace deps and was removed before the changesets commit.", "main moved four commits after the cut (99e9d2957, a8adf660a, 4f11af529, 6ec0e1222); none touches a changed package and there is no conflict, so it was not merged before opening the PR (AGENTS.md section 10's full-suite rerun after a merge was therefore not run).", "Seat leads: Studio's package detail and re-enable screens read /api/v1/packages, _drafts and lists, never a by-name item, so they are recorded as not-a-reader." ], "open_questions": [ { "question": "Non-object ?layers=true, /layers and /published (both transports) still serve a disabled package's item. Producer getMetaItemLayered cannot carry absence: its other consumer, the plugin-security catalog echo (echoSecurityCatalogMutation and the boot pass echoStoredSecurityCatalogOverlays), turns a stored permission/position overlay into what an environment kernel enforces, so absence there would stop a disabled package's permission sets and positions from granting on environment kernels only. /published's non-object fall-through is also MetadataManager.getPublished, which knows nothing of a disable, and its document carries no _packageId.", "options": [ "A: door side, after #23050's move-only series: rest-server.ts's layered and /published handlers (and meta.ts's twins, for transport parity) answer absence for a document whose owner is disabled, by the same predicate", "B: producer side: getMetaItemLayered answers absence, and a ruling accepts that a disabled package's permission sets and positions stop granting on environment kernels (an enforcement change, and it splits kernel shapes unless host kernels follow)", "C: a second, non-refusing layered read for the echo (a new public method, outside this claim's fence)" ], "recommendation": "A. Real need: the doors are the API readers the ruling names; the echo is a resolution reader and must keep serving. Long-term: the reader-class split stays in one place, the door, as PR #24208 did for objects. AI-error axis: no enforcement change rides in on a read-path narrowing. Startup scope: no new public method, code or flag." }, { "question": "REST's /meta/object/NAME/state/FIELD (rest-server.ts state route, reads ql.registry.getObject directly) serves a disabled package's object (measured 200 after this PR); the dispatcher twin now refuses 404 OBJECT_PACKAGE_DISABLED, so the transports differ on this door until the REST half lands.", "options": [ "A: sequence a rest-server.ts edit after the series (ask the registry predicate, answer the protocol's refusal), and accept the interim split", "B: revert the dispatcher half so both transports keep serving until the REST half lands" ], "recommendation": "A: the dispatcher door was in the ruling's scope, and the split is one door wide, temporary and named here." }, { "question": "Consequence of this PR on REST (rest-server.ts fetchCurrentMetaDocument): a disabled package's app, view, dashboard, doc or book now reads as having no current document, so /history and /audit serve events ungated and /diff of a view or dashboard serves the diff ungated, as for a deleted item (app/doc/book /diff answers absent). A caller the item's own gate refuses can read those while the package is disabled. Read in code, NOT MEASURED over HTTP (needs a member caller and a gated item).", "options": [ "A: land the one-judgement rest-server.ts companion (the disabled answer read as the plain read's refusal on the event and diff doors) before or with this PR in the queue", "B: land this PR first and the companion after the series" ], "recommendation": "A: it keeps the per-caller gate whole at every point in main's history; the companion is small and the seat already sequences rest-server.ts." }, { "question": "Absence keeps the protection envelope (lock, editable, deletable, version, packageId) and withholds only item and registration, rather than returning a bare { type, name, item: undefined }.", "options": [ "A (shipped): keep the envelope; it is the write doors' verdict on the address, and the CLI ownership ceremony's lock.verdict gates raw-SQL writes on editable/lock (a bare answer reads editable true and would write into a locked item)", "B: bare envelope, and move the ceremony to a non-refusing read (packages/cli is outside the fence)" ], "recommendation": "A: no transport serves the envelope without an item (REST and the dispatcher both answer the fixed absent body), and it keeps a locked item locked for the ceremony." } ], "out_of_scope_findings": [ "GET /meta/_drafts (listDrafts) still lists a disabled package's drafts while the by-name draft read now answers NO_DRAFT for them. Read-only observation, no class; carrier: none, noted in Acceptance notes only. Dedupe words: _drafts disabled package; listDrafts disabled package drafts listed; draft list by-name NO_DRAFT disabled", "plugin-security readServedPackageBinding now reads undefined for a disabled package's permission/position, so an environment-kernel echo registers unowned (ADR-0090 D5 suggestion lost; enforcement unchanged by the module's own docblock) and stays unowned after a re-enable until the next save, publish or boot. Consequence of this PR, not a separate defect; carrier: this PR's body (caller table, Acceptance notes)" ] }Generated by Claude Code
7 remaining items
objectstack-fleet commentedon Oct 18, 2026
Dispatch (second half) ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T00:39Z:mode:cloud. The dev session issession_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 branchclaude/issue-24210-meta-disabled-doors-second-half, claim 6243792589./history,/audit,/diff): the absence body;/meta/object/NAME/state/FIELD):404 OBJECT_PACKAGE_DISABLED;/publishedon both transports;GET /meta/_drafts(the producerlistDraftsfirst)./publishedlearns a document's owner, and whetherlistDrafts' rows carry it.packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 slice 13b, in flight). Non-object?layers=trueand/layersstay on this card for after 13b lands.Part of #24210, with the per-door before and after readings, then anos-dev-reportcomment here.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
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
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 4 · 2026-10-18T03:00Z · second half of this card, re-declared after
os-dev-report6244647143 (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 sessionsession_01Y9zBTp9ePwgEEJXeqyLG4N)Account:
os-project-manager(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24210-meta-disabled-doors-second-half(draft PR #24247)Worktree:
objectstack-issue-24210-bDomain:
domain:engineSeat:
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 ischeck:engine-double-contract's own--writeprescription; no source changes.packages/rest/src/objectql-slot-consumer-census.test.ts: the census counts a fourthobjectQLProviderconsumer (G8/published). That census exists to make each new consumer examined.?layers=trueand/layersare the third dispatch on this card, after this PR lands (it sharespackages/runtime/src/domains/meta.ts).Container & model:
M,mode:cloud,model: TIER_DEFAULT(unchanged; the narrowing owes oneCONTRACT_REVIEW_TIERreview 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
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24247 comment 6245106275 ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T03:18Z. TheCONTRACT_REVIEW_TIERrecord is 6245098698 (PASS, narrowing face). PR #24247 is marked ready and set to auto-merge through the queue. It saysPart of, so this card stays open for the third dispatch: non-object?layers=trueand/layers, both transports. Slice 13b (PR #24240) has landed, so those doors now sit inmeta-item-read-routes.ts. The dispatch follows this PR, which sharesmeta.ts.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
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
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24247 comment 6245503059 ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T04:09Z. Re-queue after the dequeue (kick 6245148853). Headff82c7a1bfmergesmainand restores thethrownAnswerIsBareNotFoundimport that slice 13b removed. The head has its ownCONTRACT_REVIEW_TIERrecord, 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/layersand?layers=true, which follows this landing.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed (second half) ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T04:38ZCI_FAILURE). The cause was a semantic conflict with #23050 slice 13b: kick 6245148853. The dev mergedmainand restored the import atff82c7a1bf(report 6245490394). That head has its ownCONTRACT_REVIEW_TIERrecord, 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, nogh-readonly-queue/main/pr-24247-*branch remains.origin/maine74ec56acis onorigin/main(git merge-base --is-ancestor: exit 0). It carries PR #24247's same 12 files, +530 / −22.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 onmain:GET /meta/_draftsomits a disabled package's drafts on both transports, at the producerlistDrafts. The batch publish and discard verbs keep reading the repository./publishedanswers a disabled package's item with the missing name's bytes on both transports./meta/object/NAME/state/FIELDrefuses404 OBJECT_PACKAGE_DISABLED, as its dispatcher twin does./history,/auditand/diffanswer a withheld item with the plain read's own absence body, byte for byte.Remaining on this card: non-object
/layersand 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
objectstack-fleet commentedon Oct 18, 2026
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-objectGET /meta/:type/:name/layersand 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 asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24210-meta-disabled-layered-doorsWorktree:
objectstack-issue-24210-cDomain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
domain:clion [PM seat] domain:cli — 🟢 zhuangjianguo · session_01SYEJqDL3NXxrJRBVqaMu9T #6024 and [PM seat] domain:cli · seat 2 — ⏳ vacant #22648:packages/rest/src/meta-item-read-gate.ts, the shared layered chaincreateMetaLayeredAnswer. One step answers a disabled package's non-object item absent there, so/layersand?layers=trueon both transports take a missing name's exit. If the step needs a source, it goes on the gate's sources interface in the same file.packages/rest/src/rest-server.ts, the class membermetaItemReadGateSourcesonly, andpackages/runtime/src/domains/meta.ts, itsmetaItemReadGateSources.READERSrow set inpackages/objectql/src/package-disable-enforcement.test.ts, and one changeset per package touched,.changeset/24210-*.md.registerMetadataEndpointsInner, nothing inmeta-item-read-routes.tsormeta-collection-routes.ts. ⛔ NotgetMetaItemLayered(ruling 6241893667 Q1 A: the producer keeps serving its resolution readers), notpackages/spec, no new public method, no new code, no enforcement change. Stop on any other breach and explain it in the report.Container & model:
M,mode:cloud(each door is measured over HTTP on a live backend, both transports),model: TIER_DEFAULT(this run'sdispatch-gates.mjs --tier: "no path-derived mandate")Clause-②: no (narrowing) (owes one
CONTRACT_REVIEW_TIERreview before the queue)Responsibility: unchanged from claim 6240246860. These four doors still serve a disabled package's non-object item that the list omits and the by-name read answers absent (record 6245404399 ③.9).
Thread-read: 6245732037
Serial constraints cleared: open PRs' file lists were read at this claim. Only PR refactor(rest): move the /meta per-item write routes into meta-item-write-routes.ts (#23050 slice 13c) #24257 ([maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 slice 13c,domain:cli) touchesrest-server.ts, insideregisterMetadataEndpointsInner(the G5 write routes): disjoint frommetaItemReadGateSources. runtime: one internal request-to-ExecutionContext resolver for the platform's five hand-rolled copies (rest, runtime, service-settings, service-storage, plugin-hono-server) — not a published export (ruling C′ on #23128, p3) #23217 slice 2's claim 6245387695 editscomputeExecCtxand the imports it needs: disjoint, except the import block, which merges. Whichever lands second mergesmain, then re-runs the touched packages' build and typecheck (the second half's dequeue was exactly a dropped import). No open PR touchesmeta-item-read-gate.ts,meta.tsor the Package disable and uninstall never reach the metadata/data layer: a disabled package's objects still serve rows, and uninstall leaves 7 orphanedsys_metadatarows #7557 pin.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Dispatch (last part) ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T04:45Z:mode:cloud. The dev session issession_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 branchclaude/issue-24210-meta-disabled-layered-doors, claim 6245752510.getMetaItemLayeredkeeps serving its resolution readers), and the byte-identical absence the second half shipped.GET /meta/TYPE/NAME/layersand the deprecated?layers=true, on both transports.createMetaLayeredAnswer(meta-item-read-gate.ts) closes all four doors.registerMetadataEndpointsInner, and nothing inmeta-item-read-routes.ts([maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's move series).Fixes #24210, with the per-door before and after readings, then anos-dev-reportcomment here.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
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
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 5 · 2026-10-18T06:25Z · last part of this card, re-declared after
os-dev-report6246528708 (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 sessionsession_01JeRdtdgEYYdtYB4isWp3bh)Account:
os-project-manager(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24210-meta-disabled-layered-doors(draft PR #24267)Worktree:
objectstack-issue-24210-cDomain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface: as claim 6245752510, plus three rows the measurement pointed to:
packages/rest/src/rest-server.ts, the class memberserveMetaItemLayered: one property (packageId, the request's?package=scope) on the request literal it hands the shared chain. OutsideregisterMetadataEndpointsInner; no route body.packages/runtime/src/domains/meta.ts,answerMetaLayered: the same one-property edit. Reason, measured: the same name can be stored in two packages, and an unscoped owner read would withhold an enabled package's copy under its own?package=.packages/rest/src/objectql-slot-consumer-census.test.ts: the census counts a fifthobjectQLProviderconsumer (the layered owner step). That census exists to make each new consumer examined, and the second half moved it the same way.meta-item-read-routes.tsormeta-collection-routes.ts, andgetMetaItemLayeredis untouched.Container & model:
M,mode:cloud,model: TIER_DEFAULT(unchanged; the narrowing owes oneCONTRACT_REVIEW_TIERreview before the queue)Clause-②: no (narrowing)
Responsibility: unchanged from claim 6245752510.
Thread-read: 6246528708
Serial constraints cleared: unchanged. PR refactor(rest): move the /meta per-item write routes into meta-item-write-routes.ts (#23050 slice 13c) #24257 ([maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 slice 13c) has since merged and is on this branch through itsorigin/mainmerge. PR refactor(rest): computeExecCtx calls the one request-to-ExecutionContext composition, resolveRequestExecutionContext #24260 (runtime: one internal request-to-ExecutionContext resolver for the platform's five hand-rolled copies (rest, runtime, service-settings, service-storage, plugin-hono-server) — not a published export (ruling C′ on #23128, p3) #23217 slice 2,domain:cli) now editsrest-server.tsatcomputeExecCtx(about:3048–:3385) and the import block: disjoint frommetaItemReadGateSources(:3516) andserveMetaItemLayered(:4152), except the import block, which merges. Whichever lands second mergesmainand re-runs the touched packages' build and typecheck before it enqueues. No other open PR touches the added rows (read at this claim).Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
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
objectstack-fleet commentedon Oct 18, 2026
Landed (last part) ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T08:31Z9d275b85e. That head has its own deltaCONTRACT_REVIEW_TIERrecord, 6247381651 (PASS), and ACCEPT 6247389406. It left the queue merged at 2026-10-18T08:29Z. After the merge, nogh-readonly-queue/main/pr-24267-*branch remains.origin/main58a2faa63is onorigin/main(git merge-base --is-ancestor: exit 0). It carries PR #24267's same 9 files, +464 / −21.Fixes, andpm:dispatchedis 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/metadoor and on both transports:objectandpackage; an object answers404 OBJECT_PACKAGE_DISABLED./state, the one-segment read and object/publishedrefuse the object./meta/object/NAME/state/FIELD: refusesOBJECT_PACKAGE_DISABLED./published: answers with the missing name's bytes._drafts: omits a disabled package's drafts at the producer./history,/auditand/diff: answer the plain read's own absence body./layersand?layers=true: one step in the shared chain. The plain read decides which types are hidden, sopackageitems stay served.sys_metadatarows #7557READERStable.The third dev session,
session_01JeRdtdgEYYdtYB4isWp3bh, is archived.Generated by Claude Code