@bridge-ds/core 0.18.0 → 0.20.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.
Files changed (69) hide show
  1. package/LICENSE +17 -0
  2. package/bridge.config.schema.json +21 -0
  3. package/dist/audit/index.js +3 -3
  4. package/dist/audit/report.d.ts +17 -2
  5. package/dist/branch-actions.d.ts +7 -1
  6. package/dist/build-requests-github.d.ts +25 -0
  7. package/dist/build-requests.d.ts +403 -0
  8. package/dist/changelog-model.d.ts +102 -0
  9. package/dist/component-spec.d.ts +3 -1
  10. package/dist/config.d.ts +26 -0
  11. package/dist/css-color.d.ts +25 -0
  12. package/dist/docs-proposal.d.ts +1 -0
  13. package/dist/explorations.d.ts +1 -0
  14. package/dist/fidelity-references.d.ts +12 -0
  15. package/dist/fidelity.d.ts +12 -1
  16. package/dist/figma-fix-loop.d.ts +153 -0
  17. package/dist/figma-fix-run.d.ts +103 -0
  18. package/dist/figma-fixes.d.ts +169 -0
  19. package/dist/figma-push-github.d.ts +7 -2
  20. package/dist/figma-push.d.ts +15 -2
  21. package/dist/gap-case.d.ts +143 -0
  22. package/dist/github-client.d.ts +31 -0
  23. package/dist/id-map.d.ts +10 -0
  24. package/dist/index.d.ts +17 -0
  25. package/dist/index.js +3996 -818
  26. package/dist/index.js.map +1 -1
  27. package/dist/layered-review.d.ts +2 -0
  28. package/dist/measure-location.d.ts +42 -0
  29. package/dist/measure-status.d.ts +127 -0
  30. package/dist/measure-workflow.d.ts +48 -0
  31. package/dist/measured-only.d.ts +30 -0
  32. package/dist/measured-spec.d.ts +349 -0
  33. package/dist/measured-tokens.d.ts +25 -0
  34. package/dist/motion-docs.d.ts +51 -0
  35. package/dist/node/audit.d.ts +11 -3
  36. package/dist/node/index.d.ts +1 -0
  37. package/dist/node/index.js +1640 -24
  38. package/dist/node/index.js.map +1 -1
  39. package/dist/node/measure-script.d.ts +1 -0
  40. package/dist/node/measure.d.ts +212 -0
  41. package/dist/node/spec-css.d.ts +9 -1
  42. package/dist/node/tokens-script.d.ts +3 -0
  43. package/dist/pr-composer.d.ts +5 -0
  44. package/dist/previews.d.ts +4 -1
  45. package/dist/{prod-branch-DenQkr_e.js → prod-branch-CJZwMfVD.js} +223 -183
  46. package/dist/prod-branch-CJZwMfVD.js.map +1 -0
  47. package/dist/prod-branch.d.ts +1 -1
  48. package/dist/repo-model.d.ts +7 -1
  49. package/dist/{repo-source-CRfhLO87.js → repo-source-CshjjC7x.js} +1485 -629
  50. package/dist/repo-source-CshjjC7x.js.map +1 -0
  51. package/dist/repo-source-github.d.ts +8 -0
  52. package/dist/repo-source.d.ts +6 -0
  53. package/dist/{report-DVEmUr0p.js → report-CE-jHAuw.js} +17 -6
  54. package/dist/{report-DVEmUr0p.js.map → report-CE-jHAuw.js.map} +1 -1
  55. package/dist/requester.d.ts +14 -0
  56. package/dist/requests.d.ts +2 -0
  57. package/dist/review-gate.d.ts +189 -2
  58. package/dist/risk.d.ts +12 -0
  59. package/dist/share-branch.d.ts +1 -1
  60. package/dist/snapshot.d.ts +34 -0
  61. package/dist/suggestion-review.d.ts +45 -2
  62. package/dist/suggestions-github.d.ts +33 -1
  63. package/dist/suggestions.d.ts +6 -0
  64. package/dist/tier1-publish.d.ts +48 -0
  65. package/dist/token-proposal.d.ts +7 -0
  66. package/dist/try-in-product.d.ts +9 -0
  67. package/package.json +1 -1
  68. package/dist/prod-branch-DenQkr_e.js.map +0 -1
  69. package/dist/repo-source-CRfhLO87.js.map +0 -1
@@ -0,0 +1,14 @@
1
+ export interface Requester {
2
+ /** The display name, when the line gives one. */
3
+ name: string | null;
4
+ /** The GitHub login, without "@", when the line gives one. */
5
+ login: string | null;
6
+ }
7
+ /** The neutral author of a change whose requester deleted their Bridge account (api/account rewrites the line). */
8
+ export declare const FORMER_MEMBER = "A former member";
9
+ /** How a sentence names a requester by first name ("Waiting for Priya"): the former member stays whole. */
10
+ export declare const requesterFirstName: (name: string) => string;
11
+ /** The requester a body names, null when it names none (or names it in a form Bridge does not write). */
12
+ export declare function requesterOf(body: string | null | undefined): Requester | null;
13
+ /** The line Bridge writes for a requester: "Requested by Ann Leroy (@annleroy)." */
14
+ export declare function requesterLine(r: Requester): string;
@@ -45,6 +45,8 @@ export interface ComponentChangeRequest {
45
45
  * `exploration` label and the brief says to work on that branch (docs/EXPLORATIONS.md).
46
46
  */
47
47
  exploration?: string | undefined;
48
+ /** Build request details (before and after images, props needed, spec path): build-requests.ts. */
49
+ build?: import('./build-requests').BuildRequestExtra | undefined;
48
50
  }
