@company-semantics/contracts 28.1.0 → 30.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,13 +33,7 @@ export function renderAuthOtp(
33
33
 
34
34
  const blocks: Block[] = [
35
35
  paragraph("Copy/paste code in login form:"),
36
- ctaBox({
37
- label: otp,
38
- padding: "20px 24px",
39
- fontSize: "20px",
40
- borderRadiusZero: true,
41
- letterSpacing: "4px",
42
- }),
36
+ ctaBox({ label: otp, variant: "code" }),
43
37
  keyValue("Status", "VALID"),
44
38
  keyValue(
45
39
  "Expires in",
@@ -13,11 +13,15 @@
13
13
  * for inline emphasis). No template hand-writes markup or raw strings.
14
14
  */
15
15
 
16
- import { COMPANY_NAME, MONO_FONT_STACK, SUPPORT_EMAIL } from "./constants";
16
+ import { COMPANY_NAME, MONO_FONT_STACK } from "./constants";
17
17
  import { escapeHtml } from "./escape-html";
18
18
 
19
19
  const MONO = `font-family: ${MONO_FONT_STACK};`;
20
20
 
21
+ /** The single font size for every email element (HTML). Plain text is monospace
22
+ * so it carries no size — this keeps one visual size across both surfaces. */
23
+ const FONT_SIZE = "13px";
24
+
21
25
  // =============================================================================
22
26
  // Core types
23
27
  // =============================================================================
@@ -100,7 +104,7 @@ export function textShell(blocks: Block[]): string {
100
104
  // =============================================================================
101
105
 
102
106
  /**
103
- * Email paragraph — mono (matching the body shell), 14px, configurable spacing.
107
+ * Email paragraph — mono (matching the body shell), 13px, configurable spacing.
104
108
  * The single paragraph primitive: greeting / keyValue / footer / signature all
105
109
  * build on it, and templates use it directly for body lines.
106
110
  */
@@ -110,7 +114,7 @@ export function paragraph(
110
114
  ): Block {
111
115
  const { html, text } = renderInline(content);
112
116
  return {
113
- html: `<p style="${MONO} font-size: 14px; margin: ${SPACING[spacing]};">${html}</p>`,
117
+ html: `<p style="${MONO} font-size: ${FONT_SIZE}; margin: ${SPACING[spacing]};">${html}</p>`,
114
118
  text,
115
119
  spacing,
116
120
  };
@@ -141,20 +145,19 @@ export function footer(
141
145
  : escapeHtml(firstLine);
142
146
  const text = secondLine ? `${firstLine}\n${secondLine}` : firstLine;
143
147
  return {
144
- html: `<p style="${MONO} font-size: 14px; margin: ${SPACING[spacing]};">${html}</p>`,
148
+ html: `<p style="${MONO} font-size: ${FONT_SIZE}; margin: ${SPACING[spacing]};">${html}</p>`,
145
149
  text,
146
150
  spacing,
147
151
  };
148
152
  }
149
153
 
150
154
  /**
151
- * Trailing sign-off. Owns the full company sign-off in both surfaces: the
152
- * company (or custom `signer`) name and a "Questions? Contact <support>" line.
155
+ * Trailing sign-off the company (or custom `signer`) name.
153
156
  */
154
157
  export function signature(signer: string = COMPANY_NAME): Block {
155
158
  return {
156
- html: `<p style="${MONO} font-size: 14px; margin: ${SPACING.none};">${escapeHtml(signer)}<br><span style="color: #888;">Questions? Contact <a href="mailto:${SUPPORT_EMAIL}" style="color: #888;">${SUPPORT_EMAIL}</a></span></p>`,
157
- text: `---\n${signer}\nQuestions? Contact ${SUPPORT_EMAIL}`,
159
+ html: `<p style="${MONO} font-size: ${FONT_SIZE}; margin: ${SPACING.none};">${escapeHtml(signer)}</p>`,
160
+ text: `---\n${signer}`,
158
161
  spacing: "none",
159
162
  };
160
163
  }
@@ -173,7 +176,7 @@ export const ACCESS_PHRASE: Record<"editor" | "commenter" | "viewer", string> =
173
176
  /** Centered continuation dots separator (chat-shared). */
174
177
  export function chatDots(): Block {
175
178
  return {
176
- html: `<p style="${MONO} font-size: 20px; margin: 0 0 16px 0; max-width: 320px; text-align: center;">⋮</p>`,
179
+ html: `<p style="${MONO} font-size: ${FONT_SIZE}; margin: 0 0 16px 0; max-width: 320px; text-align: center;">⋮</p>`,
177
180
  text: " ⋮",
178
181
  spacing: "normal",
179
182
  };
@@ -190,17 +193,36 @@ export interface CtaBoxOptions {
190
193
  /** When present, the label links to this URL (and the URL rides under the
191
194
  * plain-text box). */
192
195
  href?: string;
193
- /** Inner-cell padding, e.g. `"16px 24px"`. */
196
+ /** Visual kind. `"action"` = link CTA (default); `"code"` = copy/paste code
197
+ * box (square corners, letter-spaced). */
198
+ variant?: "action" | "code";
199
+ }
200
+
201
+ /** Component-owned styling per CTA variant. Templates supply only `label`/`href`/
202
+ * `variant` — the only allowable UI is the component, so all CSS lives here,
203
+ * never at the call site. */
204
+ interface CtaVariantStyle {
194
205
  padding: string;
195
- /** Label font-size, e.g. `"16px"`. */
196
206
  fontSize: string;
197
- /** Optional table `max-width`, e.g. `"220px"`. */
198
207
  maxWidth?: string;
199
- /** Emit `border-radius: 0;` (OTP code box). */
200
- borderRadiusZero?: boolean;
201
- /** Optional `letter-spacing`, e.g. `"4px"` (OTP code box). */
208
+ square: boolean;
202
209
  letterSpacing?: string;
203
210
  }
211
+ const CTA_VARIANTS: Record<"action" | "code", CtaVariantStyle> = {
212
+ action: {
213
+ padding: "16px 24px",
214
+ fontSize: FONT_SIZE,
215
+ maxWidth: "220px",
216
+ square: false,
217
+ },
218
+ code: {
219
+ padding: "16px 24px",
220
+ fontSize: FONT_SIZE,
221
+ maxWidth: "220px",
222
+ square: true,
223
+ letterSpacing: "4px",
224
+ },
225
+ };
204
226
 
205
227
  /** Padding columns on each side of the `>> LABEL <<` line in the ASCII box. */
206
228
  const CTA_BOX_PAD = 3;
@@ -216,19 +238,13 @@ function asciiCtaBox(label: string): string {
216
238
  /** The bordered `>> LABEL <<` CTA box (OTP / JOIN / OPEN / VIEW TEAM / …). When
217
239
  * `href` is given, HTML links the label and plain text prints the URL below. */
218
240
  export function ctaBox(opts: CtaBoxOptions): Block {
219
- const {
220
- label,
221
- href,
222
- padding,
223
- fontSize,
224
- maxWidth,
225
- borderRadiusZero,
226
- letterSpacing,
227
- } = opts;
241
+ const { label, href, variant = "action" } = opts;
242
+ const { padding, fontSize, maxWidth, square, letterSpacing } =
243
+ CTA_VARIANTS[variant];
228
244
 
229
245
  const tableStyle =
230
246
  `border: 2px solid #1a1a1a;` +
231
- (borderRadiusZero ? ` border-radius: 0;` : ``) +
247
+ (square ? ` border-radius: 0;` : ``) +
232
248
  ` margin: 0 0 20px 0;` +
233
249
  (maxWidth ? ` max-width: ${maxWidth};` : ``);
234
250
 
@@ -307,10 +323,10 @@ function clampMessage(text: string): string {
307
323
  }
308
324
 
309
325
  /**
310
- * Right-aligned user chat message. Truncated by clampMessage. The avatar rides
311
- * the bottom (bottom-aligned like a real chat). When `from` is given it renders
312
- * as an attribution below, right-aligned to the message's right edge (clear of
313
- * the face). Doubles as the "message from …" quote.
326
+ * Right-aligned user chat message. Truncated by clampMessage. In plain text the
327
+ * face sits beside the last message line. When `from` is given (the sender's
328
+ * name) it renders as an attribution below, right-aligned to the message's right
329
+ * edge.
314
330
  */
315
331
  export function chatUser(content: string, from?: string): Block {
316
332
  const clamped = clampMessage(content);
@@ -318,42 +334,47 @@ export function chatUser(content: string, from?: string): Block {
318
334
  const attributionRow = from
319
335
  ? `
320
336
  <tr>
321
- <td style="${MONO} font-size: 11px; color: #888; text-align: right; padding-top: 4px;">${escapeHtml(from)}</td>
337
+ <td style="${MONO} font-size: ${FONT_SIZE}; color: #888; text-align: right; padding-top: 4px;">${escapeHtml(from)}</td>
322
338
  <td></td>
323
339
  </tr>`
324
340
  : "";
325
341
  const html = `<table cellpadding="0" cellspacing="0" border="0" style="margin: 0 0 16px 0;">
326
342
  <tr>
327
- <td style="border: 1px solid #1a1a1a; border-radius: 12px 12px 0 12px; padding: 10px 14px; min-width: 280px; text-align: right; ${MONO} font-size: 13px; background: #f5f5f5;">${escapeHtml(clamped)}</td>
328
- <td style="${MONO} font-size: 14px; padding-left: 8px; vertical-align: bottom;">(•̀_ರ╮)</td>
343
+ <td style="border: 1px solid #1a1a1a; border-radius: 12px 12px 0 12px; padding: 10px 14px; min-width: 280px; text-align: right; ${MONO} font-size: ${FONT_SIZE}; background: #f5f5f5;">${escapeHtml(clamped)}</td>
344
+ <td style="${MONO} font-size: ${FONT_SIZE}; padding-left: 8px; vertical-align: bottom;">(•̀_ರ╮)</td>
329
345
  </tr>${attributionRow}
330
346
  </table>`;
331
347
 
332
348
  const border = "─".repeat(MESSAGE_WIDTH + 1);
333
349
  const lines = wrapText(clamped, MESSAGE_WIDTH);
334
350
  const body = lines.map((line) => `│${line.padStart(MESSAGE_WIDTH)} │`);
335
- const box = [`┌${border}┐`, ...body, `└${border}┘ (•̀_ರ╮)`];
351
+ // Face beside the last message line (one row up from the bottom border).
352
+ body[body.length - 1] += " (•̀_ರ╮)";
353
+ const box = [`┌${border}┐`, ...body, `└${border}┘`];
336
354
  if (from) box.push(from.padStart(MESSAGE_WIDTH + 3));
337
355
 
338
356
  return { html, text: box.join("\n"), spacing: "normal" };
339
357
  }
340
358
 
341
- /** Left-aligned assistant chat message with `[c_S]` avatar (bottom-aligned). */
359
+ /** Left-aligned assistant chat message with `[c_S]` avatar; in plain text the
360
+ * avatar sits beside the last message line (one row up from the bottom border). */
342
361
  export function chatAssistant(content: string): Block {
343
362
  const clamped = clampMessage(content);
344
363
 
345
364
  const html = `<table cellpadding="0" cellspacing="0" border="0" style="margin: 16px 0;">
346
365
  <tr>
347
- <td style="${MONO} font-size: 14px; padding-right: 8px; vertical-align: bottom;">[c_S]</td>
348
- <td style="border: 1px solid #1a1a1a; border-radius: 12px 12px 12px 0; padding: 10px 14px; min-width: 280px; ${MONO} font-size: 13px; background: #f5f5f5;">${escapeHtml(clamped)}</td>
366
+ <td style="${MONO} font-size: ${FONT_SIZE}; padding-right: 8px; vertical-align: bottom;">[c_S]</td>
367
+ <td style="border: 1px solid #1a1a1a; border-radius: 12px 12px 12px 0; padding: 10px 14px; min-width: 280px; ${MONO} font-size: ${FONT_SIZE}; background: #f5f5f5;">${escapeHtml(clamped)}</td>
349
368
  </tr>
350
369
  </table>`;
351
370
 
352
371
  const lineWidth = 36;
353
372
  const lines = wrapText(clamped, lineWidth);
354
373
  const top = " ┌──────────────────────────────────────┐";
355
- const bot = "[c_S] └──────────────────────────────────────┘";
374
+ const bot = " └──────────────────────────────────────┘";
356
375
  const boxLines = lines.map((line) => ` │ ${line.padEnd(lineWidth)} │`);
376
+ // Avatar beside the last message line (one row up from the bottom border).
377
+ boxLines[boxLines.length - 1] = `[c_S] ${boxLines[boxLines.length - 1].slice(7)}`;
357
378
 
358
379
  return { html, text: [top, ...boxLines, bot].join("\n"), spacing: "normal" };
359
380
  }
@@ -31,13 +31,7 @@ export function renderChatShared(payload: ChatSharedPayload): Block[] {
31
31
 
32
32
  blocks.push(
33
33
  chatDots(),
34
- ctaBox({
35
- label: "SEE MORE",
36
- href: shareUrl,
37
- padding: "12px 20px",
38
- fontSize: "14px",
39
- maxWidth: "200px",
40
- }),
34
+ ctaBox({ label: "SEE MORE", href: shareUrl }),
41
35
  footer(`This share was sent via ${COMPANY_NAME}.`),
42
36
  paragraph(NOTICE, "tight"),
43
37
  signature(),
@@ -30,13 +30,7 @@ export function renderAccessApproved(payload: AccessApprovedPayload): Block[] {
30
30
  bold(`"${docTitle}"`),
31
31
  ` — you ${ACCESS_PHRASE[accessLevel]}.`,
32
32
  ]),
33
- ctaBox({
34
- label: "OPEN",
35
- href: docUrl,
36
- padding: "16px 24px",
37
- fontSize: "16px",
38
- maxWidth: "220px",
39
- }),
33
+ ctaBox({ label: "OPEN", href: docUrl }),
40
34
  footer(`This notification was sent via ${COMPANY_NAME}.`),
41
35
  signature(),
42
36
  ];
@@ -36,16 +36,10 @@ export function renderAccessRequested(
36
36
  ]),
37
37
  ];
38
38
 
39
- if (message) blocks.push(chatUser(message, `Message from ${requesterName}`));
39
+ if (message) blocks.push(chatUser(message, requesterName));
40
40
 
41
41
  blocks.push(
42
- ctaBox({
43
- label: "REVIEW",
44
- href: reviewUrl,
45
- padding: "16px 24px",
46
- fontSize: "16px",
47
- maxWidth: "220px",
48
- }),
42
+ ctaBox({ label: "REVIEW", href: reviewUrl }),
49
43
  footer(`This notification was sent via ${COMPANY_NAME}.`, OWNER_NOTE),
50
44
  signature(),
51
45
  );
@@ -22,12 +22,7 @@ export function renderOrgInvite(payload: OrgInvitePayload): Block[] {
22
22
 
23
23
  return [
24
24
  paragraph("Workspace invitation."),
25
- ctaBox({
26
- label: "JOIN",
27
- href: acceptUrl,
28
- padding: "20px 24px",
29
- fontSize: "16px",
30
- }),
25
+ ctaBox({ label: "JOIN", href: acceptUrl }),
31
26
  keyValue("From", inviterName),
32
27
  keyValue("Workspace", orgName),
33
28
  keyValue("Role", role),
@@ -30,16 +30,10 @@ export function renderOwnershipTransfer(
30
30
  ]),
31
31
  ];
32
32
 
33
- if (note) blocks.push(chatUser(note, "Message from the current owner"));
33
+ if (note) blocks.push(chatUser(note, "the current owner"));
34
34
 
35
35
  blocks.push(
36
- ctaBox({
37
- label: "ACCEPT",
38
- href: acceptUrl,
39
- padding: "16px 24px",
40
- fontSize: "16px",
41
- maxWidth: "220px",
42
- }),
36
+ ctaBox({ label: "ACCEPT", href: acceptUrl }),
43
37
  keyValue("Expires in", `${expiresInDays} days`, "normal"),
44
38
  footer(
45
39
  "If you did not expect this invitation, you can safely ignore this email.",
@@ -46,16 +46,10 @@ export function renderShareGranted(payload: ShareGrantedPayload): Block[] {
46
46
  ]),
47
47
  ];
48
48
 
49
- if (message) blocks.push(chatUser(message, `Message from ${granterName}`));
49
+ if (message) blocks.push(chatUser(message, granterName));
50
50
 
51
51
  blocks.push(
52
- ctaBox({
53
- label: "OPEN",
54
- href: ctaUrl,
55
- padding: "16px 24px",
56
- fontSize: "16px",
57
- maxWidth: "220px",
58
- }),
52
+ ctaBox({ label: "OPEN", href: ctaUrl }),
59
53
  footer(`This notification was sent via ${COMPANY_NAME}.`, NOTICE),
60
54
  signature(),
61
55
  );
@@ -37,16 +37,10 @@ export function renderUnitOwnerGranted(
37
37
  ]),
38
38
  ];
39
39
 
40
- if (message) blocks.push(chatUser(message, `Message from ${granterName}`));
40
+ if (message) blocks.push(chatUser(message, granterName));
41
41
 
42
42
  blocks.push(
43
- ctaBox({
44
- label: "VIEW TEAM",
45
- href: ctaUrl,
46
- padding: "16px 24px",
47
- fontSize: "16px",
48
- maxWidth: "220px",
49
- }),
43
+ ctaBox({ label: "VIEW TEAM", href: ctaUrl }),
50
44
  footer(`This notification was sent via ${COMPANY_NAME}.`, NOTICE),
51
45
  signature(),
52
46
  );
package/src/index.ts CHANGED
@@ -352,15 +352,13 @@ export type {
352
352
  CompanyMdTreeNode,
353
353
  CompanyMdDoc,
354
354
  CompanyMdContextBankItem,
355
- // Sharing and ACL types (PRD-00306)
355
+ // Sharing and ACL types (PRD-00306). Legacy AccessSource / AccessReason /
356
+ // EffectiveAccess / EvaluationStep / AccessExplanation removed with
357
+ // CompanyMdAccessEvaluator (ADR-BE-392); the ONE AccessSource is re-exported
358
+ // from ./permissions below.
356
359
  AccessLevel,
357
360
  SharePolicy,
358
361
  AclEntry,
359
- AccessSource,
360
- AccessReason,
361
- EffectiveAccess,
362
- EvaluationStep,
363
- AccessExplanation,
364
362
  ShareState,
365
363
  PermissionAuditEntry,
366
364
  } from "./org/index";
package/src/org/README.md CHANGED
@@ -17,10 +17,7 @@ Shared type vocabulary for organization ownership, type classification, and tran
17
17
  ## Public API
18
18
 
19
19
  - `AcceptInviteRequest` _(type)_ — Request payload for accepting an organization invite.
20
- - `AccessExplanation` _(type)_ — Full access explanation with evaluation trace.
21
20
  - `AccessLevel` _(type)_ — Document access level.
22
- - `AccessReason` _(type)_
23
- - `AccessSource` _(type)_ — Source of an access grant.
24
21
  - `AcknowledgeSystemEventResponse` _(type)_
25
22
  - `AcknowledgeSystemEventResponseSchema`
26
23
  - `AclEntry` _(type)_
@@ -93,8 +90,6 @@ Shared type vocabulary for organization ownership, type classification, and tran
93
90
  - `DomainResponseSchema`
94
91
  - `DomainStatus` _(type)_ — Status of a domain claim within an organization. - 'pending': Domain claimed but not yet verified -…
95
92
  - `DomainVerificationMethod` _(type)_ — Method used to verify domain ownership. - 'dnstxt': DNS TXT record verification (primary method) - 'email'…
96
- - `EffectiveAccess` _(type)_ — Effective access for a user on a document.
97
- - `EvaluationStep` _(type)_ — Single step in the access evaluation trace.
98
93
  - `ExecutionContext` _(type)_
99
94
  - `ExecutionContextSchema` — ExecutionContext — the RUN in which an intent was produced or applied.
100
95
  - `ExecutionScope` _(type)_ — Execution scope determines whose identity is used when executing actions. - 'self': Actions execute under the…
package/src/org/index.ts CHANGED
@@ -227,16 +227,15 @@ export type {
227
227
  CompanyMdContextBankItem,
228
228
  } from "./company-md";
229
229
 
230
- // Sharing and ACL types (PRD-00306)
230
+ // Sharing and ACL types (PRD-00306). The legacy AccessSource / AccessReason /
231
+ // EffectiveAccess / EvaluationStep / AccessExplanation were removed with
232
+ // CompanyMdAccessEvaluator (ADR-BE-392); ShareState.effectiveAccess now carries
233
+ // the projection EffectiveAccessResponse, and the ONE AccessSource lives in
234
+ // permissions/access-source.
231
235
  export type {
232
236
  AccessLevel,
233
237
  SharePolicy,
234
238
  AclEntry,
235
- AccessSource,
236
- AccessReason,
237
- EffectiveAccess,
238
- EvaluationStep,
239
- AccessExplanation,
240
239
  ShareState,
241
240
  PermissionAuditEntry,
242
241
  } from "./sharing";
@@ -1,3 +1,5 @@
1
+ import type { EffectiveAccessResponse } from "../permissions/share-api.js";
2
+
1
3
  /**
2
4
  * Document access level.
3
5
  * Privilege order: editor > commenter > viewer.
@@ -23,81 +25,25 @@ export interface AclEntry {
23
25
  readonly grantedAt: string;
24
26
  }
25
27
 
26
- /**
27
- * Source of an access grant.
28
- * Used in EffectiveAccess.reasons and EvaluationStep.source.
29
- *
30
- * `unit_delegation` covers leadership/delegation grants on the doc's owning
31
- * unit via the authority projection (ADR-BE-169). Distinct from
32
- * `unit_baseline` (which is membership-role-based) — delegations live on the
33
- * `org_unit_authority_grants` table and carry an explicit scope set.
34
- *
35
- * `visibility` covers the general-access band (`private` | `unit` | `org` on
36
- * the entity's `visibility` column) — the "General access" tier in the share
37
- * dialog. `unit` grants viewer to everyone home-in or matrixed-into the owning
38
- * unit's subtree (members ∪ contributors, flowing down); at the org root `unit`
39
- * normalizes to org-wide. `org` grants viewer to every org member. This is a
40
- * first-class source so the deterministic evaluator, the in-memory open gate,
41
- * and the materialized grant-compiler all resolve the band identically. See
42
- * ADR-CONTRACTS-075 and backend ADR-BE-374.
43
- */
44
- export type AccessSource =
45
- | "org_rbac"
46
- | "sharing_policy"
47
- | "visibility"
48
- | "unit_baseline"
49
- | "unit_delegation"
50
- | "acl_grant"
51
- | "doc_ownership";
52
-
53
- export interface AccessReason {
54
- readonly source: AccessSource;
55
- readonly detail: string;
56
- }
57
-
58
- /**
59
- * Effective access for a user on a document.
60
- *
61
- * INVARIANTS:
62
- * - canShare is granted ONLY by: doc ownership, ownership of the owning unit,
63
- * or org-level org.manage_goals / org.manage_company_md.
64
- * - canShare is NEVER granted by ACL entries or sharing policy.
65
- * - org_edit via sharing policy does NOT imply canShare.
66
- * - Unit membership grants baseline access ONLY to resources owned by that unit.
67
- */
68
- export interface EffectiveAccess {
69
- readonly level: "none" | AccessLevel;
70
- readonly reasons: ReadonlyArray<AccessReason>;
71
- readonly canShare: boolean;
72
- }
73
-
74
- /**
75
- * Single step in the access evaluation trace.
76
- * Enables full traceability for debugging, audit UI, and compliance.
77
- */
78
- export interface EvaluationStep {
79
- readonly source: AccessSource;
80
- readonly checked: boolean;
81
- readonly granted: AccessLevel | null;
82
- readonly detail: string;
83
- }
84
-
85
- /**
86
- * Full access explanation with evaluation trace.
87
- * Returned by GoalsAccessEvaluator.explain().
88
- */
89
- export interface AccessExplanation extends EffectiveAccess {
90
- readonly evaluationTrace: ReadonlyArray<EvaluationStep>;
91
- }
92
-
93
28
  /**
94
29
  * Complete sharing state for a document.
95
- * Returned by GET /api/goals/docs/:slug/sharing.
30
+ * Returned by GET /api/company-md/docs/:id/sharing.
31
+ *
32
+ * `effectiveAccess` is the ONE effective-access authority's answer
33
+ * ({@link EffectiveAccessResponse}: a nullable `access_level` + the
34
+ * `source_chain` of provenance tokens from the single `access-source` vocabulary
35
+ * — `explicit` | `ownership` | `visibility` | `inheritance` | `authority` |
36
+ * `context_association` | `migration`). The legacy per-doc `EffectiveAccess` /
37
+ * `AccessExplanation` shapes (their own 7-token `AccessSource`, `reasons`,
38
+ * `evaluationTrace`) were removed when `CompanyMdAccessEvaluator` was deleted and
39
+ * the response was collapsed onto the single authority (ADR-BE-392). There is now
40
+ * ONE `AccessSource` in the package (the projection one, from
41
+ * `permissions/access-source`).
96
42
  */
97
43
  export interface ShareState {
98
44
  readonly sharingPolicy: SharePolicy;
99
45
  readonly acl: ReadonlyArray<AclEntry>;
100
- readonly effectiveAccess: EffectiveAccess;
46
+ readonly effectiveAccess: EffectiveAccessResponse;
101
47
  }
102
48
 
103
49
  /**