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
automation: a runAs:'user' run carries no posture rung, so a platform administrator's flow is served organization ledger rows only #28070
Found by the contract review of PR #28047 (card #26750, ruling A 6457448424): should-fix 1 in record 6520912495.
What happens
After #26750, deployment-level ledger rows (tenant_id NULL) are served only to a request whose execution context carries the PLATFORM_ADMIN posture rung. The rung is derived in resolveAuthzContext (packages/core/src/security/resolve-authz-context.ts) and carried by assemble-execution-context.ts. An automation run with runAs: 'user' builds its context in service-automation instead: seedRunGrants (packages/services/service-automation/src/run-resume.ts) seeds positions, permissions, peers and email, and no posture. So a flow run as a platform administrator that reads the ledger is served organization rows only.
This fails closed (fewer rows, never more), so it is not a leak. It is a gap between "the platform administrator reads every row" and what a flow running as that administrator gets.
Question for triage
Should seedRunGrants seed the posture rung the way resolveAuthzContext derives it (one source of truth for the rung), or is "a flow that needs deployment-level ledger rows reads them in system context" the intended answer (then a docs sentence)? No known flow reads the ledger today; the measurement belongs to the claimant.
Re-check: grep -n "posture" packages/services/service-automation/src/run-resume.ts → no hit expected.
Read fresh. It fails closed (fewer rows), so it is not a leak. A runAs: 'user' run derives its posture rung through the same resolveAuthzContext derivation a request uses, for the user it runs as, with no second derivation. Pin it with a platform-admin flow reading a deployment-level ledger row.
Claim: PM loop round 2 · 2026-10-30T12:16Z
Session: session_016WV4cwHfcrFZ8RzH1TiP48 (card thread; seat session_016WV4czuTWNgKR22fKv7jia)
Account: os-elon-musk
Branch: claude/project-thread-sl17ka (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)
Worktree: objectstack-issue-28070
Domain: domain:server
Seat: domain:server#3
File surface: packages/services/service-automation/src/: run-resume.ts (seedRunGrants), plugin.ts (one hunk in the grants bridge forwarding resolveUserAuthzGrants' own posture), runtime-identity.ts (SeededRunContext / RunIdentityContext / resolveRunDataContext's user arm carry it), engine.ts (two small hunks: FlowUserGrants.posture and the seedRunGrants docblock), tests beside them, .changeset/*. Amended 12:25Z on the thread's measure: the rung reaches a data node only through those four. ⛔ No second posture derivation, and no edit to packages/core, packages/spec or ui/.
Container & model: M, mode:thread, model: opus (TIER_DEFAULT), effort: high
Clause-②: yes (widening: the barrel-exported RunIdentityContext and resolveRunDataContext's answer gain posture, and FlowUserGrants gains one; amended 12:25Z from the thread's reading; a TIER_DEFAULT contract-review record by the lane seat is owed before the queue)
Metadata-protocol: none
Responsibility: service-automation's runAs: 'user' run seeding the posture rung the request path derives for the same user.
Thread-read: 6522159967
Premise: holds on main @ 5448585d: seedRunGrants (run-resume.ts:196) seeds no posture.
Serial constraints cleared: no open PR touches this surface at claim time (checked against the open PR list). resolve-authz-context.ts is edited by #27733 (this seat, one thread): this card only imports from it. #28056 (retriage) will want run-resume.ts / engine.ts later. Re-checked 12:25Z for the amended surface: no open PR touches service-automation/src/{plugin,runtime-identity,engine,run-resume}.ts.
Dispatch-note: p2. Taken into a slot freed by PR #28072 / #28067 merging (2026-10-30 12:13Z).
{
"issue": 28070,
"status": "done",
"branch": "claude/project-thread-sl17ka",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28098",
"session": "session_016WV4cwHfcrFZ8RzH1TiP48",
"premise_still_valid": true,
"summary": "A runAs:'user' run now carries its user's posture rung, taken off the resolveUserAuthzGrants resolution the plugin's grants bridge already makes (no second derivation). seedRunGrants seeds it only together with the positions/permission sets it was derived from, the decision switch deletes the submitter's rung, and resolveRunDataContext's user arm carries it (validated against AuthzPostureSchema) to every data node. A platform administrator's flow is now served the ledger's deployment-level rows, as their request is; member and runAs:'system' runs are unchanged.",
"tests": "New runas-user-posture-rung.integration.test.ts: 12/12 green (envelope layer + real stack with SecurityPlugin and AuditPlugin). Ablation 1 (posture carry off in runtime-identity.ts, marker edit, restored): 4 red incl. the card repro, member/system pins stay green. Ablation 2 (delete switched.posture removed): 1 red. pnpm --filter @objectstack/service-automation test: 228 files / 2888 tests, exit 0. typecheck: exit 0.",
"gates": "dispatch-gates --commands at cfd3bf44: 93 derived; 91 exit 0, 2 exit 3 NOT MEASURED (check:skill-examples, check:dual-build-cjs-loads: prerequisite whole-tree build); pnpm lint exit 0. --ran: 93 accounted, 91 run, 2 NOT-MEASURED, 0 UNRUN. PR check-runs at cfd3bf44: 50 total, 38 success, 12 skipped by path filter, 0 failed (Test Core 8/8, Type Check workspace/source/consumer/debt, Lint families, Build Core, Build Docs, Check Changeset, PR-body guards).",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "4 — reads only: issue_read x2 (card body, comments), pull_request_read get_check_runs x1, plus subscribe_pr_activity (session tool, not a GitHub write)",
"api_writes": "2 — relay pr_create (PR #28098), relay comment (this report)",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: named producer — the REST automation door (packages/runtime/src/domains/automation.ts:204-205) and the action door (packages/runtime/src/action-execution.ts:2077-2078) forward ec.positions/ec.permissions but not ec.posture, so a flow started there carries grants and no rung (fails closed) · dedupe words: runAs posture door forward rung",
"carrier: #28099 (seat-filed) · noted"
]
}
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 真挡得住的权限 | none | P②
Triage: p2 ·
domain:server·area:access· 2026-10-30T11:58Z⛔ Not a claim.
runAs: 'user'run derives its posture rung through the sameresolveAuthzContextderivation a request uses, for the user it runs as, with no second derivation. Pin it with a platform-admin flow reading a deployment-level ledger row.bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
Claim: PM loop round 2 · 2026-10-30T12:16Z
Session:
session_016WV4cwHfcrFZ8RzH1TiP48(card thread; seatsession_016WV4czuTWNgKR22fKv7jia)Account:
os-elon-muskBranch:
claude/project-thread-sl17ka(the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)Worktree:
objectstack-issue-28070Domain:
domain:serverSeat:
domain:server#3File surface:
packages/services/service-automation/src/:run-resume.ts(seedRunGrants),plugin.ts(one hunk in the grants bridge forwardingresolveUserAuthzGrants' ownposture),runtime-identity.ts(SeededRunContext/RunIdentityContext/resolveRunDataContext's user arm carry it),engine.ts(two small hunks:FlowUserGrants.postureand theseedRunGrantsdocblock), tests beside them,.changeset/*. Amended 12:25Z on the thread's measure: the rung reaches a data node only through those four. ⛔ No second posture derivation, and no edit topackages/core,packages/specorui/.Container & model:
M,mode:thread,model: opus (TIER_DEFAULT),effort: highClause-②: yes (widening: the barrel-exported
RunIdentityContextandresolveRunDataContext's answer gainposture, andFlowUserGrantsgains one; amended 12:25Z from the thread's reading; aTIER_DEFAULTcontract-review record by the lane seat is owed before the queue)Metadata-protocol: none
Responsibility:
service-automation'srunAs: 'user'run seeding the posture rung the request path derives for the same user.Thread-read: 6522159967
Premise: holds on
main@5448585d:seedRunGrants(run-resume.ts:196) seeds noposture.Serial constraints cleared: no open PR touches this surface at claim time (checked against the open PR list).
resolve-authz-context.tsis edited by #27733 (this seat, one thread): this card only imports from it. #28056 (retriage) will wantrun-resume.ts/engine.tslater. Re-checked 12:25Z for the amended surface: no open PR touchesservice-automation/src/{plugin,runtime-identity,engine,run-resume}.ts.Dispatch-note: p2. Taken into a slot freed by PR #28072 / #28067 merging (2026-10-30 12:13Z).
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
ACCEPT → PR #28098 comment 6523502420 · 2026-10-30T13:04Z · contract review PASS 6523187496 · ready + auto-merge armed through the relay. Door-started runs: #28099.
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4cwHfcrFZ8RzH1TiP48
os-dev-report
{ "issue": 28070, "status": "done", "branch": "claude/project-thread-sl17ka", "pr": "https://github.com/objectstack-ai/objectstack/pull/28098", "session": "session_016WV4cwHfcrFZ8RzH1TiP48", "premise_still_valid": true, "summary": "A runAs:'user' run now carries its user's posture rung, taken off the resolveUserAuthzGrants resolution the plugin's grants bridge already makes (no second derivation). seedRunGrants seeds it only together with the positions/permission sets it was derived from, the decision switch deletes the submitter's rung, and resolveRunDataContext's user arm carries it (validated against AuthzPostureSchema) to every data node. A platform administrator's flow is now served the ledger's deployment-level rows, as their request is; member and runAs:'system' runs are unchanged.", "tests": "New runas-user-posture-rung.integration.test.ts: 12/12 green (envelope layer + real stack with SecurityPlugin and AuditPlugin). Ablation 1 (posture carry off in runtime-identity.ts, marker edit, restored): 4 red incl. the card repro, member/system pins stay green. Ablation 2 (delete switched.posture removed): 1 red. pnpm --filter @objectstack/service-automation test: 228 files / 2888 tests, exit 0. typecheck: exit 0.", "gates": "dispatch-gates --commands at cfd3bf44: 93 derived; 91 exit 0, 2 exit 3 NOT MEASURED (check:skill-examples, check:dual-build-cjs-loads: prerequisite whole-tree build); pnpm lint exit 0. --ran: 93 accounted, 91 run, 2 NOT-MEASURED, 0 UNRUN. PR check-runs at cfd3bf44: 50 total, 38 success, 12 skipped by path filter, 0 failed (Test Core 8/8, Type Check workspace/source/consumer/debt, Lint families, Build Core, Build Docs, Check Changeset, PR-body guards).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "4 — reads only: issue_read x2 (card body, comments), pull_request_read get_check_runs x1, plus subscribe_pr_activity (session tool, not a GitHub write)", "api_writes": "2 — relay pr_create (PR #28098), relay comment (this report)", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: named producer — the REST automation door (packages/runtime/src/domains/automation.ts:204-205) and the action door (packages/runtime/src/action-execution.ts:2077-2078) forward ec.positions/ec.permissions but not ec.posture, so a flow started there carries grants and no rung (fails closed) · dedupe words: runAs posture door forward rung", "carrier: #28099 (seat-filed) · noted" ] }Generated by Claude Code