@usefragments/core 1.10.2 → 2.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.
Files changed (130) hide show
  1. package/dist/{chunk-MZ4SW3TP.js → chunk-3IOWHECM.js} +8 -1
  2. package/dist/{chunk-MZ4SW3TP.js.map → chunk-3IOWHECM.js.map} +1 -1
  3. package/dist/chunk-7ULGH74M.js +66 -0
  4. package/dist/chunk-7ULGH74M.js.map +1 -0
  5. package/dist/{chunk-XN3LSDPY.js → chunk-BMPYIUZE.js} +612 -344
  6. package/dist/chunk-BMPYIUZE.js.map +1 -0
  7. package/dist/{chunk-YF65VYRY.js → chunk-ML5S6QNU.js} +191 -169
  8. package/dist/chunk-ML5S6QNU.js.map +1 -0
  9. package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
  10. package/dist/chunk-MZ2FS7U4.js.map +1 -0
  11. package/dist/chunk-PWIJMOI4.js +202 -0
  12. package/dist/chunk-PWIJMOI4.js.map +1 -0
  13. package/dist/chunk-RYFULE43.js +578 -0
  14. package/dist/chunk-RYFULE43.js.map +1 -0
  15. package/dist/codes/index.d.ts +2 -2
  16. package/dist/codes/index.js +3 -2
  17. package/dist/compiled-types/index.d.ts +327 -2
  18. package/dist/compiled-types/index.js +1 -1
  19. package/dist/generate/index.d.ts +3 -2
  20. package/dist/{governance-CLk_wkP9.d.ts → governance-hOPXGbbs.d.ts} +474 -515
  21. package/dist/governance-telemetry.d.ts +6 -0
  22. package/dist/governance-telemetry.js +1 -1
  23. package/dist/{index-_sxhUNqx.d.ts → index-C8bcXVav.d.ts} +448 -448
  24. package/dist/index.d.ts +3280 -1305
  25. package/dist/index.js +2414 -350
  26. package/dist/index.js.map +1 -1
  27. package/dist/manifest.d.ts +228 -0
  28. package/dist/manifest.js +24 -0
  29. package/dist/manifest.js.map +1 -0
  30. package/dist/preview/index.js +45 -1
  31. package/dist/preview/index.js.map +1 -1
  32. package/dist/preview-runtime.d.ts +1 -2
  33. package/dist/preview-runtime.js +150 -14
  34. package/dist/preview-runtime.js.map +1 -1
  35. package/dist/react-types.d.ts +1 -2
  36. package/dist/registry.d.ts +1412 -203
  37. package/dist/registry.js +30 -3
  38. package/dist/schemas/index.d.ts +1 -1
  39. package/dist/schemas/index.js +3 -2
  40. package/dist/storyAdapter.d.ts +1 -2
  41. package/dist/storyAdapter.js +11 -49
  42. package/dist/storyAdapter.js.map +1 -1
  43. package/dist/test-utils.d.ts +3 -2
  44. package/dist/topology/index.d.ts +1 -1
  45. package/dist/topology/index.js +1 -1
  46. package/package.json +8 -2
  47. package/src/__tests__/contract-parser.test.ts +318 -277
  48. package/src/__tests__/preview-runtime-hook.test.tsx +315 -0
  49. package/src/__tests__/preview-runtime.test.tsx +30 -8
  50. package/src/__tests__/schema.test.ts +191 -14
  51. package/src/analysis-plan/analysis-plan-v1.test.ts +320 -0
  52. package/src/analysis-plan/coverage.ts +181 -0
  53. package/src/analysis-plan/digest.ts +141 -0
  54. package/src/analysis-plan/index.ts +34 -0
  55. package/src/analysis-plan/types.ts +207 -0
  56. package/src/approved-contract-tokens.test.ts +39 -0
  57. package/src/approved-contract-tokens.ts +18 -0
  58. package/src/codes/__tests__/codes.test.ts +13 -0
  59. package/src/codes/codes.ts +40 -0
  60. package/src/compiled-types/index.ts +640 -39
  61. package/src/compiled-types/parse.test.ts +145 -4
  62. package/src/component-contract.ts +95 -53
  63. package/src/composition.ts +7 -13
  64. package/src/constants.ts +3 -6
  65. package/src/contract/hash.test.ts +20 -0
  66. package/src/contract/hash.ts +66 -9
  67. package/src/contract/index.ts +24 -1
  68. package/src/contract/manifest.test.ts +94 -0
  69. package/src/contract/manifest.ts +68 -0
  70. package/src/contract/preimage.test.ts +219 -1
  71. package/src/contract/preimage.ts +326 -6
  72. package/src/contract/stamp.test.ts +3 -0
  73. package/src/contract/stamp.ts +1 -1
  74. package/src/contract-parser.ts +54 -30
  75. package/src/defineFragment.test.ts +476 -91
  76. package/src/defineFragment.ts +204 -114
  77. package/src/domain-ids.test.ts +35 -0
  78. package/src/domain-ids.ts +61 -0
  79. package/src/evaluation/evaluate.test.ts +522 -0
  80. package/src/evaluation/evaluate.ts +690 -0
  81. package/src/evaluation/evaluation-v2-receipt-v1.test.ts +772 -0
  82. package/src/evaluation/index.ts +58 -0
  83. package/src/evaluation/receipt.ts +753 -0
  84. package/src/evaluation/types.ts +406 -0
  85. package/src/facts/builders.ts +2 -0
  86. package/src/facts/compile.ts +29 -6
  87. package/src/facts/fact-index.ts +13 -3
  88. package/src/facts/fact-integrity-v1.test.ts +172 -0
  89. package/src/facts/facts.test.ts +15 -0
  90. package/src/facts/ids.ts +46 -3
  91. package/src/facts/index.ts +14 -1
  92. package/src/facts/integrity.ts +134 -0
  93. package/src/facts/types.ts +36 -0
  94. package/src/governance-integrity.test.ts +1 -0
  95. package/src/governance-integrity.ts +5 -3
  96. package/src/governance-telemetry.ts +8 -0
  97. package/src/governance.ts +70 -8
  98. package/src/index.ts +230 -37
  99. package/src/preview/validation.test.ts +62 -0
  100. package/src/preview/validation.ts +48 -2
  101. package/src/preview-runtime.tsx +227 -20
  102. package/src/registry-install-plan.ts +200 -109
  103. package/src/registry-shards.test.ts +263 -0
  104. package/src/registry.ts +237 -0
  105. package/src/repository-binding.test.ts +50 -0
  106. package/src/repository-binding.ts +96 -0
  107. package/src/rules/families.test.ts +36 -0
  108. package/src/rules/finding.ts +7 -2
  109. package/src/rules/index.ts +17 -1
  110. package/src/rules/rule-config.test.ts +66 -0
  111. package/src/rules/rule-config.ts +73 -0
  112. package/src/rules/rules.test.ts +26 -0
  113. package/src/rules/tokens-css-vars-must-be-defined.test.ts +51 -2
  114. package/src/rules/tokens-css-vars-must-be-defined.ts +34 -1
  115. package/src/schema.ts +293 -113
  116. package/src/schemas/index.ts +1 -1
  117. package/src/storyAdapter.test.ts +68 -12
  118. package/src/storyAdapter.ts +44 -75
  119. package/src/topology/resolve-area.ts +1 -1
  120. package/src/types.ts +258 -40
  121. package/dist/chunk-3LLRNCPX.js.map +0 -1
  122. package/dist/chunk-RANPUC6C.js +0 -72
  123. package/dist/chunk-RANPUC6C.js.map +0 -1
  124. package/dist/chunk-XN3LSDPY.js.map +0 -1
  125. package/dist/chunk-YF65VYRY.js.map +0 -1
  126. package/src/fragment-types.ts +0 -214
  127. package/src/react-create-element.test.ts +0 -22
  128. package/src/react-create-element.ts +0 -12
  129. package/src/storyFilters.test.ts +0 -350
  130. package/src/storyFilters.ts +0 -253
