@usefragments/core 1.5.2 → 1.6.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 (41) hide show
  1. package/dist/{chunk-BAHCOAVG.js → chunk-WVFNDPM4.js} +423 -189
  2. package/dist/chunk-WVFNDPM4.js.map +1 -0
  3. package/dist/codes/index.d.ts +1 -1
  4. package/dist/codes/index.js +1 -1
  5. package/dist/compiled-types/index.d.ts +1 -1
  6. package/dist/generate/index.d.ts +1 -1
  7. package/dist/{governance-pKrfh517.d.ts → governance-DxFipN5V.d.ts} +609 -20
  8. package/dist/index.d.ts +638 -43
  9. package/dist/index.js +304 -37
  10. package/dist/index.js.map +1 -1
  11. package/dist/react-types.d.ts +1 -1
  12. package/dist/test-utils.d.ts +1 -1
  13. package/package.json +1 -1
  14. package/src/__tests__/policy-exclude.test.ts +180 -0
  15. package/src/canonical-bridge.ts +69 -1
  16. package/src/canonical-direction.test.ts +118 -0
  17. package/src/canonical-direction.ts +43 -2
  18. package/src/codes/__tests__/codes.test.ts +14 -1
  19. package/src/codes/codes.ts +40 -0
  20. package/src/facts/builders.ts +35 -0
  21. package/src/facts/compile.ts +135 -21
  22. package/src/facts/fact-index.ts +15 -2
  23. package/src/facts/facts.test.ts +6 -2
  24. package/src/facts/index.ts +9 -6
  25. package/src/facts/types.ts +45 -9
  26. package/src/governance-integrity.test.ts +174 -3
  27. package/src/governance-integrity.ts +305 -5
  28. package/src/governance.ts +87 -1
  29. package/src/index.ts +38 -1
  30. package/src/policy-exclude.ts +113 -0
  31. package/src/rules/families.test.ts +69 -0
  32. package/src/rules/families.ts +52 -0
  33. package/src/rules/index.ts +6 -0
  34. package/src/rules/jsx-preferred-import-path.ts +29 -11
  35. package/src/rules/rules.test.ts +125 -1
  36. package/src/rules/styles-no-raw-color.ts +13 -4
  37. package/src/rules/styles-no-raw-dimensions.ts +13 -4
  38. package/src/rules/styles-no-raw-spacing.ts +12 -4
  39. package/src/rules/styles-no-raw-typography.ts +13 -4
  40. package/src/rules/utils.ts +39 -0
  41. package/dist/chunk-BAHCOAVG.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { ComponentType, ReactNode, JSX } from 'react';
2
- export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-pKrfh517.js';
2
+ export { F as FragmentDefinition, a as FragmentDefinitionV2 } from './governance-DxFipN5V.js';
3
3
  import 'zod';
4
4
  import './topology/index.js';
5
5
  import './types-xJ2xyp_G.js';
@@ -1,4 +1,4 @@
1
- import { C as CompiledBlock, b as CompiledFragment } from './governance-pKrfh517.js';
1
+ import { C as CompiledBlock, b as CompiledFragment } from './governance-DxFipN5V.js';
2
2
  import 'zod';
3
3
  import 'react';
4
4
  import './topology/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefragments/core",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
4
4
  "license": "MIT",
5
5
  "description": "Core types, schemas, and runtime API for Fragments component definitions",
6
6
  "author": "Conan McNicholl",