49
51
  export interface RequestIssue {
50
52
  title: string;
@@ -1,3 +1,4 @@
1
+ import type { FigmaPush } from './build-requests';
1
2
  import type { OverridePolicy } from './config';
2
3
  import { type ShipPolicy } from './rights';
3
4
  import { type RiskTier } from './risk';
@@ -12,8 +13,22 @@ export interface PullStateLike {
12
13
  draft?: boolean;
13
14
  merged?: boolean;
14
15
  merged_at?: string | null;
16
+ /** GitHub labels (objects or names): `drafted-by-claude` makes a draft ready for review (isDraft). */
17
+ labels?: readonly ({
18
+ name?: string;
19
+ } | string)[] | null | undefined;
15
20
  }
16
21
  export type PullState = 'merged' | 'closed' | 'draft' | 'open';
22
+ /**
23
+ * Where Claude stands on a library branch opened from Figma (figma-push labels): `waiting` while it carries the spec
24
+ * only (a developer builds it with Claude), `drafted` once the code is written. Null on any other branch.
25
+ */
26
+ export declare function claudeStage(labels: PullStateLike['labels']): 'waiting' | 'drafted' | null;
27
+ /**
28
+ * A draft on GitHub that still reads as one. Decision RT-1: once Claude drafted the code (`drafted-by-claude`), the
29
+ * branch is ready for review even while GitHub still calls it a draft; the merge marks it ready first (branch-actions).
30
+ */
31
+ export declare const isDraft: (p: Pick<PullStateLike, "draft" | "labels">) => boolean;
17
32
  /** Merged wins over closed (GitHub reports merged pull requests as closed), closed over draft, draft over open. */
18
33
  export declare function pullState(p: PullStateLike): PullState;
19
34
  /** Label and pill tone of each state, for list and detail views. */
@@ -33,7 +48,13 @@ export interface CiStatusLike {
33
48
  context: string;
34
49
  state: 'success' | 'failure' | 'error' | 'pending' | string;
35
50
  target_url?: string | null;
51
+ description?: string | null;
36
52
  }
53
+ /**
54
+ * A status the hosting platform posts when it refused to run at all (Vercel "Deployment rate limited"): it says nothing
55
+ * about the change, and GitHub does not require it, so it never counts as a failed check.
56
+ */
57
+ export declare function isPlatformRefusal(s: Pick<CiStatusLike, 'state' | 'description'>): boolean;
37
58
  export type CiState = 'pass' | 'fail' | 'pending' | 'none';
38
59
  export interface CiSummary {
39
60
  state: CiState;
@@ -56,13 +77,29 @@ export interface ReviewLike {
56
77
  } | null;
57
78
  state: 'APPROVED' | 'CHANGES_REQUESTED' | 'COMMENTED' | 'DISMISSED' | 'PENDING' | string;
58
79
  submitted_at?: string | null;
80
+ /** The commit the review was given on (GitHub `commit_id`): tells whether the branch moved on since. */
81
+ commit_id?: string | null;
59
82
  }
60
83
  export interface ApprovalSummary {
61
84
  approvers: string[];
62
85
  changesRequested: string[];
63
86
  design: boolean;
64
87
  tech: boolean;
88
+ /**
89
+ * One owner approved (design or tech list, teams expanded): the Tier 1 rule of docs/GOVERNANCE.md, "1 code owner",
90
+ * the same as Settings says ("one owner"). With no owner configured, any approval counts. Absent (a summary built by
91
+ * hand): any approval counts.
92
+ */
93
+ owner?: boolean;
94
+ /**
95
+ * A token value change (withTokenValueApprovals, decision TV-1 in docs/BUILD-REQUESTS.md): token values changed or
96
+ * tokens added, nothing renamed or removed, no code. The design approval is all it needs: once it is in, `tech` counts
97
+ * as given and the change ships after the automatic checks. mergeGate never asks for the tech approval then.
98
+ */
99
+ valuesOnly?: boolean;
65
100
  }
101
+ /** Whether the Tier 1 approval is in: one owner approved (GOVERNANCE.md "1 code owner"), else any approval without owners. */
102
+ export declare const hasOwnerApproval: (a: ApprovalSummary) => boolean;
66
103
  export interface ApprovalRules {
67
104
  /**
68
105
  * `governance.soloOwnerCanApproveBoth`: when the owners resolve to one person holding both roles, that person's
@@ -75,6 +112,41 @@ export declare function summarizeApprovals(reviews: ReviewLike[], owners: {
75
112
  design: string[];
76
113
  tech: string[];
77
114
  }, author?: string, rules?: ApprovalRules): ApprovalSummary;
115
+ /**
116
+ * The reviewers to look again: the author updated the branch after every changes request that still counts (the
117
+ * latest decisive review of each of those reviewers was given on a commit other than `headSha`). Then it is the
118
+ * reviewers' turn, not the author's, like GitHub's re-request but read from what happened (an email member cannot
119
+ * request reviewers). Empty while one request was given on the current head, or a review does not say its commit.
120
+ * The merge gate is unchanged: the changes request still blocks until that reviewer approves.
121
+ */
122
+ export declare function lookAgainReviewers(reviews: ReviewLike[], headSha: string | null | undefined, author?: string): string[];
123
+ /**
124
+ * The approvals a pull request carries from the Bridge suggestion it applies (its body says "Closes #n"): the owners
125
+ * approved that exact change on the suggestion, so the pull request that applies it is not gated a second time for the
126
+ * same approval (docs/GOVERNANCE.md, Suggestions). Only when the suggestion is approved (every approval its tier needs,
127
+ * no changes requested) and the pull request's tier is not higher than the tier it was approved at. Changes requested
128
+ * on the pull request itself still block, and CI, conflicts and the ship right gate the merge as before.
129
+ */
130
+ export declare function withSuggestionApprovals(pr: ApprovalSummary, suggestion: {
131
+ approvals: ApprovalSummary;
132
+ tier: RiskTier;
133
+ } | null | undefined, prTier: RiskTier | null | undefined): ApprovalSummary;
134
+ /**
135
+ * The approvals a push from the Figma plugin carries (docs/BUILD-REQUESTS.md): a push counts as design-approved, since
136
+ * keeping unvalidated work out of Figma main is the team's discipline. Token values only (scope `token-values`) also
137
+ * count as tech-approved: they ship after the automatic checks. A rename or removal, code, or unread token changes still
138
+ * need the tech approval. A guardrail file (`outside`) gets no credit. Changes requested on the pull request, CI, a
139
+ * conflict and the ship right gate the merge as before.
140
+ */
141
+ export declare function withFigmaPushApprovals(pr: ApprovalSummary, push: FigmaPush | null | undefined): ApprovalSummary;
142
+ /**
143
+ * Decision TV-1 (docs/BUILD-REQUESTS.md, "Token values from Studio"): a token value change follows the rule of a push
144
+ * from Figma, whoever opened it (Studio, the CLI, the MCP server, an agent). Once the design approval is given, it
145
+ * ships after the automatic checks with no developer code OK: the tech approval counts as given. A rename or removal,
146
+ * unread token changes, code or a guardrail file is not a value change (isTokenValueChange) and still needs it. Changes
147
+ * requested, CI, a conflict and the ship right gate the merge as before.
148
+ */
149
+ export declare function withTokenValueApprovals(pr: ApprovalSummary, valuesOnly: boolean | null | undefined): ApprovalSummary;
78
150
  export type MissingApproval = 'design approval' | 'tech approval' | 'approval';
79
151
  export interface MergeGate {
80
152
  allowed: boolean;
@@ -88,6 +160,21 @@ export interface MergeGate {
88
160
  }
89
161
  /** Whether `login` may use the owner override. Case-insensitive, a leading @ is ignored. */
90
162
  export declare function canOverride(login: string | null | undefined, policy: OverridePolicy | null | undefined): boolean;
163
+ /**
164
+ * Who may use the owner override: the logins `governance.override.allowed` names that are also owners (design or
165
+ * tech lists, teams expanded). docs/GOVERNANCE.md: "A listed owner may merge past missing approvals only". Settings,
166
+ * the Home and the branch page read this one list, so the override is never offered to someone who is not an owner.
167
+ * When an owner entry is a team whose members are not known (a "/" entry), the allowed list stands as it is.
168
+ */
169
+ export declare function overrideHolders(policy: OverridePolicy | null | undefined, owners: {
170
+ design: string[];
171
+ tech: string[];
172
+ }): string[];
173
+ /** The override policy limited to its holders (overrideHolders): what the gate and the who-acts rules read. */
174
+ export declare const ownerOverridePolicy: (policy: OverridePolicy | null | undefined, owners: {
175
+ design: string[];
176
+ tech: string[];
177
+ }) => OverridePolicy | null;
91
178
  /**
92
179
  * Whether the Merge button is enabled, and why not. Mirrors docs/GOVERNANCE.md.
93
180
  * The owner override only ever covers missing approvals: CI failing or running, changes requested, a draft,
@@ -110,8 +197,96 @@ export declare function mergeGate(input: {
110
197
  ship?: ShipPolicy | null;
111
198
  }): MergeGate;
112
199
  /** The gate reason of a conflict, in the words a designer reads. Detected by identity, never by regex. */
200
+ /**
201
+ * The verdict on one pull request, assembled once for every surface: Studio's branch page and row status, the MCP
202
+ * tools (bridge_branch_status, bridge_merge_branch, bridge_what_needs_me) and the CLI (`bridge branch status`,
203
+ * `bridge branch merge`). One composition of the rules of docs/GOVERNANCE.md, so a branch never reads ready in one
204
+ * place and blocked in another:
205
+ * - the pull request that applies an approved suggestion ("Closes #n") carries its approvals (withSuggestionApprovals);
206
+ * - the owner override is limited to the owners `governance.override.allowed` names (ownerOverridePolicy);
207
+ * - Tier 1 needs one owner approval (hasOwnerApproval, inside mergeGate and reviewStatusKind).
208
+ * `gate` is null while the tests or the reviews could not be read: an unknown read never reads as mergeable.
209
+ */
210
+ export interface BranchVerdictInput {
211
+ tier: RiskTier | null;
212
+ ci: CiSummary | null;
213
+ /** The pull request's own approvals (summarizeApprovals); null when its reviews could not be read. */
214
+ approvals: ApprovalSummary | null;
215
+ /** The approved Bridge suggestion the pull request applies (approvedSuggestionOnGitHub), or null. */
216
+ suggestion?: {
217
+ approvals: ApprovalSummary;
218
+ tier: RiskTier;
219
+ } | null | undefined;
220
+ /** The approver lists (approverLists: rights applied, teams expanded). */
221
+ owners: {
222
+ design: string[];
223
+ tech: string[];
224
+ };
225
+ /** `governance.override` as configured; the verdict limits it to its owner holders. */
226
+ override?: OverridePolicy | null | undefined;
227
+ draft?: boolean | undefined;
228
+ /** The pull request's labels: `drafted-by-claude` reads a draft as ready for review (isDraft). */
229
+ labels?: PullStateLike['labels'];
230
+ mergeable?: boolean | null | undefined;
231
+ state?: string | undefined;
232
+ merged_at?: string | null | undefined;
233
+ /** Login of the viewer; null or absent leaves out who may ship and the override (signed out, no token). */
234
+ viewer?: string | null | undefined;
235
+ exploration?: boolean | undefined;
236
+ ship?: ShipPolicy | null | undefined;
237
+ /** Pushed from the Figma plugin (figmaPushOf): design approved by the push, tech too for token values only. */
238
+ figmaPush?: FigmaPush | null | undefined;
239
+ /**
240
+ * The pull request only changes token values (isTokenValueChange over its real files and token changes), whoever
241
+ * opened it: the design approval is all it needs (withTokenValueApprovals, decision TV-1). Absent: not known, the
242
+ * tech approval is still asked.
243
+ */
244
+ tokenValues?: boolean | null | undefined;
245
+ /**
246
+ * Auto-merge is armed on the pull request (GitHub `auto_merge` set): Bridge armed a token value change once its design
247
+ * approval was in (/api/ship, decision TV-1). With `tokenValues` and the design approval, it reads `autoShip`.
248
+ */
249
+ autoMerge?: boolean | null | undefined;
250
+ /**
251
+ * A lead published this Tier 1 change without review (the app's review starts with TIER1_PUBLISH_HEADING, on the
252
+ * head commit; docs/GOVERNANCE.md "Tier 1 publish by leads"). With `autoMerge` and the checks not failing, it reads
253
+ * `autoShip`. A failing check drops it back to review: the status reads as any other change waiting for an approval.
254
+ */
255
+ tier1Published?: boolean | null | undefined;
256
+ /**
257
+ * The reviewers to look again (lookAgainReviewers over the reviews and the head sha): the author updated the branch
258
+ * after every changes request. The status stays `changes-requested` and the merge stays blocked; only its words say
259
+ * "Updated, look again".
260
+ */
261
+ lookAgain?: readonly string[] | null | undefined;
262
+ }
263
+ export interface BranchVerdict {
264
+ /** The approvals the gate counts: the pull request's own plus the ones carried from its approved suggestion. */
265
+ approvals: ApprovalSummary | null;
266
+ /** The owner override as the gate reads it: its holders only. Null when none is configured. */
267
+ override: OverridePolicy | null;
268
+ gate: MergeGate | null;
269
+ status: ReviewStatusKind;
270
+ /** The words of the status, the same in Studio, the MCP server and the CLI (reviewStatusLabel). */
271
+ label: string;
272
+ /** Reviewers to look again (the input's lookAgain while changes are requested), else empty. */
273
+ lookAgain: string[];
274
+ /**
275
+ * A token push from Figma with values only, still open: Bridge merges it once the automatic checks pass, nobody
276
+ * reviews it. Also a token value change whose design approval is in and whose merge Bridge armed (`autoMerge`,
277
+ * decision TV-1). Review queues and "Needs you" leave it out; Studio shows "On its way to the library".
278
+ */
279
+ autoShip: boolean;
280
+ }
281
+ export declare function branchVerdict(input: BranchVerdictInput): BranchVerdict;
113
282
  export declare const CONFLICT_REASON = "It conflicts with main: resolve it with Update from main on the branch.";
114
283
  export declare const OVERRIDE_HEADING = "**Bridge owner override**";
284
+ /**
285
+ * The shortest override reason accepted. docs/GOVERNANCE.md: the reason is always required, at least
286
+ * `minReasonLength` characters (20 by default). `requireReason: false` no longer waives it: an override without a
287
+ * reason would leave the audit trail empty.
288
+ */
289
+ export declare function overrideReasonMin(policy: Pick<OverridePolicy, 'minReasonLength'>): number;
115
290
  /** Problem with an override reason, or null when it is accepted. */
116
291
  export declare function overrideReasonProblem(reason: string, policy: OverridePolicy): string | null;
117
292
  /**
@@ -163,6 +338,7 @@ export declare const REVIEW_STATUS_ORDER: Record<ReviewStatusKind, number>;
163
338
  export declare function reviewStatusKind(input: {
164
339
  state?: string;
165
340
  draft?: boolean;
341
+ labels?: PullStateLike['labels'];
166
342
  merged_at?: string | null;
167
343
  ci: CiSummary | null;
168
344
  approvals: ApprovalSummary | null;
@@ -174,6 +350,14 @@ export declare function reviewStatusKind(input: {
174
350
  * working out whether an approved branch merges cleanly; it never stands for missing tests.
175
351
  */
176
352
  export declare const REVIEW_STATUS_LABEL: Record<ReviewStatusKind, string>;
353
+ /** The words of a branch the author updated after every changes request: the reviewers who asked look again. */
354
+ export declare const LOOK_AGAIN_STATUS_LABEL = "Updated, look again";
355
+ /**
356
+ * The words of a status. Changes requested on a branch the author updated since (lookAgainReviewers not empty) reads
357
+ * "Updated, look again", never the stale "Changes requested": the merge gate is the same, the next step is the
358
+ * reviewers'.
359
+ */
360
+ export declare function reviewStatusLabel(kind: ReviewStatusKind, lookAgain?: readonly string[] | null): string;
177
361
  /** The words of a CI summary. A repo without automated checks reads as that, calmly, never as pending or unknown. */
178
362
  export declare const CI_STATE_LABEL: Record<CiState, string>;
179
363
  export interface QueuePull extends PullStateLike {
@@ -233,8 +417,11 @@ export declare function reopenEligibility(x: {
233
417
  branch: 'exists' | 'gone' | 'unknown';
234
418
  }): ReopenEligibility;
235
419
  export type ApprovalRole = 'design' | 'tech' | 'approval';
236
- /** The approvals a tier needs, as mergeGate counts them (Tier 2 and 3: design and tech; Tier 1: one approval). */
237
- export declare function requiredApprovals(tier: RiskTier | null): ApprovalRole[];
420
+ /**
421
+ * The approvals a tier needs, as mergeGate counts them (Tier 2 and 3: design and tech; Tier 1: one approval). A token
422
+ * value change (`valuesOnly`, withTokenValueApprovals) needs the design approval only.
423
+ */
424
+ export declare function requiredApprovals(tier: RiskTier | null, valuesOnly?: boolean | null): ApprovalRole[];
238
425
  /** The approvals still missing on a branch, from its tier and the reviews read (empty when either is unknown). */
239
426
  export declare function pendingApprovals(tier: RiskTier | null | undefined, approvals: ApprovalSummary | null | undefined): ApprovalRole[];
240
427
  /**
package/dist/risk.d.ts CHANGED
@@ -32,6 +32,18 @@ export interface RiskResult {
32
32
  bump?: SemverBump | undefined;
33
33
  }
34
34
  export declare const RISK_LABELS: Record<RiskTier, string>;
35
+ /**
36
+ * The tier line of a token change pushed from the Figma plugin (docs/BUILD-REQUESTS.md): a push is design-approved, so
37
+ * the approvals part of RISK_LABELS would mislead. Token values ship once the checks pass; a rename or a removal waits for
38
+ * a developer OK. Keeps "Tier N" first so parseTier reads it. Same rule as branchVerdict `autoShip`.
39
+ */
40
+ export declare function figmaPushTierLabel(tier: RiskTier, changes: readonly {
41
+ kind: TokenChangeKind | string;
42
+ }[]): string;
43
+ /** The sentence after it: what happens next to a token change pushed from Figma. */
44
+ export declare const figmaPushNextLine: (changes: readonly {
45
+ kind: TokenChangeKind | string;
46
+ }[]) => string;
35
47
  /** Kind and description-only flag of any token change. */
36
48
  export declare function summarizeTokenChange(c: AcceptedChange | TokenChangeSummary): TokenChangeSummary;
37
49
  /** Semver bump of a set of token changes: renames and removals major, other changes minor, descriptions patch. */
@@ -1,5 +1,5 @@
1
1
  /** The dialog's explanation, in designer words. */
2
- export declare const SHARE_EXPLAIN = "Product teams can install this branch in their app before it is merged, and remove it any time.";
2
+ export declare const SHARE_EXPLAIN = "Product teams can install this change in their app before it is in the library, and remove it any time.";
3
3
  export type ChangeLifecycle = 'open' | 'draft' | 'merged' | 'closed';
4
4
  type Source = 'pkg.pr.new' | 'release' | 'other';
5
5
  export type ShareState =
@@ -1,6 +1,8 @@
1
1
  import type { AuditMetrics, AuditReport } from './audit/types';
2
+ import type { ChangelogReleaseRow } from './changelog-model';
2
3
  import type { ContrastPairResult } from './contrast-model';
3
4
  import type { SpecCoverage } from './component-spec';
5
+ import { type MotionDocs } from './motion-docs';
4
6
  import { type RepoSource } from './repo-source';
5
7
  import type { RepoModel } from './repo-model';
6
8
  export declare const SNAPSHOT_SCHEMA_VERSION = 1;
@@ -10,6 +12,17 @@ export declare const SNAPSHOT_FILE = "snapshot.json";
10
12
  export declare const SNAPSHOT_AUDITS_DIR = "audits";
11
13
  /** Above this, a snapshot is refused (a published file the browser downloads whole). */
12
14
  export declare const SNAPSHOT_MAX_BYTES = 5000000;
15
+ /** Releases a snapshot keeps, newest first (the pending one counts); older ones stay in the CHANGELOG.md files. */
16
+ export declare const SNAPSHOT_MAX_RELEASES = 60;
17
+ /** A workspace package of the repository, for the Library's Install page. */
18
+ export interface SnapshotPackage {
19
+ name: string;
20
+ version: string;
21
+ /** Not published to npm. */
22
+ private: boolean;
23
+ /** Repo-relative folder ("." for the root package). */
24
+ dir: string;
25
+ }
13
26
  /** One adoption audit: the summary lives in snapshot.json, the full report in audits/<id>.json next to it. */
14
27
  export interface SnapshotAudit {
15
28
  id: string;
@@ -85,6 +98,19 @@ export interface RepoSnapshot {
85
98
  * written before Bridge read global stylesheets: a reader then reads those files from the repository.
86
99
  */
87
100
  styles?: string[];
101
+ /**
102
+ * Releases from the packages' CHANGELOG.md files and the pending changesets, newest first, at most
103
+ * SNAPSHOT_MAX_RELEASES. Dates come from release tags or git history, absent when the clone has neither. Absent from
104
+ * a snapshot written before Bridge read releases, or when the repository has no changelog.
105
+ */
106
+ releases?: ChangelogReleaseRow[];
107
+ /** Workspace packages (name, version, private, folder). Absent from a snapshot written before Bridge read them. */
108
+ packages?: SnapshotPackage[];
109
+ /**
110
+ * Motion and interaction docs per component (motionDocs), keyed by component name, computed from the specs file the
111
+ * snapshot does not carry. Absent without a readable specs file or a component scan.
112
+ */
113
+ motion?: Record<string, MotionDocs>;
88
114
  }
89
115
  export declare class SnapshotError extends Error {
90
116
  }
@@ -120,7 +146,15 @@ export interface BuildSnapshotInput {
120
146
  committedAt?: string;
121
147
  generatedAt?: string;
122
148
  audits?: SnapshotAudit[];
149
+ /** collectReleases() of the repository; kept to the newest SNAPSHOT_MAX_RELEASES. */
150
+ releases?: ChangelogReleaseRow[];
151
+ packages?: SnapshotPackage[];
123
152
  }
153
+ /**
154
+ * Motion docs of every scanned component, from the parsed specs file: what the Library's motion section shows. Null
155
+ * without a specs file or a component scan.
156
+ */
157
+ export declare function snapshotMotion(model: Pick<RepoModel, 'components' | 'specs'>): Record<string, MotionDocs> | null;
124
158
  /** Counts of a model for the snapshot summary. */
125
159
  export declare function snapshotSummary(model: RepoModel): SnapshotSummary;
126
160
  /** Assembles the snapshot from a model and the files it was read from. The specs file becomes the coverage list. */
@@ -1,5 +1,5 @@
1
- import type { Owners } from './config';
2
- import { type ApprovalRole, type ApprovalSummary } from './review-gate';
1
+ import type { OverridePolicy, Owners } from './config';
2
+ import { type ApprovalRole, type ApprovalSummary, type MissingApproval } from './review-gate';
3
3
  import { type Rights } from './rights';
4
4
  import type { RiskTier } from './risk';
5
5
  import type { TeamStates } from './teams';
@@ -51,13 +51,54 @@ export interface SuggestionGovernance {
51
51
  teams?: TeamStates | null | undefined;
52
52
  /** `governance.soloOwnerCanApproveBoth`: the only owner's approval covers design and tech (review-gate summarizeApprovals). */
53
53
  soloOwnerCanApproveBoth?: boolean | undefined;
54
+ /**
55
+ * `governance.override`: an owner it allows, whom nobody else can review, approves their own suggestion as owner with
56
+ * a written reason (suggestionOverrideOf, docs/GOVERNANCE.md "Owner override"). Absent: no override.
57
+ */
58
+ override?: OverridePolicy | null | undefined;
54
59
  }
55
60
  export interface SuggestionApprovals extends ApprovalSummary {
56
61
  /** People whose latest review is on an older payload: it no longer counts. */
57
62
  stale: string[];
58
63
  /** People whose review does not count because they are not owners (or not collaborators when no owner is set). */
59
64
  ignored: string[];
65
+ /** Approved with the owner override on this version (suggestionOverrideOf): who, why, and what it went without. */
66
+ override?: SuggestionOverrideRecord | undefined;
60
67
  }
68
+ export declare const SUGGESTION_OVERRIDE_MARK = "bridge-suggestion-override";
69
+ export interface SuggestionOverrideRecord {
70
+ login: string;
71
+ reason: string;
72
+ missing: string[];
73
+ /** The version it was approved on (suggestionPayloadSha). */
74
+ payloadSha: string;
75
+ }
76
+ /** The override comment: the PR override's audit line ("Approved as owner from Bridge."), the version, the record. */
77
+ export declare function composeSuggestionOverrideComment(r: {
78
+ login: string;
79
+ reason: string;
80
+ missing: MissingApproval[];
81
+ payloadSha: string;
82
+ }): string;
83
+ /** Reads an override comment on a suggestion back; null for any other comment. */
84
+ export declare function parseSuggestionOverrideComment(body: string | null | undefined): SuggestionOverrideRecord | null;
85
+ /**
86
+ * Who may approve this suggestion as owner, and why not: an owner `governance.override` allows (overrideHolders), on
87
+ * their own suggestion, when nobody else in the approver lists can review it. Null when they may.
88
+ */
89
+ export declare function suggestionOverrideProblem(x: {
90
+ viewer: string | null | undefined;
91
+ author: string;
92
+ gov: SuggestionGovernance;
93
+ }): string | null;
94
+ /**
95
+ * The owner override in force on this version: the latest override comment for `payloadSha`, posted by the person it
96
+ * names, who may use it (suggestionOverrideProblem) with a reason long enough. Null otherwise.
97
+ */
98
+ export declare function suggestionOverrideOf(comments: readonly SuggestionCommentLike[], gov: SuggestionGovernance, opts: {
99
+ payloadSha: string;
100
+ author?: string | null | undefined;
101
+ }): SuggestionOverrideRecord | null;
61
102
  /**
62
103
  * Approvals on a suggestion, as summarizeApprovals counts them on a pull request, from the review comments: only
63
104
  * comments by people in the approver lists (governance owners and rights) count; with no owner configured at all, only
@@ -68,6 +109,8 @@ export interface SuggestionApprovals extends ApprovalSummary {
68
109
  export declare function summarizeSuggestionApprovals(comments: readonly SuggestionCommentLike[], gov: SuggestionGovernance, opts: {
69
110
  payloadSha: string;
70
111
  author?: string | null | undefined;
112
+ /** A token value suggestion (suggestionIsTokenValues): the design approval is all it needs (decision TV-1). */
113
+ valuesOnly?: boolean | undefined;
71
114
  }): SuggestionApprovals;
72
115
  /** The review state for the status machine: changes requested beats approved; approved when no approval is missing. */
73
116
  export declare function suggestionReviewState(tier: RiskTier, approvals: ApprovalSummary): 'needs-review' | 'changes-requested' | 'approved';
@@ -1,7 +1,8 @@
1
1
  import { GitHubClient, type IssueSummary, type PullRequestSummary } from './github-client';
2
+ import { type Requester } from './requester';
2
3
  import { type SuggestionAppliedRecord, type SuggestionApprovals, type SuggestionCommentLike, type SuggestionGovernance, type SuggestionReviewEvent } from './suggestion-review';
3
4
  import { type ComposeSuggestionOptions, type ParsedSuggestion, type SuggestionKind, type SuggestionPayloadV1, type SuggestionStatus } from './suggestions';
4
- import type { ApprovalRole } from './review-gate';
5
+ import { type ApprovalRole } from './review-gate';
5
6
  import { type BridgeConfig } from './config';
6
7
  import { type SuggestionApplyOp, type SuggestionApplyPlan, type SuggestionWorkspace } from './suggestion-apply';
7
8
  export interface CreatedSuggestion {
@@ -32,6 +33,11 @@ export interface SuggestionListItem {
32
33
  title: string;
33
34
  url: string;
34
35
  author: string;
36
+ /**
37
+ * Who asked for it, as Bridge records it in the body ("Requested by Ann Leroy."): the person to show, since the
38
+ * GitHub author is often a lead or an agent acting for them. Null when the body names nobody.
39
+ */
40
+ requester: Requester | null;
35
41
  createdAt: string;
36
42
  state: 'open' | 'closed';
37
43
  stateReason: string | null;
@@ -86,6 +92,21 @@ export declare function getSuggestionOnGitHub(gh: GitHubClient, n: number, gov:
86
92
  pulls?: PullRequestSummary[] | undefined;
87
93
  outdated?: boolean | undefined;
88
94
  }): Promise<SuggestionDetail>;
95
+ /** An approved Bridge suggestion a pull request applies: its number, approvals and the tier they were given at. */
96
+ export interface ApprovedSuggestion {
97
+ number: number;
98
+ approvals: SuggestionApprovals;
99
+ tier: 1 | 2 | 3;
100
+ }
101
+ /**
102
+ * The approved Bridge suggestion a pull request applies, from the issues its body closes ("Closes #n", the first three):
103
+ * its approvals and the tier they were given at, for branchVerdict (withSuggestionApprovals). Null when it closes
104
+ * none, or none is an approved suggestion. Studio, the MCP server and the CLI read it through this one call. A read
105
+ * that fails counts as no suggestion: the pull request then needs its own approvals, never fewer.
106
+ */
107
+ export declare function approvedSuggestionOnGitHub(gh: GitHubClient, body: string | null | undefined, gov: SuggestionGovernance): Promise<ApprovedSuggestion | null>;
108
+ /** One issue: the approved suggestion it holds, or null (not a suggestion, not approved). Throws when it cannot be read. */
109
+ export declare function approvedSuggestionNumber(gh: GitHubClient, n: number, gov: SuggestionGovernance): Promise<ApprovedSuggestion | null>;
89
110
  /**
90
111
  * Keeps exactly one status label on the issue, the one the status calls for (suggestionLabelChanges): an open state
91
112
  * label while open, the final label ("suggestion: applied", "suggestion: declined") once closed, the old ones removed.
@@ -153,6 +174,17 @@ export declare function reviewSuggestionOnGitHub(gh: GitHubClient, n: number, in
153
174
  by?: string | undefined;
154
175
  at?: Date | undefined;
155
176
  }, gov: SuggestionGovernance): Promise<SuggestionReviewResult>;
177
+ /**
178
+ * The owner override on a suggestion (docs/GOVERNANCE.md "Owner override"): the owner it allows, whom nobody else can
179
+ * review, approves their own suggestion as owner with a written reason. Checks the rule (suggestionOverrideProblem),
180
+ * the reason (overrideReasonProblem), the version, then posts the audit comment and re-syncs the state label. A
181
+ * developer then applies it as any approved suggestion; the pull request carries the approvals.
182
+ */
183
+ export declare function overrideSuggestionOnGitHub(gh: GitHubClient, n: number, input: {
184
+ reason: string;
185
+ payloadSha?: string | undefined;
186
+ by?: string | undefined;
187
+ }, gov: SuggestionGovernance): Promise<SuggestionReviewResult>;
156
188
  /**
157
189
  * Records that a suggestion is being applied (the applied marker comment) and sets its status label: "suggestion:
158
190
  * applying" while the pull request is open, "suggestion: applied" (the old labels removed) once it merged or the issue
@@ -173,6 +173,12 @@ export interface SuggestionStatusInput {
173
173
  /** The dry-run apply found a conflict with the current default branch. */
174
174
  outdated?: boolean | undefined;
175
175
  }
176
+ /**
177
+ * Decision TV-1 (docs/BUILD-REQUESTS.md, "Token values from Studio"): a token suggestion that only changes values or
178
+ * adds tokens (every edit a `set` or an `add`, no file beside its changeset) needs the design approval only, the same
179
+ * rule as a pull request (isTokenValueChange). A rename, a removal or any other kind still needs the tech approval.
180
+ */
181
+ export declare function suggestionIsTokenValues(p: Pick<SuggestionPayloadV1, 'kind' | 'tokens' | 'files' | 'docs' | 'componentSpec'>): boolean;
176
182
  /**
177
183
  * Where a suggestion stands. Closed: applied when a linked pull request merged or the issue was closed as completed,
178
184
  * declined otherwise. Open: applying when a linked pull request is open or an applied marker exists; outdated when the
@@ -0,0 +1,48 @@
1
+ import type { Owners } from './config';
2
+ import type { RiskTier } from './risk';
3
+ /** The setting, absent counting as on. */
4
+ export declare const leadsPublishTier1On: (gov: {
5
+ leadsPublishTier1?: boolean | undefined;
6
+ } | null | undefined) => boolean;
7
+ /** Which reviewers lead a Tier 1 change: tech when it touches stories or tests (code-only Tier 1), else design. */
8
+ export type Tier1Domain = 'design' | 'tech';
9
+ export declare function tier1Domain(paths: readonly string[]): Tier1Domain;
10
+ export interface Tier1PublishInput {
11
+ /** Login of the person publishing (the pull request's author). */
12
+ viewer: string | null | undefined;
13
+ /** classifyRisk over the change. Only Tier 1 publishes without review. */
14
+ tier: RiskTier | null | undefined;
15
+ domain: Tier1Domain;
16
+ /** The approver lists (approverLists: rights applied, teams expanded). */
17
+ approvers: Owners;
18
+ /** The viewer is an admin of the team in Bridge. */
19
+ admin: boolean;
20
+ /** governance.leadsPublishTier1 (leadsPublishTier1On). */
21
+ enabled: boolean;
22
+ }
23
+ /** Whether this person may publish this change without review. */
24
+ export declare function canPublishTier1(x: Tier1PublishInput): boolean;
25
+ /** The button that replaces "Send for review" for someone who may publish. */
26
+ export declare const PUBLISH_LABEL = "Publish";
27
+ /** The first line of the app's review on the pull request: how History and the readers recognise it. */
28
+ export declare const TIER1_PUBLISH_HEADING = "**Bridge: published without review (Tier 1)**";
29
+ /** The record, on the pull request and in History. */
30
+ export declare const publishedWithoutReview: (name: string) => string;
31
+ /** The app's review body: the heading, then the record and why it needed no review. */
32
+ export declare function tier1PublishReviewBody(name: string, login: string): string;
33
+ /** Who published it, read back from the app's review body, or null when the body is not one. */
34
+ export declare function tier1PublishedBy(body: string | null | undefined): string | null;
35
+ /**
36
+ * Who published this pull request without review, read from its reviews: the app's approving review with the heading,
37
+ * on the head commit when the head is known (an older one was dismissed by the push after it). Null when none.
38
+ */
39
+ export declare function tier1PublishOf(reviews: readonly {
40
+ state: string;
41
+ body?: string | null | undefined;
42
+ commit_id?: string | null | undefined;
43
+ }[] | null | undefined, headSha?: string | null | undefined): string | null;
44
+ /**
45
+ * Who may publish without review, in one sentence, for the CLI, the MCP server and Settings. Admins are a Bridge
46
+ * team role, so the sentence names the role, not people.
47
+ */
48
+ export declare function tier1PublishSentence(enabled: boolean): string;
@@ -52,6 +52,8 @@ export declare function planTokenProposal(input: {
52
52
  tool?: string | undefined;
53
53
  /** More body paragraphs, after the generated body (the product line, "Needed by"). */
54
54
  bodyExtra?: string[] | undefined;
55
+ /** Pushed from the Figma plugin: the body says design-approved and what happens next (docs/BUILD-REQUESTS.md). */
56
+ fromFigma?: boolean | undefined;
55
57
  }): TokenProposalPlan | null;
56
58
  export interface OpenedTokenProposal {
57
59
  branch: string;
@@ -74,6 +76,11 @@ export declare function writeTokenProposalOnGitHub(gh: GitHubClient, plan: Token
74
76
  labelsAfter?: string[] | undefined;
75
77
  /** Who proposes it, in the commit message: "Bridge MCP (source: mcp)". */
76
78
  tool?: string | undefined;
79
+ /**
80
+ * Open it as a draft or not. Default: a draft from tier 2, until design review. A token push from Figma with values
81
+ * only opens ready (false): it ships after the automatic checks (docs/BUILD-REQUESTS.md).
82
+ */
83
+ draft?: boolean | undefined;
77
84
  }): Promise<OpenedTokenProposal>;
78
85
  export interface SuggestedTokenProposal {
79
86
  status: 'suggested';
@@ -17,3 +17,12 @@ export declare function productPreview(apps: PreviewApps, ctx: {
17
17
  * Only http(s) previews: anything else (a javascript: or data: URL in a hostile comment) gives ''.
18
18
  */
19
19
  export declare function tryInProductUrl(preview: string, pr: number, screen: ProductScreen | null): string;
20
+ /**
21
+ * True when nothing reported an online preview of the branch (no Vercel status, deployment or comment, only config
22
+ * templates): explorations and library branches are not deployed, a developer tries them on their machine.
23
+ */
24
+ export declare function noOnlinePreview(previews: readonly {
25
+ source: string | null;
26
+ }[]): boolean;
27
+ /** The command that puts a branch on a developer's machine, quoted when the name has shell characters. */
28
+ export declare function localTryCommand(branch: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge-ds/core",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Bridge core: bridge.config.json, DTCG token loading (tiers and layered modes), alias resolution, WCAG contrast, diffs, minimal-diff edits, PR body, the shared risk tier classifier, changesets and a fetch-based GitHub client. No Figma API, no DOM; Node-only helpers (git, reading a product folder) under @bridge-ds/core/node; the adoption audit under @bridge-ds/core/audit.",
6
6
  "license": "MIT",