contract-coverage.test.tsfeat(permissions): one authority for every access decision Consolidates every "may this caller act on this data" decision onto a single function, and closes eleven instances of one bug class found along the way. ## The authority services/auth/authorize.ts returns a VERDICT, never a role. There is deliberately no isOrgAdmin on the return type, so no call site can branch on one, and changing the permission model is a change to the DEFAULT_POLICY table rather than a sweep through the codebase. Policy is a frozen constant keyed (resource, scope); a missing key fails closed. resolvePolicy(orgId) is the seam for per-org policy later, so this ships with ZERO schema changes. Replaces canActOnTarget, resolveOrgMembership, userIsOrgAdmin, an inline same-org check in listTasksTool, and eleven `if (!isOrgAdmin)` sites that each re-derived the rule. One of them computed it on the wrong user. ## Holes closed All the same shape: a caller-supplied identifier resolving to another user's data below the gate. applyConfigChangeTool userId input, write readConnectionSettings userId input, read writeDocumentTool org-linked playbook path redirect listDocumentsTool same, ungated, plus displayDocumentTool configureStrategistTool bare aopId, no check at all getSentEmailsTool userId input, verbatim sent/drafted bodies aopAgents.listForAop subagent docs to any authenticated caller agent-action-queue 5 routes, incl. delete and replay (destructive) custom-field-management ownerId resolved from the verdict, then ignored overview-instance org-scope write with no tenant comparison admin router ~45 procedures + 14 sub-routers, cross-tenant Two were destructive, three were cross-tenant writes, one was introduced by an earlier fix in this same change set and caught by a later sweep. ## Feature Org admins can administer a teammate's playbook and configuration. targetUserId is uniform across the tool layer, the tRPC layer gained the org-admin path, and the frontend gained a scoped provider and member picker that never renders one person's data labelled as another's. ## Enforcement Five guards keyed on the LIVE REGISTRY, not hand-written lists, each with an anti-vacuity floor and each proven by planting the violation: no bare userId inputs on any registered tool every targetUserId file reaches the authority every mutating action is administrable or documented self-only role reads confined to the authority (depcruise rule + source scan) redirect sites consult the authority about the RESOLVED target ## Notes - agentExecutionEnabled is Cedar-staff-only, enforced in the single writer. Stronger than SELF, so deliberately not a policy row. - playbook:org and document:org are ANY_MEMBER, matching today's behaviour. 57 of 73 orgs have no admin; tightening is one cell once they do. - Fixes two org-role gates that read undefined on every request and so refused everyone, including statistics.getOrgOverview. - config-write split: apply-change moved to connection-write. 17,615 -> 10,142 bytes against the connector's silent-drop ceiling. Verified: server 799 files / 9,310 tests; mail 316 suites / 2,959 tests; types and deps:check clean. End-to-end against the production Cedar org with a temporary non-staff account, 42 assertions across member/admin/viewer, since staff accounts cannot exercise the org-grant branch. Account deleted and org state verified identical to its pre-test backup. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 8:01 AMpermission-error.test.tsfeat(permissions): one authority for every access decision Consolidates every "may this caller act on this data" decision onto a single function, and closes eleven instances of one bug class found along the way. ## The authority services/auth/authorize.ts returns a VERDICT, never a role. There is deliberately no isOrgAdmin on the return type, so no call site can branch on one, and changing the permission model is a change to the DEFAULT_POLICY table rather than a sweep through the codebase. Policy is a frozen constant keyed (resource, scope); a missing key fails closed. resolvePolicy(orgId) is the seam for per-org policy later, so this ships with ZERO schema changes. Replaces canActOnTarget, resolveOrgMembership, userIsOrgAdmin, an inline same-org check in listTasksTool, and eleven `if (!isOrgAdmin)` sites that each re-derived the rule. One of them computed it on the wrong user. ## Holes closed All the same shape: a caller-supplied identifier resolving to another user's data below the gate. applyConfigChangeTool userId input, write readConnectionSettings userId input, read writeDocumentTool org-linked playbook path redirect listDocumentsTool same, ungated, plus displayDocumentTool configureStrategistTool bare aopId, no check at all getSentEmailsTool userId input, verbatim sent/drafted bodies aopAgents.listForAop subagent docs to any authenticated caller agent-action-queue 5 routes, incl. delete and replay (destructive) custom-field-management ownerId resolved from the verdict, then ignored overview-instance org-scope write with no tenant comparison admin router ~45 procedures + 14 sub-routers, cross-tenant Two were destructive, three were cross-tenant writes, one was introduced by an earlier fix in this same change set and caught by a later sweep. ## Feature Org admins can administer a teammate's playbook and configuration. targetUserId is uniform across the tool layer, the tRPC layer gained the org-admin path, and the frontend gained a scoped provider and member picker that never renders one person's data labelled as another's. ## Enforcement Five guards keyed on the LIVE REGISTRY, not hand-written lists, each with an anti-vacuity floor and each proven by planting the violation: no bare userId inputs on any registered tool every targetUserId file reaches the authority every mutating action is administrable or documented self-only role reads confined to the authority (depcruise rule + source scan) redirect sites consult the authority about the RESOLVED target ## Notes - agentExecutionEnabled is Cedar-staff-only, enforced in the single writer. Stronger than SELF, so deliberately not a policy row. - playbook:org and document:org are ANY_MEMBER, matching today's behaviour. 57 of 73 orgs have no admin; tightening is one cell once they do. - Fixes two org-role gates that read undefined on every request and so refused everyone, including statistics.getOrgOverview. - config-write split: apply-change moved to connection-write. 17,615 -> 10,142 bytes against the connector's silent-drop ceiling. Verified: server 799 files / 9,310 tests; mail 316 suites / 2,959 tests; types and deps:check clean. End-to-end against the production Cedar org with a temporary non-staff account, 42 assertions across member/admin/viewer, since staff accounts cannot exercise the org-grant branch. Account deleted and org state verified identical to its pre-test backup. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 8:01 AMscope-not-honoured-notice.test.tsxfeat(permissions): one authority for every access decision Consolidates every "may this caller act on this data" decision onto a single function, and closes eleven instances of one bug class found along the way. ## The authority services/auth/authorize.ts returns a VERDICT, never a role. There is deliberately no isOrgAdmin on the return type, so no call site can branch on one, and changing the permission model is a change to the DEFAULT_POLICY table rather than a sweep through the codebase. Policy is a frozen constant keyed (resource, scope); a missing key fails closed. resolvePolicy(orgId) is the seam for per-org policy later, so this ships with ZERO schema changes. Replaces canActOnTarget, resolveOrgMembership, userIsOrgAdmin, an inline same-org check in listTasksTool, and eleven `if (!isOrgAdmin)` sites that each re-derived the rule. One of them computed it on the wrong user. ## Holes closed All the same shape: a caller-supplied identifier resolving to another user's data below the gate. applyConfigChangeTool userId input, write readConnectionSettings userId input, read writeDocumentTool org-linked playbook path redirect listDocumentsTool same, ungated, plus displayDocumentTool configureStrategistTool bare aopId, no check at all getSentEmailsTool userId input, verbatim sent/drafted bodies aopAgents.listForAop subagent docs to any authenticated caller agent-action-queue 5 routes, incl. delete and replay (destructive) custom-field-management ownerId resolved from the verdict, then ignored overview-instance org-scope write with no tenant comparison admin router ~45 procedures + 14 sub-routers, cross-tenant Two were destructive, three were cross-tenant writes, one was introduced by an earlier fix in this same change set and caught by a later sweep. ## Feature Org admins can administer a teammate's playbook and configuration. targetUserId is uniform across the tool layer, the tRPC layer gained the org-admin path, and the frontend gained a scoped provider and member picker that never renders one person's data labelled as another's. ## Enforcement Five guards keyed on the LIVE REGISTRY, not hand-written lists, each with an anti-vacuity floor and each proven by planting the violation: no bare userId inputs on any registered tool every targetUserId file reaches the authority every mutating action is administrable or documented self-only role reads confined to the authority (depcruise rule + source scan) redirect sites consult the authority about the RESOLVED target ## Notes - agentExecutionEnabled is Cedar-staff-only, enforced in the single writer. Stronger than SELF, so deliberately not a policy row. - playbook:org and document:org are ANY_MEMBER, matching today's behaviour. 57 of 73 orgs have no admin; tightening is one cell once they do. - Fixes two org-role gates that read undefined on every request and so refused everyone, including statistics.getOrgOverview. - config-write split: apply-change moved to connection-write. 17,615 -> 10,142 bytes against the connector's silent-drop ceiling. Verified: server 799 files / 9,310 tests; mail 316 suites / 2,959 tests; types and deps:check clean. End-to-end against the production Cedar org with a temporary non-staff account, 42 assertions across member/admin/viewer, since staff accounts cannot exercise the org-grant branch. Account deleted and org state verified identical to its pre-test backup. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 8:01 AM