@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/build/hash.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,13 @@
1
+ import { createHash } from 'node:crypto';
2
+ /**
3
+ * Generic content hash (hex, truncated to 16 chars — matches `computeFingerprint`'s length so
4
+ * every content-addressed id in the system reads the same length). Used for markdown section
5
+ * hashes (lockfile drift detection) and the generated-rules.json divergence hash — deliberately
6
+ * NOT the same function as `computeFingerprint` (baseline/fingerprint.ts), which is a violation
7
+ * fingerprint with its own semantics; this is a plain content hash with no domain meaning beyond
8
+ * "did these bytes change."
9
+ */
10
+ export function sha256Hex(content) {
11
+ return createHash('sha256').update(content, 'utf8').digest('hex').slice(0, 16);
12
+ }
13
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/build/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { BaselineEntry } from '../baseline/store.js';
2
+ import type { Violation } from '../types/violation.js';
3
+ export interface ImpactDelta {
4
+ /** Violations the proposed ruleset produces that the current effective ruleset does not, and
5
+ * that aren't already tolerated by the baseline — "adds N new violations" (ADR 011). */
6
+ readonly addedNew: readonly Violation[];
7
+ /** Baseline entries whose violation the current effective ruleset produces but the proposed one
8
+ * does not (the rule that used to catch it is gone or narrowed) — "masks M baselined" (ADR 011):
9
+ * the debt doesn't get *fixed*, it stops being *evaluated*, which is worth flagging separately
10
+ * from a genuine fix. */
11
+ readonly maskedBaselined: readonly BaselineEntry[];
12
+ }
13
+ /**
14
+ * Compiles the proposed ruleset's impact relative to the current one (ADR 011's build-gate
15
+ * default: "adds N new violations / masks M baselined"). Pure — both violation sets are supplied
16
+ * by the caller (one fresh scan, evaluated twice against two rule sets — see
17
+ * `packages/cli/src/commands/build.ts`), so this function does no scanning itself.
18
+ */
19
+ export declare function computeImpactDelta(currentViolations: readonly Violation[], proposedViolations: readonly Violation[], baselineEntries: readonly BaselineEntry[]): ImpactDelta;
20
+ //# sourceMappingURL=impact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/build/impact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B;4FACwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC;;;6BAGyB;IACzB,QAAQ,CAAC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;CACpD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,iBAAiB,EAAE,SAAS,SAAS,EAAE,EACvC,kBAAkB,EAAE,SAAS,SAAS,EAAE,EACxC,eAAe,EAAE,SAAS,aAAa,EAAE,GACxC,WAAW,CASb"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Compiles the proposed ruleset's impact relative to the current one (ADR 011's build-gate
3
+ * default: "adds N new violations / masks M baselined"). Pure — both violation sets are supplied
4
+ * by the caller (one fresh scan, evaluated twice against two rule sets — see
5
+ * `packages/cli/src/commands/build.ts`), so this function does no scanning itself.
6
+ */
7
+ export function computeImpactDelta(currentViolations, proposedViolations, baselineEntries) {
8
+ const currentIds = new Set(currentViolations.map((v) => v.id));
9
+ const proposedIds = new Set(proposedViolations.map((v) => v.id));
10
+ const baselinedIds = new Set(baselineEntries.map((e) => e.fingerprint));
11
+ const addedNew = proposedViolations.filter((v) => !currentIds.has(v.id) && !baselinedIds.has(v.id));
12
+ const maskedBaselined = baselineEntries.filter((e) => currentIds.has(e.fingerprint) && !proposedIds.has(e.fingerprint));
13
+ return { addedNew, maskedBaselined };
14
+ }
15
+ //# sourceMappingURL=impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/build/impact.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,iBAAuC,EACvC,kBAAwC,EACxC,eAAyC;IAEzC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IAExE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpG,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IAExH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export * from './hash.js';
2
+ export * from './sections.js';
3
+ export * from './schema.js';
4
+ export * from './provenance.js';
5
+ export * from './ground.js';
6
+ export * from './tier1.js';
7
+ export * from './tier2.js';
8
+ export * from './propose.js';
9
+ export * from './diff.js';
10
+ export * from './impact.js';
11
+ export * from './merge.js';
12
+ export * from './types.js';
13
+ export * from './export-ir.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export * from './hash.js';
2
+ export * from './sections.js';
3
+ export * from './schema.js';
4
+ export * from './provenance.js';
5
+ export * from './ground.js';
6
+ export * from './tier1.js';
7
+ export * from './tier2.js';
8
+ export * from './propose.js';
9
+ export * from './diff.js';
10
+ export * from './impact.js';
11
+ export * from './merge.js';
12
+ export * from './types.js';
13
+ export * from './export-ir.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { RuleIR } from '../types/ir.js';
2
+ /** Thrown when a hand-authored (`align.config.ts`) rule and a doc-generated rule share an id but
3
+ * disagree on structural content — a fail-fast config-load error (CODING_BEST_PRACTICES.md §21),
4
+ * never a silent pick-one. */
5
+ export declare class GeneratedRuleCollisionError extends Error {
6
+ readonly ruleId: string;
7
+ constructor(ruleId: string, baseRule: RuleIR, generatedRule: RuleIR);
8
+ }
9
+ /**
10
+ * The config-integration mechanism for `align build` (ADR 011): merges `.align/generated-rules.json`
11
+ * into the hand-authored `align.config.ts` ruleset's rule list. Called by the CLI's config loader
12
+ * (`packages/cli/src/config.ts`) — never by `defineProject` itself, which stays fs-free (core's
13
+ * pure/no-I/O discipline, CODING_BEST_PRACTICES.md §14/15). This is the "least magical" of the
14
+ * options ADR 011 named (an explicit `withGeneratedRules()` call in every `align.config.ts` vs. an
15
+ * automatic merge at the loader boundary): every existing surface (`check`, `doctor`, `mcp`,
16
+ * `build` itself) picks up generated rules for free, with zero required edits to a human-authored
17
+ * file — "nothing machine-written lives inside a human-edited file" (ADR 011) extends to not even
18
+ * requiring an *import statement* referencing the machine-written file.
19
+ *
20
+ * Collision policy: an id present in both lists must be structurally identical (same `kind` and
21
+ * selectors) or this throws `GeneratedRuleCollisionError`. When identical, the generated rule's
22
+ * richer provenance (`sourceFile`/`sourceLineRange`/`sourceQuote`) is merged onto the surviving
23
+ * rule so its violations still quote the doc; a hand-authored `.because()` is preserved by
24
+ * prefixing it onto the generated "Enforced by ..." phrase rather than being discarded.
25
+ */
26
+ export declare function mergeGeneratedRules(baseRules: readonly RuleIR[], generatedRules: readonly RuleIR[]): readonly RuleIR[];
27
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/build/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;8BAE8B;AAC9B,qBAAa,2BAA4B,SAAQ,KAAK;aAElC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM;CASxB;AA2BD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAkCtH"}
@@ -0,0 +1,85 @@
1
+ /** Thrown when a hand-authored (`align.config.ts`) rule and a doc-generated rule share an id but
2
+ * disagree on structural content — a fail-fast config-load error (CODING_BEST_PRACTICES.md §21),
3
+ * never a silent pick-one. */
4
+ export class GeneratedRuleCollisionError extends Error {
5
+ ruleId;
6
+ constructor(ruleId, baseRule, generatedRule) {
7
+ super(`Rule id '${ruleId}' is defined both in align.config.ts and in .align/generated-rules.json ` +
8
+ `with different content. Rename one of them, or make the two definitions agree. ` +
9
+ `config: ${JSON.stringify(stripProvenance(baseRule))} vs generated: ${JSON.stringify(stripProvenance(generatedRule))}`);
10
+ this.ruleId = ruleId;
11
+ this.name = 'GeneratedRuleCollisionError';
12
+ }
13
+ }
14
+ function stripProvenance(r) {
15
+ const { provenance: _provenance, ...rest } = r;
16
+ return rest;
17
+ }
18
+ /** Sorted-key JSON serialization (same technique as `diff.ts`'s `canonical`) — comparison must not
19
+ * depend on incidental object property insertion order between a DSL-constructed rule and a
20
+ * ground.ts-constructed one. */
21
+ function canonical(value) {
22
+ return JSON.stringify(value, (_key, val) => {
23
+ if (val !== null && typeof val === 'object' && !Array.isArray(val)) {
24
+ const sorted = {};
25
+ for (const key of Object.keys(val).sort()) {
26
+ sorted[key] = val[key];
27
+ }
28
+ return sorted;
29
+ }
30
+ return val;
31
+ });
32
+ }
33
+ function structurallyEqual(a, b) {
34
+ return canonical(stripProvenance(a)) === canonical(stripProvenance(b));
35
+ }
36
+ /**
37
+ * The config-integration mechanism for `align build` (ADR 011): merges `.align/generated-rules.json`
38
+ * into the hand-authored `align.config.ts` ruleset's rule list. Called by the CLI's config loader
39
+ * (`packages/cli/src/config.ts`) — never by `defineProject` itself, which stays fs-free (core's
40
+ * pure/no-I/O discipline, CODING_BEST_PRACTICES.md §14/15). This is the "least magical" of the
41
+ * options ADR 011 named (an explicit `withGeneratedRules()` call in every `align.config.ts` vs. an
42
+ * automatic merge at the loader boundary): every existing surface (`check`, `doctor`, `mcp`,
43
+ * `build` itself) picks up generated rules for free, with zero required edits to a human-authored
44
+ * file — "nothing machine-written lives inside a human-edited file" (ADR 011) extends to not even
45
+ * requiring an *import statement* referencing the machine-written file.
46
+ *
47
+ * Collision policy: an id present in both lists must be structurally identical (same `kind` and
48
+ * selectors) or this throws `GeneratedRuleCollisionError`. When identical, the generated rule's
49
+ * richer provenance (`sourceFile`/`sourceLineRange`/`sourceQuote`) is merged onto the surviving
50
+ * rule so its violations still quote the doc; a hand-authored `.because()` is preserved by
51
+ * prefixing it onto the generated "Enforced by ..." phrase rather than being discarded.
52
+ */
53
+ export function mergeGeneratedRules(baseRules, generatedRules) {
54
+ const baseById = new Map(baseRules.map((r) => [r.id, r]));
55
+ const merged = [];
56
+ const consumedBaseIds = new Set();
57
+ for (const generated of generatedRules) {
58
+ const base = baseById.get(generated.id);
59
+ if (base === undefined) {
60
+ merged.push(generated);
61
+ continue;
62
+ }
63
+ if (!structurallyEqual(base, generated)) {
64
+ throw new GeneratedRuleCollisionError(generated.id, base, generated);
65
+ }
66
+ consumedBaseIds.add(base.id);
67
+ const baseBecause = base.provenance.because;
68
+ const generatedBecause = generated.provenance.because;
69
+ const because = baseBecause === undefined
70
+ ? generatedBecause
71
+ : generatedBecause === undefined
72
+ ? baseBecause
73
+ : `${baseBecause} ${generatedBecause}`;
74
+ merged.push({
75
+ ...generated,
76
+ provenance: { ...generated.provenance, ...(because === undefined ? {} : { because }) },
77
+ });
78
+ }
79
+ for (const base of baseRules) {
80
+ if (!consumedBaseIds.has(base.id))
81
+ merged.push(base);
82
+ }
83
+ return merged;
84
+ }
85
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/build/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type ComponentName, type RepoRelativePath } from '../types/branded.js';
2
+ import type { ComponentDefinitionIR } from '../types/ir.js';
3
+ import type { BuildProposal } from './types.js';
4
+ /**
5
+ * Extracts, grounds, and deduplicates rule proposals from an architecture/best-practices markdown
6
+ * doc (ADR 011's precision ladder, tiers 1+2 — this stage's CLI path is zero-LLM; tier 3 prose
7
+ * surfaces as a `proseSections` scaffold, never compiled here). Pure — no I/O, no LLM.
8
+ *
9
+ * Rule ids are content-addressed (derived from `kind` + the rule's own selectors, the same scheme
10
+ * `dsl/index.ts` uses — see `ground.ts`), which is what makes ADR 011's rule-level diff
11
+ * minimization trivial: an IR-identical re-proposal always produces the same id, so a caller
12
+ * diffing this function's output against a previous run's `rules` array sees an empty diff for
13
+ * anything that didn't structurally change, with zero stateful bookkeeping.
14
+ */
15
+ export declare function proposeRulesFromDoc(docText: string, docPath: RepoRelativePath, components: Readonly<Record<ComponentName, ComponentDefinitionIR>>, registeredHostPredicates?: ReadonlySet<string>): BuildProposal;
16
+ //# sourceMappingURL=propose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propose.d.ts","sourceRoot":"","sources":["../../src/build/propose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AACjH,OAAO,KAAK,EAAE,qBAAqB,EAAU,MAAM,gBAAgB,CAAC;AAKpE,OAAO,KAAK,EAAE,aAAa,EAA6E,MAAM,YAAY,CAAC;AAkB3H;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,EAClE,wBAAwB,GAAE,WAAW,CAAC,MAAM,CAAa,GACxD,aAAa,CA+Ff"}
@@ -0,0 +1,126 @@
1
+ import { toRepoRelativePath } from '../types/branded.js';
2
+ import { parseMarkdownDoc } from './sections.js';
3
+ import { extractFencedAlignBlocks } from './tier1.js';
4
+ import { extractStructuredBullets } from './tier2.js';
5
+ import { groundFragment } from './ground.js';
6
+ /** Structural equality for conflict detection — everything except `id` (which two candidates
7
+ * sharing a group already share by definition) and `provenance` (which legitimately differs by
8
+ * source location even for the same semantic rule). */
9
+ function structurallyEqual(a, b) {
10
+ const strip = (r) => {
11
+ const { provenance: _provenance, ...rest } = r;
12
+ return rest;
13
+ };
14
+ return JSON.stringify(strip(a)) === JSON.stringify(strip(b));
15
+ }
16
+ /**
17
+ * Extracts, grounds, and deduplicates rule proposals from an architecture/best-practices markdown
18
+ * doc (ADR 011's precision ladder, tiers 1+2 — this stage's CLI path is zero-LLM; tier 3 prose
19
+ * surfaces as a `proseSections` scaffold, never compiled here). Pure — no I/O, no LLM.
20
+ *
21
+ * Rule ids are content-addressed (derived from `kind` + the rule's own selectors, the same scheme
22
+ * `dsl/index.ts` uses — see `ground.ts`), which is what makes ADR 011's rule-level diff
23
+ * minimization trivial: an IR-identical re-proposal always produces the same id, so a caller
24
+ * diffing this function's output against a previous run's `rules` array sees an empty diff for
25
+ * anything that didn't structurally change, with zero stateful bookkeeping.
26
+ */
27
+ export function proposeRulesFromDoc(docText, docPath, components, registeredHostPredicates = new Set()) {
28
+ const { lines, sections } = parseMarkdownDoc(docText);
29
+ const flagged = [];
30
+ const proseSections = [];
31
+ const candidates = [];
32
+ const sectionMeta = [];
33
+ for (const section of sections) {
34
+ const tier1 = extractFencedAlignBlocks(lines, section, docPath);
35
+ const tier2 = extractStructuredBullets(lines, section, docPath);
36
+ flagged.push(...tier1.errors, ...tier2.errors);
37
+ for (const f of tier1.fragments) {
38
+ const result = groundFragment(f.fragment, section.anchor, docPath, f.sourceLineRange, f.sourceQuote, components, registeredHostPredicates);
39
+ if (result.ok)
40
+ candidates.push({ sectionAnchor: section.anchor, rule: result.rule });
41
+ else
42
+ flagged.push(result.flagged);
43
+ }
44
+ for (const b of tier2.bullets) {
45
+ const result = groundFragment(b.fragment, section.anchor, docPath, b.sourceLineRange, b.sourceQuote, components, registeredHostPredicates);
46
+ if (result.ok)
47
+ candidates.push({ sectionAnchor: section.anchor, rule: result.rule });
48
+ else
49
+ flagged.push(result.flagged);
50
+ }
51
+ let tier;
52
+ if (tier1.fragments.length > 0 || tier1.errors.length > 0)
53
+ tier = 'verbatim';
54
+ else if (tier2.bullets.length > 0 || tier2.errors.length > 0)
55
+ tier = 'bullet';
56
+ else if (section.bodyText.trim().length > 0)
57
+ tier = 'prose';
58
+ else
59
+ tier = 'empty';
60
+ if (tier === 'prose') {
61
+ proseSections.push({
62
+ anchor: section.anchor,
63
+ headingText: section.headingText,
64
+ startLine: section.startLine,
65
+ endLine: section.endLine,
66
+ concerns: [],
67
+ });
68
+ }
69
+ sectionMeta.push({
70
+ anchor: section.anchor,
71
+ headingText: section.headingText,
72
+ startLine: section.startLine,
73
+ endLine: section.endLine,
74
+ contentHash: section.contentHash,
75
+ tier,
76
+ });
77
+ }
78
+ // Resolve id collisions: candidates sharing an id are either the same rule proposed from
79
+ // multiple places (fine — keep the earliest by document order) or a genuine disagreement
80
+ // (different `includeTypeOnly`/`canDependOn` for the same scope/layer) — flag every candidate in
81
+ // a disagreeing group rather than silently picking a winner (ADR 011: never silently written).
82
+ const byId = new Map();
83
+ for (const c of candidates) {
84
+ const list = byId.get(c.rule.id);
85
+ if (list === undefined)
86
+ byId.set(c.rule.id, [c]);
87
+ else
88
+ list.push(c);
89
+ }
90
+ const finalRules = [];
91
+ const finalRuleIdsBySection = new Map();
92
+ const recordRuleId = (anchor, id) => {
93
+ const list = finalRuleIdsBySection.get(anchor);
94
+ if (list === undefined)
95
+ finalRuleIdsBySection.set(anchor, [id]);
96
+ else
97
+ list.push(id);
98
+ };
99
+ for (const [id, group] of byId) {
100
+ const first = group[0];
101
+ if (first === undefined)
102
+ continue;
103
+ const allEqual = group.every((c) => structurallyEqual(c.rule, first.rule));
104
+ if (allEqual) {
105
+ finalRules.push(first.rule);
106
+ recordRuleId(first.sectionAnchor, first.rule.id);
107
+ continue;
108
+ }
109
+ for (const c of group) {
110
+ flagged.push({
111
+ section: c.sectionAnchor,
112
+ sourceFile: c.rule.provenance.sourceFile === undefined ? docPath : toRepoRelativePath(c.rule.provenance.sourceFile),
113
+ sourceLineRange: c.rule.provenance.sourceLineRange ?? { startLine: 1, endLine: 1 },
114
+ sourceQuote: c.rule.provenance.sourceQuote ?? '',
115
+ reason: 'conflicting-rule-id',
116
+ detail: `Rule id '${id}' was proposed with disagreeing content across sections — resolve the conflict in the doc before building.`,
117
+ });
118
+ }
119
+ }
120
+ const sectionsOut = sectionMeta.map((s) => ({
121
+ ...s,
122
+ ruleIds: [...new Set(finalRuleIdsBySection.get(s.anchor) ?? [])],
123
+ }));
124
+ return { sections: sectionsOut, rules: finalRules, flagged, proseSections };
125
+ }
126
+ //# sourceMappingURL=propose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propose.js","sourceRoot":"","sources":["../../src/build/propose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { RepoRelativePath } from '../types/branded.js';
2
+ import type { RuleProvenance } from '../types/ir.js';
3
+ import type { SourceRange } from '../types/violation.js';
4
+ /**
5
+ * Renders the "Enforced by <file>:<lines>: '<quote>'" phrase every doc-built violation message
6
+ * carries (ADR 011's acceptance criterion, `IMPLEMENTATION_PLAN.md` Stage 4 goal text). Single
7
+ * line ranges render as `N`, not `N-N`.
8
+ */
9
+ export declare function renderEnforcedBy(sourceFile: RepoRelativePath, range: SourceRange, quote: string): string;
10
+ /**
11
+ * Builds a doc-built rule's `RuleProvenance` block (ADR 011, `docs/ir-schema.md`). `.because()`
12
+ * and `sourceQuote` converge on the same terminal-output field (ADR 011 Consequences) — `because`
13
+ * is auto-populated from the "Enforced by..." phrase; an author-supplied override (a tier-1
14
+ * fragment's `because` field, or a hand-written `.because()` on a rule align build later merges
15
+ * with — see `merge.ts`) is prepended rather than replacing it, so provenance is never silently
16
+ * lost.
17
+ */
18
+ export declare function buildProvenance(sourceFile: RepoRelativePath, sourceLineRange: SourceRange, sourceQuote: string, authorBecause?: string): RuleProvenance;
19
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/build/provenance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxG;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,WAAW,EAC5B,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,cAAc,CAIhB"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Renders the "Enforced by <file>:<lines>: '<quote>'" phrase every doc-built violation message
3
+ * carries (ADR 011's acceptance criterion, `IMPLEMENTATION_PLAN.md` Stage 4 goal text). Single
4
+ * line ranges render as `N`, not `N-N`.
5
+ */
6
+ export function renderEnforcedBy(sourceFile, range, quote) {
7
+ const lines = range.startLine === range.endLine ? `${range.startLine}` : `${range.startLine}-${range.endLine}`;
8
+ return `Enforced by ${sourceFile}:${lines}: '${quote}'`;
9
+ }
10
+ /**
11
+ * Builds a doc-built rule's `RuleProvenance` block (ADR 011, `docs/ir-schema.md`). `.because()`
12
+ * and `sourceQuote` converge on the same terminal-output field (ADR 011 Consequences) — `because`
13
+ * is auto-populated from the "Enforced by..." phrase; an author-supplied override (a tier-1
14
+ * fragment's `because` field, or a hand-written `.because()` on a rule align build later merges
15
+ * with — see `merge.ts`) is prepended rather than replacing it, so provenance is never silently
16
+ * lost.
17
+ */
18
+ export function buildProvenance(sourceFile, sourceLineRange, sourceQuote, authorBecause) {
19
+ const enforcedBy = renderEnforcedBy(sourceFile, sourceLineRange, sourceQuote);
20
+ const because = authorBecause === undefined ? enforcedBy : `${authorBecause} ${enforcedBy}`;
21
+ return { because, sourceFile, sourceLineRange, sourceQuote };
22
+ }
23
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../src/build/provenance.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAA4B,EAAE,KAAkB,EAAE,KAAa;IAC9F,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;IAC/G,OAAO,eAAe,UAAU,IAAI,KAAK,MAAM,KAAK,GAAG,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,UAA4B,EAC5B,eAA4B,EAC5B,WAAmB,EACnB,aAAsB;IAEtB,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,UAAU,EAAE,CAAC;IAC5F,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;AAC/D,CAAC"}