@@ -0,0 +1,180 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ compileGlobalGovernanceFacts,
5
+ describePolicyExclude,
6
+ detectUnmatchedPolicyExcludes,
7
+ governanceConfigSchema,
8
+ matchPolicyExclude,
9
+ normalizePolicyExcludes,
10
+ type GovernanceConfig,
11
+ } from "../index.js";
12
+
13
+ describe("policy exclude normalization", () => {
14
+ it("accepts bare globs and { glob, reason } records alike", () => {
15
+ expect(
16
+ normalizePolicyExcludes([
17
+ "src/legacy/**",
18
+ { glob: "vendor/**", reason: "vendor drop, tracked in DS-611" },
19
+ ])
20
+ ).toEqual([
21
+ { glob: "src/legacy/**" },
22
+ { glob: "vendor/**", reason: "vendor drop, tracked in DS-611" },
23
+ ]);
24
+ });
25
+
26
+ it("returns undefined when nothing usable is authored", () => {
27
+ expect(normalizePolicyExcludes(undefined)).toBeUndefined();
28
+ expect(normalizePolicyExcludes([])).toBeUndefined();
29
+ expect(normalizePolicyExcludes(["", { reason: "no glob" }, 7])).toBeUndefined();
30
+ });
31
+
32
+ it("matches `**` across segments and `*` within one", () => {
33
+ const excludes = normalizePolicyExcludes(["src/legacy/vendor/**", "src/*.tsx"]);
34
+
35
+ expect(matchPolicyExclude(excludes, "src/legacy/vendor/deep/Widget.tsx")).toEqual({
36
+ glob: "src/legacy/vendor/**",
37
+ });
38
+ expect(matchPolicyExclude(excludes, "./src/App.tsx")).toEqual({ glob: "src/*.tsx" });
39
+ expect(matchPolicyExclude(excludes, "src\\App.tsx")).toEqual({ glob: "src/*.tsx" });
40
+ expect(matchPolicyExclude(excludes, "src/nested/App.tsx")).toBeUndefined();
41
+ expect(matchPolicyExclude(undefined, "src/App.tsx")).toBeUndefined();
42
+ });
43
+
44
+ it("describes an exclusion with its scope, glob, and reason", () => {
45
+ expect(
46
+ describePolicyExclude("style.rawColors.forbid", {
47
+ glob: "vendor/**",
48
+ reason: "vendor drop",
49
+ })
50
+ ).toBe("style.rawColors.forbid exclude matched vendor/** — vendor drop");
51
+ expect(describePolicyExclude("styles/no-raw-color", { glob: "vendor/**" })).toBe(
52
+ "styles/no-raw-color exclude matched vendor/**"
53
+ );
54
+ });
55
+ });
56
+
57
+ describe("global record excludes compile onto policy facts", () => {
58
+ const policy: GovernanceConfig = {
59
+ styles: [
60
+ {
61
+ kind: "style.rawColors.forbid",
62
+ except: [],
63
+ prefer: "token",
64
+ severity: "warn",
65
+ exclude: [{ glob: "src/legacy/vendor/**", reason: "vendor drop" }],
66
+ },
67
+ ],
68
+ jsx: [
69
+ {
70
+ kind: "jsx.importPath.prefer",
71
+ from: "@mui/material",
72
+ to: "@/components",
73
+ severity: "warn",
74
+ exclude: ["src/legacy/vendor/**"],
75
+ },
76
+ ],
77
+ };
78
+
79
+ it("is accepted by the shipped schema", () => {
80
+ expect(() => governanceConfigSchema.parse(policy)).not.toThrow();
81
+ });
82
+
83
+ it("threads the normalized excludes onto the compiled facts", () => {
84
+ const facts = compileGlobalGovernanceFacts(policy);
85
+ const color = facts.find((fact) => fact.kind === "style_raw_color_forbidden");
86
+ const importPath = facts.find((fact) => fact.kind === "jsx_import_path_preferred");
87
+
88
+ expect(color).toMatchObject({
89
+ exclude: [{ glob: "src/legacy/vendor/**", reason: "vendor drop" }],
90
+ });
91
+ expect(importPath).toMatchObject({ exclude: [{ glob: "src/legacy/vendor/**" }] });
92
+ });
93
+
94
+ it("keeps a user exclude alive when a preset record shares the same fact id", () => {
95
+ // A resolved policy is `[...presetRecords, ...userRecords]`, and the singleton style
96
+ // kinds are content-addressed on kind alone — so only one of the two survives
97
+ // indexing. The user's exemption must not be the casualty.
98
+ const facts = compileGlobalGovernanceFacts({
99
+ styles: [
100
+ { kind: "style.rawColors.forbid", except: [], prefer: "token", severity: "warn" },
101
+ {
102
+ kind: "style.rawColors.forbid",
103
+ except: [],
104
+ prefer: "token",
105
+ severity: "warn",
106
+ exclude: ["src/legacy/vendor/**"],
107
+ },
108
+ ],
109
+ });
110
+
111
+ const colorFacts = facts.filter((fact) => fact.kind === "style_raw_color_forbidden");
112
+ expect(colorFacts).toHaveLength(2);
113
+ // Both copies carry the union, so whichever the index keeps is the armed one.
114
+ for (const fact of colorFacts) {
115
+ expect(fact).toMatchObject({ exclude: [{ glob: "src/legacy/vendor/**" }] });
116
+ }
117
+ });
118
+
119
+ it("leaves fact identity untouched so fingerprints do not move", () => {
120
+ const withExclude = compileGlobalGovernanceFacts(policy);
121
+ const withoutExclude = compileGlobalGovernanceFacts({
122
+ styles: [{ kind: "style.rawColors.forbid", except: [], prefer: "token", severity: "warn" }],
123
+ jsx: [
124
+ {
125
+ kind: "jsx.importPath.prefer",
126
+ from: "@mui/material",
127
+ to: "@/components",
128
+ severity: "warn",
129
+ },
130
+ ],
131
+ });
132
+
133
+ expect(withExclude.map((fact) => fact.id)).toEqual(withoutExclude.map((fact) => fact.id));
134
+ // …and a policy with no excludes stays byte-identical to the pre-1.6.0 shape.
135
+ expect(withoutExclude.every((fact) => !("exclude" in fact))).toBe(true);
136
+ });
137
+ });
138
+
139
+ describe("detectUnmatchedPolicyExcludes", () => {
140
+ const policy: GovernanceConfig = {
141
+ styles: [
142
+ {
143
+ kind: "style.rawColors.forbid",
144
+ except: [],
145
+ prefer: "token",
146
+ severity: "warn",
147
+ exclude: ["src/legacy/vendor/**"],
148
+ },
149
+ ],
150
+ rules: { "styles/no-raw-spacing": { exclude: ["src/deleted-area/**"] } },
151
+ };
152
+
153
+ it("stays silent when every glob covers a scanned file", () => {
154
+ expect(
155
+ detectUnmatchedPolicyExcludes(policy, [
156
+ "src/legacy/vendor/Widget.tsx",
157
+ "src/deleted-area/Old.tsx",
158
+ ])
159
+ ).toEqual([]);
160
+ });
161
+
162
+ it("names each glob that scoped nothing, on both authoring routes", () => {
163
+ const diagnostics = detectUnmatchedPolicyExcludes(policy, ["src/App.tsx"]);
164
+
165
+ expect(diagnostics).toMatchObject([
166
+ {
167
+ code: "FUI9006",
168
+ kind: "unmatched-exclude",
169
+ path: "govern.rules.styles/no-raw-spacing.exclude",
170
+ },
171
+ { code: "FUI9006", kind: "unmatched-exclude", path: "govern.styles[0].exclude" },
172
+ ]);
173
+ expect(diagnostics[0]?.message).toContain("src/deleted-area/**");
174
+ expect(diagnostics[1]?.message).toContain("style.rawColors.forbid");
175
+ });
176
+
177
+ it("does not flag excludes when nothing was scanned at all", () => {
178
+ expect(detectUnmatchedPolicyExcludes(policy, [])).toEqual([]);
179
+ });
180
+ });
@@ -1,5 +1,5 @@
1
1
  import type { ContractCanonicalMappingInput } from "./contract/preimage.js";
