Skip to content

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

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): a served answer that disagrees with the operation it describes, with named producers. The source is the isolated contract review of PR #25101 (#25063), boundary flags 1–2 of 6313542596 (judged parity only: none of these serves rows), and the #25063 dev's round-2 finding. 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 #9387 ruling (5323310017) applies, as on #25063. ⛔ No probe or context shape goes on this card, its PR or any comment.

Reader (who acts): triage grades and routes it.

The finding

  • PR fix(plugin-security)!: a delegated operation runs only in an organization the delegator's membership backs #25101 refuses every engine operation of an on-behalf-of context whose live active organization the delegator's membership does not back (ADR-0090 D10, through vetOrganizationClaim at the operation prologue).
  • Several plugin-security paths resolve a delegator on their own, and do not apply that rule. So for such a context they can answer "admitted" while the operation is refused:
    • the admission probes canReadObject, canWriteObject and canExport;
    • resolveProjectionFieldMask / getReadableFields;
    • the explain engine (explainAccessForCaller);
    • describeDelegationNarrowing;
    • delegatedWriteContexts.
  • None of them serves rows: their consumers run the operation through the engine, which refuses.
  • The raw-SQL read-scope provider is NOT in this card. It serves rows, and is filed separately.
  • resolveDelegatorContext's comment in explain-engine.ts ("the agent and its delegator are, by construction, in the same org") could point at the prologue check.
  • Reach: in process only. The one producer of an on-behalf-of context (the /mcp door) never builds the shape.

Governing text

ADR-0090 D10, and the probes' own promise to answer as the operation does.

Dedupe

Searched (semantic): delegated on-behalf-of probe canWriteObject explain parity organization membership. The hits are #25063 (the parent rule) and #24822, plus closed cards; none is this finding.

Dedupe words: delegated organization membership probe parity · canWriteObject explain on-behalf-of organization not backed · resolveDelegatorContext same-org comment.

Activity

  1. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    on Oct 21, 2026
  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card thread · domain:services seat 1 (seat post #6021, project run by the Sales account) · p3 · area:access · the whole card · 2026-10-22T19:03Z
    Session: session_01DPxprKQnzCbhdMTXUGRu31
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-a7kyo3 (the harness-pinned branch for this session; it names no issue, so the Duplicate Fix Guard's branch-name hint will not match — this claim is the identity record)
    Worktree: objectstack-issue-25107
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (read on origin/main cc8d886d4; stop on a breach and explain it in the report):

    ⛔ Disclosure (#9387, 5323310017): this claim, the PR and every comment state the rule and an abstract reach only; no probe or context shape.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25107,
    "status": "done",
    "branch": "claude/project-thread-a7kyo3",
    "pr": "#25288",
    "session": "session_01DPxprKQnzCbhdMTXUGRu31 (card thread)",
    "premise_still_valid": true,
    "summary": "One helper, delegatedOrganizationRefusal (explain-engine.ts, beside resolveDelegatorContext), asks vetOrganizationClaim once with the prologue inputs (live organization, the delegator own memberships, the posture). The prologue and computeDelegatedReadFilter now ask it (behaviour byte-identical, their 42 cells unchanged), and canReadObject, canWriteObject, canExport, resolveProjectionFieldMask (the four field projections), explainOnce, describeDelegationNarrowing and delegatedWriteContexts ask it at the prologue point, so on a walled posture each answers the refusal the operation already gets. ExplainEngineDeps gains a required tenancyPosture (wired by the plugin; the seven hand-built test deps pass single). Changeset patch with the BREAKING banner, Clause-②: no (narrowing), Metadata-protocol: none, ADR-0087 not-required (runtime-interface-only packages/plugins/plugin-security/src/explain-engine.ts#ExplainEngineDeps).",
    "tests": "Rule file delegated-organization-membership.test.ts 42 to 66 cases (refusal 10, preservation 11, parity 2, enumeration 1), 66/66. Red first (both sources at base cc8d886, new tests kept): Tests 13 failed | 53 passed, exactly the 13 new non-preservation cells. Ablation, scripts/ablation-replace.mjs WRAP mode, one leg per site: describeDelegationNarrowing 3 red, delegatedWriteContexts 3, resolveProjectionFieldMask 7, canReadObject 9, canWriteObject 9, canExport 9, explainOnce 11, the helper itself 36 (every refusal cell incl. the prologue and read-scope 24); each restore proven (blob == HEAD security-plugin.ts 4e9f0dc892a5 / explain-engine.ts aee792270aa0, git diff HEAD empty). Source-imported subject, no dist leg. Package suite on 8bb2d5e: Test Files 209 passed (209), Tests 4320 passed | 45 skipped (4365); typecheck exit 0. The first package run on c631ec4 caught 1 moved pin (explain-dependency-fault.test.ts control: its walled-stack delegator had no membership in the agent organization, the refused shape); fixture now seeds the membership, 16/16. Cross-package on-behalf-of suites through the real SecurityPlugin: objectql nested-composition 12/12, service-storage 2 files 152/152, plugin-sharing authored-row-write-deferral 20/20, plugin-audit 2 files 83/83, runtime http-dispatcher 248/248, each exit 0. All under os-verify-lock.",
    "gates": "dispatch-gates --commands on 8bb2d5e: 70 derived, all exit 0 (check:dual-build-cjs-loads after building its 8 missing dists; check:type-check-debt re-run under the lock after a queue-timeout exit 3). --ran: 70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. Roster gates under touched dirs (check-changeset-fixed, check:error-code-casing, check:filter-alias-parity): exit 0. CI on the PR: in_progress at report time; the type-check lanes and path-scheduled jobs are declared to CI.",
    "dogfood_battery": "8bb2d5e0 · os-verify-lock: VERDICT command-exit 0 · held the lock 1150s · Test Files 289 passed | 3 skipped (292) · Tests 2481 passed | 18 skipped (2499) · no failures (closure built first: turbo build --filter=@objectstack/dogfood^... exit 0)",
    "mcp_calls": "8 — reads only: issue_read x2, pull_request_read x5, search_pull_requests x1 (plus the harness PR-activity subscription, not a GitHub write)",
    "api_writes": "5 relay strokes, all via scripts/pm (objectstack-fleet[bot]): label-write #25107 (assignees os-sales, labels +pm:dispatched -pm:queue); post-stamped claim comment 6348946648 on #25107; dispatch pr_create #25288 (POST /pulls draft, POST /issues/25288/assignees os-sales); dispatch pr_request_reviewers (POST /pulls/25288/requested_reviewers os-sales); post-stamped this report on #25107. git push is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [],
    "notes": "Serial: #25256 (#25095) landed on main after the branch was cut; it edits security-plugin.ts in hunks disjoint from these (git merge-tree clean), and CI reads the merge ref. A CONTRACT_REVIEW_TIER review of record is owed before the queue (narrowing). PR body was written once at pr_create."
    }

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · 2026-10-22T21:47Z

    reading value
    PR #25288 MERGED through the queue (ACCEPT 6351583081, contract review 6350743954)
    origin/main carries 5801a4f2c9

    State: the card closed completed through the PR; this stroke clears pm:dispatched and the assignee.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions