claude-flow 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 (55) hide show
  1. package/.claude/helpers/.helpers-version +1 -0
  2. package/.claude/helpers/auto-memory-hook.mjs +59 -274
  3. package/.claude/helpers/helpers.manifest.json +12 -0
  4. package/.claude/helpers/hook-handler.cjs +132 -112
  5. package/.claude/helpers/intelligence.cjs +992 -196
  6. package/package.json +1 -1
  7. package/v3/@claude-flow/cli/dist/src/commands/memory-backup.d.ts +11 -0
  8. package/v3/@claude-flow/cli/dist/src/commands/memory-backup.js +46 -0
  9. package/v3/@claude-flow/cli/dist/src/commands/memory.js +2 -1
  10. package/v3/@claude-flow/cli/dist/src/config/harness-feedback-applier.d.ts +50 -0
  11. package/v3/@claude-flow/cli/dist/src/config/harness-feedback-applier.js +122 -0
  12. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.d.ts +39 -0
  13. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.js +154 -0
  14. package/v3/@claude-flow/cli/dist/src/config/proven-config-rvfa.d.ts +23 -0
  15. package/v3/@claude-flow/cli/dist/src/config/proven-config-rvfa.js +73 -0
  16. package/v3/@claude-flow/cli/dist/src/config/proven-config.d.ts +86 -0
  17. package/v3/@claude-flow/cli/dist/src/config/proven-config.js +176 -0
  18. package/v3/@claude-flow/cli/dist/src/index.js +35 -0
  19. package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.d.ts +10 -0
  20. package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +107 -18
  21. package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.d.ts +17 -0
  22. package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.js +79 -0
  23. package/v3/@claude-flow/cli/dist/src/services/evolve-proof.d.ts +247 -0
  24. package/v3/@claude-flow/cli/dist/src/services/evolve-proof.js +341 -0
  25. package/v3/@claude-flow/cli/dist/src/services/harness-benchmark.d.ts +68 -0
  26. package/v3/@claude-flow/cli/dist/src/services/harness-benchmark.js +94 -0
  27. package/v3/@claude-flow/cli/dist/src/services/harness-canary.d.ts +60 -0
  28. package/v3/@claude-flow/cli/dist/src/services/harness-canary.js +69 -0
  29. package/v3/@claude-flow/cli/dist/src/services/harness-corpus-harvester.d.ts +51 -0
  30. package/v3/@claude-flow/cli/dist/src/services/harness-corpus-harvester.js +74 -0
  31. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-generations.d.ts +111 -0
  32. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-generations.js +354 -0
  33. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.d.ts +25 -0
  34. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.js +101 -0
  35. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.d.ts +48 -0
  36. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.js +207 -0
  37. package/v3/@claude-flow/cli/dist/src/services/harness-hosts.d.ts +40 -0
  38. package/v3/@claude-flow/cli/dist/src/services/harness-hosts.js +88 -0
  39. package/v3/@claude-flow/cli/dist/src/services/harness-improvement-ledger.d.ts +63 -0
  40. package/v3/@claude-flow/cli/dist/src/services/harness-improvement-ledger.js +101 -0
  41. package/v3/@claude-flow/cli/dist/src/services/harness-loop.d.ts +53 -0
  42. package/v3/@claude-flow/cli/dist/src/services/harness-loop.js +85 -0
  43. package/v3/@claude-flow/cli/dist/src/services/harness-qualification.d.ts +66 -0
  44. package/v3/@claude-flow/cli/dist/src/services/harness-qualification.js +143 -0
  45. package/v3/@claude-flow/cli/dist/src/services/harness-replay.d.ts +37 -0
  46. package/v3/@claude-flow/cli/dist/src/services/harness-replay.js +92 -0
  47. package/v3/@claude-flow/cli/dist/src/services/harness-verify.d.ts +33 -0
  48. package/v3/@claude-flow/cli/dist/src/services/harness-verify.js +26 -0
  49. package/v3/@claude-flow/cli/dist/src/services/harness-worker.d.ts +23 -0
  50. package/v3/@claude-flow/cli/dist/src/services/harness-worker.js +66 -0
  51. package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +24 -0
  52. package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +94 -0
  53. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +16 -1
  54. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +84 -0
  55. package/v3/@claude-flow/cli/package.json +1 -1