2
- import type { CanonicalBridgeV1 } from "./governance.js";
2
+ import type { CanonicalBridgeV1, CanonicalSource } from "./governance.js";
3
3
  import { ownedImportMatchesRoot } from "./package-identity-match.js";
4
4
 
5
5
  export function canonicalBridgeUnderlyingKey(input: {
@@ -28,6 +28,74 @@ export function canonicalBridgeContractMappings(
28
28
  }));
29
29
  }
30
30
 
31
+ /**
32
+ * One authored bridge, projected into the two facts a consumer needs to honor
33
+ * it WITHOUT widening it: the npm canonical source its underlying export
34
+ * implies, and the local component the user actually sanctioned.
35
+ *
36
+ * The pairing is the point. A bridge says "`src/components/Button.tsx#Button`
37
+ * wraps `@mui/material#Button`" — a fact about ONE component. Reading only the
38
+ * source half turns it into "`@mui/material#Button` is canonical in this repo",
39
+ * which is a strictly wider claim: every component in the tree that happens to
40
+ * render that export inherits the sanction it was never given.
41
+ */
42
+ export interface CanonicalBridgeIdentityBinding {
43
+ /** The npm canonical source this bridge's underlying export implies. */
44
+ source: Extract<CanonicalSource, { kind: "npm" }>;
45
+ /** The sanctioned local component — `<repo-relative-file>#<exportName>`. */
46
+ localComponentKey: string;
47
+ }
48
+
49
+ /**
50
+ * The per-component bindings an authored bridge set implies — one definition,
51
+ * because every consumer of "which components did the user sanction, against
52
+ * which package export" must see the same set.
53
+ *
54
+ * Consumed by `classify`'s RENDER_ROOT_CANONICAL context, which gates the
55
+ * signal on `localComponentKey`: only the component the bridge names may claim
56
+ * the bridge's canonical, so a sanction stays as narrow as the user authored it.
57
+ */
58
+ export function canonicalBridgeIdentityBindings(
59
+ bridges: readonly CanonicalBridgeV1[] | undefined
60
+ ): CanonicalBridgeIdentityBinding[] {
61
+ return (bridges ?? []).map((bridge) => ({
62
+ source: {
63
+ kind: "npm",
64
+ specifier: bridge.underlying.packageName,
65
+ include: [bridge.underlying.exportName],
66
+ },
67
+ localComponentKey: bridge.local.componentKey,
68
+ }));
69
+ }
70
+
71
+ /**
72
+ * The npm canonical sources an authored bridge set implies — the source half of
73
+ * `canonicalBridgeIdentityBindings`, for consumers that resolve the whole tree
74
+ * at once and scope the result themselves.
75
+ *
76
+ * A bridge states that `underlying.packageName#exportName` IS the canonical
77
+ * thing a local component wraps, so identity resolution treats that export as
78
+ * declared, exactly as an explicit `govern.canonicalSources` npm entry would.
79
+ * Before this projection was shared, a repo whose decision lived only in
80
+ * `canonicalBridges` (hand-authored, or written by `identity sanction`)
81
+ * resolved render roots at `canonical` grade in the pipeline while the
82
+ * classifier saw no declaration at all — the authored fact reached one consumer
83
+ * and silently missed the other.
84
+ *
85
+ * Consumer: the scan pipeline's identity input
86
+ * (`buildCanonicalDirectionIdentityInput`), which resolves render roots for the
87
+ * whole definition set and reports the grade rather than acting on it.
88
+ * Anything that turns the grade into an AUTHORED decision must use
89
+ * `canonicalBridgeIdentityBindings` instead — this projection deliberately
90
+ * drops `local.componentKey`, so on its own it cannot tell a sanctioned wrapper
91
+ * from any other component that renders the same export.
92
+ */
93
+ export function canonicalBridgeIdentitySources(
94
+ bridges: readonly CanonicalBridgeV1[] | undefined
95
+ ): Array<Extract<CanonicalSource, { kind: "npm" }>> {
96
+ return canonicalBridgeIdentityBindings(bridges).map((binding) => binding.source);
97
+ }
98
+
31
99
  export function canonicalBridgeMatchesUnderlyingImport(
32
100
  bridge: CanonicalBridgeV1,
33
101
  source: string,
@@ -0,0 +1,118 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { FactIndex, makeComponentDefinitionFact } from "./facts/index.js";
4
+ import type { ComponentDefinitionRenderRoot } from "./facts/index.js";
5
+ import { projectCanonicalDirectionConflicts } from "./canonical-direction.js";
6
+
7
+ const NPM_SOURCE = {
8
+ kind: "npm",
9
+ specifier: "@mui/material",
10
+ include: ["Button", "TextField"],
11
+ } as const;
12
+
13
+ function wrapperDefinition(input: {
14
+ file: string;
15
+ exportName: string;
16
+ renderRoot: ComponentDefinitionRenderRoot;
17
+ exported?: boolean;
18
+ }) {
19
+ return makeComponentDefinitionFact({
20
+ file: input.file,
21
+ exportName: input.exportName,
22
+ exported: input.exported ?? true,
23
+ componentKey: `${input.file}#${input.exportName}`,
24
+ renderRoot: input.renderRoot,
25
+ propSurface: [],
26
+ });
27
+ }
28
+
29
+ describe("projectCanonicalDirectionConflicts", () => {
30
+ it("projects a conflict from a package-identity canonical key", () => {
31
+ const ix = new FactIndex();
32
+ ix.add(
33
+ wrapperDefinition({
34
+ file: "src/components/Button.tsx",
35
+ exportName: "Button",
36
+ renderRoot: {
37
+ resolution: "canonical",
38
+ canonical: "@mui/material#Button",
39
+ importSource: "@mui/material",
40
+ },
41
+ })
42
+ );
43
+
44
+ expect(projectCanonicalDirectionConflicts(ix, [NPM_SOURCE])).toMatchObject([
45
+ {
46
+ packageName: "@mui/material",
47
+ exportName: "Button",
48
+ wrapperComponentKey: "src/components/Button.tsx#Button",
49
+ state: "unresolved",
50
+ },
51
+ ]);
52
+ });
53
+
54
+ // A canonical package vendored inside the repo (a workspace package or the
55
+ // committed archetype fixture's fake-packages/) resolves the wrapper's render
56
+ // root to its local file identity. The original import specifier is still
57
+ // exact and source-backed — the conflict must not vanish when resolution
58
+ // goes local, or init detects an ambiguity the scan can never see.
59
+ it("projects a conflict through the import source when the canonical key resolved locally", () => {
60
+ const ix = new FactIndex();
61
+ ix.add(
62
+ wrapperDefinition({
63
+ file: "src/components/Button.tsx",
64
+ exportName: "Button",
65
+ renderRoot: {
66
+ resolution: "canonical",
67
+ canonical: "fake-packages/mui-material/index.js#Button",
68
+ importSource: "@mui/material",
69
+ },
70
+ })
71
+ );
72
+
73
+ expect(projectCanonicalDirectionConflicts(ix, [NPM_SOURCE])).toMatchObject([
74
+ {
75
+ packageName: "@mui/material",
76
+ exportName: "Button",
77
+ wrapperComponentKey: "src/components/Button.tsx#Button",
78
+ wrapperFile: "src/components/Button.tsx",
79
+ wrapperExportName: "Button",
80
+ state: "unresolved",
81
+ },
82
+ ]);
83
+ });
84
+
85
+ it("ignores locally-resolved roots whose import source is not a canonical package", () => {
86
+ const ix = new FactIndex();
87
+ ix.add(
88
+ wrapperDefinition({
89
+ file: "src/components/Button.tsx",
90
+ exportName: "Button",
91
+ renderRoot: {
92
+ resolution: "canonical",
93
+ canonical: "packages/other-kit/index.ts#Button",
94
+ importSource: "@acme/other-kit",
95
+ },
96
+ })
97
+ );
98
+
99
+ expect(projectCanonicalDirectionConflicts(ix, [NPM_SOURCE])).toEqual([]);
100
+ });
101
+
102
+ it("honors the include list on the import-source fallback", () => {
103
+ const ix = new FactIndex();
104
+ ix.add(
105
+ wrapperDefinition({
106
+ file: "src/components/Chip.tsx",
107
+ exportName: "Chip",
108
+ renderRoot: {
109
+ resolution: "canonical",
110
+ canonical: "fake-packages/mui-material/index.js#Chip",
111
+ importSource: "@mui/material",
112
+ },
113
+ })
114
+ );
115
+
116
+ expect(projectCanonicalDirectionConflicts(ix, [NPM_SOURCE])).toEqual([]);
117
+ });
118
+ });
@@ -1,7 +1,9 @@
1
1
  import type { CanonicalSource } from "./governance.js";
2
- import type { FactIndex } from "./facts/index.js";
2
+ import type { ComponentDefinitionRenderRoot, FactIndex } from "./facts/index.js";
3
3
  import { ownedImportMatchesRoot } from "./package-identity-match.js";
4
4
 
5
+ type CanonicalRenderRoot = Extract<ComponentDefinitionRenderRoot, { resolution: "canonical" }>;
6
+
5
7
  export interface CanonicalDirectionConflict {
6
8
  packageName: string;
7
9
  exportName: string;
@@ -32,7 +34,7 @@ export function projectCanonicalDirectionConflicts(
32
34
 
33
35
  for (const definition of ix.byKind("component_definition")) {
34
36
  if (!definition.exported || definition.renderRoot.resolution !== "canonical") continue;
35
- const target = npmTarget(definition.renderRoot.canonical, npmSources);
37
+ const target = npmTarget(definition.renderRoot, npmSources);
36
38
  if (!target) continue;
37
39
 
38
40
  const identity = identityByComponent.get(definition.componentKey);
@@ -83,6 +85,16 @@ export function canonicalDirectionConflictsTarget(
83
85
  }
84
86
 
85
87
  function npmTarget(
88
+ renderRoot: CanonicalRenderRoot,
89
+ sources: readonly Extract<CanonicalSource, { kind: "npm" }>[]
90
+ ): { packageName: string; exportName: string } | null {
91
+ return (
92
+ npmTargetFromCanonicalKey(renderRoot.canonical, sources) ??
93
+ npmTargetFromImportSource(renderRoot, sources)
94
+ );
95
+ }
96
+
97
+ function npmTargetFromCanonicalKey(
86
98
  canonicalTarget: string,
87
99
  sources: readonly Extract<CanonicalSource, { kind: "npm" }>[]
88
100
  ): { packageName: string; exportName: string } | null {
@@ -108,6 +120,35 @@ function npmTarget(
108
120
  return null;
109
121
  }
110
122
 
123
+ /**
124
+ * A canonical package that also lives in the repo (a vendored fixture package
125
+ * or a workspace package whose package.json name matches the specifier)
126
+ * resolves to its local file identity, so the canonical key carries no package
127
+ * name. The original import specifier still does — and it is just as exact and
128
+ * source-backed, so the conflict must not vanish when resolution goes local
129
+ * (init's detector sees it; the scan must see the same set).
130
+ */
131
+ function npmTargetFromImportSource(
132
+ renderRoot: CanonicalRenderRoot,
133
+ sources: readonly Extract<CanonicalSource, { kind: "npm" }>[]
134
+ ): { packageName: string; exportName: string } | null {
135
+ const separator = renderRoot.canonical.lastIndexOf("#");
136
+ if (separator <= 0 || separator === renderRoot.canonical.length - 1) return null;
137
+ const exportName = renderRoot.canonical
138
+ .slice(separator + 1)
139
+ .split(".")
140
+ .at(-1);
141
+ if (!exportName || exportName === "default") return null;
142
+
143
+ for (const source of sources) {
144
+ if (!ownedImportMatchesRoot(renderRoot.importSource, source.specifier)) continue;
145
+ if (source.exclude?.includes(exportName)) continue;
146
+ if (source.include && !source.include.includes(exportName)) continue;
147
+ return { packageName: source.specifier, exportName };
148
+ }
149
+ return null;
150
+ }
151
+
111
152
  function packageSubpath(importPath: string, packageName: string): string {
112
153
  return importPath === packageName ? "" : importPath.slice(packageName.length + 1);
113
154
  }
@@ -11,7 +11,20 @@ const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = path.dirname(__filename);
12
12
  const registryPath = path.resolve(__dirname, "../../../../../docs/codes/registry.json");
13
13
 
14
- const SUBSYSTEM_EMITTED_CODES = ["FUI9001", "FUI9002", "FUI9003", "FUI9004", "FUI9005"] as const;
14
+ const SUBSYSTEM_EMITTED_CODES = [
15
+ "FUI9001",
16
+ "FUI9002",
17
+ "FUI9003",
18
+ "FUI9004",
19
+ "FUI9005",
20
+ "FUI9006",
21
+ "FUI9007",
22
+ "FUI9008",
23
+ // Emitted by the CLI scan layer, not the rule engine: the contract's token
24
+ // sources are read before any rule runs, and an unreadable one aborts the
25
+ // scan rather than producing a finding.
26
+ "FUI9009",
27
+ ] as const;
15
28
 
16
29
  const RESERVED_CODES = {
17
30
  FUI1001: "Reserved for canonical primitive findings; no core rule emits it.",
@@ -488,6 +488,46 @@ export const CODES = [
488
488
  fixAvailable: false,
489
489
  evidenceRequired: false,
490
490
  }),
491
+ code({
492
+ code: "FUI9006",
493
+ ruleId: "config/unmatched-exclude",
494
+ category: "system",
495
+ defaultSeverity: "moderate",
496
+ title: "Rule exclude matched no scanned file",
497
+ lifecycle: "experimental",
498
+ fixAvailable: false,
499
+ evidenceRequired: false,
500
+ }),
501
+ code({
502
+ code: "FUI9007",
503
+ ruleId: "config/colliding-record",
504
+ category: "system",
505
+ defaultSeverity: "moderate",
506
+ title: "Governance record was dropped as a duplicate",
507
+ lifecycle: "experimental",
508
+ fixAvailable: false,
509
+ evidenceRequired: false,
510
+ }),
511
+ code({
512
+ code: "FUI9008",
513
+ ruleId: "config/overridden-record-severity",
514
+ category: "system",
515
+ defaultSeverity: "moderate",
516
+ title: "Rule override outranks a record's authored severity",
517
+ lifecycle: "experimental",
518
+ fixAvailable: false,
519
+ evidenceRequired: false,
520
+ }),
521
+ code({
522
+ code: "FUI9009",
523
+ ruleId: "contract/unreadable-token-source",
524
+ category: "system",
525
+ defaultSeverity: "critical",
526
+ title: "Contract token source cannot be read",
527
+ lifecycle: "experimental",
528
+ fixAvailable: false,
529
+ evidenceRequired: false,
530
+ }),
491
531
  ] as const satisfies readonly FuiCode[];
492
532
 
493
533
  export const byCode: ReadonlyMap<string, FuiCode> = new Map(