Repository navigation
Description
Ruled: 6457448424 · letter A · 2026-10-28T00:39Z
One-line question: In a single-organization deployment, when a customer gives an auditor (not a platform administrator) the right to read the whole compliance ledger, should that auditor also see the history of deployment-wide configuration changes (settings and other deployment-level state), or only the history of the organization's business records?
Filing gate: ③ maintainer-directed — the maintainer, in the 18.2.0 wave-2 PM session (2026-10-27), verbatim: 「组织级审计权限能不能看到部署级的行 应该立决裁卡」. Raised by the os-dev report on the security card #26640 (open question 1). The security detail that led here stays withheld on #26640; this card asks only the scope question, which no longer carries key material on new writes (#26683, merged) and whose at-rest residue is #26687.
Background
The ledger has no organization column (ADR-0131 D7). Its writers stamp the organization a row is about in tenant_id; rows about deployment-level objects (objects with no organization column) carry tenant_id NULL. The read-all capability lifts the parent-record read gate only; the row scope comes from the platform row policy sys_audit_log_org (tenant_id == current_user.organization_id) in plugin-security's default sets.
Governing text
packages/spec/src/security/capabilities.ts:81, the capability description: "Under an organization wall an organization reader's row scope is the rows about its organization (tenant_id); a platform administrator reads every row, deployment-level rows included." — it states the walled postures and the platform administrator; it says nothing aboutsingle.packages/spec/src/security/capabilities.ts:70-80(comment): "under a wall-enforcing tenancy posture the platform row policy on that field (sys_audit_log_org…) still bounds a holder to its own organization's rows … A platform administrator reaches every row, deployment-level rows included, through its superuser read bypass, not through this capability."- ADR-0131 point 2–3: "In a single-tenant deployment the organization is the environment"; "A table with no organization column is deployment-level or code-level, governed by permission, not by a wall."
Protocol: no published schema shape changes under either option; option C changes a capability's published description text (spec), option A changes runtime read behaviour.
Premise (re-check)
git grep -n "sys_audit_log_org" packages/plugins/plugin-security/src/objects/default-permission-sets.ts→ the policy istenant_id == current_user.organization_id, so under a wall a NULL-tenant row is never in an organization reader's scope.- Under
singlethere is no wall, so that bound does not apply, and an organization-scoped holder's scope is every row its ledger grant reaches, deployment-level rows included (measured on security(plugin-audit): a sys_secret confidentiality gap, observed beside the 18.2.0 wave-2 clauses — detail withheld pending maintainer #26640, detail withheld). NOT measured undergroup/isolatedwith a custom set that omits the default row policy — the execution step below measures it first.
Options
| Option | What it does | What the customer sees | Effort |
|---|---|---|---|
| A | One rule in every posture: deployment-level ledger rows (tenant_id NULL) are served only to the platform administrator (or a separately named platform capability); an organization-scoped holder reads only rows about its organization(s). |
A single-org auditor no longer sees deployment configuration history; the platform administrator still does. Walled postures unchanged. | server lane · 1 PR (audit-log-read-visibility.ts + pins + capability text sentence). No same-size precedent; nearest is #22266 (+1065/−257), which built the row scope itself. |
| C | Keep the behaviour; amend the capability text to say that under single the organization scope includes deployment-level rows, because there the organization is the environment. |
Nothing changes at runtime; the documented scope finally covers single. |
spec lane · 1 PR (text only). No measured precedent. |
(Option B from the dev report — stamping deployment-level rows with the default organization under single — is not offered: it re-attributes history and reverses ADR-0131 D7's meaning of deployment-level.)
Business meaning
- A = the "data audit" vs "setup audit" split most platforms make: reading what happened to records is one right; reading what happened to the system's configuration is an administrator's right (Salesforce: Setup Audit Trail needs "View Setup and Configuration"; ServiceNow: configuration audit is admin-gated).
- C = in a one-company deployment the company is the system, so its auditor sees everything the ledger holds, as one auditor does in a small on-prem install.
Four axes (business stance)
① Long-term: A makes the three postures behave the same (deployment rows are never an organization's), shrinking a posture-specific exception; C writes a posture difference into the capability's contract and keeps it forever.
② Business pull today: the auditor role is a custom grant (held by default only by platform administrators), so few stock deployments reach it; under C nothing breaks; under A a customer who built such an auditor on single loses configuration history visibility.
③ Anti-AI-error / failure mode: under C, a wrong grant fails silently (an auditor sees configuration history nobody intended); under A, it fails loudly (the auditor sees fewer rows and asks).
④ Startup non-sprawl: C costs one sentence; A costs one read rule and its pins, but no new key or declared surface.
os-decision-facets
- ① 项目长远合理性:A 让三种部署形态同一规则(部署级行永不属于组织),缩小特例;C 把形态差异写进权限契约,永久保留。
- ② 实际业务拉动:该能力默认只有平台管理员持有,自建审计员角色的 single 部署才会碰到;今天拉动小。
- ③ 防 AI 犯错:C 下授错权是静默多看;A 下是响亮少看。
- ④ 创业阶段不扩散:C 只改一句文字;A 加一条读规则与测试,不新增可声明键。
Prior rulings read: "audit log capability organization reader row scope deployment-level rows platform administrator tenant_id NULL" → 35 hits (none rules this question; #15207 / ADR-0131 D7 set the column model; #24164 is a different boundary); ADR-0131 D7; thread: none
Recommendation
A. Only by ①: in two years the platform's ledger read model should be one rule across postures, with configuration history behind an administrator-level right, as the mainstream platforms model it; C freezes a single-only exception into a published contract. Confidence gap: not measured whether any shipped example or customer relies on a non-admin auditor reading configuration history under single.
Self-check: only ① gives A; do ②③④ flip it — no (② is small, ③ favours A, ④ favours C on cost but not enough to flip a contract question).
Not C: it documents the gap instead of closing it. Not B: see above.
After the ruling
- A: dispatch one server-lane card: first measure
group/isolatedwith a custom set lacking the default row policy (if NULL-tenant rows reach an organization holder there, that is a defect fixed in the same PR); then add the read rule inaudit-log-read-visibility.ts(NULL-tenant rows only to the platform administrator), pins for each posture, and one sentence in the capability description; changeset + upgrade note forsingleoperators who granted the capability. - C: dispatch one spec-lane card for the capability description sentence and the docs page that restates it.
Related: #26640 (withheld), #26683, #26687, #15207, #22266.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: decision card, intake labels · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T17:06Z⛔ Not a claim.
priority:p2·domain:server·area:access·pm:awaiting-maintaineradded. Who reads deployment-level audit rows under thesingleposture is a security boundary, so the letter is the maintainer's.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #415 · 2026-10-27T22:55Z
Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #415 as item 2 of 5. Seat increment (re-verified on
origin/mainaa00362fe5):packages/spec/src/security/capabilities.ts:81describesview_all_audit_logfor the walled postures and the platform administrator and says nothing aboutsingle; the row policysys_audit_log_orgis in plugin-security's default sets (default-permission-sets.ts:214); the read rule lives inpackages/plugins/plugin-audit/src/audit-log-read-visibility.ts(with unit and integration tests beside it) — the file A edits. ADR-0131 D7: the ledger has no organization column; deployment-level rows carrytenant_idNULL.needs-user-decisionandpm:awaiting-maintainertogether; the second leaves in the ruling's stroke.single-only exception into a published capability text. ② the capability is held by default only by platform administrators; asingledeployment that built its own auditor is the one case. ③ under C a wrong grant fails silently (configuration history seen by someone nobody intended); under A it fails loudly (fewer rows, the auditor asks). ④ A is one read rule and pins, no key; C one sentence.group/isolatedwith a custom set lacking the default row policy — if NULL-tenant rows reach an organization holder there, the same PR fixes it). Not C (documents the gap) and not B (re-attributes history against ADR-0131 D7). Workload: server · 1 PR · S; changeset + upgrade note forsingleoperators who granted the capability.Prior rulings read: ADR-0131 D7, feat(spec,services): deployment-level state has no organization column — settings global rung, plumbing objects, the audit ledger, #12699 made total (ADR-0131 D7) #15207, feat(plugin-audit,plugin-security)!: sys_audit_log loses its injected organization column; tenant_id carries the organization a row is about and scopes organization readers (ADR-0131 D7) #22266, security(plugin-audit): a sys_secret confidentiality gap, observed beside the 18.2.0 wave-2 clauses — detail withheld pending maintainer #26640 (withheld detail), fix(plugin-audit): the compliance ledger keeps no copy of secret-store key material #26683, cli:
os migrateentry point for the plugin-audit key-material ledger-copy rewrite (the remaining half of #26640) #26687; thread: none.objectstack-fleet commentedon Oct 28, 2026
Ruling: batch #415 item 2 · letter A · maintainer 「同意」 2026-10-28T00:34Z
Recorded by the director seat (summon #51 continuation,
session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708). The maintainer answered batch #415 with 「同意」 — every seat letter. Brief 6455606806. Letter A: in every tenancy posture,singleincluded, deployment-level ledger rows (tenant_idNULL, ADR-0131 D7) are served only to the platform administrator (its superuser read bypass, or a separately named platform capability if the lane prefers one); an organization-scoped holder ofview_all_audit_logreads only the rows about its organization(s). The capability description (capabilities.ts:81) gains the sentence that says so forsingle. Not C (documents the gap) and not B (re-attributes history).Workload: server · 1 PR · S — first the measurement the card orders (
group/isolatedwith a custom set that omitssys_audit_log_org; a NULL-tenant leak there is fixed in the same PR), then the read rule inpackages/plugins/plugin-audit/src/audit-log-read-visibility.tswith a pin per posture, the capability sentence, a changeset and an upgrade note forsingleoperators who granted the capability to a non-administrator.Clause-②: no(a read narrows for a custom grant; the lane declares the arm),Metadata-protocol: none.Execution:
needs-user-decision→pm:queuein this act (domain:server, p2,area:access);pm:awaiting-maintainerremoved in the same stroke. The withheld detail stays on #26640.Ruling-ref:names this comment.objectstack-fleet commentedon Oct 30, 2026
domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk
Claim: PM loop round 1 · 2026-10-30T09:13Z
Session:
session_016WV4cwC9jjzyBACUnk1V9V(card thread; seatsession_016WV4czVnUdQQCVKSW5J2uk)Account:
os-elon-muskBranch:
claude/project-thread-edzevh(the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread opened)Worktree:
objectstack-issue-26750Domain:
domain:serverSeat:
domain:server#1File surface:
packages/plugins/plugin-audit/src/audit-log-read-visibility.tsand its tests underpackages/plugins/plugin-audit/src/; the row-policy measurement may readpackages/plugins/plugin-security/src/objects/default-permission-sets.ts(a fix there only if the measurement finds the NULL-tenant leak the ruling names); the ONE capability-description sentence inpackages/spec/src/security/capabilities.ts(view_all_audit_log), carried by the ruling's "server · 1 PR" workload;.changeset/*and the upgrade note; one sentence incontent/docs/permissions/record-view-auditing.mdx(what aview_all_audit_logholder is served —Skills-docs: updated here); the ruling's pin sweep in two dogfood files the thread named (amended by the seat after the full dogfood on0c235ff8):packages/qa/dogfood/test/audit-log-audit-capability.dogfood.test.ts(bulk rows stamped with the holder's organization; subject unchanged) andpackages/qa/dogfood/test/admin-ledger-decision-metadata.dogfood.test.ts(re-armed: producer at rest + the ruling's read-side pin, never dropped). Stop and report on any other breach.Container & model:
Streated asM(the judgement is in a security read rule, not a gate),mode:thread,model: opus (TIER_DEFAULT; dispatch-gates --tier: no path-derived mandate),effort: xhigh(access boundary)Clause-②: no (narrowing) — non-admin ledger readers lose the deployment-level rows under
single; seat amendment on the card thread's ask (the ruling leaves the arm to the lane)Metadata-protocol: none
Responsibility: n/a — not a defect card (ruled decision implementation, Ruling-ref 6457448424)
Thread-read: 6457448424
Premise: holds (re-read on
origin/main7a378234:capabilities.tsstill says nothing aboutsingle;audit-log-read-visibility.tscarries no NULL-tenant rule)Serial constraints cleared: no open PR touches
audit-log-read-visibility.ts,capabilities.tsordefault-permission-sets.ts(21 open PRs read); no in-flight claim names them.Contract review: owed before the queue (
Clause-②: no (narrowing)— a read narrows for a custom grant; the spec line is description text).objectstack-fleet commentedon Oct 30, 2026
domain:server#1 · os-elon-musk · session_016WV4cwC9jjzyBACUnk1V9V
os-dev-report
{
"issue": 26750,
"status": "done",
"branch": "claude/project-thread-edzevh",
"head": "e94fcd240ecc95c3fecb352f7010958877dade4a",
"pr": "#28047",
"session": "session_016WV4cwC9jjzyBACUnk1V9V",
"premise_still_valid": true,
"summary": "Ruling A (6457448424) implemented on the plugin-audit read middleware: a non-system ledger read by anyone who is not the platform administrator (the carried PLATFORM_ADMIN posture rung, ADR-0095 D2/D3) gets the clause tenant_id is neither null nor empty, ANDed after the parent-record gate, so deployment-level rows are served to the platform administrator alone in every posture, view_all_audit_log holders included. The view_all_audit_log description gains the single-posture sentence (and its comment names the rung), the record-view-auditing page gains one sentence, and the changeset declares Clause-② no (narrowing) with a BREAKING banner and a single-operator upgrade note. Step-1 measurement: no NULL-tenant leak for the card's literal custom set under group/isolated; the cross-organization half (a set authoring its own ledger row policy, or granting the read bypass on the ledger) is an abstract acceptance note on the PR. Four fixtures that read system-inserted, tenant-less ledger rows as non-admins were re-armed (two dogfood files after the seat amended the surface; two plugin-audit tests inside the declared surface).",
"tests": "Under the verify lock, final source head: pnpm --filter @objectstack/plugin-audit typecheck exit 0 (check:test-typecheck: 0 files held); plugin-audit suite 66/66 files, 1077/1077 tests; spec src/security + src/identity 26/26, 601/601; plugin-security 237/237 files, 5119 passed + 45 skipped; plugin-auth (durability-swallow-repair, admin-user-endpoints) 115/115; service-settings config-change-audit 17/17; service-automation (resume-decider-identity, runas-attribution-contract) 23/23; the two re-armed dogfood files 23/23. Ablations through scripts/ablation-replace.mjs, each restored with git diff HEAD empty: (1) rule line replaced by a marker -> 20 red of 64 across the three plugin-audit pin files, every posture of the new integration file included; (2) predicate also reads a platform_admin position -> the rung-only pin red (1 of 64); (3) rule removed against the re-armed leaf fixture -> the holder clause pin red (1 of 33).",
"gates": "Local, each command's exit captured to a file before any pipe, then node scripts/pm/dispatch-gates.mjs --ran FILE. 173abc3: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN, every family exit 0. af925f2: 106 derived (identical list), 106 run, 0 NOT-MEASURED, 0 UNRUN, every family exit 0. 3fa0b74 (spec comment + changeset): 106 derived (identical list), 106 run, 0 NOT-MEASURED, 0 UNRUN; six spec families (check:api-surface, check:browser-reachable-entries, check:dual-source-exports, check:entry-nameability, check:exported-any, check:skill-examples) first exited 3 on a spec dist older than the comment edit, and after pnpm --filter @objectstack/spec build all six plus check:generated exit 0, then the reconciliation above. Final head e94fcd2 changes only the literal types of three plugin-audit test files and was not re-run through dispatch-gates (a declared narrowing): pnpm --filter @objectstack/plugin-audit typecheck exit 0, plugin-audit suite 1077/1077, dogfood tsc --noEmit exit 0. Also pnpm lint exit 0 (173abc3) and eslint on the spec file exit 0 (3fa0b74). CI on 3fa0b74: every check-run passed or skipped except Type Check · workspace (and its TypeScript Type Check rollup), red on @objectstack/plugin-audit#typecheck (check:test-typecheck: 13 test-file type errors), fixed in e94fcd2. CI on e94fcd2: in_progress at report time.",
"dogfood_battery": "173abc30 · os-verify-lock: VERDICT command-exit 0 · held 18m38s · Test Files 357 passed | 3 skipped (360) · Tests 2877 passed | 20 skipped (2897) · no failure. First run on 46f5ab6: VERDICT command-exit 1, 2 files red (audit-log-audit-capability.dogfood.test.ts:352 and admin-ledger-decision-metadata.dogfood.test.ts armed check), both the ruling's effect on stale fixtures, fixed in 173abc3 after the seat amended the surface. Later heads change only plugin-audit tests, the changeset and a spec comment, none of which a dogfood file runs.",
"line_budget": "11 files, +565 / -119 against main (check-governed-merges: NOT governed, 0 of the touched paths on the register; under the 3000-line human-merge threshold). Source rule: one middleware line plus two exported helpers in audit-log-read-visibility.ts; spec: one description sentence and one comment clause.",
"deviations": [
"Assumption corrected: the ruling names the admin's superuser read bypass, but viewAllRecords skips plugin-security's Layer 1 only and never reaches the plugin-audit middleware, so the rule reads the carried PLATFORM_ADMIN rung (PLATFORM_OPERATOR_POSTURE) instead; no new capability.",
"File surface grew twice, both recorded on claim 6519222523: one sentence in content/docs/permissions/record-view-auditing.mdx (Skills-docs: updated here), and the two dogfood fixtures, named to the seat before they were touched.",
"Two plugin-audit fixtures outside the original pin files (audit-log-field-redaction.test.ts, parent-record-read-leaf.integration.test.ts) were re-armed inside the declared surface; the seat confirmed no split.",
"Three gate families measured only after prerequisites in the shallow cloud clone: check-plugin-teardown-shape --self-test (pinned commit fetched at depth 1), spec check:skill-examples (client SDK declarations present), check:dual-build-cjs-loads (six package builds). Their first run on 0c235ff exited 3; every later run measured.",
"CI red on 3fa0b74, Type Check · workspace: @objectstack/plugin-audit#typecheck (check:test-typecheck) found 13 type errors in three new or edited test files (12 TS2322, a reader's posture widened to string; 1 TS2488, spread of an optional capability list). Not caught locally because dispatch-gates does not derive the package typecheck; fixed in e94fcd2 with literal rungs and a non-empty precondition, no debt-ledger entry.",
"The PR body was written once at creation and names af925f2 as the final head; the final head is e94fcd2 (3fa0b74: changeset bullet and spec comment from the seat's contract review; e94fcd2: test typing). The body is not re-edited (relay rule); the seat carries the corrected head.",
"Commit a6a400f7 was amended to 173abc3 before push to replace a harness-suggested co-author trailer that named a model with the model-free trailer."
],
"files_changed": [
".changeset/26750-ledger-deployment-level-rows.md",
"content/docs/permissions/record-view-auditing.mdx",
"packages/plugins/plugin-audit/src/audit-log-read-visibility.ts",
"packages/plugins/plugin-audit/src/audit-log-read-visibility.test.ts",
"packages/plugins/plugin-audit/src/audit-log-read-visibility.integration.test.ts",
"packages/plugins/plugin-audit/src/audit-log-deployment-level-rows.integration.test.ts",
"packages/plugins/plugin-audit/src/audit-log-field-redaction.test.ts",
"packages/plugins/plugin-audit/src/parent-record-read-leaf.integration.test.ts",
"packages/qa/dogfood/test/admin-ledger-decision-metadata.dogfood.test.ts",
"packages/qa/dogfood/test/audit-log-audit-capability.dogfood.test.ts",
"packages/spec/src/security/capabilities.ts"
],
"mcp_calls": "7, all reads: mcp__github__issue_read get x1, get_comments x3; mcp__github__pull_request_read get x1, get_check_runs x1; mcp__github__get_job_logs x1. No GitHub MCP write. Separately, one platform PR-activity subscription (subscribe_pr_activity on #28047) so the CI result reaches this thread.",
"api_writes": "2, both through the fleet-write relay: pr_create (run 41042326425, #28047, body read back identical, 16206 bytes) and this report comment (post-stamped). Plus git push x4 (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: named producer: packages/services/service-automation/src/run-resume.ts seedRunGrants seeds positions and permissions into a runAs:'user' run context but no posture rung, so a platform administrator's automation run reads the ledger as a non-admin (organization rows only, fail-closed, never wider) · evidence: run-resume.ts:196 and the seat's contract review on #28047 · dedupe words: seedRunGrants posture rung runAs user ledger · carrier: seat files it · noted, not filed",
"class: a · reach: exception: security · a custom set that authors its own sys_audit_log row policy, or grants the read bypass on the ledger, is served rows about other organizations under a wall (measured in step 1; the deployment-level half is closed by this PR) · evidence: abstract acceptance note on #28047, withheld detail stays on #26640 · dedupe words: ledger cross-organization custom row policy read bypass · carrier: seat · noted, not filed"
]
}
objectstack-fleet commentedon Oct 30, 2026
domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk
ACCEPT → PR #28047 comment 6521933160 · ruling A delivered · ready + auto-merge armed 2026-10-30T11:49Z. Follow-up filed: #28070 (runAs user runs carry no posture rung).