S2 contract 2 of 9: the RBAC grant model the hierarchy schema contract (#1435) attaches to.
Defines:
Three authority layers, none substitutable: platform role (better-auth member/admin), hierarchy grants, workspace membership (SOT REQ-ID-001) — §1
Vocabulary: viewer | member | owner, one totally-ordered set at every level, constraint-checked — §2
Evaluation: deny-by-default, down-the-chain only, effective role = max over user-direct and team-conferred grants on ancestors, live fail-closed evaluation — §3
Grant management: owner-managed, no self-escalation, node creation confers no automatic grant (explicit initial owner in the same audited operation) — §4
Embedded decision for Jason's ruling: platform admin confers no implicit tenant access (§1.1).
Sources: PRD Part I §4; native-kanban SOT Amendment A1 §8.1.3/§8.3; hierarchy-schema contract (PR #1435); identity-lifecycle contract §1.4 (PR #1433).
Independent review (gate 16) to follow before merge. No self-merge without review.
S2 contract 2 of 9: the RBAC grant model the hierarchy schema contract (#1435) attaches to.
Defines:
- Three authority layers, none substitutable: platform role (better-auth member/admin), hierarchy grants, workspace membership (SOT REQ-ID-001) — §1
- Vocabulary: viewer | member | owner, one totally-ordered set at every level, constraint-checked — §2
- Evaluation: deny-by-default, down-the-chain only, effective role = max over user-direct and team-conferred grants on ancestors, live fail-closed evaluation — §3
- Grant management: owner-managed, no self-escalation, node creation confers no automatic grant (explicit initial owner in the same audited operation) — §4
- Transfer authority: both-sides effective owner, completing contract 1 §4.2 — §5
- Revocation propagation: row deletion is the revocation; next-decision bound plus the 30s/next-message socket bound shared with identity §7.1 — §6
- Verification matrix extending A1 §8.3 acceptance 2–3 and contract 1 §6 — §7
Embedded decision for Jason's ruling: platform admin confers no implicit tenant access (§1.1).
Sources: PRD Part I §4; native-kanban SOT Amendment A1 §8.1.3/§8.3; hierarchy-schema contract (PR #1435); identity-lifecycle contract §1.4 (PR #1433).
Independent review (gate 16) to follow before merge. No self-merge without review.
Defines the hierarchy grant vocabulary (viewer|member|owner, totally
ordered), evaluation semantics (deny-by-default, down-the-chain, max rule,
live fail-closed evaluation), grant management (owner-managed, no
self-escalation, explicit bootstrap of authority), transfer authority
(both-sides owner, completing contract 1 §4.2), and revocation propagation
with the same 30s/next-message bound as identity §7.1.
Embedded decision for ratification: platform admin confers no implicit
tenant access (§1.1).
Sources: PRD Part I §4, native-kanban SOT Amendment A1 §8.1.3/§8.3,
hierarchy-schema contract (PR #1435), identity-lifecycle contract §1.4
(PR #1433).
- name the two shipped platform-admin bypass paths (command-authorization
hasScope short-circuit, MCP tenant-admin scope derivation) as
non-conformant; 7.4 retires them explicitly
- suspend team grant subjects pending a team contract; command surface
refuses team-subject grants; evaluation semantics pre-specified
- no-self-escalation restated with future-decoupling rationale; observable
is an audit invariant, not an unconstructible refusal test
- bootstrap seeding scoped to top-level company creation; child nodes
inherit via down-the-chain, resolving the 7.7/4.3 contradiction
- exact A1 quote (not a bypass of workspace authorization)
- subject/role audit fields attributed as this contract's addition
- principal-position consequence named (1.3) with matrix cell
- added observables: zero membership rows, fail-closed fault injection,
no existence oracle
- role-string namespacing rule (4.5)
- ruling request names the PRD-admins interpretive resolution
rev-code-01
approved these changes 2026-08-26 23:44:45 +00:00
APPROVED — rev-code-01, pinned to head 501112d2a4e8e14d4e12d2a8fc67f9d7fb713d29 (re-verified unmoved immediately before posting).
Scope: doc-only, single file docs/requirements/rbac-grant-model.md (+259 lines, two commits). Verified independently of the GLM 5.3 r1/r2 model reviews; every codebase-factual claim measured by me at this head:
§1.1(a) admin bypass — command layer: confirmed exact. command-authorization.service.ts:158hasScope body is if (role === 'admin') return true; — every command scope allowed for platform admin, no grant consulted.
§1.1(b) admin bypass — MCP: confirmed exact. mcp.service.ts:98-99deriveMcpToolScopesForUser maps role admin → TENANT_ADMIN_MCP_SCOPES, which includes brain_create_task / brain_update_task (task create/update, as the contract states).
§1.1 AdminGuard claim: confirmed — the guard's platform-role test is userRole !== 'admin' → Forbidden (admin.guard.ts:111); that check is the platform role's only meaning, as stated.
§4.5 third vocabulary: confirmed — CommandRole = 'admin' | 'member' | 'viewer' (command-authorization.service.ts:8), colliding strings with users.role and the new hierarchy set; the namespacing rule is motivated by a real collision.
§1.4 legacy teams table: confirmed — teams (schema.ts:127) carries ownerId/managerId authority columns, no workspace binding; the suspension rationale is factual.
Source quotes exact: PRD Part I §4 line 93-94 ("admins restrict access per company, estate, and project; grants are evaluated down the chain"); SOT A1 §8.1.3 line 411 ("not a bypass of workspace authorization") with the surrounding "chain adds where grants can be declared" context; A1 §8.3 acceptance 2-3 ("exactly the workspace permissions the chain implies", "no cross-tenant existence oracles"); REQ-TEN-001 line 126 (principals require ACTIVE workspace membership); REQ-ID-001 (workspace-bound identity scope); users.role text default member.
Contract-1 cross-references verified against PR #1435's ref: §3.1 reserves team_id → teams.id in the exactly-one-of CHECK with role vocabulary "owned by contract 2 §2"; §4.2's "authority over BOTH" is what §5 completes; §5.2's audit event enumerates actor/verb/target(+transfer parents) and does NOT enumerate subject/role — §4.4's "this contract's addition" claim is exact. Contract 1 rev 3 is explicitly co-versioned against this contract's revision 2 at this exact head 501112d2 — mutual consistency is pinned from both sides.
Gates run by me in a fresh worktree at this head: format:check rc=0 (pinned prettier; both commits content-only, parent also clean — this PR needed no style commit; the check's ability to discriminate was demonstrated this session on identical commands); web-deps build rc=0; typecheck rc=0 (45/45 incl. preflight); lint rc=0 (25/25).
Document quality: every MUST has a bounded §7 observable; §4.2's no-self-escalation observable is honestly downgraded to the §7.7 audit-stream invariant with the reason stated (under the three-role vocabulary the rule is unviolable, so a refusal test cannot exist yet); the r2 model review's NEW-1 (top-level company creation authority not answerable from the text) is real but fails closed and is recorded for contract 3 — agreed non-blocking. The embedded §1.1 ruling (platform admin confers no implicit tenant access) and the PRD-"admins" interpretive resolution are both surfaced explicitly for ratification, not silently decided.
Non-blocking notes:
[S1] Cross-references to docs/requirements/hierarchy-schema.md (#1435) and identity-lifecycle.md (#1433) dangle at this head — both sibling PRs are open. Deliberate sequencing, but merge order should land the referenced contracts with (or before) this one, or land them together, so a fresh clone of next never carries dead links.
[S2] NEW-1 from the r2 review stays open for contract 3 (wizard/top-level creation authority attribution) — worth an explicit entry in that contract's brief.
CI note: 2819 running on this head at review time. Merge waits on 2819 terminal green.
**APPROVED — rev-code-01, pinned to head `501112d2a4e8e14d4e12d2a8fc67f9d7fb713d29`** (re-verified unmoved immediately before posting).
Scope: doc-only, single file `docs/requirements/rbac-grant-model.md` (+259 lines, two commits). Verified independently of the GLM 5.3 r1/r2 model reviews; every codebase-factual claim measured by me at this head:
1. **§1.1(a) admin bypass — command layer:** confirmed exact. `command-authorization.service.ts:158` `hasScope` body is `if (role === 'admin') return true;` — every command scope allowed for platform admin, no grant consulted.
2. **§1.1(b) admin bypass — MCP:** confirmed exact. `mcp.service.ts:98-99` `deriveMcpToolScopesForUser` maps role `admin` → `TENANT_ADMIN_MCP_SCOPES`, which includes `brain_create_task` / `brain_update_task` (task create/update, as the contract states).
3. **§1.1 AdminGuard claim:** confirmed — the guard's platform-role test is `userRole !== 'admin'` → Forbidden (`admin.guard.ts:111`); that check is the platform role's only meaning, as stated.
4. **§4.5 third vocabulary:** confirmed — `CommandRole = 'admin' | 'member' | 'viewer'` (`command-authorization.service.ts:8`), colliding strings with `users.role` and the new hierarchy set; the namespacing rule is motivated by a real collision.
5. **§1.4 legacy teams table:** confirmed — `teams` (schema.ts:127) carries `ownerId`/`managerId` authority columns, no workspace binding; the suspension rationale is factual.
6. **Source quotes exact:** PRD Part I §4 line 93-94 ("admins restrict access per company, estate, and project; grants are evaluated down the chain"); SOT A1 §8.1.3 line 411 ("not a bypass of workspace authorization") with the surrounding "chain adds where grants can be declared" context; A1 §8.3 acceptance 2-3 ("exactly the workspace permissions the chain implies", "no cross-tenant existence oracles"); REQ-TEN-001 line 126 (principals require ACTIVE workspace membership); REQ-ID-001 (workspace-bound identity scope); `users.role` text default `member`.
7. **Contract-1 cross-references** verified against PR #1435's ref: §3.1 reserves `team_id → teams.id` in the exactly-one-of CHECK with role vocabulary "owned by contract 2 §2"; §4.2's "authority over BOTH" is what §5 completes; §5.2's audit event enumerates actor/verb/target(+transfer parents) and does NOT enumerate subject/role — §4.4's "this contract's addition" claim is exact. Contract 1 rev 3 is explicitly co-versioned against this contract's revision 2 at this exact head `501112d2` — mutual consistency is pinned from both sides.
Gates run by me in a fresh worktree at this head: `format:check` rc=0 (pinned prettier; both commits content-only, parent also clean — this PR needed no style commit; the check's ability to discriminate was demonstrated this session on identical commands); web-deps build rc=0; `typecheck` rc=0 (45/45 incl. preflight); `lint` rc=0 (25/25).
Document quality: every MUST has a bounded §7 observable; §4.2's no-self-escalation observable is honestly downgraded to the §7.7 audit-stream invariant with the reason stated (under the three-role vocabulary the rule is unviolable, so a refusal test cannot exist yet); the r2 model review's NEW-1 (top-level company creation authority not answerable from the text) is real but fails closed and is recorded for contract 3 — agreed non-blocking. The embedded §1.1 ruling (platform admin confers no implicit tenant access) and the PRD-"admins" interpretive resolution are both surfaced explicitly for ratification, not silently decided.
Non-blocking notes:
- [S1] Cross-references to `docs/requirements/hierarchy-schema.md` (#1435) and `identity-lifecycle.md` (#1433) dangle at this head — both sibling PRs are open. Deliberate sequencing, but merge order should land the referenced contracts with (or before) this one, or land them together, so a fresh clone of `next` never carries dead links.
- [S2] NEW-1 from the r2 review stays open for contract 3 (wizard/top-level creation authority attribution) — worth an explicit entry in that contract's brief.
CI note: 2819 running on this head at review time. Merge waits on 2819 terminal green.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
S2 contract 2 of 9: the RBAC grant model the hierarchy schema contract (#1435) attaches to.
Defines:
Embedded decision for Jason's ruling: platform admin confers no implicit tenant access (§1.1).
Sources: PRD Part I §4; native-kanban SOT Amendment A1 §8.1.3/§8.3; hierarchy-schema contract (PR #1435); identity-lifecycle contract §1.4 (PR #1433).
Independent review (gate 16) to follow before merge. No self-merge without review.
APPROVED — rev-code-01, pinned to head
501112d2a4e8e14d4e12d2a8fc67f9d7fb713d29(re-verified unmoved immediately before posting).Scope: doc-only, single file
docs/requirements/rbac-grant-model.md(+259 lines, two commits). Verified independently of the GLM 5.3 r1/r2 model reviews; every codebase-factual claim measured by me at this head:command-authorization.service.ts:158hasScopebody isif (role === 'admin') return true;— every command scope allowed for platform admin, no grant consulted.mcp.service.ts:98-99deriveMcpToolScopesForUsermaps roleadmin→TENANT_ADMIN_MCP_SCOPES, which includesbrain_create_task/brain_update_task(task create/update, as the contract states).userRole !== 'admin'→ Forbidden (admin.guard.ts:111); that check is the platform role's only meaning, as stated.CommandRole = 'admin' | 'member' | 'viewer'(command-authorization.service.ts:8), colliding strings withusers.roleand the new hierarchy set; the namespacing rule is motivated by a real collision.teams(schema.ts:127) carriesownerId/managerIdauthority columns, no workspace binding; the suspension rationale is factual.users.roletext defaultmember.team_id → teams.idin the exactly-one-of CHECK with role vocabulary "owned by contract 2 §2"; §4.2's "authority over BOTH" is what §5 completes; §5.2's audit event enumerates actor/verb/target(+transfer parents) and does NOT enumerate subject/role — §4.4's "this contract's addition" claim is exact. Contract 1 rev 3 is explicitly co-versioned against this contract's revision 2 at this exact head501112d2— mutual consistency is pinned from both sides.Gates run by me in a fresh worktree at this head:
format:checkrc=0 (pinned prettier; both commits content-only, parent also clean — this PR needed no style commit; the check's ability to discriminate was demonstrated this session on identical commands); web-deps build rc=0;typecheckrc=0 (45/45 incl. preflight);lintrc=0 (25/25).Document quality: every MUST has a bounded §7 observable; §4.2's no-self-escalation observable is honestly downgraded to the §7.7 audit-stream invariant with the reason stated (under the three-role vocabulary the rule is unviolable, so a refusal test cannot exist yet); the r2 model review's NEW-1 (top-level company creation authority not answerable from the text) is real but fails closed and is recorded for contract 3 — agreed non-blocking. The embedded §1.1 ruling (platform admin confers no implicit tenant access) and the PRD-"admins" interpretive resolution are both surfaced explicitly for ratification, not silently decided.
Non-blocking notes:
docs/requirements/hierarchy-schema.md(#1435) andidentity-lifecycle.md(#1433) dangle at this head — both sibling PRs are open. Deliberate sequencing, but merge order should land the referenced contracts with (or before) this one, or land them together, so a fresh clone ofnextnever carries dead links.CI note: 2819 running on this head at review time. Merge waits on 2819 terminal green.