Skip to content

Commit 228e74a

Browse files
committed
feat(spec,plugin-sharing): SharingRuleRow declares the deploymentWide marker; an org-less row's delete by an org admin is pinned a no-op
Claude-Session: https://claude.ai/code/session_01XFEb4fSDbe4fK94S3x5FKX Co-authored-by: Claude <noreply@anthropic.com>
1 parent 357025d commit 228e74a

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎packages/plugins/plugin-sharing/src/sharing-rule-service.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ type DeclaredRuleScope = string | typeof DEPLOYMENT_WIDE_SCOPE;
161161
* typed marker that replaced the organization-less (`organization_id: null`)
162162
* projection, in the shape of the declared webhooks' `deploymentWide` (the
163163
* epic's ruling A on the walled shape of a declared webhook, record
164-
* 6278609968). It carries no `organization_id` at all.
164+
* 6278609968). It carries no `organization_id` at all. The marker itself is
165+
* declared on the spec contract (`SharingRuleRow.deploymentWide`); this type
166+
* narrows it to present.
165167
*/
166168
export interface DeploymentWideSharingRule extends SharingRuleRow {
167169
readonly deploymentWide: true;

‎packages/plugins/plugin-sharing/src/sharing-rule.test.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,18 @@ describe('[#7761] getRule by-id is scoped to the caller organization', () => {
12961296
expect(grantsOf(seededId)).toEqual([]);
12971297
});
12981298

1299+
it('an organization-less row is unreachable BY ID — delete is a no-op that leaves the row AND its grants intact', async () => {
1300+
// The successor of the retired #7795 delete guard: the row is no longer
1301+
// refused by class, it is simply not the org admin's to address. Grants
1302+
// are materialised under BOOT (this double registers no organization
1303+
// object, so the row sweeps the deployment) so the no-op has something to lose.
1304+
await rules.evaluateRule(seededId, BOOT);
1305+
expect(grantsOf(seededId)).toHaveLength(1);
1306+
await rules.deleteRule(seededId, ORG1_ADMIN);
1307+
expect(engine._tables.sys_sharing_rule.find((r) => r.id === seededId)).toBeTruthy();
1308+
expect(grantsOf(seededId)).toHaveLength(1);
1309+
});
1310+
12991311
// ── unchanged behaviour ──────────────────────────────────────────────
13001312

13011313
it('the caller’s OWN org rule is still fully addressable by id', async () => {

‎packages/spec/src/contracts/sharing-service.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,15 @@ export type SharingRuleRecipientType = 'user' | 'team' | 'business_unit' | 'posi
481481
export interface SharingRuleRow {
482482
id: string;
483483
organization_id?: string | null;
484+
/**
485+
* [ADR-0131 D2/D8] `true` on a DECLARED rule's projection that sweeps the
486+
* whole deployment — the one projection a declared rule is served as under
487+
* the `single` posture. Such a projection carries no `organization_id`:
488+
* deployment-wide is this typed marker, never a NULL organization. Absent on
489+
* every stored `sys_sharing_rule` row, and on a declared rule's
490+
* per-organization projections under a walled posture.
491+
*/
492+
deploymentWide?: true;
484493
name: string;
485494
label: string;
486495
description?: string | null;

0 commit comments

Comments
 (0)