@@ -0,0 +1,522 @@
1
+ /**
2
+ * Kernel unit spec — behaviors outside the parity corpus's reach. The
3
+ * scenario-level semantics (all 44 authored verdicts) are asserted by
4
+ * `@repo/parity`'s kernel suite; this file pins the mechanics: determinism
5
+ * under input reordering, facts-mode evidence, eligibility edges, waiver
6
+ * expiry boundaries, and explicit policy overrides.
7
+ */
8
+ import { describe, expect, it } from "vitest";
9
+
10
+ import { CONTRACT_PREIMAGE_SCHEMA } from "../contract/preimage.js";
11
+ import {
12
+ FactIndex,
13
+ compileGlobalGovernanceFacts,
14
+ makeStyleDeclarationFact,
15
+ } from "../facts/index.js";
16
+ import { g } from "../governance.js";
17
+ import { contractHash } from "../contract/hash.js";
18
+ import type { ContractPreimage } from "../contract/preimage.js";
19
+ import { evaluate } from "./evaluate.js";
20
+ import type { EvaluationFindingInput, EvaluationInput } from "./types.js";
21
+
22
+ const PREIMAGE: ContractPreimage = {
23
+ schema: CONTRACT_PREIMAGE_SCHEMA,
24
+ domains: {
25
+ components: "a".repeat(64),
26
+ tokens: "b".repeat(64),
27
+ canonicalMap: "c".repeat(64),
28
+ policy: "d".repeat(64),
29
+ },
30
+ };
31
+ const FCID = contractHash(PREIMAGE);
32
+ const NOW = 1_756_400_000_000;
33
+
34
+ function baseInput(overrides: Partial<EvaluationInput> = {}): EvaluationInput {
35
+ return {
36
+ contract: { state: "pinned", fcid: FCID, preimage: PREIMAGE },
37
+ scope: { kind: "full", coverage: { filesScanned: 3 } },
38
+ evidence: { kind: "findings", findings: [] },
39
+ policy: { failOnWarnings: false },
40
+ integrity: { governance: "healthy" },
41
+ evaluatedAt: NOW,
42
+ ...overrides,
43
+ };
44
+ }
45
+
46
+ function styleError(fingerprint = "f-1"): EvaluationFindingInput {
47
+ return { fingerprint, ruleId: "styles/no-raw-color", severity: "error" };
48
+ }
49
+
50
+ describe("evaluate — mechanics", () => {
51
+ it("verifies the FCID by recomputation and reports a mismatch as integrity", () => {
52
+ const ok = evaluate(baseInput());
53
+ expect(ok.integrity.fcidVerified).toBe(true);
54
+ expect(ok.verdict).toBe("pass");
55
+
56
+ const mismatch = evaluate(
57
+ baseInput({ contract: { state: "pinned", fcid: "0".repeat(64), preimage: PREIMAGE } })
58
+ );
59
+ expect(mismatch.integrity.fcidVerified).toBe(false);
60
+ expect(mismatch.reasons).toContain("contractIdentityMismatch");
61
+ expect(mismatch.verdict).toBe("indeterminate");
62
+ });
63
+
64
+ it("marks identity unavailable (never a mismatch) when no preimage is supplied", () => {
65
+ const result = evaluate(baseInput({ contract: { state: "pinned", fcid: FCID } }));
66
+ expect(result.integrity.fcidVerified).toBe("unavailable");
67
+ expect(result.reasons).not.toContain("contractIdentityMismatch");
68
+ expect(result.verdict).toBe("pass");
69
+ });
70
+
71
+ it("is invariant under evidence and waiver ordering", () => {
72
+ const findings = [styleError("b"), styleError("a")];
73
+ const waivers = [{ fingerprint: "b" }, { fingerprint: "a" }];
74
+ const forward = evaluate(baseInput({ evidence: { kind: "findings", findings }, waivers }));
75
+ const reversed = evaluate(
76
+ baseInput({
77
+ evidence: { kind: "findings", findings: [...findings].reverse() },
78
+ waivers: [...waivers].reverse(),
79
+ })
80
+ );
81
+ expect(forward.inputDigest).toBe(reversed.inputDigest);
82
+ expect(forward.resultDigest).toBe(reversed.resultDigest);
83
+ expect(forward.findings).toEqual(reversed.findings);
84
+ });
85
+
86
+ it("digests a missing evidence grade identically to an explicit source_backed", () => {
87
+ const explicit = evaluate(
88
+ baseInput({
89
+ evidence: {
90
+ kind: "findings",
91
+ findings: [{ ...styleError(), evidenceGrade: "source_backed" }],
92
+ },
93
+ })
94
+ );
95
+ const omitted = evaluate(
96
+ baseInput({ evidence: { kind: "findings", findings: [styleError()] } })
97
+ );
98
+ expect(explicit.inputDigest).toBe(omitted.inputDigest);
99
+ expect(explicit.resultDigest).toBe(omitted.resultDigest);
100
+ });
101
+
102
+ it("digests same-fingerprint waivers order-invariantly across expiries", () => {
103
+ const waivers = [
104
+ { fingerprint: "w", expiresAt: NOW + 100 },
105
+ { fingerprint: "w", expiresAt: NOW + 200 },
106
+ ];
107
+ const forward = evaluate(baseInput({ waivers }));
108
+ const reversed = evaluate(baseInput({ waivers: [...waivers].reverse() }));
109
+ expect(forward.inputDigest).toBe(reversed.inputDigest);
110
+ expect(forward.resultDigest).toBe(reversed.resultDigest);
111
+ });
112
+
113
+ it("digests absent, Infinity, and NaN waiver expiries identically (all never expire)", () => {
114
+ const absent = evaluate(baseInput({ waivers: [{ fingerprint: "w" }] }));
115
+ const infinite = evaluate(
116
+ baseInput({ waivers: [{ fingerprint: "w", expiresAt: Number.POSITIVE_INFINITY }] })
117
+ );
118
+ const nan = evaluate(baseInput({ waivers: [{ fingerprint: "w", expiresAt: Number.NaN }] }));
119
+ expect(infinite.inputDigest).toBe(absent.inputDigest);
120
+ expect(nan.inputDigest).toBe(absent.inputDigest);
121
+ expect(infinite.resultDigest).toBe(absent.resultDigest);
122
+ expect(nan.resultDigest).toBe(absent.resultDigest);
123
+
124
+ // The verdict agrees with the digest even at a non-finite evaluatedAt:
125
+ // an Infinity expiry never expires, so digest-equal inputs judge equal.
126
+ const atInfinity = evaluate(
127
+ baseInput({
128
+ evaluatedAt: Number.POSITIVE_INFINITY,
129
+ waivers: [{ fingerprint: "w", expiresAt: Number.POSITIVE_INFINITY }],
130
+ })
131
+ );
132
+ expect(atInfinity.reasons).not.toContain("expiredSuppression");
133
+ expect(atInfinity.verdict).toBe("pass");
134
+ });
135
+
136
+ it("digests unknown presets order-invariantly", () => {
137
+ const forward = evaluate(
138
+ baseInput({ integrity: { governance: "healthy", unknownPresets: ["b", "a"] } })
139
+ );
140
+ const reversed = evaluate(
141
+ baseInput({ integrity: { governance: "healthy", unknownPresets: ["a", "b"] } })
142
+ );
143
+ expect(forward.inputDigest).toBe(reversed.inputDigest);
144
+ expect(forward.resultDigest).toBe(reversed.resultDigest);
145
+ });
146
+
147
+ it("collapses duplicate fingerprints to one finding, keeping the harder state", () => {
148
+ const result = evaluate(
149
+ baseInput({
150
+ evidence: {
151
+ kind: "findings",
152
+ findings: [
153
+ { fingerprint: "dup", ruleId: "styles/no-raw-color", severity: "warn" },
154
+ { fingerprint: "dup", ruleId: "styles/no-raw-color", severity: "error" },
155
+ ],
156
+ },
157
+ })
158
+ );
159
+ // The warn row is advisory (not gated); the error row pends — it wins.
160
+ expect(result.findings).toHaveLength(1);
161
+ expect(result.findings[0]?.severity).toBe("error");
162
+ expect(result.findings[0]?.state).toBe("pending_cloud_trust");
163
+ });
164
+
165
+ it("never synthesizes a blocker from contradictory duplicates", () => {
166
+ // Each row is advisory on its own (advisory-tier error / non-gated warn).
167
+ // A field-wise merge would fabricate a non-advisory error that blocks
168
+ // under full trust — the hardest GENUINE state is advisory.
169
+ const result = evaluate(
170
+ baseInput({
171
+ evidence: {
172
+ kind: "findings",
173
+ findings: [
174
+ { ...styleError("dup"), advisory: true },
175
+ { fingerprint: "dup", ruleId: "styles/no-raw-color", severity: "warn" },
176
+ ],
177
+ },
178
+ baseline: { identityVerified: true, findingStates: { dup: "introduced" } },
179
+ trust: { source: "verified", mappings: {} },
180
+ })
181
+ );
182
+ expect(result.findings[0]?.state).toBe("advisory");
183
+ expect(result.verdict).toBe("pass");
184
+ });
185
+
186
+ it("preserves a duplicate's earned block across rule identities", () => {
187
+ // The style row blocks alone (evidence-class trust affirmative); the
188
+ // canonical row is advisory (definitive mapping negative). A merged row
189
+ // keyed to the lexically-smaller canonical rule would erase the block.
190
+ const result = evaluate(
191
+ baseInput({
192
+ evidence: {
193
+ kind: "findings",
194
+ findings: [
195
+ { fingerprint: "dup", ruleId: "components/prefer-library", severity: "error" },
196
+ styleError("dup"),
197
+ ],
198
+ },
199
+ baseline: { identityVerified: true, findingStates: { dup: "introduced" } },
200
+ trust: { source: "verified", mappings: { dup: "unconfirmed" } },
201
+ })
202
+ );
203
+ expect(result.findings[0]?.state).toBe("blocked");
204
+ expect(result.findings[0]?.ruleId).toBe("styles/no-raw-color");
205
+ expect(result.verdict).toBe("block");
206
+ });
207
+
208
+ it("never combines contradictory records' facts into an unsupported block", () => {
209
+ // Record A: verified source but existing debt with an unconfirmed
210
+ // mapping. Record B: introduced with a confirmed mapping but an unchecked
211
+ // source. Folding their facts together would block; neither record
212
+ // supports it alone — the hardest genuine state is B's pending hold.
213
+ const result = evaluate(
214
+ baseInput({
215
+ evidence: {
216
+ kind: "findings",
217
+ findings: [
218
+ {
219
+ fingerprint: "dup",
220
+ ruleId: "components/prefer-library",
221
+ severity: "error",
222
+ sourceTrust: "verified",
223
+ mappingTrust: "unconfirmed",
224
+ baselineState: "existing",
225
+ },
226
+ {
227
+ fingerprint: "dup",
228
+ ruleId: "components/prefer-library",
229
+ severity: "error",
230
+ sourceTrust: "unknown",
231
+ mappingTrust: "confirmed",
232
+ baselineState: "introduced",
233
+ },
234
+ ],
235
+ },
236
+ baseline: { identityVerified: true, findingStates: {} },
237
+ })
238
+ );
239
+ expect(result.findings[0]?.state).toBe("pending_cloud_trust");
240
+ expect(result.verdict).toBe("indeterminate");
241
+ });
242
+
243
+ it("never lets a waived record lend its facts or waiver to an unwaived duplicate", () => {
244
+ // A fully-trusted suppressed record plus an unjudged unsuppressed record:
245
+ // the waived record is advisory, the peer pends on its own unknowns —
246
+ // nothing blocks.
247
+ const result = evaluate(
248
+ baseInput({
249
+ evidence: {
250
+ kind: "findings",
251
+ findings: [
252
+ {
253
+ ...styleError("dup"),
254
+ sourceTrust: "verified",
255
+ baselineState: "introduced",
256
+ waived: true,
257
+ },
258
+ { ...styleError("dup"), sourceTrust: "unknown", baselineState: "unknown" },
259
+ ],
260
+ },
261
+ baseline: { identityVerified: true, findingStates: {} },
262
+ })
263
+ );
264
+ expect(result.findings[0]?.state).toBe("pending_cloud_trust");
265
+ expect(result.verdict).toBe("indeterminate");
266
+ expect(result.counts.blocked).toBe(0);
267
+ });
268
+
269
+ it("honors a definitive mapping negative on a contract-tier rule under a trust:evidence override", () => {
270
+ const result = evaluate(
271
+ baseInput({
272
+ evidence: {
273
+ kind: "findings",
274
+ findings: [
275
+ { fingerprint: "f-1", ruleId: "components/prefer-library", severity: "error" },
276
+ ],
277
+ },
278
+ policy: {
279
+ failOnWarnings: false,
280
+ rules: { "components/prefer-library": { trust: "evidence" } },
281
+ },
282
+ baseline: { identityVerified: true, findingStates: { "f-1": "introduced" } },
283
+ trust: { source: "verified", mappings: { "f-1": "unconfirmed" } },
284
+ })
285
+ );
286
+ // The override waives the mapping UNKNOWN, never a proven
287
+ // non-confirmation.
288
+ expect(result.findings[0]?.state).toBe("advisory");
289
+ expect(result.verdict).toBe("pass");
290
+ });
291
+
292
+ it("treats a waiver expiring exactly at evaluatedAt as expired", () => {
293
+ const active = evaluate(baseInput({ waivers: [{ fingerprint: "w", expiresAt: NOW + 1 }] }));
294
+ expect(active.reasons).not.toContain("expiredSuppression");
295
+ expect(active.verdict).toBe("pass");
296
+
297
+ const expired = evaluate(baseInput({ waivers: [{ fingerprint: "w", expiresAt: NOW }] }));
298
+ expect(expired.reasons).toContain("expiredSuppression");
299
+ expect(expired.verdict).toBe("block");
300
+ });
301
+
302
+ it("never lets advisory-tier or weak-evidence findings become candidates", () => {
303
+ const result = evaluate(
304
+ baseInput({
305
+ evidence: {
306
+ kind: "findings",
307
+ findings: [
308
+ { ...styleError("advisory"), advisory: true },
309
+ { ...styleError("heuristic"), evidenceGrade: "runtime_advisory" },
310
+ { ...styleError("unclassifiable"), evidenceGrade: "not-a-grade" },
311
+ { fingerprint: "info", ruleId: "styles/no-raw-color", severity: "info" },
312
+ ],
313
+ },
314
+ })
315
+ );
316
+ expect(result.findings.every((finding) => finding.state === "advisory")).toBe(true);
317
+ expect(result.verdict).toBe("pass");
318
+ });
319
+
320
+ it("honors an explicit trust:none policy override", () => {
321
+ const result = evaluate(
322
+ baseInput({
323
+ evidence: { kind: "findings", findings: [styleError()] },
324
+ policy: { failOnWarnings: false, rules: { "styles/no-raw-color": { trust: "none" } } },
325
+ })
326
+ );
327
+ expect(result.findings[0]?.state).toBe("blocked");
328
+ expect(result.verdict).toBe("block");
329
+ });
330
+
331
+ it("never enforces a proven non-reproduction, even under trust:none", () => {
332
+ const result = evaluate(
333
+ baseInput({
334
+ evidence: { kind: "findings", findings: [styleError()] },
335
+ policy: { failOnWarnings: false, rules: { "styles/no-raw-color": { trust: "none" } } },
336
+ trust: { source: "unverified", mappings: {} },
337
+ })
338
+ );
339
+ expect(result.findings[0]?.state).toBe("advisory");
340
+ expect(result.verdict).toBe("pass");
341
+ });
342
+
343
+ it("honors a definitive mapping negative on a contract-tier rule, even under trust:none", () => {
344
+ const result = evaluate(
345
+ baseInput({
346
+ evidence: {
347
+ kind: "findings",
348
+ findings: [
349
+ { fingerprint: "f-1", ruleId: "components/prefer-library", severity: "error" },
350
+ ],
351
+ },
352
+ policy: {
353
+ failOnWarnings: false,
354
+ rules: { "components/prefer-library": { trust: "none" } },
355
+ },
356
+ baseline: { identityVerified: true, findingStates: { "f-1": "introduced" } },
357
+ trust: { source: "verified", mappings: { "f-1": "unconfirmed" } },
358
+ })
359
+ );
360
+ // The override waives UNKNOWNS, never a proven non-confirmation — a
361
+ // contract-tier finding without a confirmed mapping has no named fix.
362
+ expect(result.findings[0]?.state).toBe("advisory");
363
+ expect(result.verdict).toBe("pass");
364
+ });
365
+
366
+ it("takes adoption facts independently of baseline identity verification", () => {
367
+ const regressed = evaluate(baseInput({ adoption: { regressed: true } }));
368
+ expect(regressed.reasons).toContain("adoptionRegression");
369
+ expect(regressed.verdict).toBe("block");
370
+
371
+ const unavailable = evaluate(baseInput({ adoption: "unavailable" }));
372
+ expect(unavailable.reasons).toContain("adoptionBaselineUnavailable");
373
+ expect(unavailable.verdict).toBe("indeterminate");
374
+ });
375
+
376
+ it("resolves per-finding source trust over the run default", () => {
377
+ const result = evaluate(
378
+ baseInput({
379
+ evidence: { kind: "findings", findings: [styleError("trusted"), styleError("unchecked")] },
380
+ baseline: {
381
+ identityVerified: true,
382
+ findingStates: { trusted: "introduced", unchecked: "introduced" },
383
+ },
384
+ trust: { source: "unknown", sources: { trusted: "verified" }, mappings: {} },
385
+ })
386
+ );
387
+ const byFingerprint = Object.fromEntries(
388
+ result.findings.map((finding) => [finding.fingerprint, finding.state])
389
+ );
390
+ // The verified blocker blocks; the unknown peer pends without diluting it.
391
+ expect(byFingerprint).toEqual({ trusted: "blocked", unchecked: "pending_cloud_trust" });
392
+ expect(result.verdict).toBe("block");
393
+ });
394
+
395
+ it("keeps the hardest genuinely-supported duplicate, order-invariantly", () => {
396
+ const advisoryRow: EvaluationFindingInput = { ...styleError("dup"), advisory: true };
397
+ const gatingRow = styleError("dup");
398
+ const forward = evaluate(
399
+ baseInput({ evidence: { kind: "findings", findings: [advisoryRow, gatingRow] } })
400
+ );
401
+ const reversed = evaluate(
402
+ baseInput({ evidence: { kind: "findings", findings: [gatingRow, advisoryRow] } })
403
+ );
404
+ expect(forward).toEqual(reversed);
405
+ // The non-advisory row's pending hold outranks the advisory downgrade.
406
+ expect(forward.findings[0]?.state).toBe("pending_cloud_trust");
407
+ });
408
+
409
+ it("holds default candidates with no trust facts at pending_cloud_trust", () => {
410
+ const result = evaluate(
411
+ baseInput({ evidence: { kind: "findings", findings: [styleError()] } })
412
+ );
413
+ expect(result.findings[0]?.state).toBe("pending_cloud_trust");
414
+ expect(result.verdict).toBe("indeterminate");
415
+ expect(result.derived.gatingFindingsPresent).toBe(false);
416
+ });
417
+
418
+ it("blocked findings win the verdict over pending ones when integrity is intact", () => {
419
+ const result = evaluate(
420
+ baseInput({
421
+ evidence: { kind: "findings", findings: [styleError("pending"), styleError("gated")] },
422
+ policy: { failOnWarnings: false, rules: {} },
423
+ baseline: { identityVerified: true, findingStates: { gated: "introduced" } },
424
+ trust: { source: "verified", mappings: {} },
425
+ })
426
+ );
427
+ // "pending" resolves the same facts — introduced (absent entry on a
428
+ // verified baseline) + verified source: both block.
429
+ expect(result.verdict).toBe("block");
430
+ expect(result.derived.gatingFindingsPresent).toBe(true);
431
+ });
432
+
433
+ it("derives warnPresent without ever emitting it as a reason", () => {
434
+ const result = evaluate(
435
+ baseInput({
436
+ evidence: {
437
+ kind: "findings",
438
+ findings: [{ fingerprint: "w", ruleId: "styles/no-raw-color", severity: "warn" }],
439
+ },
440
+ })
441
+ );
442
+ expect(result.derived.warnPresent).toBe(true);
443
+ expect(result.reasons).not.toContain("warnPresent" as never);
444
+ expect(result.verdict).toBe("pass");
445
+ });
446
+ });
447
+
448
+ // ---------------------------------------------------------------------------
449
+ // Facts mode — rule config parity with the engine scan
450
+ // ---------------------------------------------------------------------------
451
+
452
+ /** One raw-hex `color` declaration under a raw-colour policy: `styles/no-raw-color` fires. */
453
+ function rawColorIndex(rules?: Record<string, unknown>): FactIndex {
454
+ const ix = new FactIndex();
455
+ ix.addMany(
456
+ compileGlobalGovernanceFacts({
457
+ styles: [
458
+ g.styles
459
+ .rawColors()
460
+ .forbid({ except: ["transparent"], prefer: "token", severity: "error" }),
461
+ ],
462
+ ...(rules ? { rules } : {}),
463
+ })
464
+ );
465
+ ix.add(
466
+ makeStyleDeclarationFact({
467
+ file: "src/icon.module.scss",
468
+ selector: ".icon",
469
+ declarationPath: "0:0",
470
+ property: "color",
471
+ value: "#00ff00",
472
+ location: { file: "src/icon.module.scss", line: 2, column: 2 },
473
+ })
474
+ );
475
+ return ix;
476
+ }
477
+
478
+ describe("evaluate — facts mode honours governance_rule_config", () => {
479
+ it("runs the rule and gates at its record severity when no rule config exists", () => {
480
+ const result = evaluate(baseInput({ evidence: { kind: "facts", facts: rawColorIndex() } }));
481
+ expect(result.findings).toHaveLength(1);
482
+ expect(result.findings[0]).toMatchObject({ ruleId: "styles/no-raw-color", severity: "error" });
483
+ expect(result.findings[0].stateReasons).not.toContain("ineligible:warn-not-gated");
484
+ });
485
+
486
+ it("never runs a rule the config disabled — a disabled rule cannot block --ci", () => {
487
+ // The engine scan prints nothing for a disabled rule; the kernel used to re-run
488
+ // it over the same facts and block on findings no surface showed.
489
+ const result = evaluate(
490
+ baseInput({
491
+ evidence: {
492
+ kind: "facts",
493
+ facts: rawColorIndex({ "styles/no-raw-color": { enabled: false } }),
494
+ },
495
+ })
496
+ );
497
+ expect(result.findings).toEqual([]);
498
+ expect(result.counts.blocked).toBe(0);
499
+ expect(result.derived.warnPresent).toBe(false);
500
+ expect(result.verdict).toBe("pass");
501
+ });
502
+
503
+ it("applies the configured severity exactly as the engine does", () => {
504
+ const result = evaluate(
505
+ baseInput({
506
+ evidence: {
507
+ kind: "facts",
508
+ facts: rawColorIndex({ "styles/no-raw-color": { enabled: true, severity: "warn" } }),
509
+ },
510
+ policy: { failOnWarnings: false },
511
+ })
512
+ );
513
+ expect(result.findings).toHaveLength(1);
514
+ expect(result.findings[0]).toMatchObject({
515
+ ruleId: "styles/no-raw-color",
516
+ severity: "warn",
517
+ state: "advisory",
518
+ stateReasons: ["ineligible:warn-not-gated"],
519
+ });
520
+ expect(result.counts.blocked).toBe(0);
521
+ });
522
+ });