@spikedpunch/align-core 0.1.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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/dist/baseline/fingerprint.d.ts +19 -0
  4. package/dist/baseline/fingerprint.d.ts.map +1 -0
  5. package/dist/baseline/fingerprint.js +0 -0
  6. package/dist/baseline/fingerprint.js.map +1 -0
  7. package/dist/baseline/store.d.ts +81 -0
  8. package/dist/baseline/store.d.ts.map +1 -0
  9. package/dist/baseline/store.js +111 -0
  10. package/dist/baseline/store.js.map +1 -0
  11. package/dist/build/diff.d.ts +22 -0
  12. package/dist/build/diff.d.ts.map +1 -0
  13. package/dist/build/diff.js +61 -0
  14. package/dist/build/diff.js.map +1 -0
  15. package/dist/build/export-ir.d.ts +11 -0
  16. package/dist/build/export-ir.d.ts.map +1 -0
  17. package/dist/build/export-ir.js +4 -0
  18. package/dist/build/export-ir.js.map +1 -0
  19. package/dist/build/ground.d.ts +32 -0
  20. package/dist/build/ground.d.ts.map +1 -0
  21. package/dist/build/ground.js +162 -0
  22. package/dist/build/ground.js.map +1 -0
  23. package/dist/build/hash.d.ts +10 -0
  24. package/dist/build/hash.d.ts.map +1 -0
  25. package/dist/build/hash.js +13 -0
  26. package/dist/build/hash.js.map +1 -0
  27. package/dist/build/impact.d.ts +20 -0
  28. package/dist/build/impact.d.ts.map +1 -0
  29. package/dist/build/impact.js +15 -0
  30. package/dist/build/impact.js.map +1 -0
  31. package/dist/build/index.d.ts +14 -0
  32. package/dist/build/index.d.ts.map +1 -0
  33. package/dist/build/index.js +14 -0
  34. package/dist/build/index.js.map +1 -0
  35. package/dist/build/merge.d.ts +27 -0
  36. package/dist/build/merge.d.ts.map +1 -0
  37. package/dist/build/merge.js +85 -0
  38. package/dist/build/merge.js.map +1 -0
  39. package/dist/build/propose.d.ts +16 -0
  40. package/dist/build/propose.d.ts.map +1 -0
  41. package/dist/build/propose.js +126 -0
  42. package/dist/build/propose.js.map +1 -0
  43. package/dist/build/provenance.d.ts +19 -0
  44. package/dist/build/provenance.d.ts.map +1 -0
  45. package/dist/build/provenance.js +23 -0
  46. package/dist/build/provenance.js.map +1 -0
  47. package/dist/build/schema.d.ts +1825 -0
  48. package/dist/build/schema.d.ts.map +1 -0
  49. package/dist/build/schema.js +128 -0
  50. package/dist/build/schema.js.map +1 -0
  51. package/dist/build/sections.d.ts +30 -0
  52. package/dist/build/sections.d.ts.map +1 -0
  53. package/dist/build/sections.js +58 -0
  54. package/dist/build/sections.js.map +1 -0
  55. package/dist/build/tier1.d.ts +24 -0
  56. package/dist/build/tier1.d.ts.map +1 -0
  57. package/dist/build/tier1.js +68 -0
  58. package/dist/build/tier1.js.map +1 -0
  59. package/dist/build/tier2.d.ts +42 -0
  60. package/dist/build/tier2.d.ts.map +1 -0
  61. package/dist/build/tier2.js +187 -0
  62. package/dist/build/tier2.js.map +1 -0
  63. package/dist/build/types.d.ts +42 -0
  64. package/dist/build/types.d.ts.map +1 -0
  65. package/dist/build/types.js +2 -0
  66. package/dist/build/types.js.map +1 -0
  67. package/dist/components/glob.d.ts +10 -0
  68. package/dist/components/glob.d.ts.map +1 -0
  69. package/dist/components/glob.js +52 -0
  70. package/dist/components/glob.js.map +1 -0
  71. package/dist/components/registry.d.ts +51 -0
  72. package/dist/components/registry.d.ts.map +1 -0
  73. package/dist/components/registry.js +121 -0
  74. package/dist/components/registry.js.map +1 -0
  75. package/dist/dsl/factories.d.ts +86 -0
  76. package/dist/dsl/factories.d.ts.map +1 -0
  77. package/dist/dsl/factories.js +145 -0
  78. package/dist/dsl/factories.js.map +1 -0
  79. package/dist/dsl/index.d.ts +46 -0
  80. package/dist/dsl/index.d.ts.map +1 -0
  81. package/dist/dsl/index.js +67 -0
  82. package/dist/dsl/index.js.map +1 -0
  83. package/dist/dsl/reserved-names.typecheck.d.ts +2 -0
  84. package/dist/dsl/reserved-names.typecheck.d.ts.map +1 -0
  85. package/dist/dsl/reserved-names.typecheck.js +16 -0
  86. package/dist/dsl/reserved-names.typecheck.js.map +1 -0
  87. package/dist/dsl/verb-manifest.d.ts +12 -0
  88. package/dist/dsl/verb-manifest.d.ts.map +1 -0
  89. package/dist/dsl/verb-manifest.js +101 -0
  90. package/dist/dsl/verb-manifest.js.map +1 -0
  91. package/dist/fix/apply.d.ts +67 -0
  92. package/dist/fix/apply.d.ts.map +1 -0
  93. package/dist/fix/apply.js +163 -0
  94. package/dist/fix/apply.js.map +1 -0
  95. package/dist/fix/index.d.ts +3 -0
  96. package/dist/fix/index.d.ts.map +1 -0
  97. package/dist/fix/index.js +3 -0
  98. package/dist/fix/index.js.map +1 -0
  99. package/dist/fix/schema.d.ts +180 -0
  100. package/dist/fix/schema.d.ts.map +1 -0
  101. package/dist/fix/schema.js +41 -0
  102. package/dist/fix/schema.js.map +1 -0
  103. package/dist/gates/advisories.d.ts +11 -0
  104. package/dist/gates/advisories.d.ts.map +1 -0
  105. package/dist/gates/advisories.js +27 -0
  106. package/dist/gates/advisories.js.map +1 -0
  107. package/dist/gates/types.d.ts +36 -0
  108. package/dist/gates/types.d.ts.map +1 -0
  109. package/dist/gates/types.js +8 -0
  110. package/dist/gates/types.js.map +1 -0
  111. package/dist/index.d.ts +17 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +17 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/orchestrator.d.ts +51 -0
  116. package/dist/orchestrator.d.ts.map +1 -0
  117. package/dist/orchestrator.js +304 -0
  118. package/dist/orchestrator.js.map +1 -0
  119. package/dist/payload/builder.d.ts +49 -0
  120. package/dist/payload/builder.d.ts.map +1 -0
  121. package/dist/payload/builder.js +61 -0
  122. package/dist/payload/builder.js.map +1 -0
  123. package/dist/payload/mermaid.d.ts +22 -0
  124. package/dist/payload/mermaid.d.ts.map +1 -0
  125. package/dist/payload/mermaid.js +89 -0
  126. package/dist/payload/mermaid.js.map +1 -0
  127. package/dist/plugin/registry.d.ts +24 -0
  128. package/dist/plugin/registry.d.ts.map +1 -0
  129. package/dist/plugin/registry.js +24 -0
  130. package/dist/plugin/registry.js.map +1 -0
  131. package/dist/rules/component-refs.d.ts +26 -0
  132. package/dist/rules/component-refs.d.ts.map +1 -0
  133. package/dist/rules/component-refs.js +81 -0
  134. package/dist/rules/component-refs.js.map +1 -0
  135. package/dist/rules/evaluators.d.ts +50 -0
  136. package/dist/rules/evaluators.d.ts.map +1 -0
  137. package/dist/rules/evaluators.js +236 -0
  138. package/dist/rules/evaluators.js.map +1 -0
  139. package/dist/rules/host-rules.d.ts +109 -0
  140. package/dist/rules/host-rules.d.ts.map +1 -0
  141. package/dist/rules/host-rules.js +152 -0
  142. package/dist/rules/host-rules.js.map +1 -0
  143. package/dist/rules/index.d.ts +7 -0
  144. package/dist/rules/index.d.ts.map +1 -0
  145. package/dist/rules/index.js +7 -0
  146. package/dist/rules/index.js.map +1 -0
  147. package/dist/rules/manifest-evaluators.d.ts +32 -0
  148. package/dist/rules/manifest-evaluators.d.ts.map +1 -0
  149. package/dist/rules/manifest-evaluators.js +124 -0
  150. package/dist/rules/manifest-evaluators.js.map +1 -0
  151. package/dist/rules/rule-category.d.ts +13 -0
  152. package/dist/rules/rule-category.d.ts.map +1 -0
  153. package/dist/rules/rule-category.js +27 -0
  154. package/dist/rules/rule-category.js.map +1 -0
  155. package/dist/rules/tarjan.d.ts +9 -0
  156. package/dist/rules/tarjan.d.ts.map +1 -0
  157. package/dist/rules/tarjan.js +102 -0
  158. package/dist/rules/tarjan.js.map +1 -0
  159. package/dist/scanner.d.ts +12 -0
  160. package/dist/scanner.d.ts.map +1 -0
  161. package/dist/scanner.js +2 -0
  162. package/dist/scanner.js.map +1 -0
  163. package/dist/telemetry/diff.d.ts +27 -0
  164. package/dist/telemetry/diff.d.ts.map +1 -0
  165. package/dist/telemetry/diff.js +54 -0
  166. package/dist/telemetry/diff.js.map +1 -0
  167. package/dist/telemetry/index.d.ts +4 -0
  168. package/dist/telemetry/index.d.ts.map +1 -0
  169. package/dist/telemetry/index.js +4 -0
  170. package/dist/telemetry/index.js.map +1 -0
  171. package/dist/telemetry/serialize.d.ts +10 -0
  172. package/dist/telemetry/serialize.d.ts.map +1 -0
  173. package/dist/telemetry/serialize.js +11 -0
  174. package/dist/telemetry/serialize.js.map +1 -0
  175. package/dist/telemetry/types.d.ts +138 -0
  176. package/dist/telemetry/types.d.ts.map +1 -0
  177. package/dist/telemetry/types.js +3 -0
  178. package/dist/telemetry/types.js.map +1 -0
  179. package/dist/types/branded.d.ts +26 -0
  180. package/dist/types/branded.d.ts.map +1 -0
  181. package/dist/types/branded.js +24 -0
  182. package/dist/types/branded.js.map +1 -0
  183. package/dist/types/graph.d.ts +46 -0
  184. package/dist/types/graph.d.ts.map +1 -0
  185. package/dist/types/graph.js +2 -0
  186. package/dist/types/graph.js.map +1 -0
  187. package/dist/types/index.d.ts +6 -0
  188. package/dist/types/index.d.ts.map +1 -0
  189. package/dist/types/index.js +6 -0
  190. package/dist/types/index.js.map +1 -0
  191. package/dist/types/ir.d.ts +1747 -0
  192. package/dist/types/ir.d.ts.map +1 -0
  193. package/dist/types/ir.js +120 -0
  194. package/dist/types/ir.js.map +1 -0
  195. package/dist/types/manifest.d.ts +58 -0
  196. package/dist/types/manifest.d.ts.map +1 -0
  197. package/dist/types/manifest.js +2 -0
  198. package/dist/types/manifest.js.map +1 -0
  199. package/dist/types/violation.d.ts +94 -0
  200. package/dist/types/violation.d.ts.map +1 -0
  201. package/dist/types/violation.js +56 -0
  202. package/dist/types/violation.js.map +1 -0
  203. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 align contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @spikedpunch/align-core