@@ -0,0 +1,247 @@
1
+ import { type PromotionVerdict, type AcceptResult } from './harness-benchmark.js';
2
+ import { type ProvenConfigManifest } from '../config/proven-config.js';
3
+ /**
4
+ * The promotion rule is versioned so a receipt pins exactly which semantics
5
+ * decided it. v1+sig = the accept() conjunction AND a statistical-significance
6
+ * term: the per-held-out-task deltas must have a positive one-sided 95% bootstrap
7
+ * lower bound, so a small-N mean gain can't ride on noise. The canary term is a
8
+ * SEPARATE deployment-safety signal (a distinct slice), not held-out dominance.
9
+ */
10
+ export declare const PROMOTION_RULE_VERSION = "accept/v1+sig";
11
+ export declare const PROOF_LABEL = "single-round proof-of-mechanism";
12
+ export declare const NOT_CLAIMS: readonly ["not flywheel proof", "not compounding learning", "not production learning"];
13
+ export interface HoldoutTask {
14
+ taskId: string;
15
+ baselineScore: number;
16
+ candidateScore: number;
17
+ }
18
+ export interface DecisionReceipt {
19
+ promotionRuleVersion: string;
20
+ verdictInputs: PromotionVerdict;
21
+ result: AcceptResult;
22
+ significant: boolean;
23
+ deltaCILow: number;
24
+ promoted: boolean;
25
+ reason: string;
26
+ }
27
+ export interface ShadowRegistration {
28
+ registrationId: string;
29
+ state: 'shadow';
30
+ served: false;
31
+ candidateManifestHash: string;
32
+ registeredAt: number;
33
+ }
34
+ export interface CostReceipt {
35
+ usd: number;
36
+ llmCalls: number;
37
+ tier: string;
38
+ notes: string;
39
+ }
40
+ /** Multi-dimensional deltas vs the parent — distinguishes *why* a change is (un)safe. */
41
+ export interface ChangeDeltas {
42
+ benchmark: number;
43
+ security: number;
44
+ cost: number;
45
+ }
46
+ /**
47
+ * Causal promotion record (not just provenance). Answers *why* a candidate won —
48
+ * and, aggregated across the lineage, *which mutation classes* reliably pay off.
49
+ * This is what turns the lineage from an audit trail into a knowledge base.
50
+ */
51
+ export interface PromotionRecord {
52
+ parentManifestHash: string | null;
53
+ candidateManifestHash: string;
54
+ mutationClass: string;
55
+ mutationSummary: string;
56
+ deltas: ChangeDeltas;
57
+ decisionReceipt: DecisionReceipt;
58
+ }
59
+ /** Regression ancestry — a rejected candidate records the gate that killed it + its ancestor. */
60
+ export interface RegressionRecord {
61
+ candidateManifestHash: string;
62
+ ancestor: string | null;
63
+ mutationClass: string;
64
+ failureCause: 'holdout' | 'security' | 'drift' | 'replay' | 'governance' | 'canary' | 'significance';
65
+ failedTerms: string[];
66
+ }
67
+ export interface EvolveReceiptBundle {
68
+ label: typeof PROOF_LABEL;
69
+ disclaimers: typeof NOT_CLAIMS;
70
+ generation: number;
71
+ parent: string | null;
72
+ branch: string;
73
+ kind: 'synthetic' | 'real';
74
+ createdAt: number;
75
+ inputHoldoutHash: string;
76
+ baselineManifestHash: string;
77
+ candidateManifestHash: string;
78
+ meetsPromotionRule: {
79
+ version: string;
80
+ result: boolean;
81
+ };
82
+ decisionReceipt: DecisionReceipt;
83
+ shadow: ShadowRegistration | null;
84
+ costReceipt: CostReceipt;
85
+ mutationClass: string;
86
+ mutationSummary: string;
87
+ deltas: ChangeDeltas;
88
+ promotion: PromotionRecord | null;
89
+ regression: RegressionRecord | null;
90
+ holdout: HoldoutTask[];
91
+ baselineManifest: ProvenConfigManifest;
92
+ candidateManifest: ProvenConfigManifest;
93
+ }
94
+ /** Classify a policy mutation by which fields changed → a repeatable mutation CLASS + a diff summary. */
95
+ export declare function classifyMutation(baseline: Record<string, number>, candidate: Record<string, number>): {
96
+ mutationClass: string;
97
+ mutationSummary: string;
98
+ };
99
+ export interface AssembleOpts {
100
+ generation: number;
101
+ parent: string | null;
102
+ branch: string;
103
+ now: number;
104
+ kind: 'synthetic' | 'real';
105
+ cost: {
106
+ tier: string;
107
+ notes: string;
108
+ };
109
+ redblue?: 'PASS' | 'FAIL' | 'SKIPPED';
110
+ drift?: number;
111
+ canaryRollbackRate?: number;
112
+ layer?: string;
113
+ corpus?: string;
114
+ }
115
+ /**
116
+ * Assemble a receipt bundle from a holdout + configs. This is the SHARED core:
117
+ * the synthetic proof and a REAL measured round produce byte-identical bundle
118
+ * structure and run the SAME versioned accept() — so `verifyReceiptBundle`
119
+ * replays a real bundle exactly as it replays the synthetic fixture.
120
+ */
121
+ export declare function assembleBundle(baseline: Record<string, number>, candidate: Record<string, number>, holdout: HoldoutTask[], o: AssembleOpts): EvolveReceiptBundle;
122
+ /**
123
+ * Build a REAL evolve-round receipt bundle from MEASURED holdout scores (live
124
+ * retrieval over a frozen anchor). Same gate, same replayability as the
125
+ * synthetic proof — but `kind: 'real'` and the scores come from actual runs.
126
+ * `redblue` should be 'FAIL' if the candidate regressed a frozen security/anchor
127
+ * slice; drift from real distribution shift. $0 (no LLM/network on this path).
128
+ */
129
+ export declare function runRealEvolveRound(opts: {
130
+ baseline: Record<string, number>;
131
+ candidate: Record<string, number>;
132
+ holdout: HoldoutTask[];
133
+ generation: number;
134
+ parent: string | null;
135
+ branch?: string;
136
+ now: number;
137
+ redblue?: 'PASS' | 'FAIL' | 'SKIPPED';
138
+ drift?: number;
139
+ canaryRollbackRate?: number;
140
+ corpus: string;
141
+ }): EvolveReceiptBundle;
142
+ /**
143
+ * Run ONE deterministic synthetic evolve round and produce the receipt bundle.
144
+ * `now` is injected (no Date in the pure path) for reproducible fixtures.
145
+ * The default scenario is a strict Pareto improvement (candidate ≥ baseline on
146
+ * every task, > on the mean) so the full PROMOTE→SHADOW path is exercised; pass
147
+ * `regress: true` to exercise the REJECT path instead.
148
+ */
149
+ export declare function runSyntheticProofRound(opts?: {
150
+ now: number;
151
+ generation?: number;
152
+ regress?: boolean;
153
+ parent?: string | null;
154
+ branch?: string;
155
+ baseline?: Record<string, number>;
156
+ candidate?: Record<string, number>;
157
+ }): EvolveReceiptBundle;
158
+ export interface VerifyReport {
159
+ valid: boolean;
160
+ hashChecks: {
161
+ inputHoldout: boolean;
162
+ baselineManifest: boolean;
163
+ candidateManifest: boolean;
164
+ };
165
+ recomputed: {
166
+ baselineHeldOut: number;
167
+ candidateHeldOut: number;
168
+ canaryRollbackRate: number;
169
+ decision: AcceptResult;
170
+ };
171
+ decisionMatches: boolean;
172
+ ruleVersionMatches: boolean;
173
+ noAutoServe: boolean;
174
+ causalConsistent: boolean;
175
+ explanation: string;
176
+ mismatches: string[];
177
+ }
178
+ /**
179
+ * Independently verify a receipt bundle WITHOUT trusting any service log: rehash
180
+ * the embedded holdout + manifests, recompute the held-out means + canary rate
181
+ * from the embedded per-task scores, RE-RUN the same versioned accept(), and
182
+ * confirm the recomputed decision equals the recorded one. Also confirms the
183
+ * SHADOW registration is not served (no auto-serve). Pure; never throws.
184
+ */
185
+ export declare function verifyReceiptBundle(bundle: EvolveReceiptBundle): VerifyReport;
186
+ export interface LineageTelemetry {
187
+ generations: number;
188
+ candidatesEvaluated: number;
189
+ promotions: number;
190
+ rejections: number;
191
+ cumulativeHeldOutImprovement: number;
192
+ rootHash: string | null;
193
+ branches: string[];
194
+ lineageIntact: boolean;
195
+ allReplayable: boolean;
196
+ nodes: Array<{
197
+ generation: number;
198
+ branch: string;
199
+ promoted: boolean;
200
+ parent: string | null;
201
+ candidateManifestHash: string;
202
+ mutationClass: string;
203
+ delta: number;
204
+ replayable: boolean;
205
+ }>;
206
+ problems: string[];
207
+ }
208
+ /**
209
+ * Reconstruct + audit a lineage DAG. Independently replays every bundle and
210
+ * checks the graph invariants back to the immutable root. Pure; never throws.
211
+ */
212
+ export declare function reconstructLineage(bundles: EvolveReceiptBundle[]): LineageTelemetry;
213
+ export interface MutationStat {
214
+ mutationClass: string;
215
+ attempts: number;
216
+ promotions: number;
217
+ meanDelta: number;
218
+ }
219
+ /**
220
+ * Aggregate per-mutation-class effectiveness across a lineage. After enough
221
+ * generations the optimizer can bias toward classes with higher historical
222
+ * payoff — meta-learning grounded in evidence, not intuition.
223
+ */
224
+ export declare function mutationEffectiveness(bundles: EvolveReceiptBundle[]): MutationStat[];
225
+ export type PlateauStatus = 'insufficient-data' | 'active' | 'local-optimum' | 'noisy-benchmark' | 'optimizer-failure';
226
+ export interface PlateauReport {
227
+ status: PlateauStatus;
228
+ window: number;
229
+ medianImprovement: number;
230
+ promotionRate: number;
231
+ varianceShrinking: boolean;
232
+ candidateVariance: number;
233
+ rationale: string;
234
+ }
235
+ /**
236
+ * Distinguish local optimum vs noisy benchmark vs optimizer failure — rigorously,
237
+ * not by intuition. Over a rolling window: near-zero median improvement AND low
238
+ * promotion rate is a plateau; shrinking candidate variance ⇒ converged
239
+ * (local optimum); high, non-shrinking variance ⇒ the benchmark is noisy; low
240
+ * variance with no promotions ⇒ the optimizer stopped exploring (failure).
241
+ */
242
+ export declare function detectPlateau(bundles: EvolveReceiptBundle[], opts?: {
243
+ window?: number;
244
+ epsilon?: number;
245
+ maxPromotionRate?: number;
246
+ }): PlateauReport;
247
+ //# sourceMappingURL=evolve-proof.d.ts.map
@@ -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