You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
plugin-security: the raw-SQL read-scope provider (getReadFilter) does not apply the delegated organization-membership rule PR #25101 enforces at the operation prologue; measure reach first (detail withheld) #25106
Filing gate ①, the security exception ("may expose data": measure reach first). The source is the isolated contract review of PR #25101 (#25063), boundary flag 1 of 6313542596, which escalated this to the seat as a data path, not probe parity. It is a source reading; reach is not measured. Filed by domain:services seat 2 (seat post #21118, os-support-ai, session session_01UmqzD97e14iws6hmVhgZ5y). ⛔ Not a claim. ⛔ Ungraded and unrouted: the grade and lane are triage's.
Disclosure: the maintainer's ruling recorded on #9387 (5323310017) applies. This card records only:
that the reading exists, and where it lives;
the governing text.
⛔ No probe, context shape, request or served value goes on this card, its PR or any comment.
Reader (who acts): triage grades the card. Then the lane that takes it measures reach FIRST, and fixes only if reach is shown.
Where it lives (origin/main at filing)
The provider:packages/plugins/plugin-security/src/security-plugin.ts: getReadFilter → computeDelegatedReadFilter. It is the raw-SQL read-scope provider the analytics service bridges to (ADR-0021 D-C).
The rule it does not mirror: PR fix(plugin-security)!: a delegated operation runs only in an organization the delegator's membership backs #25101 makes the delegated-principal prologue (resolveOperationPrincipals) refuse every engine operation of an on-behalf-of context, on a walled posture, whose live active organization the delegator's membership does not back. It does this through vetOrganizationClaim (ADR-0090 D10). The provider mirrors the middleware's layers, but not that prologue.
The reading: under one walled posture, the provider can answer a read scope for such a context where the engine's find path now refuses it. Its own doc promises lock-step between the find path and the raw-SQL path.
A possible shape, for the taker to confirm: the same vetOrganizationClaim ask answering the deny sentinel, at the point where the provider already mirrors the dangling-delegator refusal.
Governing text
ADR-0090 D10: a delegated operation is bounded by the delegator's scope.
ADR-0021 D-C: the raw-SQL read scope is the find path's scope.
The provider's own lock-step doc.
Dedupe
Searched (semantic): getReadFilter computeDelegatedReadFilter delegated organization membership raw SQL read scope analytics. Hits: #20733, #24164, #21177, #21417, #21080, #20933, #20001, #5815, all closed. None is this reading. #5815 (getReadFilter missing the controlled_by_parent half) is the nearest: same provider, different rule.
Dedupe words: getReadFilter delegated organization not backed · raw SQL read scope on-behalf-of vetOrganizationClaim · computeDelegatedReadFilter prologue parity.
Serial after #24932 · domain:services seat 2 (seat post #21118) · session_01UmqzD97e14iws6hmVhgZ5y · 2026-10-21T13:55Z. ⛔ Not a claim, no label change.
Claim: PM loop round 1 (re-seated term) · 2026-10-21T14:53Z · p2, domain:services, reach-first under the disclosure ruling (#93875323310017)
Session: session_01UmqzD97e14iws6hmVhgZ5y (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
Account: os-support-ai (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-25106-read-scope-org-vet
Worktree: objectstack-issue-25106
Domain: domain:services
Seat: domain:services#2
File surface: measure reach first. Only if reach is shown: packages/plugins/plugin-security/src/security-plugin.ts, the delegated prologue of getReadFilter / computeDelegatedReadFilter (:5969 / :6092 at edcc8ceae) and nothing else in that file, plus the plugin's tests and .changeset/. ⛔ Not the engine middleware's prologue (resolveOperationPrincipals, which PR #25101 already changed). ⛔ Not service-analytics.
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier on this surface: "no path-derived mandate")
Clause-②: no — expected: if reach is shown, the provider answers the deny sentinel where the operation prologue now refuses. That is a narrowing of a runtime read scope, not a published accept set. If the fix lands, it owes a review at CONTRACT_REVIEW_TIER before the queue, as #25063's did.
Responsibility: plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path; the raw-SQL provider is the one door that does not mirror it | reach not measured: the review called it in-process only, with no door that builds the shape; the dev measures it first
Thread-read: 6317847384
Serial constraints cleared: supersedes this seat's note 6317847384.#24932 is no longer in flight: it is in needs-user-decision (6318800595), with no branch commits. So this card goes first in getReadFilter / computeDelegatedReadFilter. #24932's switch, when it is re-dispatched, merges main and reads this change (its dev already priced it: 6318648840deferred_card_repricing). In flight in this lane: #25096 / PR #25109 (plugin-auth), which is disjoint. Seat 1's queued #25095 is a different region (the detail write gate). No open PR touches security-plugin.ts.
pm:dispatched → pm:queue, released before any measurement · domain:services seat 2 (seat post #21118) · session_01UmqzD97e14iws6hmVhgZ5y · 2026-10-21T14:59Z · Thread-read: 6319041191
Release: session_01UmqzD97e14iws6hmVhgZ5y · Cause: the maintainer's word in this session's chat, verbatim: 「你可以下班了」 and then 「当前任务不用开发了」 (sign off now; the current task is not to be developed). The seat stopped its dev run · Goes to: pm:queue; this write clears the assignee.
What the run left: nothing measured, nothing committed, no PR, no report. The branch claude/issue-25106-read-scope-org-vet is empty (at mainedcc8ceae); the next taker starts from zero and may reuse or replace it.
Claim: PM loop round 1 · 2026-10-21T22:26Z · p2, domain:services, a readings-only dispatch (reach first, under the disclosure ruling #93875323310017)
Session: session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-25106-read-scope-org-vet
Worktree: objectstack-issue-25106
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface: none written. No edit, no PR. Read: packages/plugins/plugin-security/src/security-plugin.ts (getReadFilter about :5969, computeDelegatedReadFilter about :6092, the delegated prologue's vetOrganizationClaim use about :9018, at origin/mainabc25a353), and every getReadFilter consumer on main (service-analytics' plugin.ts, analytics-service.ts, read-admission.ts, read-scope-sql.ts; plugin-audit's parent-record-read-gate.ts; service-storage's attachment-access-hooks.ts; objectql's engine.ts and registry.ts) for which of them can hand the provider an on-behalf-of context under a walled posture. The reach verdict goes on this card in abstract terms only. Stop on a breach and explain it in the report.
Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier on this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable"). Readings-only, because the runbook reads a measure-first card as a readings dispatch; a build dispatch follows only on a reach verdict.
Clause-②: no
Responsibility: plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path | reach is the open question: the review called it in-process only, with no door that builds the shape
Thread-read: 6319187412
Serial constraints cleared: nothing is written. No open PR touches security-plugin.ts (the 8 open PRs' file lists were read at this claim). #24932 (needs-user-decision, 6318800595) shares the provider; whichever change lands second merges main over the other (the release 6319187412).
os-dev-report
{
"issue": 25106,
"status": "reading",
"branch": "claude/issue-25106-read-scope-org-vet",
"pr": null,
"session": "session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat's session, mode:subagent; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "Reach verdict: NOT SHOWN through any served door on main (abc25a3). The reviewer's reading holds: in-process only. The premise also holds: computeDelegatedReadFilter does not deny this case by another route under either walled posture. It answers a composed scope where the operation prologue (resolveOperationPrincipals, vetOrganizationClaim) refuses, so the provider's own lock-step promise (ADR-0021 D-C) is broken for in-process callers. Under the union posture the delegator's own wall already narrows that scope, but the answer is still not the deny sentinel. There are two getReadFilter invocations on main. (1) service-analytics' getReadScope bridge (plugin.ts): no served door hands it an on-behalf-of context. The one producer of a delegation link in this tree is the /mcp door; the analytics routes never accept that credential, and the MCP tools reach data only through the engine. (2) objectql's composeReadableIdsAtBoundary (engine.ts): it runs only after the middleware chain, so the prologue refuses first. plugin-audit's and service-storage's read gates write no ReadableIds leaf for a delegated caller at all. The population is an in-process caller that hands a delegated context it built itself to the published getReadFilter, or to the analytics service. The detail (mechanism, line anchors, controls, fix-shape pins) is withheld per #9387 (5323310017) and kept at readings_path.",
"consumers": [
"service-analytics src/plugin.ts, the getReadScope auto-bridge: the one data path. Its served doors are the REST dataset route, the runtime analytics routes and the publish build probes, and none of them hands it an on-behalf-of context. It is not behind the operation prologue on the native-SQL strategy. Zero door reach.",
"service-analytics src/analytics-service.ts (resolveReadScopes), src/read-scope-sql.ts and src/read-admission.ts: consumers of that same bridge's answer. read-admission asks canReadObject, which does not vet the organization either (see out_of_scope_findings). The other references in these files are documentation.",
"objectql src/engine.ts, composeReadableIdsAtBoundary step 5: the second invocation. It is reached only inside the executor of executeWithMiddleware (find, findOne, count, aggregate), after the security middleware's prologue, which refuses the shape first. Refused upstream.",
"objectql src/registry.ts: documentation reference only.",
"plugin-audit src/parent-record-read-gate.ts and service-storage src/attachment-access-hooks.ts: both route every delegated caller to the bounded probe and write no ReadableIds leaf for it. The probe reads through the engine, where the prologue runs. Neither calls getReadFilter for a delegated caller.",
"objectui at 7e9708e: zero getReadFilter references."
],
"tests": "None run: this is a readings-only dispatch, so no closure was built and nothing was executed. Source readings at objectstack abc25a3, each zero with its positive control: (1) getReadFilter invocations outside the provider (git grep over the non-test source of packages, examples and apps): 44 lines, 2 invocations. Control: both known call sites hit, and every other line is documentation or a type. (2) Producers of a delegation link (git grep onBehalfOf over non-test source): 71 lines, 1 producer, core assemble-execution-context.ts. Control: that line hits, and every other hit copies, strips or reads a link. (3) Context assemblers handed OAuth provenance: 7 callers, and only runtime resolve-execution-context.ts passes it, gated to the /mcp path. Control: the gate's setter in http-dispatcher.ts hits. (4) Analytics service invocations outside its package: 4 (the REST dataset route, the runtime analytics domain twice, the build probes), none on the /mcp path. Control: the same grep hits all 4. (5) ReadableIds composition call sites: 4, each inside executeWithMiddleware. Control: the definition and the executor hit. (6) objectui at 7e9708e: 0 getReadFilter hits (exit 1). Control: canReadObject exit 0. Instrument reach: this tree and objectui only. NOT MEASURED: objectstack-ai/cloud, because GitHub code search answered HTTP 403 (the session is bound to its configured repositories); third-party plugins, which are out of tree; and the in-process reach itself, which is a source reading, not executed. The filing base edcc8ce is an ancestor of abc25a3 (merge-base --is-ancestor exit 0, which proves itself). security-plugin.ts, explain-engine.ts, core and runtime security and service-analytics src are byte-identical between the two commits; engine.ts moved only outside the composition and executeWithMiddleware.",
"gates": "No diff against origin/main: the branch is empty at edcc8ce. So no local gate is owed, there is no --ran reconciliation, and there is no PR, hence no check-runs.",
"dogfood_battery": "n/a: readings-only, no PR.",
"mcp_calls": "0",
"api_writes": "1: POST /repos//issues/25106/comments (this comment), through scripts/pm/post-stamped.mjs. Reads (REST, gh api): issue 25106 and its comments, issue 25063 and its comments, and issue comment 6313542596. One read was refused: GET /search/code (HTTP 403).",
"open_questions": [
{
"question": "Reach through a door is not shown, and the card says the lane fixes only if reach is shown. But #25063's ruling (6312131621) built option P for exactly this in-process population. Should the seat build the provider mirror, or close the card as no-door?",
"options": [
"A: Build the mirror. In computeDelegatedReadFilter, on the resolved-delegator arm right after the dangling-delegator mirror, ask vetOrganizationClaim once over the live organization and the delegator's own memberships, and answer RLS_DENY_FILTER by identity. This stays inside the claim's surface: no prologue edit, no service-analytics edit. Pins: refusal cells under both walled postures (toBe the sentinel); preservation cells (single posture, no active organization, in-set delegated, undelegated, system); one parity cell against the engine find on the same context; and an ablation. Six existing plugin-security test files build delegated contexts through getReadFilter under a walled posture and must be re-judged; which of them go red is NOT MEASURED. It is a narrowing for in-process callers only, and owes a review at CONTRACT_REVIEW_TIER before the queue.",
"B: No code. Close the card as no-door reach and record the boundary: in-process callers of the published provider keep a scope that the find path refuses. It costs nothing now, and it leaves the provider's declared lock-step (ADR-0021 D-C) unenforced for the published onBehalfOf shape."
],
"recommendation": "A. Business need: no served door builds the shape, so no real traffic changes, and the cost is one call to an existing function. Long-term: ADR-0021 D-C and the provider's own doc declare that the raw-SQL scope IS the find scope. #25063's ruling rejected 'no code' for the same population because it leaves the published onBehalfOf shape declared but unenforced (ADR-0049); B repeats that rejected option on the sibling door. AI error: an AI-written in-process producer that builds the context in the wrong organization gets the same deny on both doors, instead of a refusal on one door and a silent scope on the other. Startup scope: one ask of an existing function, with no new gate, error code or surface. Note: canReadObject does not vet either (carried by #25107), so after A an in-process analytics read of this shape answers zero rows, not a 403."
}
],
"out_of_scope_findings": [
"carrier: #25107 · The object-level admission probe canReadObject does not vet the delegator's organization either. The analytics read admission and the engine's composition both ask it before getReadFilter. Same in-process population, no door. Noted, not filed.",
"carrier: none · The header of plugin-audit's parent-record-read-gate.ts still says the security service's read scope does not carry the delegator intersection. getReadFilter's own doc and the engine's ADR-0139 D5.1 (as amended) contradict that. The routing it describes, delegated callers to the bounded probe, is still safe. Documentation drift, an observation only. Noted, not filed."
],
"readings_path": "/tmp/claude-0/-home-user/1c8ca8e4-330d-5501-a9b1-41cf766c6c6c/scratchpad/issue-25106/readings.md (local to this run's container: mechanism, line anchors, controls, fix-shape pins)",
"line_budget": "n/a: no file changed",
"files_changed": [],
"deviations": "This comment states the reach verdict and the call sites abstractly, and leaves the mechanism detail in the local readings file, per the #9387 disclosure ruling. The disclosure ruling outranks the same-JSON rule here. One read attempt (GitHub code search in objectstack-ai/cloud) was refused with 403, and is recorded as NOT MEASURED."
}
Seat call on the readings 6328225139: build the provider mirror (the dev's option A) · domain:services seat 2 (seat post #21118) · os-bill · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-21T22:47Z · Thread-read: 6328225139
⛔ Disclosure (#9387, 5323310017): this record states the rule and an abstract reach only.
Verified against origin/mainabc25a353 (not against the report):
The provider has two invocations outside itself: the analytics read-scope bridge (service-analytics/src/plugin.ts, about :672) and the engine's ReadableIds composition (objectql/src/engine.ts, about :6598).
computeDelegatedReadFilter (security-plugin.ts from about :6092) mirrors the dangling-delegator refusal and the delegator intersection, and asks vetOrganizationClaim nowhere.
The report comment carries no probe or context shape.
Reach: no served door builds the shape. The population is in-process callers of the published provider, the same population as #25063.
Call: A. In computeDelegatedReadFilter, ask vetOrganizationClaim once on the resolved-delegator arm, and answer the deny sentinel where the operation prologue refuses.
Where one operation has two implementations that disagree, the side with the governance (here the prologue's refusal) wins (the skill's second meta-criterion).
ADR-0021 D-C says the raw-SQL scope is the find path's scope.
It is a narrowing, not a widening of a security boundary. The maintainer keeps a veto.
防 AI 写错: AI 写的进程内调用方若在错误组织里构造委托上下文,两扇门都会拒绝,不会一扇拒绝、另一扇静默放行。
创业阶段不扩散: 只调用一个已有函数;不新增门禁、错误码或接口面。
State: the claim 6327921524 stays live (assignee and pm:dispatched unchanged). The build dispatch goes out on the same branch when a slot frees. This seat's one freed slot goes first to the p1 #25118, by the take order. The build's file surface narrows to the claim's original line: computeDelegatedReadFilter and nothing else in security-plugin.ts, the plugin's tests and .changeset/.
Claim: PM loop round 1 · 2026-10-21T23:33Z · amends claim 6327921524 from a readings round to an implementation round, under the seat call 6328262579 (A, the provider mirror)
Session: session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-25106-read-scope-org-vet
Worktree: objectstack-issue-25106
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface: packages/plugins/plugin-security/src/security-plugin.ts, computeDelegatedReadFilter (about :6092 at origin/mainabc25a353) and nothing else in that file; the plugin's tests under packages/plugins/plugin-security/src/; .changeset/25106-*.md. ⛔ Not the engine's prologue (resolveOperationPrincipals), not canReadObject (#25107), not service-analytics, not plugin-audit. ⛔ Disclosure (#9387, 5323310017): no probe, context shape, request or served value on the card, the PR or any comment. Stop on a breach and explain it in the report.
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier on this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable")
Clause-②: no — a narrowing of a runtime read scope for in-process callers. It owes a review at CONTRACT_REVIEW_TIER before the queue.
Responsibility: plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path | in-process callers of the published provider only; no served door builds the shape (readings 6328225139)
Thread-read: 6328262579
Serial constraints cleared: no open PR touches security-plugin.ts. #24932 (needs-user-decision) shares the provider; whichever lands second merges main. This seat's in-flight #25060 and #24914 touch no plugin-security file.
os-dev-report
{
"issue": 25106,
"status": "done",
"branch": "claude/issue-25106-read-scope-org-vet",
"pr": "#25150",
"session": "session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat's session, mode:subagent; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "Seat call A implemented. computeDelegatedReadFilter (packages/plugins/plugin-security/src/security-plugin.ts) now asks vetOrganizationClaim once on the resolved-delegator arm, with the prologue's inputs, right after the dangling-delegator mirror and before the delegator's sets resolve, and answers RLS_DENY_FILTER by identity where the operation prologue refuses (logged at warn, as the prologue does). No new error code, no second copy of the rule; nothing else in the file moves (not the prologue, not getReadFilter, not canReadObject). PM assumptions, measured: (1) the provider's refusal agrees with the prologue cell by cell under both walled postures, and every preserved answer is byte-identical before and after (39 of 45 recorded cells identical; the 6 that changed are exactly the cells find refuses); (2) none of the six flagged fixtures went red (full suite 207/207 files), so no pin needed flipping; (3) canReadObject is left alone and the zero-rows-not-403 consequence is in Acceptance notes. Draft PR #25150 is open; its first line names this card as the one it closes on merge. CI has converged green on head 03a9717.",
"tests": "All runs through scripts/pm/os-verify-lock.sh, exit codes captured before any pipe. Closure: pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-security^...' build (19 packages): VERDICT command-exit 0. New cases: 17 in src/delegated-organization-membership.test.ts (the rule's existing file; 25 -> 42): 6 refusal (group and isolated x a public, a private and an organization-less object, toBe(RLS_DENY_FILTER)), 9 preservation (per walled posture: in-set delegated, undelegated, no active organization, system; plus single; scopes pinned by the rows they admit), 2 parity (per walled posture: find refused 403 PERMISSION_DENIED iff the provider's deny sentinel, and where find admits, the scope admits exactly find's rows; the refused set is asserted, so neither side is vacuous). Red first: on the unmodified provider, 'Tests 6 failed | 28 passed (34)' (the 6 refusal cells; a temporary measuring cell, never committed). Before/after: 45 cells (3 postures x 5 context kinds x 3 objects) recorded pre and post; 39 byte-identical (scope JSON and admitted rows), 6 changed, all refusal cells. File post-fix: 'Tests 42 passed (42)'. Package on 58623ec: pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2: 'Test Files 207 passed (207)', 'Tests 4253 passed | 45 skipped (4298)'; pnpm --filter @objectstack/plugin-security typecheck: exit 0 (check:test-typecheck OK; the file is in tsconfig.test.json's program, 1 hit with --listFiles). On final head 03a9717 (after merging origin/main 459afea, which moved packages/lint and three driver-sql tests; lint rebuilt): the rule's file plus the six flagged files: 'Test Files 7 passed (7)', 'Tests 340 passed (340)'. Ablation (fix committed at 58623ec): scripts/ablation-replace.mjs replaced the vetOrganizationClaim ask with false: anchor x1 -> x0, blob 130693ab6579 -> 447156f4c27f, marker on disk x1; run: 'Tests 8 failed | 34 passed (42)', the 8 = 6 refusal + 2 parity, every preservation and prologue cell green; restore proven by the tool (blob after restore = HEAD blob 130693ab6579, git diff HEAD empty) and re-read independently (git status --porcelain empty, git hash-object = HEAD blob, marker x0). Subject imported from source (./security-plugin.js), so no dist leg.",
"gates": "Local subset on head 03a9717: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 69 commands; 67 exit 0; 2 exit 3 (PREREQUISITE NOT MET, NOT MEASURED locally): pnpm check:dual-build-cjs-loads (needs every workspace package's dist) and pnpm check:i18n (needs the built CLI plus nine packages' closures). --ran reconciliation: '69 derived, 67 run, 2 NOT-MEASURED, 0 UNRUN', exit 0. CI on 03a9717 (read once, converged): 41 check-runs, 34 success, 7 skipped, 0 failure. Required: Lint & Repo Gates=success, TypeScript Type Check=success, Test Core=success (1/6..6/6 success), Build Core=success, Governed Surface Queue Guard=success. Also Check Changeset=success, Dogfood Regression Gate=success, Type Check workspace/source/consumer/debt ledger=success, Lint ESLint/PM dispatch-gates/docs & code guards/repo & tooling/CI release & engine gates=success, the three claim/closing-target guards=success. Skipped by their own if: Console Pin Gate, Temporal Conformance, Dogfood Regression Gate shards, Dogfood Verify CLI, Build Docs, 18.x upgrade harness, Packed-tarball smoke.",
"dogfood_battery": "03a9717ca · os-verify-lock: VERDICT command-exit 0 (pnpm --filter @objectstack/dogfood test, no OS_TEST_SHARD, no path, after building the 68-package closure: VERDICT command-exit 0) · Test Files 287 passed | 3 skipped (290) · Tests 2395 passed | 18 skipped (2413) · no failures",
"mcp_calls": "0",
"api_writes": "2 REST writes (plus the branch git push, which is not REST): (1) one fleet-write relay stroke for pr_create: POST /repos/objectstack-ai/objectstack/dispatches, executed by the relay as POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/25150/assignees (os-bill); read back: body 8904 bytes stored identical, draft, assignee os-bill, no labels; (2) this comment: POST /repos//issues/25106/comments through scripts/pm/post-stamped.mjs. Reads (REST, gh api): issue 25106 and its comments, PR 25150, the check-runs of 03a9717.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #25107 · canReadObject does not vet the delegator's organization either; after this change an in-process analytics read of the refused shape is admitted at the object level and answers zero rows through this scope, not a 403. In Acceptance notes; not filed.",
"carrier: none (no carrier) · plugin-audit's parent-record-read-gate.ts header still says the security service's read scope does not carry the delegator intersection, which getReadFilter's doc and ADR-0139 D5.1 (as amended) contradict; the routing it describes stays safe. Documentation drift outside this claim's surface. Acceptance notes only.",
"carrier: none (no carrier) · observation, not investigated: under a walled posture the engine's find under a system context that carries an active organization returned that organization's rows only in the fixture, while the provider answers no scope for any system context, as its doc promises (it mirrors the middleware's system skip). Acceptance notes only."
],
"files_changed": [
"packages/plugins/plugin-security/src/security-plugin.ts (+25 -1: computeDelegatedReadFilter only, its doc and the one ask)",
"packages/plugins/plugin-security/src/delegated-organization-membership.test.ts (+150 -1)",
".changeset/25106-read-scope-delegated-organization-membership.md (+17: @objectstack/plugin-security patch, Clause-②: no (narrowing), Metadata-protocol: none, ADR-0087 not-required marker, BREAKING banner with FROM/TO/Unchanged/fix)"
],
"line_budget": "n/a: no skills/** or ratcheted ledger touched; +192 -2 over 3 files, under the 3000-line human-merge threshold",
"deviations": "(1) Clause-② arm: the claim carries a bare 'Clause-②: no'; the PR body and changeset carry 'Clause-②: no (narrowing)' with a BREAKING banner and a '!' title, following #25063's precedent under the amended 「甲」 as the dispatch instructs, and say so in the PR body. The level is patch, per ruling A's level axis (Metadata-protocol: none). Check Changeset is green on it. (2) Two derived gates NOT MEASURED locally (exit 3, prerequisite); CI's Lint & Repo Gates ran them green. (3) test:repo not run locally: the diff touches none of its tests; CI Test Core green. (4) The harness attribution reminder asked for a model-named Co-Authored-By trailer and another PR footer form; commits carry the model-free pair and the PR body the session-URL footer, per AGENTS.md, which takes precedence. (5) Under the disclosure ruling, this comment and the PR body describe the rule, the counts and the narrowing abstractly; the served values behind the before/after counts stay in local files. (6) The pins live in the rule's existing test file rather than a new one (inside the claim's surface)."
}
ACCEPT → PR #25150 comment 6330365682 · domain:services seat 2 · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-22T01:22Z · contract review PASS at CONTRACT_REVIEW_TIER6330333152 · ready and auto-merge set through the relay; the landing note follows the merge.
merged 2026-10-22T01:52Z through the merge queue, merge commit 331613c18a
origin/main
head 331613c18, subject "fix(plugin-security)!: the raw-SQL read scope (getReadFilter) answers the deny sentinel where the delegated prologue refuses"
This card
closed completed by Fixes; no other card closed in that window
State: pm:dispatched is removed in this act. The seat call A (6328262579) is carried out, and the maintainer keeps a veto. The two readings it left are carried by #25107 (canReadObject) and by PR #25150's Acceptance notes. #24932 (blocked on #25144) merges main over this change when it is re-dispatched.
Activity
objectstack-fleet commentedon Oct 21, 2026
Serial after #24932 ·
domain:servicesseat 2 (seat post #21118) ·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-21T13:55Z. ⛔ Not a claim, no label change.6317834763) rewrites thecontrolled_by_parenthalf ofgetReadFilterandcomputeDelegatedReadFilterinsecurity-plugin.ts. That is the provider this card names.computeDelegatedReadFilteron themainthat carries plugin-security: a controlled_by_parent child's read loads every readable master id with no limit (twice per request), so its CPU grows with the master table and the read answers 500 "too many SQL variables" past ~32.8k masters #24932's merge, not on the filing-timemain. The reach-first order and the skills: five principle gaps the #9296 wave exposed in checklist-test / checklist-author / dogfood-verification #9387 disclosure rule are unchanged.objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 1 (re-seated term) · 2026-10-21T14:53Z · p2,
domain:services, reach-first under the disclosure ruling (#93875323310017)Session:
session_01UmqzD97e14iws6hmVhgZ5y(the dispatching seat; the dev is amode:subagentrun of this session, told apart by its branch)Account:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-25106-read-scope-org-vetWorktree:
objectstack-issue-25106Domain:
domain:servicesSeat:
domain:services#2File surface: measure reach first. Only if reach is shown:
packages/plugins/plugin-security/src/security-plugin.ts, the delegated prologue ofgetReadFilter/computeDelegatedReadFilter(:5969/:6092atedcc8ceae) and nothing else in that file, plus the plugin's tests and.changeset/. ⛔ Not the engine middleware's prologue (resolveOperationPrincipals, which PR #25101 already changed). ⛔ Notservice-analytics.Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tieron this surface: "no path-derived mandate")Clause-②: no — expected: if reach is shown, the provider answers the deny sentinel where the operation prologue now refuses. That is a narrowing of a runtime read scope, not a published accept set. If the fix lands, it owes a review at
CONTRACT_REVIEW_TIERbefore the queue, as #25063's did.Responsibility:
plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path; the raw-SQL provider is the one door that does not mirror it | reach not measured: the review called it in-process only, with no door that builds the shape; the dev measures it firstThread-read: 6317847384
Serial constraints cleared: supersedes this seat's note
6317847384. #24932 is no longer in flight: it is inneeds-user-decision(6318800595), with no branch commits. So this card goes first ingetReadFilter/computeDelegatedReadFilter. #24932's switch, when it is re-dispatched, mergesmainand reads this change (its dev already priced it:6318648840deferred_card_repricing). In flight in this lane: #25096 / PR #25109 (plugin-auth), which is disjoint. Seat 1's queued #25095 is a different region (the detail write gate). No open PR touchessecurity-plugin.ts.objectstack-fleet commentedon Oct 21, 2026
pm:dispatched→pm:queue, released before any measurement ·domain:servicesseat 2 (seat post #21118) ·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-21T14:59Z · Thread-read: 6319041191Release:
session_01UmqzD97e14iws6hmVhgZ5y· Cause: the maintainer's word in this session's chat, verbatim: 「你可以下班了」 and then 「当前任务不用开发了」 (sign off now; the current task is not to be developed). The seat stopped its dev run · Goes to:pm:queue; this write clears the assignee.claude/issue-25106-read-scope-org-vetis empty (atmainedcc8ceae); the next taker starts from zero and may reuse or replace it.5323310017). The detail is boundary flag 1 of PR fix(plugin-security)!: a delegated operation runs only in an organization the delegator's membership backs #25101's review6313542596. Its order relative to plugin-security: a controlled_by_parent child's read loads every readable master id with no limit (twice per request), so its CPU grows with the master table and the read answers 500 "too many SQL variables" past ~32.8k masters #24932 (nowneeds-user-decision,6318800595): whichever lands second mergesmain.objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 1 · 2026-10-21T22:26Z · p2,
domain:services, a readings-only dispatch (reach first, under the disclosure ruling #93875323310017)Session:
session_01UnHcTADjeg3VwxF21QHj7W(the dispatching seat; the dev is amode:subagentrun of this session, told apart by its branch)Account:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-25106-read-scope-org-vetWorktree:
objectstack-issue-25106Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface: none written. No edit, no PR. Read:
packages/plugins/plugin-security/src/security-plugin.ts(getReadFilterabout:5969,computeDelegatedReadFilterabout:6092, the delegated prologue'svetOrganizationClaimuse about:9018, atorigin/mainabc25a353), and everygetReadFilterconsumer onmain(service-analytics'plugin.ts,analytics-service.ts,read-admission.ts,read-scope-sql.ts;plugin-audit'sparent-record-read-gate.ts;service-storage'sattachment-access-hooks.ts;objectql'sengine.tsandregistry.ts) for which of them can hand the provider an on-behalf-of context under a walled posture. The reach verdict goes on this card in abstract terms only. Stop on a breach and explain it in the report.Container & model:
S,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tieron this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable"). Readings-only, because the runbook reads a measure-first card as a readings dispatch; a build dispatch follows only on a reach verdict.Clause-②: no
Responsibility:
plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path | reach is the open question: the review called it in-process only, with no door that builds the shapeThread-read: 6319187412
Serial constraints cleared: nothing is written. No open PR touches
security-plugin.ts(the 8 open PRs' file lists were read at this claim). #24932 (needs-user-decision,6318800595) shares the provider; whichever change lands second mergesmainover the other (the release6319187412).Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{
"issue": 25106,
"status": "reading",
"branch": "claude/issue-25106-read-scope-org-vet",
"pr": null,
"session": "session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat's session, mode:subagent; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "Reach verdict: NOT SHOWN through any served door on main (abc25a3). The reviewer's reading holds: in-process only. The premise also holds: computeDelegatedReadFilter does not deny this case by another route under either walled posture. It answers a composed scope where the operation prologue (resolveOperationPrincipals, vetOrganizationClaim) refuses, so the provider's own lock-step promise (ADR-0021 D-C) is broken for in-process callers. Under the union posture the delegator's own wall already narrows that scope, but the answer is still not the deny sentinel. There are two getReadFilter invocations on main. (1) service-analytics' getReadScope bridge (plugin.ts): no served door hands it an on-behalf-of context. The one producer of a delegation link in this tree is the /mcp door; the analytics routes never accept that credential, and the MCP tools reach data only through the engine. (2) objectql's composeReadableIdsAtBoundary (engine.ts): it runs only after the middleware chain, so the prologue refuses first. plugin-audit's and service-storage's read gates write no ReadableIds leaf for a delegated caller at all. The population is an in-process caller that hands a delegated context it built itself to the published getReadFilter, or to the analytics service. The detail (mechanism, line anchors, controls, fix-shape pins) is withheld per #9387 (5323310017) and kept at readings_path.",
"consumers": [
"service-analytics src/plugin.ts, the getReadScope auto-bridge: the one data path. Its served doors are the REST dataset route, the runtime analytics routes and the publish build probes, and none of them hands it an on-behalf-of context. It is not behind the operation prologue on the native-SQL strategy. Zero door reach.",
"service-analytics src/analytics-service.ts (resolveReadScopes), src/read-scope-sql.ts and src/read-admission.ts: consumers of that same bridge's answer. read-admission asks canReadObject, which does not vet the organization either (see out_of_scope_findings). The other references in these files are documentation.",
"objectql src/engine.ts, composeReadableIdsAtBoundary step 5: the second invocation. It is reached only inside the executor of executeWithMiddleware (find, findOne, count, aggregate), after the security middleware's prologue, which refuses the shape first. Refused upstream.",
"objectql src/registry.ts: documentation reference only.",
"plugin-audit src/parent-record-read-gate.ts and service-storage src/attachment-access-hooks.ts: both route every delegated caller to the bounded probe and write no ReadableIds leaf for it. The probe reads through the engine, where the prologue runs. Neither calls getReadFilter for a delegated caller.",
"objectui at 7e9708e: zero getReadFilter references."
],
"tests": "None run: this is a readings-only dispatch, so no closure was built and nothing was executed. Source readings at objectstack abc25a3, each zero with its positive control: (1) getReadFilter invocations outside the provider (git grep over the non-test source of packages, examples and apps): 44 lines, 2 invocations. Control: both known call sites hit, and every other line is documentation or a type. (2) Producers of a delegation link (git grep onBehalfOf over non-test source): 71 lines, 1 producer, core assemble-execution-context.ts. Control: that line hits, and every other hit copies, strips or reads a link. (3) Context assemblers handed OAuth provenance: 7 callers, and only runtime resolve-execution-context.ts passes it, gated to the /mcp path. Control: the gate's setter in http-dispatcher.ts hits. (4) Analytics service invocations outside its package: 4 (the REST dataset route, the runtime analytics domain twice, the build probes), none on the /mcp path. Control: the same grep hits all 4. (5) ReadableIds composition call sites: 4, each inside executeWithMiddleware. Control: the definition and the executor hit. (6) objectui at 7e9708e: 0 getReadFilter hits (exit 1). Control: canReadObject exit 0. Instrument reach: this tree and objectui only. NOT MEASURED: objectstack-ai/cloud, because GitHub code search answered HTTP 403 (the session is bound to its configured repositories); third-party plugins, which are out of tree; and the in-process reach itself, which is a source reading, not executed. The filing base edcc8ce is an ancestor of abc25a3 (merge-base --is-ancestor exit 0, which proves itself). security-plugin.ts, explain-engine.ts, core and runtime security and service-analytics src are byte-identical between the two commits; engine.ts moved only outside the composition and executeWithMiddleware.",
"gates": "No diff against origin/main: the branch is empty at edcc8ce. So no local gate is owed, there is no --ran reconciliation, and there is no PR, hence no check-runs.",
"dogfood_battery": "n/a: readings-only, no PR.",
"mcp_calls": "0",
"api_writes": "1: POST /repos//issues/25106/comments (this comment), through scripts/pm/post-stamped.mjs. Reads (REST, gh api): issue 25106 and its comments, issue 25063 and its comments, and issue comment 6313542596. One read was refused: GET /search/code (HTTP 403).",
"open_questions": [
{
"question": "Reach through a door is not shown, and the card says the lane fixes only if reach is shown. But #25063's ruling (6312131621) built option P for exactly this in-process population. Should the seat build the provider mirror, or close the card as no-door?",
"options": [
"A: Build the mirror. In computeDelegatedReadFilter, on the resolved-delegator arm right after the dangling-delegator mirror, ask vetOrganizationClaim once over the live organization and the delegator's own memberships, and answer RLS_DENY_FILTER by identity. This stays inside the claim's surface: no prologue edit, no service-analytics edit. Pins: refusal cells under both walled postures (toBe the sentinel); preservation cells (single posture, no active organization, in-set delegated, undelegated, system); one parity cell against the engine find on the same context; and an ablation. Six existing plugin-security test files build delegated contexts through getReadFilter under a walled posture and must be re-judged; which of them go red is NOT MEASURED. It is a narrowing for in-process callers only, and owes a review at CONTRACT_REVIEW_TIER before the queue.",
"B: No code. Close the card as no-door reach and record the boundary: in-process callers of the published provider keep a scope that the find path refuses. It costs nothing now, and it leaves the provider's declared lock-step (ADR-0021 D-C) unenforced for the published onBehalfOf shape."
],
"recommendation": "A. Business need: no served door builds the shape, so no real traffic changes, and the cost is one call to an existing function. Long-term: ADR-0021 D-C and the provider's own doc declare that the raw-SQL scope IS the find scope. #25063's ruling rejected 'no code' for the same population because it leaves the published onBehalfOf shape declared but unenforced (ADR-0049); B repeats that rejected option on the sibling door. AI error: an AI-written in-process producer that builds the context in the wrong organization gets the same deny on both doors, instead of a refusal on one door and a silent scope on the other. Startup scope: one ask of an existing function, with no new gate, error code or surface. Note: canReadObject does not vet either (carried by #25107), so after A an in-process analytics read of this shape answers zero rows, not a 403."
}
],
"out_of_scope_findings": [
"carrier: #25107 · The object-level admission probe canReadObject does not vet the delegator's organization either. The analytics read admission and the engine's composition both ask it before getReadFilter. Same in-process population, no door. Noted, not filed.",
"carrier: none · The header of plugin-audit's parent-record-read-gate.ts still says the security service's read scope does not carry the delegator intersection. getReadFilter's own doc and the engine's ADR-0139 D5.1 (as amended) contradict that. The routing it describes, delegated callers to the bounded probe, is still safe. Documentation drift, an observation only. Noted, not filed."
],
"readings_path": "/tmp/claude-0/-home-user/1c8ca8e4-330d-5501-a9b1-41cf766c6c6c/scratchpad/issue-25106/readings.md (local to this run's container: mechanism, line anchors, controls, fix-shape pins)",
"line_budget": "n/a: no file changed",
"files_changed": [],
"deviations": "This comment states the reach verdict and the call sites abstractly, and leaves the mechanism detail in the local readings file, per the #9387 disclosure ruling. The disclosure ruling outranks the same-JSON rule here. One read attempt (GitHub code search in objectstack-ai/cloud) was refused with 403, and is recorded as NOT MEASURED."
}
Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Seat call on the readings
6328225139: build the provider mirror (the dev's option A) ·domain:servicesseat 2 (seat post #21118) ·os-bill·session_01UnHcTADjeg3VwxF21QHj7W· 2026-10-21T22:47Z · Thread-read: 6328225139⛔ Disclosure (#9387,
5323310017): this record states the rule and an abstract reach only.Verified against
origin/mainabc25a353(not against the report):service-analytics/src/plugin.ts, about:672) and the engine's ReadableIds composition (objectql/src/engine.ts, about:6598).computeDelegatedReadFilter(security-plugin.tsfrom about:6092) mirrors the dangling-delegator refusal and the delegator intersection, and asksvetOrganizationClaimnowhere.Reach: no served door builds the shape. The population is in-process callers of the published provider, the same population as #25063.
Call: A. In
computeDelegatedReadFilter, askvetOrganizationClaimonce on the resolved-delegator arm, and answer the deny sentinel where the operation prologue refuses.6312131621) rejected "no code" for this population, because it leaves the publishedExecutionContext.onBehalfOfshape declared but unenforced (ADR-0049).Clause-②: no(a narrowing of a runtime read scope for in-process callers). It owes a review atCONTRACT_REVIEW_TIERbefore the queue.canReadObjectdoes not vet the organization either; plugin-security: delegation probes and diagnostics (canReadObject / canWriteObject / canExport, field mask, explain) answer for an on-behalf-of context the operation prologue now refuses (PR #25101) #25107 carries that. The stale header inplugin-audit'sparent-record-read-gate.tsgoes to Acceptance notes.Four axes (中文):
State: the claim
6327921524stays live (assignee andpm:dispatchedunchanged). The build dispatch goes out on the same branch when a slot frees. This seat's one freed slot goes first to the p1 #25118, by the take order. The build's file surface narrows to the claim's original line:computeDelegatedReadFilterand nothing else insecurity-plugin.ts, the plugin's tests and.changeset/.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 1 · 2026-10-21T23:33Z · amends claim
6327921524from a readings round to an implementation round, under the seat call6328262579(A, the provider mirror)Session:
session_01UnHcTADjeg3VwxF21QHj7W(the dispatching seat; the dev is amode:subagentrun of this session, told apart by its branch)Account:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-25106-read-scope-org-vetWorktree:
objectstack-issue-25106Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface:
packages/plugins/plugin-security/src/security-plugin.ts,computeDelegatedReadFilter(about:6092atorigin/mainabc25a353) and nothing else in that file; the plugin's tests underpackages/plugins/plugin-security/src/;.changeset/25106-*.md. ⛔ Not the engine's prologue (resolveOperationPrincipals), notcanReadObject(#25107), notservice-analytics, notplugin-audit. ⛔ Disclosure (#9387,5323310017): no probe, context shape, request or served value on the card, the PR or any comment. Stop on a breach and explain it in the report.Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tieron this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable")Clause-②: no — a narrowing of a runtime read scope for in-process callers. It owes a review at
CONTRACT_REVIEW_TIERbefore the queue.Responsibility:
plugin-security's computeDelegatedReadFilter (this lane's code) mirrors the middleware's layers but not the delegated prologue PR #25101 added | the operation prologue resolveOperationPrincipals → vetOrganizationClaim already refuses on the find path | in-process callers of the published provider only; no served door builds the shape (readings 6328225139)Thread-read: 6328262579
Serial constraints cleared: no open PR touches
security-plugin.ts. #24932 (needs-user-decision) shares the provider; whichever lands second mergesmain. This seat's in-flight #25060 and #24914 touch noplugin-securityfile.Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
os-dev-report
{
"issue": 25106,
"status": "done",
"branch": "claude/issue-25106-read-scope-org-vet",
"pr": "#25150",
"session": "session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat's session, mode:subagent; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "Seat call A implemented. computeDelegatedReadFilter (packages/plugins/plugin-security/src/security-plugin.ts) now asks vetOrganizationClaim once on the resolved-delegator arm, with the prologue's inputs, right after the dangling-delegator mirror and before the delegator's sets resolve, and answers RLS_DENY_FILTER by identity where the operation prologue refuses (logged at warn, as the prologue does). No new error code, no second copy of the rule; nothing else in the file moves (not the prologue, not getReadFilter, not canReadObject). PM assumptions, measured: (1) the provider's refusal agrees with the prologue cell by cell under both walled postures, and every preserved answer is byte-identical before and after (39 of 45 recorded cells identical; the 6 that changed are exactly the cells find refuses); (2) none of the six flagged fixtures went red (full suite 207/207 files), so no pin needed flipping; (3) canReadObject is left alone and the zero-rows-not-403 consequence is in Acceptance notes. Draft PR #25150 is open; its first line names this card as the one it closes on merge. CI has converged green on head 03a9717.",
"tests": "All runs through scripts/pm/os-verify-lock.sh, exit codes captured before any pipe. Closure: pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-security^...' build (19 packages): VERDICT command-exit 0. New cases: 17 in src/delegated-organization-membership.test.ts (the rule's existing file; 25 -> 42): 6 refusal (group and isolated x a public, a private and an organization-less object, toBe(RLS_DENY_FILTER)), 9 preservation (per walled posture: in-set delegated, undelegated, no active organization, system; plus single; scopes pinned by the rows they admit), 2 parity (per walled posture: find refused 403 PERMISSION_DENIED iff the provider's deny sentinel, and where find admits, the scope admits exactly find's rows; the refused set is asserted, so neither side is vacuous). Red first: on the unmodified provider, 'Tests 6 failed | 28 passed (34)' (the 6 refusal cells; a temporary measuring cell, never committed). Before/after: 45 cells (3 postures x 5 context kinds x 3 objects) recorded pre and post; 39 byte-identical (scope JSON and admitted rows), 6 changed, all refusal cells. File post-fix: 'Tests 42 passed (42)'. Package on 58623ec: pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2: 'Test Files 207 passed (207)', 'Tests 4253 passed | 45 skipped (4298)'; pnpm --filter @objectstack/plugin-security typecheck: exit 0 (check:test-typecheck OK; the file is in tsconfig.test.json's program, 1 hit with --listFiles). On final head 03a9717 (after merging origin/main 459afea, which moved packages/lint and three driver-sql tests; lint rebuilt): the rule's file plus the six flagged files: 'Test Files 7 passed (7)', 'Tests 340 passed (340)'. Ablation (fix committed at 58623ec): scripts/ablation-replace.mjs replaced the vetOrganizationClaim ask with false: anchor x1 -> x0, blob 130693ab6579 -> 447156f4c27f, marker on disk x1; run: 'Tests 8 failed | 34 passed (42)', the 8 = 6 refusal + 2 parity, every preservation and prologue cell green; restore proven by the tool (blob after restore = HEAD blob 130693ab6579, git diff HEAD empty) and re-read independently (git status --porcelain empty, git hash-object = HEAD blob, marker x0). Subject imported from source (./security-plugin.js), so no dist leg.",
"gates": "Local subset on head 03a9717: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 69 commands; 67 exit 0; 2 exit 3 (PREREQUISITE NOT MET, NOT MEASURED locally): pnpm check:dual-build-cjs-loads (needs every workspace package's dist) and pnpm check:i18n (needs the built CLI plus nine packages' closures). --ran reconciliation: '69 derived, 67 run, 2 NOT-MEASURED, 0 UNRUN', exit 0. CI on 03a9717 (read once, converged): 41 check-runs, 34 success, 7 skipped, 0 failure. Required: Lint & Repo Gates=success, TypeScript Type Check=success, Test Core=success (1/6..6/6 success), Build Core=success, Governed Surface Queue Guard=success. Also Check Changeset=success, Dogfood Regression Gate=success, Type Check workspace/source/consumer/debt ledger=success, Lint ESLint/PM dispatch-gates/docs & code guards/repo & tooling/CI release & engine gates=success, the three claim/closing-target guards=success. Skipped by their own if: Console Pin Gate, Temporal Conformance, Dogfood Regression Gate shards, Dogfood Verify CLI, Build Docs, 18.x upgrade harness, Packed-tarball smoke.",
"dogfood_battery": "03a9717ca · os-verify-lock: VERDICT command-exit 0 (pnpm --filter @objectstack/dogfood test, no OS_TEST_SHARD, no path, after building the 68-package closure: VERDICT command-exit 0) · Test Files 287 passed | 3 skipped (290) · Tests 2395 passed | 18 skipped (2413) · no failures",
"mcp_calls": "0",
"api_writes": "2 REST writes (plus the branch git push, which is not REST): (1) one fleet-write relay stroke for pr_create: POST /repos/objectstack-ai/objectstack/dispatches, executed by the relay as POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/25150/assignees (os-bill); read back: body 8904 bytes stored identical, draft, assignee os-bill, no labels; (2) this comment: POST /repos//issues/25106/comments through scripts/pm/post-stamped.mjs. Reads (REST, gh api): issue 25106 and its comments, PR 25150, the check-runs of 03a9717.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #25107 · canReadObject does not vet the delegator's organization either; after this change an in-process analytics read of the refused shape is admitted at the object level and answers zero rows through this scope, not a 403. In Acceptance notes; not filed.",
"carrier: none (no carrier) · plugin-audit's parent-record-read-gate.ts header still says the security service's read scope does not carry the delegator intersection, which getReadFilter's doc and ADR-0139 D5.1 (as amended) contradict; the routing it describes stays safe. Documentation drift outside this claim's surface. Acceptance notes only.",
"carrier: none (no carrier) · observation, not investigated: under a walled posture the engine's find under a system context that carries an active organization returned that organization's rows only in the fixture, while the provider answers no scope for any system context, as its doc promises (it mirrors the middleware's system skip). Acceptance notes only."
],
"files_changed": [
"packages/plugins/plugin-security/src/security-plugin.ts (+25 -1: computeDelegatedReadFilter only, its doc and the one ask)",
"packages/plugins/plugin-security/src/delegated-organization-membership.test.ts (+150 -1)",
".changeset/25106-read-scope-delegated-organization-membership.md (+17: @objectstack/plugin-security patch, Clause-②: no (narrowing), Metadata-protocol: none, ADR-0087 not-required marker, BREAKING banner with FROM/TO/Unchanged/fix)"
],
"line_budget": "n/a: no skills/** or ratcheted ledger touched; +192 -2 over 3 files, under the 3000-line human-merge threshold",
"deviations": "(1) Clause-② arm: the claim carries a bare 'Clause-②: no'; the PR body and changeset carry 'Clause-②: no (narrowing)' with a BREAKING banner and a '!' title, following #25063's precedent under the amended 「甲」 as the dispatch instructs, and say so in the PR body. The level is patch, per ruling A's level axis (Metadata-protocol: none). Check Changeset is green on it. (2) Two derived gates NOT MEASURED locally (exit 3, prerequisite); CI's Lint & Repo Gates ran them green. (3) test:repo not run locally: the diff touches none of its tests; CI Test Core green. (4) The harness attribution reminder asked for a model-named Co-Authored-By trailer and another PR footer form; commits carry the model-free pair and the PR body the session-URL footer, per AGENTS.md, which takes precedence. (5) Under the disclosure ruling, this comment and the PR body describe the rule, the counts and the narrowing abstractly; the served values behind the before/after counts stay in local files. (6) The pins live in the rule's existing test file rather than a new one (inside the claim's surface)."
}
Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
ACCEPT → PR #25150 comment 6330365682 ·
domain:servicesseat 2 ·session_01UnHcTADjeg3VwxF21QHj7W· 2026-10-22T01:22Z · contract review PASS atCONTRACT_REVIEW_TIER6330333152· ready and auto-merge set through the relay; the landing note follows the merge.Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
Landed ·
domain:servicesseat 2 ·session_01UnHcTADjeg3VwxF21QHj7W· 2026-10-22T01:54Z331613c18aorigin/main331613c18, subject "fix(plugin-security)!: the raw-SQL read scope (getReadFilter) answers the deny sentinel where the delegated prologue refuses"completedbyFixes; no other card closed in that windowState:
pm:dispatchedis removed in this act. The seat call A (6328262579) is carried out, and the maintainer keeps a veto. The two readings it left are carried by #25107 (canReadObject) and by PR #25150's Acceptance notes. #24932 (blocked on #25144) mergesmainover this change when it is re-dispatched.Generated by Claude Code