@llm-dev-ops/agentics-cli 2.7.17 → 2.7.19

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.
@@ -0,0 +1,71 @@
1
+ /**
2
+ * ADR-PIPELINE-098 D8 — Coherence Gate.
3
+ *
4
+ * Post-render financial validator. Walks the deliverables directory, scans
5
+ * every emitted file for `<!-- fcv:kind=... -->` provenance tags, and
6
+ * compares the value that follows each tag against the canonical value held
7
+ * in `unit-economics.json` (per ADR-PIPELINE-066 — the single financial
8
+ * source of truth).
9
+ *
10
+ * Tolerance: ±5% by default. Range tokens like `$22.1M – $30.9M` pass when
11
+ * the canonical falls inside the range OR the range midpoint is within
12
+ * tolerance of the canonical.
13
+ *
14
+ * The gate is a pure reporter: it does NOT decide phase status. The Phase 7
15
+ * coordinator inspects the violation list and downgrades to `partial` when
16
+ * appropriate (per D8). Coherence violations are non-fatal.
17
+ *
18
+ * Implementation notes:
19
+ * - File discovery is a manual recursive walk over `node:fs` — `fast-glob`
20
+ * is not a project dependency and pulling one in just for this gate is
21
+ * avoided.
22
+ * - File read errors are non-fatal: the offending file is skipped (not
23
+ * counted in `files_scanned`), but the gate continues.
24
+ * - The unit-economics manifest schema is permissive: only the fields that
25
+ * are actually present feed the canonical map. Kinds the manifest does
26
+ * not carry are skipped silently rather than reported as violations
27
+ * (the canonical value is unknown, not wrong).
28
+ */
29
+ export type CoherenceKind = 'investment' | 'savings' | 'npv' | 'roi' | 'payback' | 'timeline';
30
+ export interface CoherenceViolation {
31
+ readonly slug: string;
32
+ readonly section: number;
33
+ readonly kind: CoherenceKind;
34
+ readonly expected: string;
35
+ readonly got: string;
36
+ readonly delta_pct: number;
37
+ readonly path: string;
38
+ }
39
+ export interface CoherenceReport {
40
+ readonly violations: ReadonlyArray<CoherenceViolation>;
41
+ readonly files_scanned: number;
42
+ readonly fcv_tags_total: number;
43
+ readonly tolerance_pct: number;
44
+ }
45
+ export interface CoherenceGateOptions {
46
+ /** Default ±5%. Applied symmetrically around the canonical value. */
47
+ readonly tolerancePct?: number;
48
+ }
49
+ /**
50
+ * Parse a single dollar token like `$22.1M`, `$500K`, `$1,200,000`.
51
+ * Returns null on failure — callers decide whether absence is a violation.
52
+ */
53
+ export declare function parseDollarToBase(s: string): number | null;
54
+ /**
55
+ * Parse a duration token like `5 months`, `1.5 years`, `12-week` and
56
+ * normalize to months. Years/weeks are converted at standard ratios
57
+ * (1 yr = 12 mo, 1 wk = 12/52 mo ≈ 0.231 mo).
58
+ */
59
+ export declare function parseDurationToMonths(s: string): number | null;
60
+ /**
61
+ * Run the coherence gate over a deliverables directory.
62
+ *
63
+ * Walks `<deliverablesDir>` recursively, scans every `*.md` / `*.json` /
64
+ * `*.csv` for fcv tags, extracts the value following each tag, and compares
65
+ * against the canonical from `unitEconomicsPath`. Returns a report — never
66
+ * throws; all errors are absorbed (unreadable files are skipped, invalid
67
+ * JSON in unit-economics yields an empty canonical map and the gate emits
68
+ * zero violations).
69
+ */
70
+ export declare function runCoherenceGate(deliverablesDir: string, unitEconomicsPath: string, options?: CoherenceGateOptions): Promise<CoherenceReport>;
71
+ //# sourceMappingURL=coherence-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coherence-gate.d.ts","sourceRoot":"","sources":["../../../src/pipeline/phase7/coherence-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,SAAS,GACT,KAAK,GACL,KAAK,GACL,SAAS,GACT,UAAU,CAAC;AAEf,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAmND;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB1D;AAgBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU9D;AAiOD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,eAAe,EAAE,MAAM,EACvB,iBAAiB,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAqE1B"}
@@ -0,0 +1,541 @@
1
+ /**
2
+ * ADR-PIPELINE-098 D8 — Coherence Gate.
3
+ *
4
+ * Post-render financial validator. Walks the deliverables directory, scans
5
+ * every emitted file for `<!-- fcv:kind=... -->` provenance tags, and
6
+ * compares the value that follows each tag against the canonical value held
7
+ * in `unit-economics.json` (per ADR-PIPELINE-066 — the single financial
8
+ * source of truth).
9
+ *
10
+ * Tolerance: ±5% by default. Range tokens like `$22.1M – $30.9M` pass when
11
+ * the canonical falls inside the range OR the range midpoint is within
12
+ * tolerance of the canonical.
13
+ *
14
+ * The gate is a pure reporter: it does NOT decide phase status. The Phase 7
15
+ * coordinator inspects the violation list and downgrades to `partial` when
16
+ * appropriate (per D8). Coherence violations are non-fatal.
17
+ *
18
+ * Implementation notes:
19
+ * - File discovery is a manual recursive walk over `node:fs` — `fast-glob`
20
+ * is not a project dependency and pulling one in just for this gate is
21
+ * avoided.
22
+ * - File read errors are non-fatal: the offending file is skipped (not
23
+ * counted in `files_scanned`), but the gate continues.
24
+ * - The unit-economics manifest schema is permissive: only the fields that
25
+ * are actually present feed the canonical map. Kinds the manifest does
26
+ * not carry are skipped silently rather than reported as violations
27
+ * (the canonical value is unknown, not wrong).
28
+ */
29
+ import { promises as fs } from 'node:fs';
30
+ import * as path from 'node:path';
31
+ // ============================================================================
32
+ // File discovery
33
+ // ============================================================================
34
+ const SUPPORTED_EXT = new Set(['.md', '.json', '.csv']);
35
+ async function walkFiles(root) {
36
+ const out = [];
37
+ async function visit(dir) {
38
+ let entries;
39
+ try {
40
+ entries = await fs.readdir(dir, { withFileTypes: true });
41
+ }
42
+ catch {
43
+ return; // unreadable dir is non-fatal
44
+ }
45
+ for (const entry of entries) {
46
+ const full = path.join(dir, entry.name);
47
+ if (entry.isSymbolicLink()) {
48
+ // Resolve the symlink target and decide based on its kind. Mirror
49
+ // entries (D6) are symlinks to top-level docs; we still want to
50
+ // scan them.
51
+ try {
52
+ const stat = await fs.stat(full);
53
+ if (stat.isFile() && SUPPORTED_EXT.has(path.extname(full).toLowerCase())) {
54
+ out.push(full);
55
+ }
56
+ else if (stat.isDirectory()) {
57
+ await visit(full);
58
+ }
59
+ }
60
+ catch {
61
+ // dangling symlink — skip
62
+ }
63
+ continue;
64
+ }
65
+ if (entry.isDirectory()) {
66
+ await visit(full);
67
+ continue;
68
+ }
69
+ if (entry.isFile() && SUPPORTED_EXT.has(path.extname(entry.name).toLowerCase())) {
70
+ out.push(full);
71
+ }
72
+ }
73
+ }
74
+ await visit(root);
75
+ return out;
76
+ }
77
+ // ============================================================================
78
+ // Section / slug derivation
79
+ // ============================================================================
80
+ /**
81
+ * Phase 7 lays files out as `<runDir>/deliverables/<NN-section>/<slug>.<ext>`.
82
+ * The leading two-digit prefix on the parent directory is the section
83
+ * number. Files outside this layout (e.g. `index.md`, `phase7-manifest.json`)
84
+ * are still scanned, but reported with `section: 0`.
85
+ */
86
+ function deriveSlugAndSection(absPath, root) {
87
+ const rel = path.relative(root, absPath);
88
+ const parts = rel.split(path.sep);
89
+ const ext = path.extname(absPath);
90
+ const slug = path.basename(absPath, ext);
91
+ // First directory under root: e.g. `01-executive` → section 1.
92
+ if (parts.length >= 2) {
93
+ const dir = parts[0];
94
+ const m = dir.match(/^(\d{1,2})-/);
95
+ if (m && m[1]) {
96
+ const n = Number.parseInt(m[1], 10);
97
+ if (Number.isFinite(n) && n >= 1 && n <= 12) {
98
+ return { slug, section: n };
99
+ }
100
+ }
101
+ }
102
+ return { slug, section: 0 };
103
+ }
104
+ // ============================================================================
105
+ // Tag scanner
106
+ // ============================================================================
107
+ const FCV_TAG_RE = /<!--\s*fcv:([^>]+?)\s*-->/g;
108
+ /**
109
+ * Parse a single fcv tag's attribute string (the bit between `fcv:` and
110
+ * the closing `-->`). Only `kind=` is significant for the gate — `scope`,
111
+ * `doc`, `src` are recorded by the renderer but not used here.
112
+ */
113
+ function parseTagAttrs(attrs) {
114
+ let kind = null;
115
+ for (const pair of attrs.split(/\s+/)) {
116
+ const eq = pair.indexOf('=');
117
+ if (eq === -1)
118
+ continue;
119
+ const key = pair.slice(0, eq).trim();
120
+ const val = pair.slice(eq + 1).trim();
121
+ if (key === 'kind')
122
+ kind = val;
123
+ }
124
+ // Kind aliasing: keep the canonical set narrow but accept renderer-side
125
+ // synonyms. `revenueImpact` → `savings`, `paybackPeriod` → `payback`.
126
+ // The exec-summary uses `timeline` for payback figures; we keep that as
127
+ // its own kind so range/duration parsing can split, then alias to
128
+ // `payback` for the canonical-value lookup later.
129
+ if (!kind)
130
+ return { kind: null };
131
+ const lower = kind.toLowerCase();
132
+ if (lower === 'revenueimpact' || lower === 'revenue-impact' || lower === 'cost-savings' || lower === 'costsavings') {
133
+ return { kind: 'savings' };
134
+ }
135
+ if (lower === 'paybackperiod' || lower === 'payback-period') {
136
+ return { kind: 'payback' };
137
+ }
138
+ if (lower === 'investment' ||
139
+ lower === 'savings' ||
140
+ lower === 'npv' ||
141
+ lower === 'roi' ||
142
+ lower === 'payback' ||
143
+ lower === 'timeline') {
144
+ return { kind: lower };
145
+ }
146
+ return { kind: null };
147
+ }
148
+ function scanTags(body) {
149
+ const out = [];
150
+ for (const m of body.matchAll(FCV_TAG_RE)) {
151
+ const attrs = m[1] ?? '';
152
+ const tailIndex = (m.index ?? 0) + m[0].length;
153
+ const { kind } = parseTagAttrs(attrs);
154
+ out.push({ kind, tailIndex });
155
+ }
156
+ return out;
157
+ }
158
+ // ============================================================================
159
+ // Value extraction (the token immediately following a tag)
160
+ // ============================================================================
161
+ /**
162
+ * Scan up to ~200 chars after a tag for the next dollar-or-duration token.
163
+ * This window comfortably covers the patterns observed in exec-summary:
164
+ * `<!-- ... --> $22.1M – $30.9M (Bottom-up unit economics ...)`
165
+ * `<!-- ... --> 5 months (break-even ...)`
166
+ * `<!-- ... --> 250% projected ($55.3M annual savings ...)`
167
+ *
168
+ * For ROI we want the percent token, not the trailing `$X.YM`. The kind
169
+ * dictates which extractor runs.
170
+ */
171
+ const VALUE_WINDOW = 240;
172
+ function extractValueText(body, fromIndex, kind) {
173
+ const window = body.slice(fromIndex, fromIndex + VALUE_WINDOW);
174
+ // Trim leading whitespace / pipe (table cells) / colon.
175
+ const trimmed = window.replace(/^[\s:|]+/, '');
176
+ if (trimmed.length === 0)
177
+ return null;
178
+ if (kind === 'roi') {
179
+ const m = trimmed.match(/^(\d+(?:\.\d+)?)\s*%/);
180
+ if (m)
181
+ return `${m[1]}%`;
182
+ // Some renderers print `ROI: 250% projected`. If the value isn't first,
183
+ // fall through to a broader scan.
184
+ const broad = trimmed.match(/(\d+(?:\.\d+)?)\s*%/);
185
+ if (broad)
186
+ return `${broad[1]}%`;
187
+ return null;
188
+ }
189
+ if (kind === 'payback' || kind === 'timeline') {
190
+ // Duration tokens: "5 months", "0.7 year(s)", "3-month", "18-week".
191
+ const m = trimmed.match(/^(\d+(?:\.\d+)?)[\s-]?(months?|years?|weeks?)\b/i);
192
+ if (m)
193
+ return `${m[1]} ${m[2].toLowerCase()}`;
194
+ // Some payback cells lead with a dollar token (the cumulative-savings
195
+ // breakeven). Fall through to dollar extraction so the gate still
196
+ // produces a comparable number.
197
+ return extractDollarToken(trimmed);
198
+ }
199
+ // investment / savings / npv → dollar token (point or range)
200
+ return extractDollarToken(trimmed);
201
+ }
202
+ function extractDollarToken(s) {
203
+ // Range: `$22.1M – $30.9M`, `$22.1M-$30.9M`, `$22.1M to $30.9M`.
204
+ const range = s.match(/^\$\s*[\d,]+(?:\.\d+)?\s*[KMB]?\s*(?:[-–—]|to)\s*\$?\s*[\d,]+(?:\.\d+)?\s*[KMB]?/i);
205
+ if (range)
206
+ return range[0].trim();
207
+ // Point: `$22.1M`, `$1,200,000`, `$500K`.
208
+ const point = s.match(/^\$\s*[\d,]+(?:\.\d+)?\s*[KMB]?/i);
209
+ if (point)
210
+ return point[0].trim();
211
+ return null;
212
+ }
213
+ // ============================================================================
214
+ // Numeric parsers
215
+ // ============================================================================
216
+ const SUFFIX_MULT = {
217
+ K: 1_000,
218
+ M: 1_000_000,
219
+ B: 1_000_000_000,
220
+ };
221
+ /**
222
+ * Parse a single dollar token like `$22.1M`, `$500K`, `$1,200,000`.
223
+ * Returns null on failure — callers decide whether absence is a violation.
224
+ */
225
+ export function parseDollarToBase(s) {
226
+ const cleaned = s.trim().replace(/^\$/, '').replace(/\s+/g, '');
227
+ if (cleaned.length === 0)
228
+ return null;
229
+ const suffixMatch = cleaned.match(/^([\d,]+(?:\.\d+)?)([KMB])$/i);
230
+ if (suffixMatch) {
231
+ const num = Number.parseFloat(suffixMatch[1].replace(/,/g, ''));
232
+ const mult = SUFFIX_MULT[suffixMatch[2].toUpperCase()] ?? 1;
233
+ if (Number.isFinite(num))
234
+ return num * mult;
235
+ return null;
236
+ }
237
+ const plainMatch = cleaned.match(/^([\d,]+(?:\.\d+)?)$/);
238
+ if (plainMatch) {
239
+ const num = Number.parseFloat(plainMatch[1].replace(/,/g, ''));
240
+ if (Number.isFinite(num))
241
+ return num;
242
+ }
243
+ return null;
244
+ }
245
+ function parseDollarRange(s) {
246
+ const parts = s.split(/\s*(?:[-–—]|\bto\b)\s*/i);
247
+ if (parts.length !== 2)
248
+ return null;
249
+ const a = parseDollarToBase(parts[0]);
250
+ const b = parseDollarToBase(parts[1]);
251
+ if (a === null || b === null)
252
+ return null;
253
+ return { low: Math.min(a, b), high: Math.max(a, b) };
254
+ }
255
+ /**
256
+ * Parse a duration token like `5 months`, `1.5 years`, `12-week` and
257
+ * normalize to months. Years/weeks are converted at standard ratios
258
+ * (1 yr = 12 mo, 1 wk = 12/52 mo ≈ 0.231 mo).
259
+ */
260
+ export function parseDurationToMonths(s) {
261
+ const m = s.trim().match(/^(\d+(?:\.\d+)?)[\s-]?(months?|years?|weeks?)\b/i);
262
+ if (!m)
263
+ return null;
264
+ const n = Number.parseFloat(m[1]);
265
+ if (!Number.isFinite(n))
266
+ return null;
267
+ const unit = m[2].toLowerCase();
268
+ if (unit.startsWith('month'))
269
+ return n;
270
+ if (unit.startsWith('year'))
271
+ return n * 12;
272
+ if (unit.startsWith('week'))
273
+ return (n * 12) / 52;
274
+ return null;
275
+ }
276
+ /**
277
+ * Best-effort canonical-value extractor for the unit-economics manifest.
278
+ *
279
+ * The manifest schema (ADR-PIPELINE-066) encodes savings directly but
280
+ * derives investment / NPV / ROI / payback at render time. To keep the
281
+ * gate self-contained we re-derive the same numbers here using the same
282
+ * rule-of-thumb the renderer uses (40% of enterprise savings as
283
+ * implementation cost, NPV = enterprise × 3.5 − impl, payback months =
284
+ * impl / enterprise × 12, ROI = enterprise / impl × 100).
285
+ *
286
+ * If the manifest carries explicit fields (`pilot_investment`,
287
+ * `enterprise_investment`, `payback_months`, `npv_5yr`, `roi_pct`) those
288
+ * take precedence over the derivation. This makes the gate forward-compatible
289
+ * with future schema extensions without breaking on today's manifests.
290
+ */
291
+ async function loadCanonical(unitEconomicsPath) {
292
+ const display = {
293
+ investment: undefined,
294
+ savings: undefined,
295
+ npv: undefined,
296
+ roi: undefined,
297
+ payback: undefined,
298
+ timeline: undefined,
299
+ };
300
+ let raw;
301
+ try {
302
+ raw = await fs.readFile(unitEconomicsPath, 'utf-8');
303
+ }
304
+ catch {
305
+ return { display };
306
+ }
307
+ let json;
308
+ try {
309
+ json = JSON.parse(raw);
310
+ }
311
+ catch {
312
+ return { display };
313
+ }
314
+ if (!isRecord(json))
315
+ return { display };
316
+ // ----- direct fields (may or may not be present) -----
317
+ const directInvestment = pickNumber(json, 'enterprise_investment') ?? pickNumber(json, 'pilot_investment');
318
+ const directSavings = pickNumber(json, 'enterprise_savings_per_year') ??
319
+ pickNumber(json, 'pilot_savings_per_year') ??
320
+ pickNumber(json, 'annual_extrapolated_savings_usd') ??
321
+ pickNumber(json, 'annual_measured_savings_usd');
322
+ const directNpv = pickNumber(json, 'npv_5yr');
323
+ const directRoi = pickNumber(json, 'roi_pct');
324
+ const directPayback = pickNumber(json, 'payback_months');
325
+ // ----- derive missing fields from annual_extrapolated_savings_usd -----
326
+ const enterpriseSavings = pickNumber(json, 'annual_extrapolated_savings_usd');
327
+ const measuredSavings = pickNumber(json, 'annual_measured_savings_usd');
328
+ const baseSavings = enterpriseSavings ?? measuredSavings;
329
+ let investmentUsd = directInvestment;
330
+ let savingsUsd = directSavings;
331
+ let npvUsd = directNpv;
332
+ let roiPct = directRoi;
333
+ let paybackMonths = directPayback;
334
+ if (savingsUsd === undefined && baseSavings !== undefined) {
335
+ savingsUsd = baseSavings;
336
+ }
337
+ if (baseSavings !== undefined && baseSavings > 0) {
338
+ const derivedImpl = Math.max(500_000, Math.round(baseSavings * 0.4));
339
+ if (investmentUsd === undefined)
340
+ investmentUsd = derivedImpl;
341
+ if (npvUsd === undefined)
342
+ npvUsd = Math.round(baseSavings * 3.5 - derivedImpl);
343
+ if (roiPct === undefined)
344
+ roiPct = Math.round((baseSavings / derivedImpl) * 100);
345
+ if (paybackMonths === undefined) {
346
+ paybackMonths = Math.max(3, Math.min(36, Math.round((derivedImpl / baseSavings) * 12)));
347
+ }
348
+ }
349
+ if (investmentUsd !== undefined)
350
+ display.investment = formatMoney(investmentUsd);
351
+ if (savingsUsd !== undefined)
352
+ display.savings = formatMoney(savingsUsd);
353
+ if (npvUsd !== undefined)
354
+ display.npv = formatMoney(npvUsd);
355
+ if (roiPct !== undefined)
356
+ display.roi = `${roiPct}%`;
357
+ if (paybackMonths !== undefined) {
358
+ const txt = `${paybackMonths} months`;
359
+ display.payback = txt;
360
+ display.timeline = txt;
361
+ }
362
+ return {
363
+ ...(investmentUsd !== undefined ? { investmentUsd } : {}),
364
+ ...(savingsUsd !== undefined ? { savingsUsd } : {}),
365
+ ...(npvUsd !== undefined ? { npvUsd } : {}),
366
+ ...(roiPct !== undefined ? { roiPct } : {}),
367
+ ...(paybackMonths !== undefined ? { paybackMonths } : {}),
368
+ display,
369
+ };
370
+ }
371
+ function isRecord(v) {
372
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
373
+ }
374
+ function pickNumber(obj, key) {
375
+ const v = obj[key];
376
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
377
+ }
378
+ function formatMoney(n) {
379
+ if (!Number.isFinite(n))
380
+ return '$0';
381
+ const abs = Math.abs(n);
382
+ if (abs >= 1_000_000)
383
+ return `$${(n / 1_000_000).toFixed(1)}M`;
384
+ if (abs >= 1_000)
385
+ return `$${(n / 1_000).toFixed(0)}K`;
386
+ return `$${Math.round(n)}`;
387
+ }
388
+ function canonicalFor(kind, c) {
389
+ switch (kind) {
390
+ case 'investment':
391
+ if (c.investmentUsd === undefined)
392
+ return null;
393
+ return { numeric: c.investmentUsd, display: c.display.investment, unit: 'usd' };
394
+ case 'savings':
395
+ if (c.savingsUsd === undefined)
396
+ return null;
397
+ return { numeric: c.savingsUsd, display: c.display.savings, unit: 'usd' };
398
+ case 'npv':
399
+ if (c.npvUsd === undefined)
400
+ return null;
401
+ return { numeric: c.npvUsd, display: c.display.npv, unit: 'usd' };
402
+ case 'roi':
403
+ if (c.roiPct === undefined)
404
+ return null;
405
+ return { numeric: c.roiPct, display: c.display.roi, unit: 'percent' };
406
+ case 'payback':
407
+ case 'timeline':
408
+ if (c.paybackMonths === undefined)
409
+ return null;
410
+ return { numeric: c.paybackMonths, display: c.display.payback, unit: 'months' };
411
+ }
412
+ }
413
+ function compareValues(got, canon, tolerancePct) {
414
+ if (canon.numeric === undefined) {
415
+ return { inTolerance: true, deltaPct: 0, nonNumeric: true };
416
+ }
417
+ // Try numeric parse based on the canonical unit.
418
+ let gotNum = null;
419
+ let gotRange = null;
420
+ if (canon.unit === 'usd') {
421
+ gotRange = parseDollarRange(got);
422
+ if (gotRange === null)
423
+ gotNum = parseDollarToBase(got);
424
+ }
425
+ else if (canon.unit === 'months') {
426
+ gotNum = parseDurationToMonths(got);
427
+ }
428
+ else if (canon.unit === 'percent') {
429
+ const m = got.match(/(-?\d+(?:\.\d+)?)\s*%/);
430
+ if (m)
431
+ gotNum = Number.parseFloat(m[1]);
432
+ }
433
+ if (gotRange) {
434
+ // Range passes when canonical falls in the range OR midpoint is within
435
+ // tolerance.
436
+ const inRange = canon.numeric >= gotRange.low && canon.numeric <= gotRange.high;
437
+ if (inRange) {
438
+ return { inTolerance: true, deltaPct: 0, nonNumeric: false };
439
+ }
440
+ const mid = (gotRange.low + gotRange.high) / 2;
441
+ const delta = pctDelta(mid, canon.numeric);
442
+ return { inTolerance: delta <= tolerancePct, deltaPct: delta, nonNumeric: false };
443
+ }
444
+ if (gotNum === null) {
445
+ // Couldn't parse — non-numeric mismatch (the deliverable said something
446
+ // we couldn't compare; flag it so a human can review).
447
+ return { inTolerance: false, deltaPct: -1, nonNumeric: true };
448
+ }
449
+ const delta = pctDelta(gotNum, canon.numeric);
450
+ return { inTolerance: delta <= tolerancePct, deltaPct: delta, nonNumeric: false };
451
+ }
452
+ function pctDelta(got, canonical) {
453
+ if (canonical === 0) {
454
+ return got === 0 ? 0 : Number.POSITIVE_INFINITY;
455
+ }
456
+ return Math.abs((got - canonical) / canonical) * 100;
457
+ }
458
+ // ============================================================================
459
+ // Entry point
460
+ // ============================================================================
461
+ /**
462
+ * Run the coherence gate over a deliverables directory.
463
+ *
464
+ * Walks `<deliverablesDir>` recursively, scans every `*.md` / `*.json` /
465
+ * `*.csv` for fcv tags, extracts the value following each tag, and compares
466
+ * against the canonical from `unitEconomicsPath`. Returns a report — never
467
+ * throws; all errors are absorbed (unreadable files are skipped, invalid
468
+ * JSON in unit-economics yields an empty canonical map and the gate emits
469
+ * zero violations).
470
+ */
471
+ export async function runCoherenceGate(deliverablesDir, unitEconomicsPath, options) {
472
+ const tolerancePct = options?.tolerancePct !== undefined && options.tolerancePct >= 0
473
+ ? options.tolerancePct
474
+ : 5;
475
+ const canonical = await loadCanonical(unitEconomicsPath);
476
+ let files;
477
+ try {
478
+ files = await walkFiles(deliverablesDir);
479
+ }
480
+ catch {
481
+ return {
482
+ violations: [],
483
+ files_scanned: 0,
484
+ fcv_tags_total: 0,
485
+ tolerance_pct: tolerancePct,
486
+ };
487
+ }
488
+ const violations = [];
489
+ let filesScanned = 0;
490
+ let fcvTagsTotal = 0;
491
+ for (const file of files) {
492
+ let body;
493
+ try {
494
+ body = await fs.readFile(file, 'utf-8');
495
+ }
496
+ catch {
497
+ // file errors are non-fatal and don't increment files_scanned
498
+ continue;
499
+ }
500
+ filesScanned += 1;
501
+ const tags = scanTags(body);
502
+ fcvTagsTotal += tags.length;
503
+ if (tags.length === 0)
504
+ continue;
505
+ const { slug, section } = deriveSlugAndSection(file, deliverablesDir);
506
+ for (const tag of tags) {
507
+ if (tag.kind === null)
508
+ continue; // unrecognized kind — not validated
509
+ const canon = canonicalFor(tag.kind, canonical);
510
+ if (canon === null)
511
+ continue; // canonical not present for this kind
512
+ const valueText = extractValueText(body, tag.tailIndex, tag.kind);
513
+ if (valueText === null)
514
+ continue; // no value to compare — silent skip
515
+ const cmp = compareValues(valueText, canon, tolerancePct);
516
+ if (cmp.inTolerance)
517
+ continue;
518
+ violations.push({
519
+ slug,
520
+ section,
521
+ kind: tag.kind,
522
+ expected: canon.display ?? String(canon.numeric),
523
+ got: valueText,
524
+ delta_pct: cmp.nonNumeric ? -1 : roundDelta(cmp.deltaPct),
525
+ path: path.resolve(file),
526
+ });
527
+ }
528
+ }
529
+ return {
530
+ violations,
531
+ files_scanned: filesScanned,
532
+ fcv_tags_total: fcvTagsTotal,
533
+ tolerance_pct: tolerancePct,
534
+ };
535
+ }
536
+ function roundDelta(n) {
537
+ if (!Number.isFinite(n))
538
+ return n;
539
+ return Math.round(n * 10) / 10;
540
+ }
541
+ //# sourceMappingURL=coherence-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coherence-gate.js","sourceRoot":"","sources":["../../../src/pipeline/phase7/coherence-gate.ts"],"names":[],"mappings":"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"}