Skip to content

Commit de6e2bf

Browse files
claude[bot]claude
andauthored
chore(spec): adopt the adjudicated proof→ledger bindings, and correct the stale systemPermissions note (#11209)
* chore(spec): adopt the adjudicated proof->ledger bindings, and correct the stale systemPermissions note Executes the spec seat's adjudication on PR #10934 (issuecomment-5376518715). Each binding was re-verified against merged origin/main @ 2b5a7ce, not the 23e27af the adjudication was written against. - showcase-fls-read-mask-strip -> permission.fields.readable (not editable). - showcase-crud-persona-matrix -> all four permission.objects.allow* verbs. - sharing-rule-org-less-caller -> permission.systemPermissions, whose ledger note turned out STALE on re-verification, so it is corrected here and the binding adopted in the same PR, per the ruling's own conditional. BOUND_PROOF_PATHS moves 20 -> 26: six additions, no removals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RadETjNRLALFLhFA3xehZP * fix(spec): name the manage_platform_settings admit path in the systemPermissions citation PM review of PR #11209: the new evidence/note text said the sharing-rule surface "refuses without manage_sharing", but assertCanManageRules (sharing-rule-service.ts:139) admits `manage_sharing` OR the legacy `manage_platform_settings` admin override, and bypasses for system contexts. Over-claiming an admit set is the declared-not-enforced class this card polices, so the three sites carrying the claim now name it. The classification is unchanged: every member of the admit set is read from `systemPermissions`, which is what the bound entry classifies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RadETjNRLALFLhFA3xehZP --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f34f56b commit de6e2bf

3 files changed

Lines changed: 92 additions & 34 deletions

File tree

packages/spec/liveness/permission.json

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,30 @@
4949
"allowCreate": {
5050
"status": "live",
5151
"verifiedAt": "2026-07-30",
52-
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION insert→allowCreate)"
52+
"proof": "packages/qa/dogfood/test/showcase-crud-persona-matrix.dogfood.test.ts#showcase-crud-persona-matrix",
53+
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION insert→allowCreate)",
54+
"note": "Proof-bound 2026-08-23 (#10959, adjudicated on PR #10934): the persona × CRUD matrix runs the create verb over real HTTP for every showcase permission set, in BOTH directions per cell — an allowed create returns an id and the row is really there, a denied one is 403 PERMISSION_DENIED and persisted nothing. Cells are judged as a UNION with the everyone-baseline set (ADR-0090 D5). One proof binds all four allow* verbs; multi-entry binding has precedent in `semantic-roles`."
5355
},
5456
"allowRead": {
5557
"status": "live",
5658
"verifiedAt": "2026-07-30",
57-
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION find/findOne/count/aggregate→allowRead)"
59+
"proof": "packages/qa/dogfood/test/showcase-crud-persona-matrix.dogfood.test.ts#showcase-crud-persona-matrix",
60+
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION find/findOne/count/aggregate→allowRead)",
61+
"note": "Proof-bound 2026-08-23 (#10959) — the read verb of the same persona × CRUD matrix, both directions per cell (a readable list answers 200 with rows; a denied one is 403 PERMISSION_DENIED). See `allowCreate` for the binding rationale."
5862
},
5963
"allowEdit": {
6064
"status": "live",
6165
"verifiedAt": "2026-07-30",
62-
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION update→allowEdit)"
66+
"proof": "packages/qa/dogfood/test/showcase-crud-persona-matrix.dogfood.test.ts#showcase-crud-persona-matrix",
67+
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION update→allowEdit)",
68+
"note": "Proof-bound 2026-08-23 (#10959) — the edit verb of the same persona × CRUD matrix, both directions per cell, asserted on POST-STATE (the allowed edit persisted; the denied edit changed nothing). See `allowCreate` for the binding rationale."
6369
},
6470
"allowDelete": {
6571
"status": "live",
6672
"verifiedAt": "2026-07-30",
67-
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION delete→allowDelete)"
73+
"proof": "packages/qa/dogfood/test/showcase-crud-persona-matrix.dogfood.test.ts#showcase-crud-persona-matrix",
74+
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:15 (OPERATION_TO_PERMISSION delete→allowDelete)",
75+
"note": "Proof-bound 2026-08-23 (#10959) — the delete verb of the same persona × CRUD matrix, both directions per cell, asserted on POST-STATE (the deleted row is gone; the denied delete left the row standing). See `allowCreate` for the binding rationale."
6876
},
6977
"allowExport": {
7078
"status": "live",
@@ -122,8 +130,9 @@
122130
"readable": {
123131
"status": "live",
124132
"verifiedAt": "2026-07-30",
133+
"proof": "packages/qa/dogfood/test/showcase-fls-read-mask-strip.dogfood.test.ts#showcase-fls-read-mask-strip",
125134
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:301",
126-
"note": "FLS read-mask."
135+
"note": "FLS read-mask. Proof-bound 2026-08-23 (#10959, adjudicated on PR #10934): the proof AUTHORS a scratch permission set carrying `readable: false` and asserts the runtime outcome both ways on the same field, row and request — the key is ABSENT from the wire (`'budget' in record` is false, which `toBeUndefined()` cannot distinguish from a mask), while the entitled caller gets the real value. The sibling `editable` is deliberately NOT bound to this proof: that file authors the key but asserts its refusal as a consequence of unreadability, not as the write-deny axis (`showcase-permission-zoo` pins that half)."
127136
},
128137
"editable": {
129138
"status": "live",
@@ -135,9 +144,10 @@
135144
},
136145
"systemPermissions": {
137146
"status": "live",
138-
"verifiedAt": "2026-07-30",
139-
"evidence": "packages/plugins/plugin-hono-server/src/hono-plugin.ts:1222",
140-
"note": "PARTIAL — app-entry/nav visibility only, not a general capability gate."
147+
"verifiedAt": "2026-08-23",
148+
"proof": "packages/qa/dogfood/test/sharing-rule-org-less-caller.dogfood.test.ts#sharing-rule-org-less-caller",
149+
"evidence": "packages/plugins/plugin-security/src/permission-evaluator.ts:267 (getSystemPermissions — ADR-0066 D3 union of the capabilities a caller's permission sets grant, into ExecutionContext.systemPermissions) + packages/plugins/plugin-sharing/src/sharing-rule-service.ts:136 (assertCanManageRules — ADR-0111 D6: every sharing-rule verb, list and get included, refuses PERMISSION_DENIED unless the caller holds `manage_sharing` OR the legacy `manage_platform_settings` admin override, with system contexts bypassing; enforced in the SERVICE so non-REST callers are covered) + packages/plugins/plugin-sharing/src/sharing-plugin.ts:993 + packages/plugins/plugin-hono-server/src/current-user-endpoints.ts:897 (/auth/me/apps — `AppSchema.requiredPermissions ⊆ ctx.systemPermissions`, the app-entry/nav half)",
150+
"note": "Re-verified 2026-08-23 (#10959) and the note CORRECTED — the previous text (\"PARTIAL — app-entry/nav visibility only, not a general capability gate\") was stale in both halves. (a) Its evidence pointer `plugin-hono-server/src/hono-plugin.ts:1222` no longer exists: that file is 717 lines and contains no `systemPermissions` reference at all — the app-entry consumer moved to `current-user-endpoints.ts`. (b) The scoping claim is falsified by ADR-0111 D6 (Accepted 2026-07-30, P0 implemented): `SharingRuleService.assertCanManageRules` reads `context.systemPermissions` and refuses the whole sharing-rule surface unless the caller holds `manage_sharing` or the legacy `manage_platform_settings` admin override (system contexts bypass), which is a DATA-LAYER authorization gate, not nav visibility — the admit set is wider than one capability, but every member of it is read from `systemPermissions`, which is what this entry classifies. The bound proof measures exactly that: it authors `system_permissions: ['manage_sharing']` on a permission set and asserts the refusal it gets back is the ORG-scope refusal and explicitly NOT `/requires the manage_sharing capability/` — i.e. the authored capability really did clear the service gate — with the org-bound holder of the same grant reading its own tenant (200) as the entitled contrast, over two organizations so a single-tenant fixture cannot pass on the broken build."
141151
},
142152
"tabPermissions": {
143153
"status": "live",

packages/spec/scripts/liveness/proof-registry.mts

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -708,15 +708,29 @@ export const HIGH_RISK_CLASSES: HighRiskClass[] = [
708708
proofId: 'sharing-rule-org-less-caller',
709709
proofRef:
710710
'packages/qa/dogfood/test/sharing-rule-org-less-caller.dogfood.test.ts#sharing-rule-org-less-caller',
711-
bound: false,
712-
ledgerBindings: [],
713-
blockedReason:
714-
'binding PROPOSED, not adopted: the file authors `system_permissions: [\'manage_sharing\']` on a '
715-
+ 'permission set, so `permission.systemPermissions` (live, carrying no proof) is a candidate — '
716-
+ "but that entry's note scopes it to \"app-entry/nav visibility only, not a general capability "
717-
+ 'gate\", which this proof measures as narrower than reality. Re-verifying the note is part of '
718-
+ 'the binding decision, and a ledger edit is the spec seat\'s review (#10773), not a '
719-
+ 'registration side effect.',
711+
bound: true,
712+
// Bound 2026-08-23 (#10959) on the spec seat's adjudication of PR #10934,
713+
// whose item ③ was conditional: bind only if the entry's note turned out
714+
// stale. The re-verification says stale on BOTH halves, so the note was
715+
// corrected in the same PR and the binding follows.
716+
// • The old evidence pointer (`plugin-hono-server/src/hono-plugin.ts:1222`)
717+
// does not exist — that file is 717 lines and never mentions
718+
// `systemPermissions`; the app-entry consumer moved to
719+
// `current-user-endpoints.ts` (`/auth/me/apps`).
720+
// • The old note's scoping claim ("app-entry/nav visibility only, not a
721+
// general capability gate") is falsified by ADR-0111 D6:
722+
// `SharingRuleService.assertCanManageRules` reads
723+
// `context.systemPermissions` and refuses every sharing-rule verb
724+
// unless the caller holds `manage_sharing` or the legacy
725+
// `manage_platform_settings` admin override (system contexts bypass)
726+
// — a data-layer gate, not nav visibility. The admit set is wider than
727+
// one capability, but every member of it is read from
728+
// `systemPermissions`, which is what the bound entry classifies.
729+
// The proof authors the key and proves the gate was CLEARED by it: the
730+
// refusal it asserts is the org-scope one and explicitly NOT
731+
// /requires the manage_sharing capability/, with the org-bound holder of
732+
// the same grant reading its own tenant as the entitled contrast.
733+
ledgerBindings: [{ type: 'permission', path: 'systemPermissions' }],
720734
},
721735
{
722736
id: 'crud-persona-matrix',
@@ -733,14 +747,22 @@ export const HIGH_RISK_CLASSES: HighRiskClass[] = [
733747
proofId: 'showcase-crud-persona-matrix',
734748
proofRef:
735749
'packages/qa/dogfood/test/showcase-crud-persona-matrix.dogfood.test.ts#showcase-crud-persona-matrix',
736-
bound: false,
737-
ledgerBindings: [],
738-
blockedReason:
739-
'binding PROPOSED, not adopted: the four cells map exactly onto `permission.objects.allowCreate '
740-
+ '/ allowRead / allowEdit / allowDelete` (all live, none carrying a proof), and a multi-entry '
741-
+ 'binding has precedent in `semantic-roles`. Whether a persona-breadth matrix should anchor '
742-
+ 'four entries or a chosen subset is a judgment call, and the ledger edit is the spec seat\'s '
743-
+ 'review (#10773).',
750+
bound: true,
751+
// Bound 2026-08-23 (#10959) — ADOPT ALL FOUR, on the spec seat's
752+
// adjudication of PR #10934. The scope worry the registration raised (one
753+
// breadth proof anchoring four properties) is answered by the file's own
754+
// shape: the EXACT allow/deny split is asserted (54 allow / 54 deny, one
755+
// verdict per set × object × verb), so a narrowing sweep — or a persona
756+
// silently failing to provision — breaks the build instead of quietly
757+
// shrinking what these four entries cite. Each verb is exercised in both
758+
// directions per cell and on post-state, not just on status codes.
759+
// Multi-entry binding has precedent in `semantic-roles`, which binds three.
760+
ledgerBindings: [
761+
{ type: 'permission', path: 'objects.allowCreate' },
762+
{ type: 'permission', path: 'objects.allowRead' },
763+
{ type: 'permission', path: 'objects.allowEdit' },
764+
{ type: 'permission', path: 'objects.allowDelete' },
765+
],
744766
},
745767
{
746768
id: 'fls-read-strip',
@@ -756,15 +778,18 @@ export const HIGH_RISK_CLASSES: HighRiskClass[] = [
756778
proofId: 'showcase-fls-read-mask-strip',
757779
proofRef:
758780
'packages/qa/dogfood/test/showcase-fls-read-mask-strip.dogfood.test.ts#showcase-fls-read-mask-strip',
759-
bound: false,
760-
ledgerBindings: [],
761-
blockedReason:
762-
'binding PROPOSED, not adopted — the strongest of the three: the file AUTHORS a scratch '
763-
+ 'permission set carrying `readable: false` and asserts the runtime outcome both ways, and '
764-
+ '`permission.fields.readable` (live, note "FLS read-mask") carries no proof. Not '
765-
+ '`fields.editable` alongside it: the file authors that key but asserts its refusal as a '
766-
+ 'consequence of unreadability rather than as the write-deny axis, which '
767-
+ '`showcase-permission-zoo` already pins. The ledger edit is the spec seat\'s review (#10773).',
781+
bound: true,
782+
// Bound 2026-08-23 (#10959) — the strongest of the three, on the spec
783+
// seat's adjudication of PR #10934. The file AUTHORS a scratch permission
784+
// set carrying `readable: false` and asserts the runtime outcome both ways
785+
// on the SAME field, row and request, so `permission.fields.readable`'s
786+
// `live` status is exactly what it gates.
787+
// NOT `fields.editable` alongside it: the file authors that key but
788+
// asserts its refusal as a CONSEQUENCE of unreadability rather than as the
789+
// write-deny axis, which `showcase-permission-zoo` already pins. Binding it
790+
// would repeat the owner-anchor / `allowTransfer` mistake — a proof cited
791+
// for a property it does not exercise.
792+
ledgerBindings: [{ type: 'permission', path: 'fields.readable' }],
768793
},
769794
];
770795

packages/spec/scripts/liveness/proof-registry.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,29 @@ describe('registry invariants', () => {
136136
// without a runtime proof; its new dogfood file asserts post-state in
137137
// three postures (widen / independent-of-read / fail-closed seam).
138138
'permission/objects.writeScope',
139+
// Bound 2026-08-23 (#10959) — the three proofs PR #10934 registered
140+
// with a PROPOSED binding, adopted after the spec seat adjudicated
141+
// them. Six paths, because two of the three are multi-entry.
142+
//
143+
// `showcase-fls-read-mask-strip` → the READ axis only. Its sibling
144+
// `permission/fields.editable` is deliberately absent: that proof
145+
// authors the key but asserts its refusal as a consequence of
146+
// unreadability, not as the write-deny axis.
147+
'permission/fields.readable',
148+
// `showcase-crud-persona-matrix` → all four verbs. The exact allow/deny
149+
// split is asserted per cell, so a narrowing sweep breaks the build
150+
// rather than quietly shrinking what these four cite (`semantic-roles`
151+
// is the multi-entry precedent).
152+
'permission/objects.allowCreate',
153+
'permission/objects.allowRead',
154+
'permission/objects.allowEdit',
155+
'permission/objects.allowDelete',
156+
// `sharing-rule-org-less-caller` → `systemPermissions`. The card held
157+
// this one back pending a measurement: its ledger note claimed
158+
// "app-entry/nav visibility only". The note was stale — ADR-0111 D6
159+
// makes it a data-layer gate at `SharingRuleService` — so the note was
160+
// corrected and the binding adopted in the same PR.
161+
'permission/systemPermissions',
139162
].sort(),
140163
);
141164
});

0 commit comments

Comments
 (0)