archcheck 0.2.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 (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +536 -0
  3. package/dist/archcheck.d.ts +99 -0
  4. package/dist/archcheck.d.ts.map +1 -0
  5. package/dist/archcheck.js +339 -0
  6. package/dist/archcheck.js.map +1 -0
  7. package/dist/baseline/index.d.ts +29 -0
  8. package/dist/baseline/index.d.ts.map +1 -0
  9. package/dist/baseline/index.js +116 -0
  10. package/dist/baseline/index.js.map +1 -0
  11. package/dist/cli/args.d.ts +36 -0
  12. package/dist/cli/args.d.ts.map +1 -0
  13. package/dist/cli/args.js +233 -0
  14. package/dist/cli/args.js.map +1 -0
  15. package/dist/cli/index.d.ts +4 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +219 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/version.d.ts +2 -0
  20. package/dist/cli/version.d.ts.map +1 -0
  21. package/dist/cli/version.js +29 -0
  22. package/dist/cli/version.js.map +1 -0
  23. package/dist/contracts/banned-syntax.d.ts +48 -0
  24. package/dist/contracts/banned-syntax.d.ts.map +1 -0
  25. package/dist/contracts/banned-syntax.js +201 -0
  26. package/dist/contracts/banned-syntax.js.map +1 -0
  27. package/dist/contracts/declared-field.d.ts +39 -0
  28. package/dist/contracts/declared-field.d.ts.map +1 -0
  29. package/dist/contracts/declared-field.js +142 -0
  30. package/dist/contracts/declared-field.js.map +1 -0
  31. package/dist/contracts/encapsulation.d.ts +28 -0
  32. package/dist/contracts/encapsulation.d.ts.map +1 -0
  33. package/dist/contracts/encapsulation.js +130 -0
  34. package/dist/contracts/encapsulation.js.map +1 -0
  35. package/dist/contracts/index.d.ts +10 -0
  36. package/dist/contracts/index.d.ts.map +1 -0
  37. package/dist/contracts/index.js +9 -0
  38. package/dist/contracts/index.js.map +1 -0
  39. package/dist/contracts/judgement.d.ts +21 -0
  40. package/dist/contracts/judgement.d.ts.map +1 -0
  41. package/dist/contracts/judgement.js +109 -0
  42. package/dist/contracts/judgement.js.map +1 -0
  43. package/dist/contracts/layers.d.ts +75 -0
  44. package/dist/contracts/layers.d.ts.map +1 -0
  45. package/dist/contracts/layers.js +244 -0
  46. package/dist/contracts/layers.js.map +1 -0
  47. package/dist/contracts/naming.d.ts +21 -0
  48. package/dist/contracts/naming.d.ts.map +1 -0
  49. package/dist/contracts/naming.js +120 -0
  50. package/dist/contracts/naming.js.map +1 -0
  51. package/dist/contracts/registry.d.ts +23 -0
  52. package/dist/contracts/registry.d.ts.map +1 -0
  53. package/dist/contracts/registry.js +48 -0
  54. package/dist/contracts/registry.js.map +1 -0
  55. package/dist/contracts/required-file.d.ts +29 -0
  56. package/dist/contracts/required-file.d.ts.map +1 -0
  57. package/dist/contracts/required-file.js +131 -0
  58. package/dist/contracts/required-file.js.map +1 -0
  59. package/dist/contracts/types.d.ts +61 -0
  60. package/dist/contracts/types.d.ts.map +1 -0
  61. package/dist/contracts/types.js +2 -0
  62. package/dist/contracts/types.js.map +1 -0
  63. package/dist/coverage/index.d.ts +55 -0
  64. package/dist/coverage/index.d.ts.map +1 -0
  65. package/dist/coverage/index.js +68 -0
  66. package/dist/coverage/index.js.map +1 -0
  67. package/dist/emit/collect.d.ts +20 -0
  68. package/dist/emit/collect.d.ts.map +1 -0
  69. package/dist/emit/collect.js +17 -0
  70. package/dist/emit/collect.js.map +1 -0
  71. package/dist/emit/header.d.ts +21 -0
  72. package/dist/emit/header.d.ts.map +1 -0
  73. package/dist/emit/header.js +20 -0
  74. package/dist/emit/header.js.map +1 -0
  75. package/dist/emit/index.d.ts +57 -0
  76. package/dist/emit/index.d.ts.map +1 -0
  77. package/dist/emit/index.js +128 -0
  78. package/dist/emit/index.js.map +1 -0
  79. package/dist/emit/merge.d.ts +23 -0
  80. package/dist/emit/merge.d.ts.map +1 -0
  81. package/dist/emit/merge.js +71 -0
  82. package/dist/emit/merge.js.map +1 -0
  83. package/dist/emit/write.d.ts +20 -0
  84. package/dist/emit/write.d.ts.map +1 -0
  85. package/dist/emit/write.js +61 -0
  86. package/dist/emit/write.js.map +1 -0
  87. package/dist/exceptions/index.d.ts +46 -0
  88. package/dist/exceptions/index.d.ts.map +1 -0
  89. package/dist/exceptions/index.js +117 -0
  90. package/dist/exceptions/index.js.map +1 -0
  91. package/dist/fs/index.d.ts +12 -0
  92. package/dist/fs/index.d.ts.map +1 -0
  93. package/dist/fs/index.js +78 -0
  94. package/dist/fs/index.js.map +1 -0
  95. package/dist/graph/cache.d.ts +12 -0
  96. package/dist/graph/cache.d.ts.map +1 -0
  97. package/dist/graph/cache.js +76 -0
  98. package/dist/graph/cache.js.map +1 -0
  99. package/dist/graph/edges.d.ts +16 -0
  100. package/dist/graph/edges.d.ts.map +1 -0
  101. package/dist/graph/edges.js +161 -0
  102. package/dist/graph/edges.js.map +1 -0
  103. package/dist/graph/index.d.ts +15 -0
  104. package/dist/graph/index.d.ts.map +1 -0
  105. package/dist/graph/index.js +90 -0
  106. package/dist/graph/index.js.map +1 -0
  107. package/dist/graph/match.d.ts +12 -0
  108. package/dist/graph/match.d.ts.map +1 -0
  109. package/dist/graph/match.js +35 -0
  110. package/dist/graph/match.js.map +1 -0
  111. package/dist/graph/program.d.ts +12 -0
  112. package/dist/graph/program.d.ts.map +1 -0
  113. package/dist/graph/program.js +54 -0
  114. package/dist/graph/program.js.map +1 -0
  115. package/dist/graph/types.d.ts +55 -0
  116. package/dist/graph/types.d.ts.map +1 -0
  117. package/dist/graph/types.js +2 -0
  118. package/dist/graph/types.js.map +1 -0
  119. package/dist/index.d.ts +21 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +20 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/init/index.d.ts +23 -0
  124. package/dist/init/index.d.ts.map +1 -0
  125. package/dist/init/index.js +218 -0
  126. package/dist/init/index.js.map +1 -0
  127. package/dist/normalize/identity.d.ts +4 -0
  128. package/dist/normalize/identity.d.ts.map +1 -0
  129. package/dist/normalize/identity.js +33 -0
  130. package/dist/normalize/identity.js.map +1 -0
  131. package/dist/normalize/index.d.ts +3 -0
  132. package/dist/normalize/index.d.ts.map +1 -0
  133. package/dist/normalize/index.js +3 -0
  134. package/dist/normalize/index.js.map +1 -0
  135. package/dist/normalize/normalize.d.ts +34 -0
  136. package/dist/normalize/normalize.d.ts.map +1 -0
  137. package/dist/normalize/normalize.js +91 -0
  138. package/dist/normalize/normalize.js.map +1 -0
  139. package/dist/probes/graph.d.ts +19 -0
  140. package/dist/probes/graph.d.ts.map +1 -0
  141. package/dist/probes/graph.js +181 -0
  142. package/dist/probes/graph.js.map +1 -0
  143. package/dist/probes/index.d.ts +5 -0
  144. package/dist/probes/index.d.ts.map +1 -0
  145. package/dist/probes/index.js +5 -0
  146. package/dist/probes/index.js.map +1 -0
  147. package/dist/probes/lint.d.ts +17 -0
  148. package/dist/probes/lint.d.ts.map +1 -0
  149. package/dist/probes/lint.js +121 -0
  150. package/dist/probes/lint.js.map +1 -0
  151. package/dist/probes/model/anthropic.d.ts +47 -0
  152. package/dist/probes/model/anthropic.d.ts.map +1 -0
  153. package/dist/probes/model/anthropic.js +100 -0
  154. package/dist/probes/model/anthropic.js.map +1 -0
  155. package/dist/probes/model/budget.d.ts +22 -0
  156. package/dist/probes/model/budget.d.ts.map +1 -0
  157. package/dist/probes/model/budget.js +27 -0
  158. package/dist/probes/model/budget.js.map +1 -0
  159. package/dist/probes/model/cache.d.ts +13 -0
  160. package/dist/probes/model/cache.d.ts.map +1 -0
  161. package/dist/probes/model/cache.js +74 -0
  162. package/dist/probes/model/cache.js.map +1 -0
  163. package/dist/probes/model/index.d.ts +20 -0
  164. package/dist/probes/model/index.d.ts.map +1 -0
  165. package/dist/probes/model/index.js +160 -0
  166. package/dist/probes/model/index.js.map +1 -0
  167. package/dist/probes/model/prompt.d.ts +16 -0
  168. package/dist/probes/model/prompt.d.ts.map +1 -0
  169. package/dist/probes/model/prompt.js +57 -0
  170. package/dist/probes/model/prompt.js.map +1 -0
  171. package/dist/probes/model/provider.d.ts +88 -0
  172. package/dist/probes/model/provider.d.ts.map +1 -0
  173. package/dist/probes/model/provider.js +48 -0
  174. package/dist/probes/model/provider.js.map +1 -0
  175. package/dist/probes/structure.d.ts +3 -0
  176. package/dist/probes/structure.d.ts.map +1 -0
  177. package/dist/probes/structure.js +95 -0
  178. package/dist/probes/structure.js.map +1 -0
  179. package/dist/probes/types.d.ts +46 -0
  180. package/dist/probes/types.d.ts.map +1 -0
  181. package/dist/probes/types.js +15 -0
  182. package/dist/probes/types.js.map +1 -0
  183. package/dist/registry/define.d.ts +40 -0
  184. package/dist/registry/define.d.ts.map +1 -0
  185. package/dist/registry/define.js +4 -0
  186. package/dist/registry/define.js.map +1 -0
  187. package/dist/registry/ids.d.ts +19 -0
  188. package/dist/registry/ids.d.ts.map +1 -0
  189. package/dist/registry/ids.js +86 -0
  190. package/dist/registry/ids.js.map +1 -0
  191. package/dist/registry/index.d.ts +45 -0
  192. package/dist/registry/index.d.ts.map +1 -0
  193. package/dist/registry/index.js +71 -0
  194. package/dist/registry/index.js.map +1 -0
  195. package/dist/registry/load.d.ts +14 -0
  196. package/dist/registry/load.d.ts.map +1 -0
  197. package/dist/registry/load.js +108 -0
  198. package/dist/registry/load.js.map +1 -0
  199. package/dist/registry/validate.d.ts +24 -0
  200. package/dist/registry/validate.d.ts.map +1 -0
  201. package/dist/registry/validate.js +160 -0
  202. package/dist/registry/validate.js.map +1 -0
  203. package/dist/report/index.d.ts +10 -0
  204. package/dist/report/index.d.ts.map +1 -0
  205. package/dist/report/index.js +30 -0
  206. package/dist/report/index.js.map +1 -0
  207. package/dist/report/json.d.ts +20 -0
  208. package/dist/report/json.d.ts.map +1 -0
  209. package/dist/report/json.js +34 -0
  210. package/dist/report/json.js.map +1 -0
  211. package/dist/report/sarif.d.ts +21 -0
  212. package/dist/report/sarif.d.ts.map +1 -0
  213. package/dist/report/sarif.js +138 -0
  214. package/dist/report/sarif.js.map +1 -0
  215. package/dist/report/terminal.d.ts +3 -0
  216. package/dist/report/terminal.d.ts.map +1 -0
  217. package/dist/report/terminal.js +117 -0
  218. package/dist/report/terminal.js.map +1 -0
  219. package/dist/report/types.d.ts +28 -0
  220. package/dist/report/types.d.ts.map +1 -0
  221. package/dist/report/types.js +11 -0
  222. package/dist/report/types.js.map +1 -0
  223. package/dist/report/verdict.d.ts +21 -0
  224. package/dist/report/verdict.d.ts.map +1 -0
  225. package/dist/report/verdict.js +31 -0
  226. package/dist/report/verdict.js.map +1 -0
  227. package/dist/result.d.ts +22 -0
  228. package/dist/result.d.ts.map +1 -0
  229. package/dist/result.js +19 -0
  230. package/dist/result.js.map +1 -0
  231. package/dist/types.d.ts +178 -0
  232. package/dist/types.d.ts.map +1 -0
  233. package/dist/types.js +11 -0
  234. package/dist/types.js.map +1 -0
  235. package/dist/util/compilerApi.d.ts +2 -0
  236. package/dist/util/compilerApi.d.ts.map +1 -0
  237. package/dist/util/compilerApi.js +28 -0
  238. package/dist/util/compilerApi.js.map +1 -0
  239. package/dist/util/glob.d.ts +43 -0
  240. package/dist/util/glob.d.ts.map +1 -0
  241. package/dist/util/glob.js +195 -0
  242. package/dist/util/glob.js.map +1 -0
  243. package/dist/util/paths.d.ts +6 -0
  244. package/dist/util/paths.d.ts.map +1 -0
  245. package/dist/util/paths.js +22 -0
  246. package/dist/util/paths.js.map +1 -0
  247. package/dist/version.d.ts +13 -0
  248. package/dist/version.d.ts.map +1 -0
  249. package/dist/version.js +12 -0
  250. package/dist/version.js.map +1 -0
  251. package/package.json +72 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * The facade. The composition root, and the only place the layers are wired together.
3
+ *
4
+ * What rule this enforces: every behaviour is reachable programmatically, because the agent hook
5
+ * does not shell out (plan 10). The CLI is `parse args → one method here → a reporter`, and it owns
6
+ * no behaviour of its own. If something is only possible through the CLI, this file is wrong.
7
+ *
8
+ * What a caller must know: `load()` returns a Result and never throws, and its failures are
9
+ * CONFIG failures — exit 2. One module graph is built per ArchCheck instance, lazily and at most
10
+ * once, so every probe shares a single parse.
11
+ */
12
+ import { join } from 'node:path';
13
+ import { identitiesOf, nextBaseline, prunedBaseline, readBaseline, staleEntries, writeBaseline, } from './baseline/index.js';
14
+ import { contractFor, validateParamsByKind, warningsFor } from './contracts/registry.js';
15
+ import { applyEmit, checkEmit, DEFAULT_GENERATED_DIR, planEmit, } from './emit/index.js';
16
+ import { applyExceptions, todayIso } from './exceptions/index.js';
17
+ import { createNodeFileSystem } from './fs/index.js';
18
+ import { readGraph } from './graph/index.js';
19
+ import { normalize } from './normalize/index.js';
20
+ import { createGraphProbe, GRAPH_REPORT_PATH, toGraphReport } from './probes/graph.js';
21
+ import { createLintProbe } from './probes/lint.js';
22
+ import { createModelProbe } from './probes/model/index.js';
23
+ import { createStructureProbe } from './probes/structure.js';
24
+ import { isSkip } from './probes/types.js';
25
+ import { nextIdLedger } from './registry/ids.js';
26
+ import { open } from './registry/index.js';
27
+ import { err, ok } from './result.js';
28
+ import { repoRelative } from './util/paths.js';
29
+ /** Exit codes are an API. An agent branches on them, so they are frozen from v0.1 (LLD §6). */
30
+ export const EXIT = {
31
+ /** Clean, or only baselined and advisory findings. */
32
+ clean: 0,
33
+ /** New violations of error-severity rules. Fix the code. */
34
+ violations: 1,
35
+ /** Configuration invalid. Fix the manifest, NOT the code. */
36
+ config: 2,
37
+ /** Emitted output is stale. Run `archcheck emit`. */
38
+ stale: 3,
39
+ /** A baselined finding no longer reproduces. Run `archcheck baseline --prune` (plan 14). */
40
+ baselineStale: 4,
41
+ };
42
+ const TIER_RANK = { deterministic: 0, proxy: 1, model: 2 };
43
+ function probes() {
44
+ return [createGraphProbe(), createLintProbe(), createStructureProbe(), createModelProbe()];
45
+ }
46
+ export function load(options = {}) {
47
+ const opened = open({
48
+ cwd: options.cwd,
49
+ configPath: options.configPath,
50
+ validateParams: validateParamsByKind,
51
+ collectWarnings: warningsFor,
52
+ isCheckable: (rule) => contractFor(rule.kind) !== undefined,
53
+ skipIdCheck: options.skipIdCheck,
54
+ });
55
+ if (!opened.ok)
56
+ return opened;
57
+ const registry = opened.value;
58
+ const tsconfigPath = registry.manifest.tsconfig
59
+ ? join(registry.root, registry.manifest.tsconfig)
60
+ : join(registry.root, 'tsconfig.json');
61
+ const generatedDir = registry.manifest.generatedDir ?? DEFAULT_GENERATED_DIR;
62
+ const plan = () => planEmit({
63
+ root: registry.root,
64
+ configPath: registry.configPath,
65
+ rules: registry.all(),
66
+ coverage: registry.coverage(),
67
+ generatedDir,
68
+ docs: registry.manifest.docs ?? [],
69
+ contractFor,
70
+ });
71
+ const explainCtx = { rules: registry.all() };
72
+ return ok({
73
+ registry,
74
+ async check(checkOptions = {}) {
75
+ const ceiling = checkOptions.only === undefined ? TIER_RANK.model : TIER_RANK[checkOptions.only];
76
+ const rules = registry.active().filter((rule) => TIER_RANK[rule.confidence] <= ceiling);
77
+ // One graph per run, built lazily and memoised. A probe that builds its own has broken the
78
+ // property the whole design rests on.
79
+ let graphOnce;
80
+ const ctx = {
81
+ root: registry.root,
82
+ tsconfigPath,
83
+ fs: createNodeFileSystem(registry.root),
84
+ configPath: repoRelative(registry.root, registry.configPath),
85
+ rules,
86
+ ...(checkOptions.changed === undefined ? {} : { changed: checkOptions.changed }),
87
+ graph: () => {
88
+ graphOnce ??= readGraph({
89
+ root: registry.root,
90
+ tsconfigPath,
91
+ ...(checkOptions.cache === undefined ? {} : { cache: checkOptions.cache }),
92
+ });
93
+ return graphOnce;
94
+ },
95
+ };
96
+ const inputs = [];
97
+ const skipped = [];
98
+ /**
99
+ * The kinds this run did NOT evaluate, collected where the skip is recorded rather than
100
+ * parsed back out of `SkipRecord.probe` — including the `'<kind> rules'` skips below, which
101
+ * name a kind no probe implements. A baseline entry whose rule has one of these kinds cannot
102
+ * be judged; every other entry still can. See `baselineStale`.
103
+ */
104
+ const unjudgedKinds = new Set();
105
+ if (checkOptions.writeGraph === true) {
106
+ const graph = ctx.graph();
107
+ if (!graph.ok)
108
+ return err(graph.error);
109
+ const { mkdirSync, writeFileSync } = await import('node:fs');
110
+ const reportPath = join(registry.root, GRAPH_REPORT_PATH);
111
+ mkdirSync(join(registry.root, '.archcheck'), { recursive: true });
112
+ writeFileSync(reportPath, `${JSON.stringify(toGraphReport(graph.value, registry.root), null, 2)}\n`);
113
+ }
114
+ for (const probe of probes()) {
115
+ const handled = rules.some((rule) => probe.handles.includes(rule.kind));
116
+ if (!handled)
117
+ continue;
118
+ if (checkOptions.ingestGraph === true && probe.name === 'graph' && probe.ingest) {
119
+ const reportPath = join(registry.root, GRAPH_REPORT_PATH);
120
+ try {
121
+ const { readFileSync } = await import('node:fs');
122
+ const report = JSON.parse(readFileSync(reportPath, 'utf8'));
123
+ inputs.push({ findings: probe.ingest(report, ctx), detectedBy: probe.name });
124
+ continue;
125
+ }
126
+ catch (error) {
127
+ skipped.push({
128
+ probe: probe.name,
129
+ reason: `cannot ingest ${GRAPH_REPORT_PATH}: ${error.message}`,
130
+ });
131
+ for (const kind of probe.handles)
132
+ unjudgedKinds.add(kind);
133
+ continue;
134
+ }
135
+ }
136
+ const outcome = await probe.run(ctx);
137
+ if (isSkip(outcome)) {
138
+ skipped.push({ probe: probe.name, reason: outcome.reason });
139
+ for (const kind of probe.handles)
140
+ unjudgedKinds.add(kind);
141
+ }
142
+ else
143
+ inputs.push({ findings: outcome, detectedBy: probe.name });
144
+ }
145
+ // A kind nobody handles is stated, never silently passed over: "declared, not checked yet"
146
+ // is a different thing from "checked and clean", and only `skipped` can say so.
147
+ const handledKinds = new Set(probes().flatMap((probe) => [...probe.handles]));
148
+ for (const kind of new Set(rules.map((rule) => rule.kind))) {
149
+ if (!handledKinds.has(kind)) {
150
+ skipped.push({
151
+ probe: `${kind} rules`,
152
+ reason: 'no probe in this version handles this kind',
153
+ });
154
+ unjudgedKinds.add(kind);
155
+ }
156
+ }
157
+ const baseline = readBaseline(registry.root);
158
+ const normalized = normalize(inputs, {
159
+ byId: (id) => registry.byId(id),
160
+ baselined: checkOptions.baselined ?? identitiesOf(baseline),
161
+ refine: (finding, rule) => contractFor(rule.kind)?.refine?.(finding, rule, explainCtx),
162
+ });
163
+ if (!normalized.ok)
164
+ return err(normalized.error);
165
+ const today = checkOptions.today ?? todayIso();
166
+ const applied = applyExceptions(registry.exceptions(), normalized.value, today);
167
+ // Counts come from the baseline FILE, so they apply only where the identities did too: an
168
+ // explicitly injected `baselined` set carries no counts, and pairing it with the file's would
169
+ // judge one run against another's numbers.
170
+ const recorded = checkOptions.baselined === undefined
171
+ ? new Map(baseline.entries.map((entry) => [entry.identity, entry.occurrences]))
172
+ : new Map();
173
+ // The count half of the ratchet. An identity can stand for several violations — evidence
174
+ // holds no position, so a third empty catch in a file hashes exactly like the first two
175
+ // (`computeIdentity`, src/normalize/identity.ts). More occurrences than the baseline recorded
176
+ // is therefore NEW debt, and saying so as `baselined: false` means every reporter and
177
+ // `exitCodeFor` already handle it: it fails as exit 1, like any other new violation. Pinned
178
+ // by 'a THIRD occurrence in a file the baseline recorded twice still fails' in
179
+ // tests/adoption.test.ts.
180
+ const diagnostics = applied.diagnostics.map((diagnostic) => {
181
+ const recordedCount = recorded.get(diagnostic.identity);
182
+ return diagnostic.baselined &&
183
+ recordedCount !== undefined &&
184
+ (diagnostic.occurrences ?? 1) > recordedCount
185
+ ? { ...diagnostic, baselined: false }
186
+ : diagnostic;
187
+ });
188
+ // The ratchet's second half, and only a run that looked can judge it. A diff-scoped run
189
+ // evaluates a fraction of the REPO and a tier-scoped run a fraction of the RULES, so neither
190
+ // judges anything — pinned by the two 'does not judge the baseline' tests in
191
+ // tests/adoption.test.ts. A skipped probe is narrower than either: it blinds this run to the
192
+ // KINDS that probe evaluates and to nothing else, so an entry whose rule no skipped probe
193
+ // would have touched is still judged ('a skipped probe suppresses staleness only for the
194
+ // rules it would have judged'). An entry naming a rule the manifest no longer defines has no
195
+ // kind to blind, and stays judged, as it was before.
196
+ const judgeable = (entry) => {
197
+ const kind = registry.byId(entry.ruleId)?.kind;
198
+ return kind === undefined || !unjudgedKinds.has(kind);
199
+ };
200
+ const baselineStale = checkOptions.changed === undefined &&
201
+ checkOptions.baselined === undefined &&
202
+ checkOptions.only === undefined
203
+ ? staleEntries(baseline, diagnostics).filter(judgeable)
204
+ : [];
205
+ return ok({
206
+ ok: !diagnostics.some((d) => d.severity === 'error' && !d.baselined && !d.exempted) &&
207
+ baselineStale.length === 0 &&
208
+ applied.expired.length === 0,
209
+ diagnostics,
210
+ skipped,
211
+ coverage: registry.coverage(),
212
+ baselineStale,
213
+ exceptions: applied.statuses,
214
+ });
215
+ },
216
+ async baseline(mode) {
217
+ // A baseline is written from an UNBASELINED run: passing an empty set makes every current
218
+ // violation visible, which is exactly what there is to record.
219
+ const run = await this.check({ baselined: new Set() });
220
+ if (!run.ok)
221
+ return err(run.error);
222
+ // Pruning is irreversible, and a degraded run cannot justify it: a probe that did not run
223
+ // makes every entry it would have reproduced look fixed, so the documented remedy for exit 4
224
+ // would erase the record instead of shrinking it. This is exit 2 through `runBaseline` in
225
+ // src/cli/index.ts, pinned by 'baseline --prune refuses on a degraded run rather than erasing
226
+ // the record'. `--write` only ever adds, so it is unaffected.
227
+ if (mode === 'prune' && run.value.skipped.length > 0) {
228
+ return err([
229
+ 'baseline --prune needs a complete run, and this one was degraded:',
230
+ ...run.value.skipped.map((skip) => ` ${skip.probe} did not run: ${skip.reason}`),
231
+ ' → pruning now would delete entries nothing checked. Fix the above and run it again',
232
+ ]);
233
+ }
234
+ const previous = readBaseline(registry.root);
235
+ const next = mode === 'write'
236
+ ? nextBaseline(previous, run.value.diagnostics, todayIso())
237
+ : prunedBaseline(previous, run.value.diagnostics);
238
+ const path = writeBaseline(registry.root, next);
239
+ // `--write` takes today's count too, so it can reduce one as well; both modes report it.
240
+ const before = new Map(previous.entries.map((entry) => [entry.identity, entry.occurrences]));
241
+ return ok({
242
+ path,
243
+ entries: next.entries.length,
244
+ removed: previous.entries.length - next.entries.length,
245
+ reduced: next.entries.filter((entry) => entry.occurrences < (before.get(entry.identity) ?? entry.occurrences)).length,
246
+ });
247
+ },
248
+ emit() {
249
+ const emitPlan = plan();
250
+ const outcome = applyEmit(emitPlan, nextIdLedger(registry.ledger(), registry.all()));
251
+ return ok({ ...outcome, unimplemented: emitPlan.unimplemented });
252
+ },
253
+ emitCheck() {
254
+ return ok(checkEmit(plan()));
255
+ },
256
+ async explain(ruleId) {
257
+ const rule = registry.byId(ruleId);
258
+ if (!rule) {
259
+ const known = registry
260
+ .all()
261
+ .map((r) => r.id)
262
+ .sort();
263
+ return err([`no rule '${ruleId}' in the manifest`, ` known rules: ${known.join(', ')}`]);
264
+ }
265
+ const contract = contractFor(rule.kind);
266
+ const lines = [
267
+ rule.statement,
268
+ ` id: ${rule.id}`,
269
+ ` kind: ${rule.kind}`,
270
+ ` severity: ${rule.severity}`,
271
+ ` confidence: ${rule.confidence}`,
272
+ ` remedy: ${rule.remedy.summary}`,
273
+ ];
274
+ if (rule.remedy.allowed && rule.remedy.allowed.length > 0) {
275
+ lines.push(` allowed: ${rule.remedy.allowed.join(', ')}`);
276
+ }
277
+ if (rule.remedy.docs)
278
+ lines.push(` docs: ${rule.remedy.docs}`);
279
+ if (rule.replaces)
280
+ lines.push(` replaces: ${rule.replaces}`);
281
+ if (!contract) {
282
+ lines.push(` note: no probe in this version checks '${rule.kind}' rules`);
283
+ }
284
+ // The live count is the reason `explain` exists: it is what an agent calls when a
285
+ // diagnostic alone was not enough to act on.
286
+ const run = await this.check();
287
+ lines.push(run.ok
288
+ ? describeViolations(ruleId, run.value.diagnostics)
289
+ : ' violations: unknown — the run itself failed');
290
+ if (run.ok) {
291
+ const example = run.value.diagnostics.find((d) => d.ruleId === ruleId);
292
+ if (example && contract) {
293
+ lines.push('');
294
+ lines.push(contract.explain(example, rule, explainCtx));
295
+ }
296
+ }
297
+ return ok(lines.join('\n'));
298
+ },
299
+ coverage: () => registry.coverage(),
300
+ });
301
+ }
302
+ /**
303
+ * What `explain` appends once the run has happened — the live violation count. It counts
304
+ * OCCURRENCES, not diagnostics: an agent asking "how many" means how many places to fix, and one
305
+ * diagnostic can stand for several (see `Diagnostic.occurrences`).
306
+ */
307
+ export function describeViolations(ruleId, diagnostics) {
308
+ const mine = diagnostics.filter((d) => d.ruleId === ruleId);
309
+ if (mine.length === 0)
310
+ return ' violations: none right now';
311
+ const total = mine.reduce((sum, d) => sum + (d.occurrences ?? 1), 0);
312
+ const lines = [` violations: ${total}`];
313
+ for (const diagnostic of mine.slice(0, 10)) {
314
+ const occurrences = diagnostic.occurrences ?? 1;
315
+ lines.push(` ${diagnostic.file}${diagnostic.line === undefined ? '' : `:${diagnostic.line}`}` +
316
+ (occurrences > 1 ? ` ×${occurrences}` : ''));
317
+ }
318
+ if (mine.length > 10)
319
+ lines.push(` … and ${mine.length - 10} more`);
320
+ return lines.join('\n');
321
+ }
322
+ /**
323
+ * The order matters. An expired exception is a MANIFEST problem (exit 2) and is checked first,
324
+ * because the violations it stopped covering are a consequence of it rather than news of their own.
325
+ * A stale baseline (exit 4) outranks live violations for the same reason: the fix is
326
+ * `baseline --prune`, and reporting exit 1 would send an agent to edit code that is already right.
327
+ */
328
+ export { buildInitManifest, inferLayers, initSummary, renderManifest, } from './init/index.js';
329
+ export { CONFIG_FILENAME } from './registry/index.js';
330
+ export function exitCodeFor(result) {
331
+ if (result.exceptions.some((exception) => exception.expired))
332
+ return EXIT.config;
333
+ if (result.baselineStale.length > 0)
334
+ return EXIT.baselineStale;
335
+ return result.diagnostics.some((d) => d.severity === 'error' && !d.baselined && !d.exempted)
336
+ ? EXIT.violations
337
+ : EXIT.clean;
338
+ }
339
+ //# sourceMappingURL=archcheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archcheck.js","sourceRoot":"","sources":["../src/archcheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { BaselineEntry, Diagnostic } from '../types.js';
2
+ export declare const BASELINE_PATH = ".archcheck/baseline.json";
3
+ export type Baseline = {
4
+ version: 1;
5
+ entries: BaselineEntry[];
6
+ };
7
+ export declare const EMPTY_BASELINE: Baseline;
8
+ export declare function baselinePathFor(root: string): string;
9
+ export declare function readBaseline(root: string): Baseline;
10
+ export declare function identitiesOf(baseline: Baseline): ReadonlySet<string>;
11
+ /**
12
+ * `baseline --write`: record everything failing right now. Existing entries keep their original
13
+ * `addedAt`, so the file stays an honest record of how long each piece of debt has been carried.
14
+ */
15
+ export declare function nextBaseline(previous: Baseline, diagnostics: readonly Diagnostic[], today: string): Baseline;
16
+ /**
17
+ * Entries that no longer describe reality. These are what exit 4 is about — and a count that has
18
+ * come DOWN is the same news as a finding that has gone: two of three empty catches fixed leaves
19
+ * an entry recording more debt than exists, which is exactly what `--prune` is for. A count that
20
+ * has gone UP is the opposite news, and `src/archcheck.ts` fails the run for it instead.
21
+ */
22
+ export declare function staleEntries(baseline: Baseline, diagnostics: readonly Diagnostic[]): BaselineEntry[];
23
+ /**
24
+ * `baseline --prune`: drop what no longer reproduces, and reduce the count of what partly does.
25
+ * The only way the file shrinks.
26
+ */
27
+ export declare function prunedBaseline(baseline: Baseline, diagnostics: readonly Diagnostic[]): Baseline;
28
+ export declare function writeBaseline(root: string, baseline: Baseline): string;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/baseline/index.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE7D,eAAO,MAAM,aAAa,6BAA6B,CAAC;AAExD,MAAM,MAAM,QAAQ,GAAG;IAAE,OAAO,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC;AAEhE,eAAO,MAAM,cAAc,EAAE,QAAsC,CAAC;AAEpE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAyBnD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAEpE;AAcD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,KAAK,EAAE,MAAM,GACZ,QAAQ,CAmBV;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,SAAS,UAAU,EAAE,GACjC,aAAa,EAAE,CAKjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,UAAU,EAAE,GAAG,QAAQ,CAa/F;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAKtE"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The baseline ratchet — adopting a rule on a codebase that already breaks it, without a flag day.
3
+ *
4
+ * What rule this enforces: the ratchet turns ONE WAY. A run fails on identities the baseline does
5
+ * not list, and it ALSO fails (exit 4) when a listed identity no longer reproduces. Without the
6
+ * second half the file only ever grows, silently over-suppressing forever — which is the failure
7
+ * mode that makes baselines worthless everywhere they are used badly (plan 14).
8
+ *
9
+ * What a caller must know: entries are keyed by IDENTITY, never by line and never by count.
10
+ * Because identity comes from evidence rather than position, moving the offending code neither
11
+ * resurrects a finding nor invents a phantom one. That property is the whole reason this works,
12
+ * and it is tested by moving a violation thirty lines down the page.
13
+ *
14
+ * A baseline entry is DEBT: no owner, no expiry, recorded so work can continue. An exception is a
15
+ * deliberate allowance with both. They are different things and live in different files.
16
+ */
17
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
18
+ import { dirname, join } from 'node:path';
19
+ export const BASELINE_PATH = '.archcheck/baseline.json';
20
+ export const EMPTY_BASELINE = { version: 1, entries: [] };
21
+ export function baselinePathFor(root) {
22
+ return join(root, BASELINE_PATH);
23
+ }
24
+ export function readBaseline(root) {
25
+ try {
26
+ const parsed = JSON.parse(readFileSync(baselinePathFor(root), 'utf8'));
27
+ const entries = parsed?.entries;
28
+ if (!Array.isArray(entries))
29
+ return EMPTY_BASELINE;
30
+ return {
31
+ version: 1,
32
+ entries: entries
33
+ .filter((entry) => typeof entry?.identity === 'string' && typeof entry?.ruleId === 'string')
34
+ // An entry written before `occurrences` existed reads as one occurrence, so an existing
35
+ // baseline file keeps working unchanged — 'an entry written before occurrences existed
36
+ // still loads, and reads as one' in baseline.test.ts.
37
+ .map((entry) => ({
38
+ ...entry,
39
+ occurrences: typeof entry.occurrences === 'number' && entry.occurrences > 0 ? entry.occurrences : 1,
40
+ })),
41
+ };
42
+ }
43
+ catch {
44
+ // No baseline is the normal state for a repo that never needed one.
45
+ return EMPTY_BASELINE;
46
+ }
47
+ }
48
+ export function identitiesOf(baseline) {
49
+ return new Set(baseline.entries.map((entry) => entry.identity));
50
+ }
51
+ /** How many occurrences each identity has RIGHT NOW. Absent means it did not reproduce at all. */
52
+ function currentCounts(diagnostics) {
53
+ return new Map(diagnostics.map((diagnostic) => [diagnostic.identity, diagnostic.occurrences ?? 1]));
54
+ }
55
+ /** Sorted by identity so a diff of this file is reviewable rather than a reshuffle. */
56
+ function sorted(entries) {
57
+ return [...entries].sort((a, b) => a.identity.localeCompare(b.identity));
58
+ }
59
+ /**
60
+ * `baseline --write`: record everything failing right now. Existing entries keep their original
61
+ * `addedAt`, so the file stays an honest record of how long each piece of debt has been carried.
62
+ */
63
+ export function nextBaseline(previous, diagnostics, today) {
64
+ const kept = new Map(previous.entries.map((entry) => [entry.identity, entry]));
65
+ for (const diagnostic of diagnostics) {
66
+ if (diagnostic.severity !== 'error' || diagnostic.exempted)
67
+ continue;
68
+ const existing = kept.get(diagnostic.identity);
69
+ // An existing entry keeps its addedAt but takes today's count: `--write` records what is
70
+ // failing right now. Only identities that REPRODUCED are touched, so a degraded run — where a
71
+ // skipped probe reports nothing — cannot quietly rewrite a count to zero.
72
+ kept.set(diagnostic.identity, {
73
+ ...(existing ?? {
74
+ identity: diagnostic.identity,
75
+ ruleId: diagnostic.ruleId,
76
+ addedAt: today,
77
+ note: 'pre-existing at adoption',
78
+ }),
79
+ occurrences: diagnostic.occurrences ?? 1,
80
+ });
81
+ }
82
+ return { version: 1, entries: sorted([...kept.values()]) };
83
+ }
84
+ /**
85
+ * Entries that no longer describe reality. These are what exit 4 is about — and a count that has
86
+ * come DOWN is the same news as a finding that has gone: two of three empty catches fixed leaves
87
+ * an entry recording more debt than exists, which is exactly what `--prune` is for. A count that
88
+ * has gone UP is the opposite news, and `src/archcheck.ts` fails the run for it instead.
89
+ */
90
+ export function staleEntries(baseline, diagnostics) {
91
+ const current = currentCounts(diagnostics);
92
+ return sorted(baseline.entries.filter((entry) => (current.get(entry.identity) ?? 0) < entry.occurrences));
93
+ }
94
+ /**
95
+ * `baseline --prune`: drop what no longer reproduces, and reduce the count of what partly does.
96
+ * The only way the file shrinks.
97
+ */
98
+ export function prunedBaseline(baseline, diagnostics) {
99
+ const current = currentCounts(diagnostics);
100
+ return {
101
+ version: 1,
102
+ entries: sorted(baseline.entries
103
+ .filter((entry) => current.has(entry.identity))
104
+ .map((entry) => ({
105
+ ...entry,
106
+ occurrences: current.get(entry.identity) ?? entry.occurrences,
107
+ }))),
108
+ };
109
+ }
110
+ export function writeBaseline(root, baseline) {
111
+ const path = baselinePathFor(root);
112
+ mkdirSync(dirname(path), { recursive: true });
113
+ writeFileSync(path, `${JSON.stringify(baseline, null, 2)}\n`);
114
+ return BASELINE_PATH;
115
+ }
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/baseline/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ReporterName } from '../report/index.js';
2
+ import { type Result } from '../result.js';
3
+ import type { Confidence } from '../types.js';
4
+ export type Command = 'check' | 'emit' | 'explain' | 'coverage' | 'baseline' | 'init' | 'version' | 'help';
5
+ export type ParsedArgs = {
6
+ command: Command;
7
+ format: ReporterName;
8
+ only?: Confidence;
9
+ /** `--changed` with no value means "ask git". */
10
+ changed?: readonly string[];
11
+ emitCheck: boolean;
12
+ /** `init --force`: overwrite an existing manifest. */
13
+ force: boolean;
14
+ /** `baseline --write` or `baseline --prune`. */
15
+ baselineMode?: 'write' | 'prune';
16
+ showBaselined: boolean;
17
+ ingestGraph: boolean;
18
+ writeGraph: boolean;
19
+ noColor: boolean;
20
+ ruleId?: string;
21
+ cwd?: string;
22
+ };
23
+ export declare const HELP = "archcheck \u2014 architecture conformance, declared once and checked everywhere\n\n archcheck run every rule, terminal output\n archcheck --changed diff-scoped \u00B7 the pre-commit path\n archcheck --format json the agent format\n archcheck --format sarif SARIF 2.1.0, for CI code scanning and editors\n archcheck --format verdict one blocking answer, for a hook\n archcheck --only deterministic skip the proxy and model tiers\n archcheck --show-baselined include findings the baseline records\n archcheck --write-graph write .archcheck/graph.json for later gates to ingest\n archcheck --ingest-graph read .archcheck/graph.json instead of re-reading the graph\n\n archcheck emit regenerate the emitted configs\n archcheck emit --check fail if the committed output is stale\n archcheck explain <ruleId> statement, remedy and current violations\n archcheck coverage the three counts, computed from the manifest\n archcheck baseline --write record today's violations as pre-existing debt\n archcheck baseline --prune drop entries that no longer reproduce\n archcheck init write a starting architecture.config.ts from the folders\n archcheck --version\n\nexit codes\n 0 clean, or only baselined and advisory findings\n 1 new violations of error-severity rules \u2192 fix the code\n 2 configuration invalid \u2192 fix the manifest, not the code\n 3 emitted output is stale \u2192 run archcheck emit\n 4 a baselined finding no longer reproduces \u2192 run archcheck baseline --prune\n";
24
+ /**
25
+ * Ask git for the changed set. Local only — archcheck makes no network call it was not told to.
26
+ *
27
+ * A git that cannot answer is a CONFIG failure (exit 2), never an empty changed set. git missing
28
+ * from PATH, an unborn or detached HEAD, or a directory outside any work tree used to be swallowed
29
+ * by a bare catch returning `[]`: every probe then filtered to nothing and the run reported clean,
30
+ * so a pre-commit hook passed every commit forever. Exit 2 because the manifest and the code are
31
+ * both fine — the environment is not, which is the same call `init` makes when it cannot read the
32
+ * module graph. `tests/cli.test.ts` → '--changed > git failing is a config error (2)' pins it.
33
+ */
34
+ export declare function gitChangedFiles(cwd: string): Result<readonly string[]>;
35
+ export declare function parseArgs(argv: readonly string[]): Result<ParsedArgs>;
36
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,OAAO,GACf,OAAO,GACP,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,MAAM,GACN,SAAS,GACT,MAAM,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iDAAiD;IACjD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAgBF,eAAO,MAAM,IAAI,2pDA2BhB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAoCtE;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CA0IrE"}