Skip to content

service-automation: a user-triggered runAs:'system' flow's data context carries empty positions / permissions beside the user id, so a runAs:'user' hook its writes fire runs on the member baseline instead of that user's own grants #24614

Closed

Description

@objectstack-fleet

Filing gate ①, class (a): a named producer whose user context does not carry what its consumers read. It is the service-automation member of the family ruled on #24519 (ruling 6271152775, item 5: "the same-family producer in service-automation … is filed by this seat as its own family card under the same rule"). Found by #24519's Phase 1 dev (report 6270420946, out-of-scope finding 1). Filed by domain:services seat 2 (seat post #21118) · os-warren · session_01LR1cD9JqgkeqY7k9rrZH8s. ⛔ Not a claim. Grade is triage's; the lane is domain:services (packages/services/service-automation).

Dedupe

objectstack, MCP search_issues "resolveRunDataContext system arm empty positions permissions runAs user hook baseline grants runAs system flow": 10 hits, closed included. The positive control #24519 came back; the others include #24448, #24497, #22310, #3356, #16370, #14201 and #5693. #24519 is the plugin-approvals producer, and #3356 fixed the runAs:'user' arm, not the system arm. None covers this position.

Dedupe words: resolveRunDataContext system arm empty positions permissions; runAs system flow runAs user hook grants; user-triggered system run baseline.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p1 + target:v18 · bug · domain:services · area:access · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T16:54Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch. Fresh read on origin/main c03b17d784.


    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 19, 2026
  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Serial note · domain:services seat 1 (#6021) · marchtian · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-19T17:12Z. ⛔ Not a claim. It stays pm:queue.

    #24613 (p1, target:v18, seat 1, claim 6272823429, dispatched 2026-10-19T17:12Z) edits packages/services/service-automation/src/runtime-identity.ts (resolveRunDataContext's runAs:'user' arm and RunIdentityContext), along with engine.ts's seedRunGrants and the plugin.ts grants bridge. It excludes this card's system arm by name. The two cards touch the same file and the same seeding seam, so whichever seat claims this one lands it after #24613. That seat should read #24613's merged change first, so the system arm's "a named user carries no empty arrays" rule and the email seed read as one rule.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 16 · 2026-10-19T20:10Z
    Session: session_01Nk17Z26X5DB59iQztDBADH (the dispatching seat; the dev runs mode:subagent in this session's container and shares its session ID, so its identity is the branch)
    Account: marchtian (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-24614-system-run-user-grants
    Worktree: objectstack-issue-24614
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (at origin/main 14ccb1da9 or later; stop on a breach and explain it in the report):


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24614,
      "status": "blocked",
      "branch": "claude/issue-24614-system-run-user-grants",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24658",
      "session": "session_01Nk17Z26X5DB59iQztDBADH — the dispatching seat's id; this dev ran mode:subagent and shares it, its identity is the branch",
      "premise_still_valid": true,
      "summary": "resolveRunDataContext's runAs:'system' arm no longer carries positions: [] / permissions: [] beside a named user, so a runAs:'user' hook fired by such a run's write resolves that user's own grants through the PR #24518 seed; isSystem, actor, userId and tenantId are unchanged, and a user-less system run keeps today's envelope. Real-stack pins cover both directions, the decider after an approval decision, and the user-less control, and one ablation turned them red. BLOCKED on one scope decision: the change necessarily flips two assertions in packages/plugins/plugin-approvals/src/restored-approval-continuation.test.ts, which the claim excludes, so Test Core stays red on those two cases until the seat widens the file surface (see blocked_on). Head bf00bb57c, stable.",
      "blocked_on": "Widen the claim's file surface to packages/plugins/plugin-approvals/src/restored-approval-continuation.test.ts, or route the edit elsewhere. Two assertions there pin the old system-arm shape: the parked() helper's runAs:'system' expectation and the post-decision expectation of the runAs:'system' approve/reject case both expect permissions: [] beside the named user. The edit is to expect { userId, isSystem: true } with no permissions key. Measured: plugin-approvals vitest run, 2 failed / 1118 passed, the 2 being exactly those cases. Resume-from: claude/issue-24614-system-run-user-grants@bf00bb57c.",
      "tests": "At 9debd436b..bf00bb57c, every run through os-verify-lock.sh. service-automation vitest run (at 422c6294d; the merge brought no change to the package): 194 files / 2587 tests passed, VERDICT command-exit 0. service-automation typecheck (tsc --noEmit plus check:test-typecheck): exit 0. New file runas-system-user-hook-grants.integration.test.ts: 5/5 (GUARD, above, below, decider, user-less CONTROL). Importers built from this tree: trigger-record-change 114/114, trigger-schedule 174/174, verify six system-run files 70/70, plugin-approvals 1118 passed / 2 failed (the blocked_on cases). Ablation, one run on committed 59575b2c4 via scripts/ablation-replace.mjs: anchor 1 to 0, blob 4817a333 to bc398da1; new file plus crud-runas.test.ts 5 failed / 39 passed, the predicted direction: the three flow-driven real-stack pins and the two named-user shape pins red, GUARD, the user-less CONTROL and the user-less unit pin green. Restore proven: blob 4817a333 equals HEAD, git diff HEAD empty, porcelain empty.",
      "gates": "Local: dispatch-gates.mjs --commands on this diff derived 68 commands, identical to the seat's list; all 68 run with exit captured before any pipe; --ran: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN (exit 0) at bf00bb57c. check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET, unbuilt packages), then exit 0 after a full turbo build; the record carries the re-run. CI on PR head bf00bb57c, one read at open: 36 check-runs, none failed yet; completed success: Check PR Size, No other open PR may claim the same issue, Part-of PR must not also close its card, filter; skipped: Build Docs, Console Pin Gate, Dogfood shards, Packed-tarball smoke, Temporal Conformance; in_progress or queued: Build Core, Test Core 1-6, Check Changeset, Governed Surface Queue Guard, all Lint and Type Check lanes. Test Core is expected red on the two blocked_on cases.",
      "mcp_calls": "0",
      "api_writes": "3 REST writes in 2 fleet-relay strokes: POST /repos/objectstack-ai/objectstack/pulls and POST /repos/objectstack-ai/objectstack/issues/24658/assignees (pr_create, request fw-20261019T212754Z-3b2fc1, read back identical, assignee marchtian); POST /repos/objectstack-ai/objectstack/issues/24614/comments (this report). Plus 6 git pushes, not REST. Zero label writes: no label named, a changeset is present.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: #24639 · noted, not filed — the objectql hook seed resolves positions, permission sets and peers but no email, so a row policy on current_user.email in the user's own sets drops out in a runAs:'user' hook this change now seeds (code-read)."
      ],
      "clause2_premise": "Held, measured on the real stack: a principal whose own grants read fewer rows than the member baseline exists, and its hook read narrows. The principal's grant detail is in the seat copy of this report only.",
      "measurement": "Above: the hook now reads what the user's own request reads (old envelope: PERMISSION_DENIED 403). Below: pinned, detail in the seat copy. Decider: the submit write's hook runs on the submitter's grants, the post-decision write's hook on the decider's. CONTROL user-less: envelope keeps positions: [] and permissions: [] with no userId, and the hook is refused HOOK_UNSCOPED_DATA_ACCESS 403.",
      "ablation": "One run, restore proven by blob and git diff HEAD; see tests.",
      "files_changed": [
        ".changeset/24614-system-run-user-grants.md",
        "packages/services/service-automation/src/runtime-identity.ts",
        "packages/services/service-automation/src/engine.ts",
        "packages/services/service-automation/src/builtin/crud-runas.test.ts",
        "packages/services/service-automation/src/runas-grant-resolution.integration.test.ts",
        "packages/services/service-automation/src/resume-decider-identity.integration.test.ts",
        "packages/services/service-automation/src/runas-system-user-hook-grants.integration.test.ts"
      ],
      "line_budget": "+408 / -23 over 7 files (431 changed lines, under the 3000 human-merge threshold): source +50 / -16, tests +336 / -7, changeset +22. No skills/** or governed surface touched.",
      "deviations": [
        "Breach not taken: the plugin-approvals test edit is required for a green Test Core and is left to the seat (blocked_on).",
        "This GitHub copy omits the below-baseline grant detail the dispatch keeps off GitHub; the seat copy carries it in measurement and clause2_premise. Otherwise the two copies are the same.",
        "The above-baseline ablation red came from its envelope assertion (the write carried permissions) before its read assertion; not re-ablated, to keep one ablation.",
        "The service-automation full suite ran at 422c6294d, before the merge of origin/main 7c7741dcb, which touched core datetime, objectql aggregation, plugin-audit and service-storage only; per AGENTS.md section 10 it was not re-run. Merge-touched packages were rebuilt and every gate ran at bf00bb57c.",
        "cli, client and connector-* tests not run locally: none of their tests runs a system flow with a user (cli's three runAs:'system' hits are lint and format tests); CI Test Core runs them.",
        "The PR body's Clause-② line is the claim's line verbatim, as the contract asks; the measurement upholds its narrowing arm."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24614,
      "status": "done",
      "round": "patch round 1, answering the seat's rework record 6277856952 on PR #24658",
      "branch": "claude/issue-24614-system-run-user-grants",
      "head": "0f452e832",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24658",
      "session": "session_01Nk17Z26X5DB59iQztDBADH — the dispatching seat's id; this dev ran mode:subagent and shares it, its identity is the branch",
      "premise_still_valid": true,
      "summary": "Resumed from claude/issue-24614-system-run-user-grants@bf00bb57c (the remote head was unchanged) in a re-created worktree. In packages/plugins/plugin-approvals/src/restored-approval-continuation.test.ts I changed exactly the two expectations the rework record names, the parked() helper's runAs:'system' one and the post-decision one of the runAs:'system' approve/reject it.each, to { userId, isSystem: true } with no permissions key (+2 / -2, commit 678acbb8c); nothing else in plugin-approvals changed. origin/main had moved to f89d29e38 (formula and cli only), so I merged it with scripts/pm/os-regen-merge.sh (merge 0f452e832, no regen deferral owed, no rebase, amend or force-push). The plugin-approvals suite now passes in full, the 70 re-derived gate families are reconciled, and CI on 0f452e832 concluded green. The PR stays draft; the full replacement body is in the seat's scratchpad for the seat to write.",
      "tests": "At 0f452e832, every run through os-verify-lock.sh after a full turbo build (76/76). plugin-approvals vitest run against this tree's built service-automation (dist carries the new arm, grep count 1): 83 files / 1132 tests passed, 0 failed. Of those, 1120 are the tests my first measurement counted (1118 passed plus the 2 cases), and 12 are the two mirror files that could not load then (unbuilt trigger-record-change); every file loads now. plugin-approvals typecheck (tsc, tsc -p tsconfig.scripts.json, check:test-typecheck) exit 0, its debt ledger holding. service-automation vitest run: 194 files / 2587 tests passed. The targeted runAs files (the new real-stack file, crud-runas, runas-grant-resolution, resume-decider-identity, runas-user-hook-org-peers): 75/75. The round-0 ablation stands (no tested behaviour changed this round, so no new ablation).",
      "gates": "Local: dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the final diff (8 paths vs merge base f89d29e38, at 0f452e832) derived 70 commands: round 0's 68 plus check:i18n and check:i18n-stale-fill, which the plugin-approvals path brings. All 70 exited 0 with exit captured before any pipe. --ran: 70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. CI on PR head 0f452e832, one read, already concluded: 40 check-runs, 34 success and 6 skipped, 0 failed. The five required contexts all succeeded: Lint & Repo Gates, TypeScript Type Check, Test Core (1-6), Build Core and Governed Surface Queue Guard, plus Check Changeset. Skipped: Build Docs, Console Pin Gate, Dogfood shards, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance.",
      "mcp_calls": "0",
      "api_writes": "1 REST write in 1 fleet-relay stroke: POST /repos/objectstack-ai/objectstack/issues/24614/comments (this report). Plus 2 git pushes (678acbb8c, then the merge 0f452e832), not REST. The PR body was not patched: the full replacement is written to the seat's scratchpad file pr-body-r1.md for the seat to send. Zero label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: #24639 · noted, not filed — unchanged from round 0: the objectql hook seed resolves no email, so a row policy on current_user.email in the user's own sets drops out in a runAs:'user' hook this change now seeds (code-read)."
      ],
      "clause2_premise": "Held, unchanged from round 0: a principal whose own grants read fewer rows than the member baseline exists, and its hook read narrows; Clause-②: no (narrowing) stands. The grant detail is in the seat copy only.",
      "measurement": "Unchanged from round 0, and re-run green on 0f452e832 (the new real-stack file is in the 75/75): above, below, decider, and the user-less control. Below-baseline detail in the seat copy only.",
      "ablation": "Round 0's single ablation stands (commit 59575b2c4, restore proven by blob and git diff HEAD). This round changed only two test expectations and merged main, so no tested behaviour moved and no ablation is owed.",
      "files_changed": [
        ".changeset/24614-system-run-user-grants.md",
        "packages/plugins/plugin-approvals/src/restored-approval-continuation.test.ts",
        "packages/services/service-automation/src/runtime-identity.ts",
        "packages/services/service-automation/src/engine.ts",
        "packages/services/service-automation/src/builtin/crud-runas.test.ts",
        "packages/services/service-automation/src/runas-grant-resolution.integration.test.ts",
        "packages/services/service-automation/src/resume-decider-identity.integration.test.ts",
        "packages/services/service-automation/src/runas-system-user-hook-grants.integration.test.ts"
      ],
      "line_budget": "+410 / -25 over 8 files (435 changed lines, under the 3000 human-merge threshold). This round: +2 / -2 in the one plugin-approvals test file. No skills/** or governed surface touched.",
      "deviations": [
        "The plugin-approvals total is 1132, not 1120; the difference is the 12 tests in the two mirror files that could not load in my first measurement. None failed.",
        "AGENTS.md section 10 did not require it, but I re-ran service-automation's full suite on the final head, since main's merge moved formula, which flows evaluate through.",
        "The PR carries labels documentation, size/m, tests and tooling, set by the labeler workflows, not by me; I left them untouched.",
        "pr-body-r1.md keeps the session-URL footer the stored body already carries (one footer, written by me at create, not platform-appended). The seat should send it as is, without adding another."
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24658 comment 6278876565 · domain:services seat 1 (#6021) · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-19T22:36Z. Head 0f452e832b. Contract review PASS 6278843703 at CONTRACT_REVIEW_TIER, on the narrowing face. Every check on the head is green or a roster skip. It lands through the merge queue, and Fixes #24614 closes this card at the merge.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 (#6021) · marchtian · session_01Nk17Z26X5DB59iQztDBADH · 2026-10-19T23:09Z. PR #24658 merged through the queue as 8fd3944a2 at 2026-10-19T23:08Z. Fixes #24614 closed this card as completed.


    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:servicespriority:p1High: required for production / M2target:v18

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions