@claude-flow/cli 3.22.0 → 3.24.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 (59) hide show
  1. package/.claude/evolve-proof/generation-0.json +211 -0
  2. package/.claude/evolve-proof/real-generation-0.json +406 -0
  3. package/.claude/evolve-proof/real-generation-1.json +406 -0
  4. package/.claude/helpers/.helpers-version +1 -0
  5. package/.claude/helpers/helpers.manifest.json +2 -2
  6. package/.claude/helpers/hook-handler.cjs +0 -0
  7. package/.claude/helpers/intelligence.cjs +10 -0
  8. package/.claude/proven-config.manifest.json +37 -0
  9. package/.claude/proven-config.signed.json +41 -0
  10. package/.claude/proven-config.signed.rvf +0 -0
  11. package/dist/src/commands/memory-backup.d.ts +11 -0
  12. package/dist/src/commands/memory-backup.js +46 -0
  13. package/dist/src/commands/memory.js +2 -1
  14. package/dist/src/config/harness-feedback-applier.d.ts +50 -0
  15. package/dist/src/config/harness-feedback-applier.js +122 -0
  16. package/dist/src/config/proven-config-refresh.d.ts +39 -0
  17. package/dist/src/config/proven-config-refresh.js +154 -0
  18. package/dist/src/config/proven-config-rvfa.d.ts +23 -0
  19. package/dist/src/config/proven-config-rvfa.js +73 -0
  20. package/dist/src/config/proven-config.d.ts +86 -0
  21. package/dist/src/config/proven-config.js +176 -0
  22. package/dist/src/index.js +35 -0
  23. package/dist/src/mcp-tools/neural-tools.d.ts +10 -0
  24. package/dist/src/mcp-tools/neural-tools.js +107 -18
  25. package/dist/src/services/daemon-autostart.d.ts +17 -0
  26. package/dist/src/services/daemon-autostart.js +79 -0
  27. package/dist/src/services/evolve-proof.d.ts +247 -0
  28. package/dist/src/services/evolve-proof.js +341 -0
  29. package/dist/src/services/harness-benchmark.d.ts +68 -0
  30. package/dist/src/services/harness-benchmark.js +94 -0
  31. package/dist/src/services/harness-canary.d.ts +60 -0
  32. package/dist/src/services/harness-canary.js +69 -0
  33. package/dist/src/services/harness-corpus-harvester.d.ts +51 -0
  34. package/dist/src/services/harness-corpus-harvester.js +74 -0
  35. package/dist/src/services/harness-flywheel-generations.d.ts +111 -0
  36. package/dist/src/services/harness-flywheel-generations.js +354 -0
  37. package/dist/src/services/harness-flywheel-runtime.d.ts +25 -0
  38. package/dist/src/services/harness-flywheel-runtime.js +101 -0
  39. package/dist/src/services/harness-flywheel.d.ts +48 -0
  40. package/dist/src/services/harness-flywheel.js +207 -0
  41. package/dist/src/services/harness-hosts.d.ts +40 -0
  42. package/dist/src/services/harness-hosts.js +88 -0
  43. package/dist/src/services/harness-improvement-ledger.d.ts +63 -0
  44. package/dist/src/services/harness-improvement-ledger.js +101 -0
  45. package/dist/src/services/harness-loop.d.ts +53 -0
  46. package/dist/src/services/harness-loop.js +85 -0
  47. package/dist/src/services/harness-qualification.d.ts +66 -0
  48. package/dist/src/services/harness-qualification.js +143 -0
  49. package/dist/src/services/harness-replay.d.ts +37 -0
  50. package/dist/src/services/harness-replay.js +92 -0
  51. package/dist/src/services/harness-verify.d.ts +33 -0
  52. package/dist/src/services/harness-verify.js +26 -0
  53. package/dist/src/services/harness-worker.d.ts +23 -0
  54. package/dist/src/services/harness-worker.js +66 -0
  55. package/dist/src/services/memory-backup.d.ts +24 -0
  56. package/dist/src/services/memory-backup.js +94 -0
  57. package/dist/src/services/worker-daemon.d.ts +16 -1
  58. package/dist/src/services/worker-daemon.js +84 -0
  59. package/package.json +1 -1
@@ -0,0 +1,341 @@
1
+ /**
2
+ * single-round proof-of-mechanism (ADR-176)
3
+ *
4
+ * SCOPE — read this before citing any output:
5
+ * This is a SINGLE-ROUND PROOF-OF-MECHANISM. It is NOT flywheel proof, NOT
6
+ * compounding learning, and NOT production learning. Its only purpose is to
7
+ * prove, on ONE deterministic synthetic round:
8
+ * (a) gate wiring — the real versioned accept() decides promotion,
9
+ * (b) receipt persistence — a self-contained bundle is written to disk,
10
+ * (c) SHADOW registration — a passing candidate is registered in shadow,
11
+ * (d) no auto-serve path — nothing is applied to the active/served policy.
12
+ * A synthetic PASS here is NOT evidence of real improvement.
13
+ *
14
+ * Independently verifiable: the bundle embeds the holdout, both manifests, the
15
+ * exact PromotionVerdict inputs, and their hashes. verifyReceiptBundle() rehashes
16
+ * everything and RE-RUNS the same versioned accept() — so a third party can
17
+ * confirm *why* the candidate passed/failed without trusting any service log.
18
+ *
19
+ * Pure Node, $0, no LLM, no network, no real store. Deterministic.
20
+ */
21
+ import { createHash } from 'node:crypto';
22
+ import { accept } from './harness-benchmark.js';
23
+ import { bootstrapDeltaCILow } from './harness-improvement-ledger.js';
24
+ import { canonicalManifestBytes } from '../config/proven-config.js';
25
+ /**
26
+ * The promotion rule is versioned so a receipt pins exactly which semantics
27
+ * decided it. v1+sig = the accept() conjunction AND a statistical-significance
28
+ * term: the per-held-out-task deltas must have a positive one-sided 95% bootstrap
29
+ * lower bound, so a small-N mean gain can't ride on noise. The canary term is a
30
+ * SEPARATE deployment-safety signal (a distinct slice), not held-out dominance.
31
+ */
32
+ export const PROMOTION_RULE_VERSION = 'accept/v1+sig';
33
+ export const PROOF_LABEL = 'single-round proof-of-mechanism';
34
+ export const NOT_CLAIMS = ['not flywheel proof', 'not compounding learning', 'not production learning'];
35
+ function sha256(s) { return 'sha256:' + createHash('sha256').update(s).digest('hex'); }
36
+ function canon(v) {
37
+ const c = (x) => Array.isArray(x) ? x.map(c)
38
+ : (x && typeof x === 'object') ? Object.fromEntries(Object.keys(x).sort().map((k) => [k, c(x[k])])) : x;
39
+ return JSON.stringify(c(v));
40
+ }
41
+ function manifestHash(m) { return sha256(canonicalManifestBytes(m).toString('utf-8')); }
42
+ const FAILURE_CAUSE = {
43
+ held_out_improves: 'holdout', redblue_pass: 'security', drift_within: 'drift',
44
+ replay_deterministic: 'replay', receipt_coverage_full: 'governance', canary_no_worse: 'canary',
45
+ };
46
+ /** Classify a policy mutation by which fields changed → a repeatable mutation CLASS + a diff summary. */
47
+ export function classifyMutation(baseline, candidate) {
48
+ const changed = Object.keys(candidate).filter((k) => candidate[k] !== baseline[k]).sort();
49
+ if (changed.length === 0)
50
+ return { mutationClass: 'none', mutationSummary: 'no change' };
51
+ const summary = changed.map((k) => `${k}:${baseline[k]}→${candidate[k]}`).join(', ');
52
+ const mutationClass = changed.length === 1 ? `retrieval:${changed[0]}` : 'retrieval:multi';
53
+ return { mutationClass, mutationSummary: summary };
54
+ }
55
+ function mean(xs) { return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0; }
56
+ function mkManifest(policyValue, layer = 'synthetic/proof', corpus = 'synthetic-proof-v1') {
57
+ const ref = sha256(canon(policyValue));
58
+ return { schema: 'ruflo.proven-config/v1', policy: { ref, value: policyValue }, layer, benchmark: { corpus, corpusHash: sha256(corpus) } };
59
+ }
60
+ /**
61
+ * Assemble a receipt bundle from a holdout + configs. This is the SHARED core:
62
+ * the synthetic proof and a REAL measured round produce byte-identical bundle
63
+ * structure and run the SAME versioned accept() — so `verifyReceiptBundle`
64
+ * replays a real bundle exactly as it replays the synthetic fixture.
65
+ */
66
+ export function assembleBundle(baseline, candidate, holdout, o) {
67
+ const baselineHeldOut = mean(holdout.map((h) => h.baselineScore));
68
+ const candidateHeldOut = mean(holdout.map((h) => h.candidateScore));
69
+ // Canary is a SEPARATE deployment-safety signal (default: strict per-held-out
70
+ // regression, preserving synthetic behavior; real rounds pass a distinct slice).
71
+ const canaryRollbackRate = o.canaryRollbackRate ?? holdout.filter((h) => h.candidateScore < h.baselineScore - 1e-9).length / holdout.length;
72
+ // Statistical-significance term (small-N noise guard): bootstrap lower bound on
73
+ // the per-task deltas must be > 0.
74
+ const deltaCILow = bootstrapDeltaCILow(holdout.map((h) => h.candidateScore - h.baselineScore));
75
+ const significant = deltaCILow > 0;
76
+ const verdictInputs = {
77
+ heldOutScore: candidateHeldOut, baselineHeldOutScore: baselineHeldOut,
78
+ redblue: o.redblue ?? 'PASS', drift: o.drift ?? 0, driftThreshold: 0.05,
79
+ replayDeterministic: true, receiptCoverage: 1, canaryRollbackRate, baselineRollbackRate: 0,
80
+ };
81
+ const result = accept(verdictInputs);
82
+ const promoted = result.accept && significant;
83
+ const baselineManifest = mkManifest(baseline, o.layer, o.corpus);
84
+ const candidateManifest = mkManifest(candidate, o.layer, o.corpus);
85
+ const baselineManifestHash = manifestHash(baselineManifest);
86
+ const candidateManifestHash = manifestHash(candidateManifest);
87
+ const inputHoldoutHash = sha256(canon(holdout));
88
+ const failed = [...result.failed, ...(!significant ? ['significant'] : [])];
89
+ const decisionReceipt = {
90
+ promotionRuleVersion: PROMOTION_RULE_VERSION, verdictInputs, result, significant, deltaCILow, promoted,
91
+ reason: promoted ? `promoted (all ${PROMOTION_RULE_VERSION} terms held)` : `rejected — ${failed.join(', ')}`,
92
+ };
93
+ const shadow = promoted ? {
94
+ registrationId: sha256(`${candidateManifestHash}|gen${o.generation}|shadow`).replace('sha256:', 'shadow:'),
95
+ state: 'shadow', served: false, candidateManifestHash, registeredAt: o.now,
96
+ } : null;
97
+ const { mutationClass, mutationSummary } = classifyMutation(baseline, candidate);
98
+ const deltas = { benchmark: candidateHeldOut - baselineHeldOut, security: o.redblue === 'FAIL' ? -1 : 0, cost: 0 };
99
+ const promotion = promoted ? { parentManifestHash: o.parent, candidateManifestHash, mutationClass, mutationSummary, deltas, decisionReceipt } : null;
100
+ const regression = promoted ? null : {
101
+ candidateManifestHash, ancestor: o.parent ?? baselineManifestHash, mutationClass,
102
+ failureCause: !significant && result.accept ? 'significance' : (FAILURE_CAUSE[result.failed[0]] ?? 'holdout'), failedTerms: failed,
103
+ };
104
+ return {
105
+ label: PROOF_LABEL, disclaimers: NOT_CLAIMS, generation: o.generation, parent: o.parent, branch: o.branch, kind: o.kind, createdAt: o.now,
106
+ inputHoldoutHash, baselineManifestHash, candidateManifestHash,
107
+ meetsPromotionRule: { version: PROMOTION_RULE_VERSION, result: promoted },
108
+ decisionReceipt, shadow,
109
+ costReceipt: { usd: 0, llmCalls: 0, tier: o.cost.tier, notes: o.cost.notes },
110
+ mutationClass, mutationSummary, deltas, promotion, regression,
111
+ holdout, baselineManifest, candidateManifest,
112
+ };
113
+ }
114
+ /**
115
+ * Build a REAL evolve-round receipt bundle from MEASURED holdout scores (live
116
+ * retrieval over a frozen anchor). Same gate, same replayability as the
117
+ * synthetic proof — but `kind: 'real'` and the scores come from actual runs.
118
+ * `redblue` should be 'FAIL' if the candidate regressed a frozen security/anchor
119
+ * slice; drift from real distribution shift. $0 (no LLM/network on this path).
120
+ */
121
+ export function runRealEvolveRound(opts) {
122
+ return assembleBundle(opts.baseline, opts.candidate, opts.holdout, {
123
+ generation: opts.generation, parent: opts.parent, branch: opts.branch ?? 'main', now: opts.now, kind: 'real',
124
+ cost: { tier: 'real-local', notes: 'measured on the frozen anchor via live retrieval — no LLM, no network' },
125
+ redblue: opts.redblue, drift: opts.drift, canaryRollbackRate: opts.canaryRollbackRate, layer: 'real/retrieval', corpus: opts.corpus,
126
+ });
127
+ }
128
+ /**
129
+ * Run ONE deterministic synthetic evolve round and produce the receipt bundle.
130
+ * `now` is injected (no Date in the pure path) for reproducible fixtures.
131
+ * The default scenario is a strict Pareto improvement (candidate ≥ baseline on
132
+ * every task, > on the mean) so the full PROMOTE→SHADOW path is exercised; pass
133
+ * `regress: true` to exercise the REJECT path instead.
134
+ */
135
+ export function runSyntheticProofRound(opts = { now: 0 }) {
136
+ const generation = opts.generation ?? 0;
137
+ const parent = opts.parent ?? null;
138
+ const branch = opts.branch ?? 'main';
139
+ const baseline = opts.baseline ?? { alpha: 0.5, subjectWeight: 2, mmrLambda: 0.7, bodyWeight: 1, typePenaltyFactor: 1 };
140
+ const candidate = opts.candidate ?? { alpha: 0.3, subjectWeight: 1, mmrLambda: 0.5, bodyWeight: 1.5, typePenaltyFactor: 0.5 };
141
+ // Deterministic synthetic holdout. Default: candidate never worse, sometimes
142
+ // better (Pareto). regress: candidate worse on one task (drives a REJECT).
143
+ const holdout = [
144
+ { taskId: 't0', baselineScore: 0.60, candidateScore: 0.70 },
145
+ { taskId: 't1', baselineScore: 0.80, candidateScore: 0.86 },
146
+ { taskId: 't2', baselineScore: 0.50, candidateScore: 0.62 },
147
+ { taskId: 't3', baselineScore: 0.72, candidateScore: 0.80 },
148
+ { taskId: 't4', baselineScore: 0.66, candidateScore: opts.regress ? 0.50 : 0.78 },
149
+ ];
150
+ return assembleBundle(baseline, candidate, holdout, {
151
+ generation, parent, branch, now: opts.now, kind: 'synthetic',
152
+ cost: { tier: 'synthetic', notes: 'deterministic synthetic round — no model, no network, no real store' },
153
+ });
154
+ }
155
+ /**
156
+ * Independently verify a receipt bundle WITHOUT trusting any service log: rehash
157
+ * the embedded holdout + manifests, recompute the held-out means + canary rate
158
+ * from the embedded per-task scores, RE-RUN the same versioned accept(), and
159
+ * confirm the recomputed decision equals the recorded one. Also confirms the
160
+ * SHADOW registration is not served (no auto-serve). Pure; never throws.
161
+ */
162
+ export function verifyReceiptBundle(bundle) {
163
+ const mismatches = [];
164
+ const hashChecks = {
165
+ inputHoldout: sha256(canon(bundle.holdout)) === bundle.inputHoldoutHash,
166
+ baselineManifest: manifestHash(bundle.baselineManifest) === bundle.baselineManifestHash,
167
+ candidateManifest: manifestHash(bundle.candidateManifest) === bundle.candidateManifestHash,
168
+ };
169
+ if (!hashChecks.inputHoldout)
170
+ mismatches.push('input holdout hash mismatch');
171
+ if (!hashChecks.baselineManifest)
172
+ mismatches.push('baseline manifest hash mismatch');
173
+ if (!hashChecks.candidateManifest)
174
+ mismatches.push('candidate manifest hash mismatch');
175
+ const baselineHeldOut = mean(bundle.holdout.map((h) => h.baselineScore));
176
+ const candidateHeldOut = mean(bundle.holdout.map((h) => h.candidateScore));
177
+ // The canary is a SEPARATE slice (not embedded) — trust the recorded rate; the
178
+ // held-out means + significance ARE independently recomputed from the holdout,
179
+ // so holdout tampering is still caught (via means/significance/decision).
180
+ const canaryRollbackRate = bundle.decisionReceipt.verdictInputs.canaryRollbackRate;
181
+ const deltaCILow = bootstrapDeltaCILow(bundle.holdout.map((h) => h.candidateScore - h.baselineScore));
182
+ const significant = deltaCILow > 0;
183
+ // Re-run the SAME versioned rule on independently-recomputed inputs.
184
+ const ruleVersionMatches = bundle.decisionReceipt.promotionRuleVersion === PROMOTION_RULE_VERSION
185
+ && bundle.meetsPromotionRule.version === PROMOTION_RULE_VERSION;
186
+ if (!ruleVersionMatches)
187
+ mismatches.push(`promotion rule version != ${PROMOTION_RULE_VERSION}`);
188
+ const decision = accept({
189
+ ...bundle.decisionReceipt.verdictInputs,
190
+ heldOutScore: candidateHeldOut, baselineHeldOutScore: baselineHeldOut,
191
+ });
192
+ const promotedRecomputed = decision.accept && significant;
193
+ const decisionMatches = promotedRecomputed === bundle.decisionReceipt.promoted && promotedRecomputed === bundle.meetsPromotionRule.result;
194
+ if (!decisionMatches)
195
+ mismatches.push('recomputed decision != recorded decision');
196
+ // no-auto-serve: a shadow registration must never be marked served.
197
+ const noAutoServe = bundle.shadow === null || bundle.shadow.served === false;
198
+ if (!noAutoServe)
199
+ mismatches.push('candidate was auto-served (served=true) — violates shadow-only');
200
+ // causal record consistency: a pass carries a promotion record (with a delta
201
+ // that matches the recompute) and no regression; a reject carries the inverse.
202
+ const benchmarkDeltaMatches = Math.abs(bundle.deltas.benchmark - (candidateHeldOut - baselineHeldOut)) < 1e-9;
203
+ const causalConsistent = promotedRecomputed
204
+ ? (bundle.promotion !== null && bundle.regression === null && benchmarkDeltaMatches)
205
+ : (bundle.promotion === null && bundle.regression !== null);
206
+ if (!causalConsistent)
207
+ mismatches.push('causal record inconsistent with the decision (promotion/regression/delta)');
208
+ const valid = hashChecks.inputHoldout && hashChecks.baselineManifest && hashChecks.candidateManifest
209
+ && ruleVersionMatches && decisionMatches && noAutoServe && causalConsistent;
210
+ const why = promotedRecomputed
211
+ ? `PASS under ${PROMOTION_RULE_VERSION}: held_out ${candidateHeldOut.toFixed(4)} > ${baselineHeldOut.toFixed(4)} (Δ CI-low ${deltaCILow.toFixed(4)} > 0, significant), canary rollback ${canaryRollbackRate} ≤ 0, all terms held`
212
+ : `FAIL under ${PROMOTION_RULE_VERSION}: ${[...decision.failed, ...(!significant ? ['significant'] : [])].join(', ')}`;
213
+ return {
214
+ valid, hashChecks,
215
+ recomputed: { baselineHeldOut, candidateHeldOut, canaryRollbackRate, decision },
216
+ decisionMatches, ruleVersionMatches, noAutoServe, causalConsistent,
217
+ explanation: `independently recomputed from the bundle (no service logs) → ${why}`,
218
+ mismatches,
219
+ };
220
+ }
221
+ /**
222
+ * Reconstruct + audit a lineage DAG. Independently replays every bundle and
223
+ * checks the graph invariants back to the immutable root. Pure; never throws.
224
+ */
225
+ export function reconstructLineage(bundles) {
226
+ const problems = [];
227
+ const ordered = [...bundles].sort((a, b) => a.generation - b.generation);
228
+ const byCandidate = new Map(ordered.map((b) => [b.candidateManifestHash, b]));
229
+ let allReplayable = true;
230
+ const nodes = ordered.map((b) => {
231
+ const rep = verifyReceiptBundle(b);
232
+ if (!rep.valid) {
233
+ allReplayable = false;
234
+ problems.push(`gen ${b.generation}: not independently replayable (${rep.mismatches.join('; ')})`);
235
+ }
236
+ return { generation: b.generation, branch: b.branch, promoted: b.decisionReceipt.promoted, parent: b.parent, candidateManifestHash: b.candidateManifestHash, mutationClass: b.mutationClass, delta: b.deltas.benchmark, replayable: rep.valid };
237
+ });
238
+ // DAG invariants.
239
+ let lineageIntact = true;
240
+ const roots = ordered.filter((b) => b.parent === null);
241
+ if (roots.length !== 1) {
242
+ lineageIntact = false;
243
+ problems.push(`expected exactly one immutable root, found ${roots.length}`);
244
+ }
245
+ for (const b of ordered) {
246
+ if (b.parent === null)
247
+ continue;
248
+ const parent = byCandidate.get(b.parent);
249
+ if (!parent) {
250
+ lineageIntact = false;
251
+ problems.push(`gen ${b.generation} (${b.branch}): parent ${b.parent.slice(0, 20)}… not found in graph`);
252
+ continue;
253
+ }
254
+ if (parent.generation >= b.generation) {
255
+ lineageIntact = false;
256
+ problems.push(`gen ${b.generation}: parent generation ${parent.generation} not older (cycle risk)`);
257
+ }
258
+ if (b.baselineManifestHash !== parent.candidateManifestHash) {
259
+ lineageIntact = false;
260
+ problems.push(`gen ${b.generation} (${b.branch}): baseline != parent's promoted candidate (did not inherit the verified policy)`);
261
+ }
262
+ }
263
+ return {
264
+ generations: ordered.length,
265
+ candidatesEvaluated: ordered.length,
266
+ promotions: ordered.filter((b) => b.decisionReceipt.promoted).length,
267
+ rejections: ordered.filter((b) => !b.decisionReceipt.promoted).length,
268
+ cumulativeHeldOutImprovement: ordered.reduce((s, b) => s + (b.decisionReceipt.promoted ? b.deltas.benchmark : 0), 0),
269
+ rootHash: roots[0]?.candidateManifestHash ?? null,
270
+ branches: [...new Set(ordered.map((b) => b.branch))],
271
+ lineageIntact: lineageIntact && allReplayable, allReplayable, nodes, problems,
272
+ };
273
+ }
274
+ /**
275
+ * Aggregate per-mutation-class effectiveness across a lineage. After enough
276
+ * generations the optimizer can bias toward classes with higher historical
277
+ * payoff — meta-learning grounded in evidence, not intuition.
278
+ */
279
+ export function mutationEffectiveness(bundles) {
280
+ const by = new Map();
281
+ for (const b of bundles) {
282
+ const e = by.get(b.mutationClass) ?? { attempts: 0, promotions: 0, deltas: [] };
283
+ e.attempts++;
284
+ if (b.decisionReceipt.promoted) {
285
+ e.promotions++;
286
+ e.deltas.push(b.deltas.benchmark);
287
+ }
288
+ by.set(b.mutationClass, e);
289
+ }
290
+ return [...by.entries()]
291
+ .map(([mutationClass, e]) => ({ mutationClass, attempts: e.attempts, promotions: e.promotions, meanDelta: mean(e.deltas) }))
292
+ .sort((a, b) => b.meanDelta - a.meanDelta);
293
+ }
294
+ function median(xs) { if (!xs.length)
295
+ return 0; const s = [...xs].sort((a, b) => a - b); const m = s.length >> 1; return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; }
296
+ function variance(xs) { if (xs.length < 2)
297
+ return 0; const mu = mean(xs); return mean(xs.map((x) => (x - mu) ** 2)); }
298
+ /**
299
+ * Distinguish local optimum vs noisy benchmark vs optimizer failure — rigorously,
300
+ * not by intuition. Over a rolling window: near-zero median improvement AND low
301
+ * promotion rate is a plateau; shrinking candidate variance ⇒ converged
302
+ * (local optimum); high, non-shrinking variance ⇒ the benchmark is noisy; low
303
+ * variance with no promotions ⇒ the optimizer stopped exploring (failure).
304
+ */
305
+ export function detectPlateau(bundles, opts = {}) {
306
+ const window = opts.window ?? 20;
307
+ const epsilon = opts.epsilon ?? 0.001;
308
+ const maxPromotionRate = opts.maxPromotionRate ?? 0.1;
309
+ const ordered = [...bundles].sort((a, b) => a.generation - b.generation);
310
+ if (ordered.length < window) {
311
+ return { status: 'insufficient-data', window, medianImprovement: 0, promotionRate: 0, varianceShrinking: false, candidateVariance: 0, rationale: `need ${window} generations, have ${ordered.length}` };
312
+ }
313
+ const recent = ordered.slice(-window);
314
+ const promotedDeltas = recent.filter((b) => b.decisionReceipt.promoted).map((b) => b.deltas.benchmark);
315
+ const medianImprovement = median(promotedDeltas);
316
+ const promotionRate = promotedDeltas.length / window;
317
+ const candScores = recent.map((b) => mean(b.holdout.map((h) => h.candidateScore)));
318
+ const firstHalfVar = variance(candScores.slice(0, window >> 1));
319
+ const secondHalfVar = variance(candScores.slice(window >> 1));
320
+ const candidateVariance = variance(candScores);
321
+ const varianceShrinking = secondHalfVar < firstHalfVar * 0.5;
322
+ let status = 'active';
323
+ let rationale = `median Δ ${medianImprovement.toFixed(4)} ≥ ε or promotion rate ${(promotionRate * 100).toFixed(0)}% ≥ ${(maxPromotionRate * 100).toFixed(0)}% — still improving`;
324
+ const plateaued = medianImprovement < epsilon && promotionRate < maxPromotionRate;
325
+ if (plateaued) {
326
+ if (varianceShrinking) {
327
+ status = 'local-optimum';
328
+ rationale = 'no gains + candidate variance shrinking → converged to a local optimum';
329
+ }
330
+ else if (candidateVariance > epsilon) {
331
+ status = 'noisy-benchmark';
332
+ rationale = 'no gains but candidates vary widely → benchmark noise is masking signal';
333
+ }
334
+ else {
335
+ status = 'optimizer-failure';
336
+ rationale = 'no gains + candidates barely vary → optimizer stopped exploring';
337
+ }
338
+ }
339
+ return { status, window, medianImprovement, promotionRate, varianceShrinking, candidateVariance, rationale };
340
+ }
341
+ //# sourceMappingURL=evolve-proof.js.map
@@ -0,0 +1,68 @@
1
+ export interface BenchTask {
2
+ id: string;
3
+ input: unknown;
4
+ expected: unknown;
5
+ weight?: number;
6
+ }
7
+ export interface HarnessBenchmarkCorpus {
8
+ version: string;
9
+ corpusHash: string;
10
+ tasks: BenchTask[];
11
+ }
12
+ /** Run a candidate policy on a task's input, producing an output. */
13
+ export type EvalFn<C> = (input: unknown, candidate: C) => unknown;
14
+ /** Grade an output against expected → [0,1]. */
15
+ export type GradeFn = (output: unknown, expected: unknown) => number;
16
+ /** Content hash of a corpus's tasks — the tamper-evident pin for the held-out set. */
17
+ export declare function hashCorpus(tasks: BenchTask[]): string;
18
+ /** Verify a corpus's declared hash matches its tasks (integrity of the benchmark). */
19
+ export declare function verifyCorpus(corpus: HarnessBenchmarkCorpus): boolean;
20
+ export interface ScoreResult {
21
+ fitness: number;
22
+ passRate: number;
23
+ n: number;
24
+ }
25
+ /** Score a candidate over a task set, on isolated tasks (no shared state). */
26
+ export declare function scoreOnTasks<C>(tasks: BenchTask[], candidate: C, evalFn: EvalFn<C>, gradeFn: GradeFn): ScoreResult;
27
+ export interface HeldOutSplit {
28
+ train: BenchTask[];
29
+ heldOut: BenchTask[];
30
+ }
31
+ /**
32
+ * Deterministic held-out split. Tasks are stably ordered by id, and the last
33
+ * `holdoutFrac` become the held-out set — disjoint from train, reproducible
34
+ * (so two runs converge, per the ADR-176 acceptance test).
35
+ */
36
+ export declare function computeHeldOutSplit(tasks: BenchTask[], holdoutFrac?: number): HeldOutSplit;
37
+ /** Independently-measured verdicts fed to accept(). Each comes from a different mechanism. */
38
+ export interface PromotionVerdict {
39
+ heldOutScore: number;
40
+ baselineHeldOutScore: number;
41
+ redblue: 'PASS' | 'FAIL' | 'SKIPPED';
42
+ drift: number;
43
+ driftThreshold: number;
44
+ replayDeterministic: boolean;
45
+ receiptCoverage: number;
46
+ canaryRollbackRate: number;
47
+ baselineRollbackRate: number;
48
+ }
49
+ export interface AcceptResult {
50
+ accept: boolean;
51
+ terms: Record<string, {
52
+ value: unknown;
53
+ pass: boolean;
54
+ }>;
55
+ failed: string[];
56
+ }
57
+ /**
58
+ * accept(candidate) ⟺
59
+ * held_out_score > baseline
60
+ * AND redblue == PASS
61
+ * AND drift <= threshold
62
+ * AND replay == deterministic
63
+ * AND receipt_cov == 100%
64
+ * AND canary.rollback_rate <= baseline
65
+ * Every term is externally measurable; ANY failure → reject.
66
+ */
67
+ export declare function accept(v: PromotionVerdict): AcceptResult;
68
+ //# sourceMappingURL=harness-benchmark.d.ts.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Benchmark corpus + held-out scoring + the accept() promotion gate
3
+ * (ADR-176 phase 2 + the multi-term promotion rule).
4
+ *
5
+ * Proof #1 (measured, held-out): a candidate is scored against a VERSIONED,
6
+ * content-hashed corpus on a held-out split — the "prove-before-ship" pattern
7
+ * distill-tuning.ts established (train/held-out disjoint by index; held-out
8
+ * scored once). Fitness is a composite, not a single gameable number.
9
+ *
10
+ * Promotion is a CONJUNCTION of externally-measurable predicates (ADR-176) —
11
+ * never a scalar. accept() takes the independently-measured verdicts (held-out
12
+ * delta, redblue, drift, deterministic replay, receipt coverage, canary
13
+ * rollback) and admits only when EVERY term holds. Multi-dimensional +
14
+ * Goodhart-resistant. Zero deps, $0.
15
+ */
16
+ import { createHash } from 'crypto';
17
+ function stable(v) {
18
+ const c = (x) => {
19
+ if (Array.isArray(x))
20
+ return x.map(c);
21
+ if (x && typeof x === 'object') {
22
+ const o = {};
23
+ for (const k of Object.keys(x).sort())
24
+ o[k] = c(x[k]);
25
+ return o;
26
+ }
27
+ return x;
28
+ };
29
+ return JSON.stringify(c(v));
30
+ }
31
+ /** Content hash of a corpus's tasks — the tamper-evident pin for the held-out set. */
32
+ export function hashCorpus(tasks) {
33
+ const canon = tasks.map(t => ({ id: t.id, input: t.input, expected: t.expected, weight: t.weight ?? 1 }))
34
+ .sort((a, b) => a.id.localeCompare(b.id));
35
+ return 'sha256:' + createHash('sha256').update(stable(canon)).digest('hex');
36
+ }
37
+ /** Verify a corpus's declared hash matches its tasks (integrity of the benchmark). */
38
+ export function verifyCorpus(corpus) {
39
+ return hashCorpus(corpus.tasks) === corpus.corpusHash;
40
+ }
41
+ /** Score a candidate over a task set, on isolated tasks (no shared state). */
42
+ export function scoreOnTasks(tasks, candidate, evalFn, gradeFn) {
43
+ if (tasks.length === 0)
44
+ return { fitness: 0, passRate: 0, n: 0 };
45
+ let weighted = 0, totalW = 0, passes = 0;
46
+ for (const t of tasks) {
47
+ const w = t.weight ?? 1;
48
+ let g = 0;
49
+ try {
50
+ g = Math.max(0, Math.min(1, gradeFn(evalFn(t.input, candidate), t.expected)));
51
+ }
52
+ catch {
53
+ g = 0;
54
+ }
55
+ weighted += g * w;
56
+ totalW += w;
57
+ if (g >= 0.999)
58
+ passes++;
59
+ }
60
+ return { fitness: totalW > 0 ? weighted / totalW : 0, passRate: passes / tasks.length, n: tasks.length };
61
+ }
62
+ /**
63
+ * Deterministic held-out split. Tasks are stably ordered by id, and the last
64
+ * `holdoutFrac` become the held-out set — disjoint from train, reproducible
65
+ * (so two runs converge, per the ADR-176 acceptance test).
66
+ */
67
+ export function computeHeldOutSplit(tasks, holdoutFrac = 0.2) {
68
+ const ordered = [...tasks].sort((a, b) => a.id.localeCompare(b.id));
69
+ const cut = Math.max(0, ordered.length - Math.max(1, Math.round(ordered.length * holdoutFrac)));
70
+ return { train: ordered.slice(0, cut), heldOut: ordered.slice(cut) };
71
+ }
72
+ /**
73
+ * accept(candidate) ⟺
74
+ * held_out_score > baseline
75
+ * AND redblue == PASS
76
+ * AND drift <= threshold
77
+ * AND replay == deterministic
78
+ * AND receipt_cov == 100%
79
+ * AND canary.rollback_rate <= baseline
80
+ * Every term is externally measurable; ANY failure → reject.
81
+ */
82
+ export function accept(v) {
83
+ const terms = {
84
+ held_out_improves: { value: `${v.heldOutScore.toFixed(4)} > ${v.baselineHeldOutScore.toFixed(4)}`, pass: v.heldOutScore > v.baselineHeldOutScore },
85
+ redblue_pass: { value: v.redblue, pass: v.redblue === 'PASS' },
86
+ drift_within: { value: `${v.drift} <= ${v.driftThreshold}`, pass: v.drift <= v.driftThreshold },
87
+ replay_deterministic: { value: v.replayDeterministic, pass: v.replayDeterministic === true },
88
+ receipt_coverage_full: { value: v.receiptCoverage, pass: v.receiptCoverage >= 1 },
89
+ canary_no_worse: { value: `${v.canaryRollbackRate} <= ${v.baselineRollbackRate}`, pass: v.canaryRollbackRate <= v.baselineRollbackRate },
90
+ };
91
+ const failed = Object.entries(terms).filter(([, t]) => !t.pass).map(([k]) => k);
92
+ return { accept: failed.length === 0, terms, failed };
93
+ }
94
+ //# sourceMappingURL=harness-benchmark.js.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Canary — separate promotion from deployment (ADR-176 phase 5).
3
+ *
4
+ * Held-out evaluation proves a candidate on FROZEN data; it has not observed
5
+ * real-world behavior. The canary runs the candidate on a bounded, reversible
6
+ * SLICE of live work and measures rollback rate, latency, cost, failure
7
+ * frequency, and acceptance — the telemetry the accept() conjunction needs
8
+ * (canary.rollback_rate <= baseline). Only after canary evidence does PROMOTE
9
+ * fire. This is what stops benchmark-specific evolution from reaching global
10
+ * rollout. Zero deps, $0 (the runner is injected; a real one meters real work).
11
+ */
12
+ /** One canary run's observed outcome. */
13
+ export interface CanaryOutcome {
14
+ ok: boolean;
15
+ rolledBack: boolean;
16
+ latencyMs: number;
17
+ costUsd: number;
18
+ accepted: boolean;
19
+ }
20
+ /** Executes a candidate on one task input under real (or simulated) conditions. */
21
+ export type CanaryRunner<C> = (input: unknown, candidate: C) => CanaryOutcome;
22
+ export interface CanaryTelemetry {
23
+ n: number;
24
+ rollbackRate: number;
25
+ failureRate: number;
26
+ acceptanceRate: number;
27
+ latencyP95: number;
28
+ latencyMean: number;
29
+ costPerTask: number;
30
+ costTotalUsd: number;
31
+ }
32
+ export interface CanaryOptions {
33
+ sampleFraction?: number;
34
+ maxSamples?: number;
35
+ }
36
+ /**
37
+ * Run the candidate on a bounded, deterministic sample of the slice and
38
+ * aggregate telemetry. Deterministic sampling (stride over id-sorted inputs) so
39
+ * two runs converge (ADR-176 acceptance test).
40
+ */
41
+ export declare function runCanary<C>(candidate: C, slice: Array<{
42
+ id: string;
43
+ input: unknown;
44
+ }>, runner: CanaryRunner<C>, opts?: CanaryOptions): CanaryTelemetry;
45
+ export interface CanaryComparison {
46
+ pass: boolean;
47
+ checks: Record<string, {
48
+ candidate: number;
49
+ baseline: number;
50
+ pass: boolean;
51
+ }>;
52
+ failed: string[];
53
+ }
54
+ /**
55
+ * Canary gate: the candidate must be NO WORSE than the baseline on rollback,
56
+ * latency, and cost (the ADR-176 metrics table constraints). Feeds accept()'s
57
+ * `canary.rollback_rate <= baseline` term plus the latency/cost guards.
58
+ */
59
+ export declare function compareCanary(candidate: CanaryTelemetry, baseline: CanaryTelemetry, tolerance?: number): CanaryComparison;
60
+ //# sourceMappingURL=harness-canary.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Canary — separate promotion from deployment (ADR-176 phase 5).
3
+ *
4
+ * Held-out evaluation proves a candidate on FROZEN data; it has not observed
5
+ * real-world behavior. The canary runs the candidate on a bounded, reversible
6
+ * SLICE of live work and measures rollback rate, latency, cost, failure
7
+ * frequency, and acceptance — the telemetry the accept() conjunction needs
8
+ * (canary.rollback_rate <= baseline). Only after canary evidence does PROMOTE
9
+ * fire. This is what stops benchmark-specific evolution from reaching global
10
+ * rollout. Zero deps, $0 (the runner is injected; a real one meters real work).
11
+ */
12
+ function percentile(sorted, p) {
13
+ if (sorted.length === 0)
14
+ return 0;
15
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));
16
+ return sorted[idx];
17
+ }
18
+ /**
19
+ * Run the candidate on a bounded, deterministic sample of the slice and
20
+ * aggregate telemetry. Deterministic sampling (stride over id-sorted inputs) so
21
+ * two runs converge (ADR-176 acceptance test).
22
+ */
23
+ export function runCanary(candidate, slice, runner, opts = {}) {
24
+ const frac = opts.sampleFraction ?? 0.1;
25
+ const cap = opts.maxSamples ?? 100;
26
+ const ordered = [...slice].sort((a, b) => a.id.localeCompare(b.id));
27
+ const want = Math.min(cap, Math.max(1, Math.round(ordered.length * frac)));
28
+ // Even stride so the sample spans the slice rather than a contiguous head.
29
+ const stride = Math.max(1, Math.floor(ordered.length / want));
30
+ const sample = [];
31
+ for (let i = 0; i < ordered.length && sample.length < want; i += stride)
32
+ sample.push(ordered[i]);
33
+ const outcomes = sample.map(s => {
34
+ try {
35
+ return runner(s.input, candidate);
36
+ }
37
+ catch {
38
+ return { ok: false, rolledBack: true, latencyMs: 0, costUsd: 0, accepted: false };
39
+ }
40
+ });
41
+ const n = outcomes.length || 1;
42
+ const lat = outcomes.map(o => o.latencyMs).sort((a, b) => a - b);
43
+ const costTotal = outcomes.reduce((s, o) => s + o.costUsd, 0);
44
+ return {
45
+ n: outcomes.length,
46
+ rollbackRate: outcomes.filter(o => o.rolledBack).length / n,
47
+ failureRate: outcomes.filter(o => !o.ok).length / n,
48
+ acceptanceRate: outcomes.filter(o => o.accepted).length / n,
49
+ latencyP95: percentile(lat, 0.95),
50
+ latencyMean: lat.reduce((s, v) => s + v, 0) / n,
51
+ costPerTask: costTotal / n,
52
+ costTotalUsd: costTotal,
53
+ };
54
+ }
55
+ /**
56
+ * Canary gate: the candidate must be NO WORSE than the baseline on rollback,
57
+ * latency, and cost (the ADR-176 metrics table constraints). Feeds accept()'s
58
+ * `canary.rollback_rate <= baseline` term plus the latency/cost guards.
59
+ */
60
+ export function compareCanary(candidate, baseline, tolerance = 1e-9) {
61
+ const checks = {
62
+ rollback_no_worse: { candidate: candidate.rollbackRate, baseline: baseline.rollbackRate, pass: candidate.rollbackRate <= baseline.rollbackRate + tolerance },
63
+ latency_no_worse: { candidate: candidate.latencyP95, baseline: baseline.latencyP95, pass: candidate.latencyP95 <= baseline.latencyP95 * (1 + 0.01) },
64
+ cost_no_worse: { candidate: candidate.costPerTask, baseline: baseline.costPerTask, pass: candidate.costPerTask <= baseline.costPerTask * (1 + 0.01) },
65
+ };
66
+ const failed = Object.entries(checks).filter(([, c]) => !c.pass).map(([k]) => k);
67
+ return { pass: failed.length === 0, checks, failed };
68
+ }
69
+ //# sourceMappingURL=harness-canary.js.map