2
+
3
+ The zero-dependency (zod only) core of [align](https://github.com/SpikedPunchVictim/align) — an
4
+ architecture-conformance verification oracle for humans and LLM coding agents.
5
+
6
+ This package owns the `Violation` model, the `RuleIR` (zod) schema, the fluent DSL, the gate
7
+ orchestrator, the baseline store, and the markdown-doc→ruleset (`/build`) and deterministic
8
+ edit-apply (`/fix`) pipelines. It has **zero framework dependencies** so it stays importable without
9
+ dragging a TypeScript compiler along — the language-specific dependency-graph scanner lives in
10
+ [`@spikedpunch/align-plugin-typescript`](https://www.npmjs.com/package/@spikedpunch/align-plugin-typescript).
11
+
12
+ Most users want the `align` CLI, not this package directly — see
13
+ [`@spikedpunch/align-cli`](https://www.npmjs.com/package/@spikedpunch/align-cli). Import this package
14
+ directly only to author `align.config.ts` or to embed the engine in a custom host.
15
+
16
+ ## Entry points
17
+
18
+ - `@spikedpunch/align-core` — `Violation`/`RuleIR` model, orchestrator, baseline store.
19
+ - `@spikedpunch/align-core/dsl` — `defineProject` and the fluent `ComponentContext` used by `align.config.ts`.
20
+ - `@spikedpunch/align-core/fix` — the deterministic byte-offset edit-apply engine (no LLM dependency).
21
+
22
+ See the [root README](https://github.com/SpikedPunchVictim/align#readme) and `ARCHITECTURE.md` for
23
+ the full design and the numbered decision record in `docs/adr/`.
24
+
25
+ ## License
26
+
27
+ MIT
@@ -0,0 +1,19 @@
1
+ import { type ViolationId } from '../types/branded.js';
2
+ /**
3
+ * Snippet-hash fingerprint (ADR 006) — stable under unrelated edits (line-number-based
4
+ * fingerprints break under reformatting/comment insertion above a violation, which is exactly
5
+ * the "baseline churn" risk the plan's Key Risks table names). Callers pass the violation's
6
+ * `snippet` plus whatever structurally-relevant fields identify it (fromFile/toFile/specifier
7
+ * for `no-dependency`, the cycle's edge set for `no-cycles`) — never line numbers.
8
+ */
9
+ export declare function computeFingerprint(parts: readonly string[]): ViolationId;
10
+ /**
11
+ * File-independent content fingerprint (ADR 006 move-transfer): `ruleId` + the violation's
12
+ * `snippet` only — deliberately excludes `file`/`fromFile`/`toFile` so a renamed file's violation
13
+ * still hashes to the same content identity. Used by `BaselineStore` to recognize an orphaned
14
+ * baseline entry (its structural `fingerprint` no longer present) as the *same* logical violation
15
+ * that moved to a different file, rather than treating it as a fixed violation plus an unrelated
16
+ * new one.
17
+ */
18
+ export declare function computeContentFingerprint(ruleId: string, snippet: string): ViolationId;
19
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/baseline/fingerprint.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAOxE;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAEtF"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/baseline/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAwB;IACzD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAE,OAAe;IACvE,OAAO,kBAAkB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,81 @@
1
+ import type { RepoRelativePath, RuleId, ViolationId } from '../types/branded.js';
2
+ import type { DependencyGraph } from '../types/graph.js';
3
+ import type { Violation } from '../types/violation.js';
4
+ export interface BaselineEntry {
5
+ readonly fingerprint: ViolationId;
6
+ readonly ruleId: RuleId;
7
+ readonly file: RepoRelativePath;
8
+ readonly acceptedAt: number;
9
+ readonly acceptedBy: 'init-seed' | 'accept-existing' | 'manual';
10
+ readonly contentFingerprint?: ViolationId;
11
+ }
12
+ export interface PruneResult {
13
+ readonly removed: readonly ViolationId[];
14
+ readonly moved: readonly {
15
+ readonly from: ViolationId;
16
+ readonly to: ViolationId;
17
+ }[];
18
+ }
19
+ export interface BaselineStore {
20
+ isBaselined(violationId: ViolationId): boolean;
21
+ accept(violations: readonly Violation[], mode: BaselineEntry['acceptedBy']): void;
22
+ acceptByRule(ruleId: RuleId, violations: readonly Violation[]): void;
23
+ prune(currentGraph: DependencyGraph, currentViolations: readonly Violation[]): PruneResult;
24
+ /** Move-transfer only (ADR 006): for every baseline entry whose structural fingerprint is no
25
+ * longer present in `currentViolations`, look for a current, not-yet-baselined violation with
26
+ * the same `ruleId`+`snippet` content in a *different* file and transfer the entry to its new
27
+ * fingerprint. Unlike `prune`, entries with no match are left in place (not removed) — intended
28
+ * to run on every `align check` so a rename doesn't turn CI red for one cycle. Returns the
29
+ * transferred pairs so the caller can report "N entries transferred (file moves)". */
30
+ reconcileMoves(currentViolations: readonly Violation[]): readonly {
31
+ readonly from: ViolationId;
32
+ readonly to: ViolationId;
33
+ }[];
34
+ show(filter?: {
35
+ readonly ruleId?: RuleId;
36
+ }): readonly BaselineEntry[];
37
+ /** Not part of docs/core-interfaces.md's contract — the CLI's persistence boundary needs a
38
+ * flat snapshot to serialize to `.align/baseline.json`; core stays fs-free (functional core /
39
+ * imperative shell, CODING_BEST_PRACTICES.md §15/§16) and only exposes plain data here. */
40
+ snapshot(): readonly BaselineEntry[];
41
+ }
42
+ /**
43
+ * Pure, in-memory baseline store — no filesystem I/O (functional core; persistence is the CLI's
44
+ * imperative-shell responsibility, loaded into / dumped out of this store as plain
45
+ * `BaselineEntry[]` data).
46
+ *
47
+ * Move detection (ADR 006): a violation's structural `fingerprint` folds in file identity (e.g.
48
+ * `fromFile`/`toFile` for no-dependency), so a rename produces a brand-new fingerprint and orphans
49
+ * the old baseline entry by construction — that's the exact "renaming a file orphans its baseline
50
+ * entries" gap ADR 006's move-transfer design targets. `contentFingerprint` (ruleId+snippet,
51
+ * file-independent) is the secondary signal that recovers the match: `applyMoves` looks for a
52
+ * current, not-already-baselined violation carrying the same content fingerprint in a *different*
53
+ * file than the orphaned entry's recorded file, and transfers the entry onto the new structural
54
+ * fingerprint instead of treating the rename as "fixed" + "new". A violation whose *original*
55
+ * fingerprint is still present is never touched by this — so a genuinely new violation with an
56
+ * identical snippet in a second location, while the original violation/file still exists, is never
57
+ * mistaken for a move (both fingerprints remain distinct baseline-relevant entries).
58
+ */
59
+ export declare class InMemoryBaselineStore implements BaselineStore {
60
+ private readonly entries;
61
+ constructor(initial?: readonly BaselineEntry[]);
62
+ isBaselined(violationId: ViolationId): boolean;
63
+ accept(violations: readonly Violation[], mode: BaselineEntry['acceptedBy']): void;
64
+ acceptByRule(ruleId: RuleId, violations: readonly Violation[]): void;
65
+ reconcileMoves(currentViolations: readonly Violation[]): readonly {
66
+ readonly from: ViolationId;
67
+ readonly to: ViolationId;
68
+ }[];
69
+ prune(_currentGraph: DependencyGraph, currentViolations: readonly Violation[]): PruneResult;
70
+ /**
71
+ * Shared move-transfer core for `reconcileMoves` and `prune` — the only difference between the
72
+ * two callers is what happens to an orphaned entry that finds no match (left alone for
73
+ * `reconcileMoves`, deleted for `prune`), so that decision is made by the caller, not here.
74
+ */
75
+ private applyMoves;
76
+ show(filter?: {
77
+ readonly ruleId?: RuleId;
78
+ }): readonly BaselineEntry[];
79
+ snapshot(): readonly BaselineEntry[];
80
+ }
81
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/baseline/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,WAAW,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IAIhE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAE,CAAC;CAErF;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC;IAC/C,MAAM,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;IAClF,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IACrE,KAAK,CAAC,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,SAAS,EAAE,GAAG,WAAW,CAAC;IAC3F;;;;;0FAKsF;IACtF,cAAc,CAAC,iBAAiB,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAE,CAAC;IAC7H,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,aAAa,EAAE,CAAC;IACtE;;+FAE2F;IAC3F,QAAQ,IAAI,SAAS,aAAa,EAAE,CAAC;CACtC;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;gBAErD,OAAO,GAAE,SAAS,aAAa,EAAO;IAIlD,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI9C,MAAM,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,IAAI;IAcjF,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI;IAOpE,cAAc,CAAC,iBAAiB,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAE;IAI5H,KAAK,CAAC,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,SAAS,EAAE,GAAG,WAAW;IAM3F;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA8ClB,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,aAAa,EAAE;IAMrE,QAAQ,IAAI,SAAS,aAAa,EAAE;CAGrC"}
@@ -0,0 +1,111 @@
1
+ import { computeContentFingerprint } from './fingerprint.js';
2
+ /**
3
+ * Pure, in-memory baseline store — no filesystem I/O (functional core; persistence is the CLI's
4
+ * imperative-shell responsibility, loaded into / dumped out of this store as plain
5
+ * `BaselineEntry[]` data).
6
+ *
7
+ * Move detection (ADR 006): a violation's structural `fingerprint` folds in file identity (e.g.
8
+ * `fromFile`/`toFile` for no-dependency), so a rename produces a brand-new fingerprint and orphans
9
+ * the old baseline entry by construction — that's the exact "renaming a file orphans its baseline
10
+ * entries" gap ADR 006's move-transfer design targets. `contentFingerprint` (ruleId+snippet,
11
+ * file-independent) is the secondary signal that recovers the match: `applyMoves` looks for a
12
+ * current, not-already-baselined violation carrying the same content fingerprint in a *different*
13
+ * file than the orphaned entry's recorded file, and transfers the entry onto the new structural
14
+ * fingerprint instead of treating the rename as "fixed" + "new". A violation whose *original*
15
+ * fingerprint is still present is never touched by this — so a genuinely new violation with an
16
+ * identical snippet in a second location, while the original violation/file still exists, is never
17
+ * mistaken for a move (both fingerprints remain distinct baseline-relevant entries).
18
+ */
19
+ export class InMemoryBaselineStore {
20
+ entries = new Map();
21
+ constructor(initial = []) {
22
+ for (const entry of initial)
23
+ this.entries.set(entry.fingerprint, entry);
24
+ }
25
+ isBaselined(violationId) {
26
+ return this.entries.has(violationId);
27
+ }
28
+ accept(violations, mode) {
29
+ const now = Date.now();
30
+ for (const v of violations) {
31
+ this.entries.set(v.id, {
32
+ fingerprint: v.id,
33
+ ruleId: v.ruleId,
34
+ file: v.file,
35
+ acceptedAt: now,
36
+ acceptedBy: mode,
37
+ contentFingerprint: computeContentFingerprint(v.ruleId, v.snippet),
38
+ });
39
+ }
40
+ }
41
+ acceptByRule(ruleId, violations) {
42
+ this.accept(violations.filter((v) => v.ruleId === ruleId), 'manual');
43
+ }
44
+ reconcileMoves(currentViolations) {
45
+ return this.applyMoves(currentViolations).moved;
46
+ }
47
+ prune(_currentGraph, currentViolations) {
48
+ const { moved, unmatchedOrphans } = this.applyMoves(currentViolations);
49
+ for (const fingerprint of unmatchedOrphans)
50
+ this.entries.delete(fingerprint);
51
+ return { removed: unmatchedOrphans, moved };
52
+ }
53
+ /**
54
+ * Shared move-transfer core for `reconcileMoves` and `prune` — the only difference between the
55
+ * two callers is what happens to an orphaned entry that finds no match (left alone for
56
+ * `reconcileMoves`, deleted for `prune`), so that decision is made by the caller, not here.
57
+ */
58
+ applyMoves(currentViolations) {
59
+ const currentIds = new Set(currentViolations.map((v) => v.id));
60
+ const orphaned = [...this.entries.values()].filter((e) => !currentIds.has(e.fingerprint));
61
+ if (orphaned.length === 0)
62
+ return { moved: [], unmatchedOrphans: [] };
63
+ // Candidate move targets: current violations not already tracked under their own fingerprint
64
+ // (a violation that's already directly baselined isn't a move — it's unchanged).
65
+ const candidatesByContent = new Map();
66
+ for (const v of currentViolations) {
67
+ if (this.entries.has(v.id))
68
+ continue;
69
+ const content = computeContentFingerprint(v.ruleId, v.snippet);
70
+ const list = candidatesByContent.get(content);
71
+ if (list === undefined)
72
+ candidatesByContent.set(content, [v]);
73
+ else
74
+ list.push(v);
75
+ }
76
+ const moved = [];
77
+ const unmatchedOrphans = [];
78
+ for (const entry of orphaned) {
79
+ const content = entry.contentFingerprint;
80
+ const candidates = content === undefined ? undefined : candidatesByContent.get(content);
81
+ const matchIdx = candidates?.findIndex((v) => v.file !== entry.file) ?? -1;
82
+ const matched = matchIdx === -1 || candidates === undefined ? undefined : candidates[matchIdx];
83
+ if (matched === undefined) {
84
+ unmatchedOrphans.push(entry.fingerprint);
85
+ continue;
86
+ }
87
+ candidates?.splice(matchIdx, 1); // consumed — don't let a second orphan claim the same target
88
+ this.entries.delete(entry.fingerprint);
89
+ this.entries.set(matched.id, {
90
+ fingerprint: matched.id,
91
+ ruleId: entry.ruleId,
92
+ file: matched.file,
93
+ acceptedAt: entry.acceptedAt,
94
+ acceptedBy: entry.acceptedBy,
95
+ ...(entry.contentFingerprint === undefined ? {} : { contentFingerprint: entry.contentFingerprint }),
96
+ });
97
+ moved.push({ from: entry.fingerprint, to: matched.id });
98
+ }
99
+ return { moved, unmatchedOrphans };
100
+ }
101
+ show(filter) {
102
+ const all = [...this.entries.values()];
103
+ if (filter?.ruleId === undefined)
104
+ return all;
105
+ return all.filter((e) => e.ruleId === filter.ruleId);
106
+ }
107
+ snapshot() {
108
+ return [...this.entries.values()];
109
+ }
110
+ }
111
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/baseline/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { RuleIR } from '../types/ir.js';
2
+ export interface RuleDiff {
3
+ readonly added: readonly RuleIR[];
4
+ readonly removed: readonly RuleIR[];
5
+ readonly changed: readonly {
6
+ readonly before: RuleIR;
7
+ readonly after: RuleIR;
8
+ }[];
9
+ readonly unchanged: readonly RuleIR[];
10
+ readonly provenanceOnlyChanged: readonly {
11
+ readonly before: RuleIR;
12
+ readonly after: RuleIR;
13
+ }[];
14
+ }
15
+ /**
16
+ * Rule-level diff minimization (ADR 011): because rule ids are content-addressed (`ground.ts`),
17
+ * an IR-identical re-proposal always keeps the same id, so this diff is a plain set comparison by
18
+ * id — no stateful matching heuristics needed. `unchanged` entries are what makes a same-doc
19
+ * rebuild an empty diff in the parts that matter (added/removed/changed are all empty).
20
+ */
21
+ export declare function diffGeneratedRules(existing: readonly RuleIR[], proposed: readonly RuleIR[]): RuleDiff;
22
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/build/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAGpC,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjF,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAQtC,QAAQ,CAAC,qBAAqB,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChG;AA6BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CA4BrG"}
@@ -0,0 +1,61 @@
1
+ /** Canonical (sorted-key) JSON serialization, so two objects built with different property
2
+ * insertion order still compare equal — full-fidelity structural comparison including
3
+ * `provenance`, since a provenance-only change (a reworded sentence whose extracted selectors
4
+ * didn't change) is exactly the "changed" case this diff must surface. */
5
+ function canonical(value) {
6
+ return JSON.stringify(value, (_key, val) => {
7
+ if (val !== null && typeof val === 'object' && !Array.isArray(val)) {
8
+ const sorted = {};
9
+ for (const key of Object.keys(val).sort()) {
10
+ sorted[key] = val[key];
11
+ }
12
+ return sorted;
13
+ }
14
+ return val;
15
+ });
16
+ }
17
+ /** Same "everything except provenance" structural view `build/propose.ts`'s local
18
+ * `structurallyEqual` uses for conflict detection — kept as a separate, private helper here
19
+ * (rather than a shared export) since the two call sites compare for different reasons (dedup
20
+ * candidates vs. classifying a diff) and a shared abstraction would be a premature one for two
21
+ * call sites doing the same one-line `delete provenance` (CODING_BEST_PRACTICES.md's rule-of-three). */
22
+ function withoutProvenance(rule) {
23
+ const { provenance: _provenance, ...rest } = rule;
24
+ return rest;
25
+ }
26
+ /**
27
+ * Rule-level diff minimization (ADR 011): because rule ids are content-addressed (`ground.ts`),
28
+ * an IR-identical re-proposal always keeps the same id, so this diff is a plain set comparison by
29
+ * id — no stateful matching heuristics needed. `unchanged` entries are what makes a same-doc
30
+ * rebuild an empty diff in the parts that matter (added/removed/changed are all empty).
31
+ */
32
+ export function diffGeneratedRules(existing, proposed) {
33
+ const existingById = new Map(existing.map((r) => [r.id, r]));
34
+ const proposedById = new Map(proposed.map((r) => [r.id, r]));
35
+ const added = [];
36
+ const changed = [];
37
+ const provenanceOnlyChanged = [];
38
+ const unchanged = [];
39
+ for (const [id, after] of proposedById) {
40
+ const before = existingById.get(id);
41
+ if (before === undefined) {
42
+ added.push(after);
43
+ }
44
+ else if (canonical(before) === canonical(after)) {
45
+ unchanged.push(after);
46
+ }
47
+ else if (canonical(withoutProvenance(before)) === canonical(withoutProvenance(after))) {
48
+ provenanceOnlyChanged.push({ before, after });
49
+ }
50
+ else {
51
+ changed.push({ before, after });
52
+ }
53
+ }
54
+ const removed = [];
55
+ for (const [id, before] of existingById) {
56
+ if (!proposedById.has(id))
57
+ removed.push(before);
58
+ }
59
+ return { added, removed, changed, unchanged, provenanceOnlyChanged };
60
+ }
61
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/build/diff.ts"],"names":[],"mappings":"AAmBA;;;0EAG0E;AAC1E,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAY,EAAE,EAAE;QAClD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,MAAM,GAA4B,EAAE,CAAC;YAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAA8B,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACrE,MAAM,CAAC,GAAG,CAAC,GAAI,GAA+B,CAAC,GAAG,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;wGAIwG;AACxG,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA2B,EAAE,QAA2B;IACzF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAwC,EAAE,CAAC;IACxD,MAAM,qBAAqB,GAAwC,EAAE,CAAC;IACtE,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,SAAS,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACxF,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QACxC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC;AACvE,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `align export-ir` (ADR 014) — the trusted-mode-only pure builder for the untrusted-mode data
3
+ * source. Zero I/O here (CODING_BEST_PRACTICES.md's functional-core discipline, same as every
4
+ * other file in this directory): the CLI's `export-ir` command loads `align.config.ts` (trusted
5
+ * context, once), calls this with the resulting `RulesetIR` + `excludes`, and writes the result to
6
+ * `.align/ruleset-ir.json` itself. Core never touches the filesystem or imports a config file.
7
+ */
8
+ import type { RulesetIR } from '../types/ir.js';
9
+ import type { ExportedRuleset } from './schema.js';
10
+ export declare function buildExportedRuleset(ruleset: RulesetIR, excludes: readonly string[], exportedAt?: number): ExportedRuleset;
11
+ //# sourceMappingURL=export-ir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-ir.d.ts","sourceRoot":"","sources":["../../src/build/export-ir.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,UAAU,GAAE,MAAmB,GAC9B,eAAe,CAEjB"}
@@ -0,0 +1,4 @@
1
+ export function buildExportedRuleset(ruleset, excludes, exportedAt = Date.now()) {
2
+ return { irVersion: '1', exportedAt, excludes: [...excludes], ruleset };
3
+ }
4
+ //# sourceMappingURL=export-ir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-ir.js","sourceRoot":"","sources":["../../src/build/export-ir.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,oBAAoB,CAClC,OAAkB,EAClB,QAA2B,EAC3B,aAAqB,IAAI,CAAC,GAAG,EAAE;IAE/B,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type ComponentName, type RepoRelativePath } from '../types/branded.js';
2
+ import type { ComponentDefinitionIR, RuleIR } from '../types/ir.js';
3
+ import type { SourceRange } from '../types/violation.js';
4
+ import type { RuleFragment } from './schema.js';
5
+ import type { FlaggedProposal } from './types.js';
6
+ /** Grounds one selector token against the components registry (ADR 003/011) — exact match only,
7
+ * case-sensitive first then case-insensitive fallback (docs commonly capitalize a component name
8
+ * mid-sentence). Returns `undefined` if nothing matches; the caller flags the proposal rather than
9
+ * guessing. */
10
+ export declare function groundComponentRef(raw: string, components: Readonly<Record<ComponentName, ComponentDefinitionIR>>): ComponentName | undefined;
11
+ export type GroundResult = {
12
+ readonly ok: true;
13
+ readonly rule: RuleIR;
14
+ } | {
15
+ readonly ok: false;
16
+ readonly flagged: FlaggedProposal;
17
+ };
18
+ /**
19
+ * Grounds a `RuleFragment` (tier-1 JSON, tier-2 bullet grammar output, or an MCP-submitted
20
+ * proposal) against the components registry and builds the final `RuleIR` node, complete with a
21
+ * content-addressed `id` and full `RuleProvenance` (ADR 011). Pure — no I/O, no LLM. Every
22
+ * selector must resolve to an existing component; the first unresolvable one flags the whole
23
+ * fragment rather than partially grounding it.
24
+ *
25
+ * `registeredHostPredicates` defaults to empty — a doc/MCP-proposed `custom.host` fragment is only
26
+ * ever groundable when its `hostRuleName` names a predicate the caller's `align.config.ts` already
27
+ * registers (docs/proposals/rule-expansion-evaluation.md §B.0); callers thread this from the same
28
+ * `hostRules` export the CLI composition root extracts for `GateOrchestrator` (`config.ts`), so
29
+ * "groundable" and "checkable" always agree.
30
+ */
31
+ export declare function groundFragment(fragment: RuleFragment, section: string, sourceFile: RepoRelativePath, sourceLineRange: SourceRange, sourceQuote: string, components: Readonly<Record<ComponentName, ComponentDefinitionIR>>, registeredHostPredicates?: ReadonlySet<string>): GroundResult;
32
+ //# sourceMappingURL=ground.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ground.d.ts","sourceRoot":"","sources":["../../src/build/ground.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC3G,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlD;;;eAGe;AACf,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,GACjE,aAAa,GAAG,SAAS,CAQ3B;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC;AAapI;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,WAAW,EAC5B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,EAClE,wBAAwB,GAAE,WAAW,CAAC,MAAM,CAAa,GACxD,YAAY,CAuHd"}
@@ -0,0 +1,162 @@
1
+ import { toComponentName, toRuleId } from '../types/branded.js';
2
+ import { ruleIRSchema } from '../types/ir.js';
3
+ import { buildProvenance } from './provenance.js';
4
+ /** Strips markdown emphasis (`` `x` ``, `"x"`, `'x'`) a fragment/bullet author might wrap a
5
+ * component name in, then trims. Grounding itself is always exact-match after this normalization
6
+ * — deliberately no fuzzy/substring matching in align's own code (ADR 011: align supplies
7
+ * validation and truth; fuzzy judgment belongs to the connected client agent in the MCP path, per
8
+ * the "ADR ambiguities" resolution documented in the Stage 3 report). */
9
+ function normalizeRef(raw) {
10
+ return raw.trim().replace(/^[`"']+|[`"']+$/g, '');
11
+ }
12
+ /** Grounds one selector token against the components registry (ADR 003/011) — exact match only,
13
+ * case-sensitive first then case-insensitive fallback (docs commonly capitalize a component name
14
+ * mid-sentence). Returns `undefined` if nothing matches; the caller flags the proposal rather than
15
+ * guessing. */
16
+ export function groundComponentRef(raw, components) {
17
+ const normalized = normalizeRef(raw);
18
+ if (normalized in components)
19
+ return toComponentName(normalized);
20
+ const lower = normalized.toLowerCase();
21
+ for (const name of Object.keys(components)) {
22
+ if (name.toLowerCase() === lower)
23
+ return toComponentName(name);
24
+ }
25
+ return undefined;
26
+ }
27
+ function flag(section, sourceFile, sourceLineRange, sourceQuote, detail, reason = 'ungroundable-selector') {
28
+ return { ok: false, flagged: { section, sourceFile, sourceLineRange, sourceQuote, reason, detail } };
29
+ }
30
+ /**
31
+ * Grounds a `RuleFragment` (tier-1 JSON, tier-2 bullet grammar output, or an MCP-submitted
32
+ * proposal) against the components registry and builds the final `RuleIR` node, complete with a
33
+ * content-addressed `id` and full `RuleProvenance` (ADR 011). Pure — no I/O, no LLM. Every
34
+ * selector must resolve to an existing component; the first unresolvable one flags the whole
35
+ * fragment rather than partially grounding it.
36
+ *
37
+ * `registeredHostPredicates` defaults to empty — a doc/MCP-proposed `custom.host` fragment is only
38
+ * ever groundable when its `hostRuleName` names a predicate the caller's `align.config.ts` already
39
+ * registers (docs/proposals/rule-expansion-evaluation.md §B.0); callers thread this from the same
40
+ * `hostRules` export the CLI composition root extracts for `GateOrchestrator` (`config.ts`), so
41
+ * "groundable" and "checkable" always agree.
42
+ */
43
+ export function groundFragment(fragment, section, sourceFile, sourceLineRange, sourceQuote, components, registeredHostPredicates = new Set()) {
44
+ const provenance = buildProvenance(sourceFile, sourceLineRange, sourceQuote, fragment.because);
45
+ switch (fragment.kind) {
46
+ case 'arch.no-dependency': {
47
+ const from = groundComponentRef(fragment.from, components);
48
+ if (from === undefined)
49
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${fragment.from}'`);
50
+ const to = groundComponentRef(fragment.to, components);
51
+ if (to === undefined)
52
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${fragment.to}'`);
53
+ const rule = ruleIRSchema.parse({
54
+ kind: 'arch.no-dependency',
55
+ id: toRuleId(`arch.no-dependency:${from}->${to}`),
56
+ from,
57
+ to,
58
+ provenance,
59
+ });
60
+ return { ok: true, rule };
61
+ }
62
+ case 'arch.no-cycles': {
63
+ let scope = 'repo';
64
+ if (fragment.scope !== undefined && fragment.scope !== 'repo') {
65
+ const grounded = groundComponentRef(fragment.scope, components);
66
+ if (grounded === undefined)
67
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${fragment.scope}'`);
68
+ scope = grounded;
69
+ }
70
+ const rule = ruleIRSchema.parse({
71
+ kind: 'arch.no-cycles',
72
+ id: toRuleId(`arch.no-cycles:${scope}`),
73
+ scope,
74
+ includeTypeOnly: fragment.includeTypeOnly ?? false,
75
+ provenance,
76
+ });
77
+ return { ok: true, rule };
78
+ }
79
+ case 'arch.layers': {
80
+ const entry = fragment.layers[0];
81
+ if (entry === undefined)
82
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, 'empty layers array');
83
+ const layer = groundComponentRef(entry.layer, components);
84
+ if (layer === undefined)
85
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${entry.layer}'`);
86
+ const canDependOn = [];
87
+ for (const ref of entry.canDependOn) {
88
+ const grounded = groundComponentRef(ref, components);
89
+ if (grounded === undefined)
90
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${ref}'`);
91
+ canDependOn.push(grounded);
92
+ }
93
+ const rule = ruleIRSchema.parse({
94
+ kind: 'arch.layers',
95
+ id: toRuleId(`arch.layers:${layer}`),
96
+ layers: [{ layer, canDependOn }],
97
+ provenance,
98
+ });
99
+ return { ok: true, rule };
100
+ }
101
+ case 'custom.host': {
102
+ // Groundable only when the name is actually registered (`hostRules` in align.config.ts) —
103
+ // otherwise this would make the dry-run report "adds 0 new violations" vacuously and
104
+ // `align check` would hard-error on it anyway (rules/host-rules.ts's `validateHostRules`
105
+ // closes the check-time half; this closes the propose/build-time half). Flagged, never
106
+ // silently written (ADR 011).
107
+ if (!registeredHostPredicates.has(fragment.hostRuleName)) {
108
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `host predicate '${fragment.hostRuleName}' is not registered in align.config.ts's ` +
109
+ `'hostRules' export, so a custom.host rule naming it cannot be evaluated and would ` +
110
+ `silently report green; register the predicate first, or keep this constraint as ` +
111
+ `prose until it does`, 'unregistered-host-rule');
112
+ }
113
+ const rule = ruleIRSchema.parse({
114
+ kind: 'custom.host',
115
+ id: toRuleId(`custom.host:${fragment.hostRuleName}`),
116
+ hostRuleName: fragment.hostRuleName,
117
+ portable: false,
118
+ provenance,
119
+ });
120
+ return { ok: true, rule };
121
+ }
122
+ case 'arch.metric': {
123
+ const target = groundComponentRef(fragment.target, components);
124
+ if (target === undefined)
125
+ return flag(section, sourceFile, sourceLineRange, sourceQuote, `unknown component '${fragment.target}'`);
126
+ const rule = ruleIRSchema.parse({
127
+ kind: 'arch.metric',
128
+ id: toRuleId(`arch.metric:${fragment.metric}:${target}`),
129
+ target,
130
+ metric: fragment.metric,
131
+ max: fragment.max,
132
+ provenance,
133
+ });
134
+ return { ok: true, rule };
135
+ }
136
+ // `security.manifest.*` (ADR 013): no `ComponentRef` to ground — the manifest scan domain has
137
+ // no notion of align's file-classified components, so grounding always succeeds (same no-op
138
+ // shape as `custom.host`'s host-predicate-name path, minus the registration check since
139
+ // neither verb here references anything by name).
140
+ case 'security.manifest.source-hygiene': {
141
+ const rule = ruleIRSchema.parse({
142
+ kind: 'security.manifest.source-hygiene',
143
+ id: toRuleId('security.manifest.source-hygiene'),
144
+ provenance,
145
+ });
146
+ return { ok: true, rule };
147
+ }
148
+ case 'security.manifest.new-dependency': {
149
+ const rule = ruleIRSchema.parse({
150
+ kind: 'security.manifest.new-dependency',
151
+ id: toRuleId('security.manifest.new-dependency'),
152
+ provenance,
153
+ });
154
+ return { ok: true, rule };
155
+ }
156
+ default: {
157
+ const exhaustive = fragment;
158
+ throw new Error(`unhandled fragment kind: ${JSON.stringify(exhaustive)}`);
159
+ }
160
+ }
161
+ }
162
+ //# sourceMappingURL=ground.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ground.js","sourceRoot":"","sources":["../../src/build/ground.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generic content hash (hex, truncated to 16 chars — matches `computeFingerprint`'s length so
3
+ * every content-addressed id in the system reads the same length). Used for markdown section
4
+ * hashes (lockfile drift detection) and the generated-rules.json divergence hash — deliberately
5
+ * NOT the same function as `computeFingerprint` (baseline/fingerprint.ts), which is a violation
6
+ * fingerprint with its own semantics; this is a plain content hash with no domain meaning beyond
7
+ * "did these bytes change."
8
+ */
9
+ export declare function sha256Hex(content: string): string;
10
+ //# sourceMappingURL=hash.d.ts.map