claude-flow 3.23.0 → 3.25.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 (53) hide show
  1. package/.claude/.proven-config-version +1 -0
  2. package/.claude/helpers/.helpers-version +1 -1
  3. package/.claude/helpers/helpers.manifest.json +2 -2
  4. package/.claude/proven-config.json +42 -0
  5. package/package.json +1 -1
  6. package/v3/@claude-flow/cli/dist/src/config/harness-feedback-applier.d.ts +50 -0
  7. package/v3/@claude-flow/cli/dist/src/config/harness-feedback-applier.js +122 -0
  8. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.d.ts +39 -0
  9. package/v3/@claude-flow/cli/dist/src/config/proven-config-refresh.js +154 -0
  10. package/v3/@claude-flow/cli/dist/src/config/proven-config-rvfa.d.ts +23 -0
  11. package/v3/@claude-flow/cli/dist/src/config/proven-config-rvfa.js +73 -0
  12. package/v3/@claude-flow/cli/dist/src/config/proven-config.d.ts +86 -0
  13. package/v3/@claude-flow/cli/dist/src/config/proven-config.js +176 -0
  14. package/v3/@claude-flow/cli/dist/src/index.js +35 -0
  15. package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.d.ts +10 -0
  16. package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +129 -20
  17. package/v3/@claude-flow/cli/dist/src/ruvector/lattice-wasm.d.ts +14 -0
  18. package/v3/@claude-flow/cli/dist/src/ruvector/lattice-wasm.js +144 -0
  19. package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.d.ts +17 -0
  20. package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.js +79 -0
  21. package/v3/@claude-flow/cli/dist/src/services/evolve-proof.d.ts +253 -0
  22. package/v3/@claude-flow/cli/dist/src/services/evolve-proof.js +343 -0
  23. package/v3/@claude-flow/cli/dist/src/services/harness-benchmark.d.ts +68 -0
  24. package/v3/@claude-flow/cli/dist/src/services/harness-benchmark.js +94 -0
  25. package/v3/@claude-flow/cli/dist/src/services/harness-canary.d.ts +60 -0
  26. package/v3/@claude-flow/cli/dist/src/services/harness-canary.js +69 -0
  27. package/v3/@claude-flow/cli/dist/src/services/harness-corpus-harvester.d.ts +51 -0
  28. package/v3/@claude-flow/cli/dist/src/services/harness-corpus-harvester.js +74 -0
  29. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-generations.d.ts +115 -0
  30. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-generations.js +360 -0
  31. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.d.ts +25 -0
  32. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.js +92 -0
  33. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.d.ts +48 -0
  34. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.js +207 -0
  35. package/v3/@claude-flow/cli/dist/src/services/harness-frozen-eval.d.ts +22 -0
  36. package/v3/@claude-flow/cli/dist/src/services/harness-frozen-eval.js +66 -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/worker-daemon.d.ts +8 -1
  52. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +42 -0
  53. package/v3/@claude-flow/cli/package.json +1 -1
@@ -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
@@ -0,0 +1,51 @@
1
+ export interface HarvestPattern {
2
+ id: string;
3
+ name?: string;
4
+ content?: string;
5
+ }
6
+ export interface HarvestedTask {
7
+ id: string;
8
+ input: {
9
+ id: string;
10
+ q: string;
11
+ targetId: string;
12
+ };
13
+ expected: string;
14
+ provenanceTier: 'oracle:self-identity';
15
+ }
16
+ export interface BlendedCorpus {
17
+ version: string;
18
+ corpusHash: string;
19
+ anchorIds: string[];
20
+ tasks: Array<{
21
+ id: string;
22
+ input: unknown;
23
+ expected: unknown;
24
+ }>;
25
+ }
26
+ /**
27
+ * Harvest up to `sample` self-retrieval tasks from real patterns. Deterministic
28
+ * stride sampling (stable across runs); skips docs whose body is too thin to
29
+ * form a discriminative query.
30
+ */
31
+ export declare function harvestSelfSupervisedTasks(patterns: HarvestPattern[], opts?: {
32
+ sample?: number;
33
+ minQueryTerms?: number;
34
+ }): HarvestedTask[];
35
+ /** Content hash over the task ids+queries — changes iff the corpus changes. */
36
+ export declare function hashBlend(tasks: Array<{
37
+ id: string;
38
+ input: unknown;
39
+ expected: unknown;
40
+ }>): string;
41
+ /**
42
+ * Blend a human-labeled anchor set with harvested tasks into one versioned,
43
+ * hashed corpus. The version encodes the sizes + hash so it visibly grows as the
44
+ * store does; `anchorIds` marks the never-regress subset.
45
+ */
46
+ export declare function blendCorpus(anchor: Array<{
47
+ id: string;
48
+ input: unknown;
49
+ expected: unknown;
50
+ }>, harvested: HarvestedTask[]): BlendedCorpus;
51
+ //# sourceMappingURL=harness-corpus-harvester.d.ts.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Corpus harvester — grows the benchmark yardstick from REAL store data so the
3
+ * flywheel gets a bigger, fresher test set as ruflo is used (ADR-176).
4
+ *
5
+ * Self-supervised self-retrieval: a stored doc is unambiguous ground truth for a
6
+ * query derived from its OWN body. To make it discriminative (so different
7
+ * retrieval configs actually score differently — a usable gradient) the query is
8
+ * built from body keywords with the doc's SUBJECT tokens removed: "can retrieval
9
+ * find this doc from its content when the obvious title words are withheld?"
10
+ * The label is the doc's identity — oracle-grade, not a proxy guess.
11
+ *
12
+ * This grows the auto signal; callers keep the human-labeled seed as a separate
13
+ * NON-REGRESSION anchor (Goodhart guard) so optimizing self-retrieval can never
14
+ * silently drift away from human relevance. Pure + deterministic (no RNG): the
15
+ * sample is a fixed stride, so the same store yields the same corpus.
16
+ */
17
+ import { createHash } from 'node:crypto';
18
+ const STOP = new Set(['the', 'a', 'an', 'of', 'to', 'in', 'on', 'for', 'and', 'or', 'is', 'was', 'with', 'by', 'at', 'as', 'it', 'this', 'that', 'from', 'be', 'are', 'so', 'if', 'we', 'i', 'you', 'not', 'no', 'do', 'via', 'per']);
19
+ function tokenize(s) {
20
+ return (s.toLowerCase().match(/[a-z0-9][a-z0-9_-]{1,}/g) ?? []).filter((t) => !STOP.has(t));
21
+ }
22
+ /** Top-K body keywords with the subject tokens removed, most-frequent first. */
23
+ function deriveQuery(name, content, k = 6) {
24
+ const nameToks = new Set(tokenize(name));
25
+ const freq = new Map();
26
+ for (const t of tokenize(content)) {
27
+ if (nameToks.has(t))
28
+ continue; // withhold the obvious title words
29
+ freq.set(t, (freq.get(t) ?? 0) + 1);
30
+ }
31
+ return [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, k).map(([t]) => t).join(' ');
32
+ }
33
+ /**
34
+ * Harvest up to `sample` self-retrieval tasks from real patterns. Deterministic
35
+ * stride sampling (stable across runs); skips docs whose body is too thin to
36
+ * form a discriminative query.
37
+ */
38
+ export function harvestSelfSupervisedTasks(patterns, opts = {}) {
39
+ const sample = Math.max(1, opts.sample ?? 40);
40
+ const minTerms = opts.minQueryTerms ?? 3;
41
+ // Deterministic order: sort by id, then take an even stride to cover the store.
42
+ const ordered = [...patterns].sort((a, b) => a.id.localeCompare(b.id));
43
+ const stride = Math.max(1, Math.floor(ordered.length / sample));
44
+ const out = [];
45
+ for (let i = 0; i < ordered.length && out.length < sample; i += stride) {
46
+ const p = ordered[i];
47
+ const q = deriveQuery(p.name ?? '', p.content ?? '', 6);
48
+ if (q.split(' ').filter(Boolean).length < minTerms)
49
+ continue; // too thin → skip
50
+ out.push({ id: `hv-${p.id}`, input: { id: `hv-${p.id}`, q, targetId: p.id }, expected: p.id, provenanceTier: 'oracle:self-identity' });
51
+ }
52
+ return out;
53
+ }
54
+ /** Content hash over the task ids+queries — changes iff the corpus changes. */
55
+ export function hashBlend(tasks) {
56
+ const canon = tasks.map((t) => `${t.id}|${JSON.stringify(t.input)}|${JSON.stringify(t.expected)}`).sort().join('\n');
57
+ return 'sha256:' + createHash('sha256').update(canon).digest('hex');
58
+ }
59
+ /**
60
+ * Blend a human-labeled anchor set with harvested tasks into one versioned,
61
+ * hashed corpus. The version encodes the sizes + hash so it visibly grows as the
62
+ * store does; `anchorIds` marks the never-regress subset.
63
+ */
64
+ export function blendCorpus(anchor, harvested) {
65
+ const tasks = [...anchor, ...harvested.map((h) => ({ id: h.id, input: h.input, expected: h.expected }))];
66
+ const corpusHash = hashBlend(tasks);
67
+ return {
68
+ version: `flywheel-a${anchor.length}-h${harvested.length}-${corpusHash.slice(7, 19)}`,
69
+ corpusHash,
70
+ anchorIds: anchor.map((a) => a.id),
71
+ tasks,
72
+ };
73
+ }
74
+ //# sourceMappingURL=harness-corpus-harvester.js.map
@@ -0,0 +1,115 @@
1
+ import { type EvolveReceiptBundle, type LineageTelemetry, type PlateauReport, type MutationStat } from './evolve-proof.js';
2
+ import { type HarvestPattern } from './harness-corpus-harvester.js';
3
+ import { type RetrievalConfig, type RankedItem } from './harness-flywheel.js';
4
+ export declare const FLYWHEEL_DIR: string[];
5
+ export declare const FROZEN_CORPUS = "harvested-selfsup-frozen-v1";
6
+ export interface AnchorTask {
7
+ id: string;
8
+ q: string;
9
+ labels: string[];
10
+ }
11
+ export interface GenerationDeps {
12
+ getPatterns: () => HarvestPattern[] | Promise<HarvestPattern[]>;
13
+ search: (q: string, cfg: RetrievalConfig) => Promise<RankedItem[]> | RankedItem[];
14
+ anchorTasks: AnchorTask[];
15
+ humanEvalHash?: string;
16
+ sample?: number;
17
+ now: number;
18
+ applyFn?: (cfg: Record<string, number>, hash: string, generation: number) => void;
19
+ }
20
+ export interface GenerationResult {
21
+ ran: boolean;
22
+ reason: string;
23
+ generation: number;
24
+ promoted?: boolean;
25
+ delta?: number;
26
+ significant?: boolean;
27
+ championConfig?: Record<string, number>;
28
+ servedChampion?: string | null;
29
+ anchorRegressed?: boolean;
30
+ }
31
+ /** Promotions only — the champion chain (generation-N.json), sorted by generation. */
32
+ export declare function loadPromotions(root: string): EvolveReceiptBundle[];
33
+ /** Every attempt (promoted + rejected) — for telemetry + mutation-effectiveness. */
34
+ export declare function loadAttempts(root: string): EvolveReceiptBundle[];
35
+ /** The current operating champion (last promotion's config), or defaults. */
36
+ export declare function currentChampion(root: string): {
37
+ config: Record<string, number>;
38
+ hash: string | null;
39
+ generation: number;
40
+ };
41
+ export interface ServedState {
42
+ championHash: string | null;
43
+ config: Record<string, number> | null;
44
+ servedAt: number | null;
45
+ fromGeneration: number | null;
46
+ }
47
+ export declare function servedChampion(root: string): ServedState;
48
+ /**
49
+ * Shadow→serve gate: apply the latest promoted champion to the ACTIVE policy iff
50
+ * it is newer than what is currently served. Called at tick START, so a champion
51
+ * promoted last tick is served this tick (a 1-generation shadow delay) — never
52
+ * auto-served the instant it is promoted.
53
+ */
54
+ export declare function serveCurrentChampionIfPending(root: string, now: number, applyFn?: GenerationDeps['applyFn']): string | null;
55
+ export interface AxisStat {
56
+ axis: string;
57
+ promotions: number;
58
+ meanDelta: number;
59
+ }
60
+ /**
61
+ * Which policy AXES have historically paid off — attribute each promotion's
62
+ * held-out Δ to the axes that changed in it. Turns the lineage into a
63
+ * knowledge base the optimizer can act on (not just audit).
64
+ */
65
+ export declare function axisEffectiveness(promotions: EvolveReceiptBundle[]): AxisStat[];
66
+ /**
67
+ * Candidate set biased by measured axis payoff (meta-learning). Every axis keeps
68
+ * a ±1 exploration floor (never abandon a dimension), but axes with a positive
69
+ * historical Δ get EXPANDED range (±2, ±3) and PAIRWISE joint moves with other
70
+ * productive axes — so compute concentrates on the dimensions that have actually
71
+ * produced gains, instead of a uniform grid. Deterministic; bounded.
72
+ */
73
+ export declare function biasedGrid(c: RetrievalConfig, ranking: AxisStat[]): RetrievalConfig[];
74
+ /**
75
+ * Run ONE generation against `root`, compounding on the persisted champion.
76
+ * Serves the prior champion first (shadow delay), then evaluates a new candidate.
77
+ */
78
+ export declare function runFlywheelGeneration(root: string, deps: GenerationDeps): Promise<GenerationResult>;
79
+ export interface DriftCheck {
80
+ checked: boolean;
81
+ rolledBack: boolean;
82
+ reason: string;
83
+ servedScore?: number;
84
+ predecessorScore?: number;
85
+ }
86
+ /**
87
+ * A real deployment-safety check on real (not fabricated) data: the store keeps
88
+ * changing as ruflo is used, so a champion benchmarked at promotion time can
89
+ * DRIFT. Each tick, re-score the currently-SERVED champion against its
90
+ * predecessor on a FRESH harvest of the current store; if it now regresses
91
+ * (self-retrieval OR the human anchor), automatically ROLL BACK the active policy
92
+ * to the predecessor. This is the honest analogue of a live-traffic canary —
93
+ * genuine ongoing measurement + genuine rollback — without fabricating traffic.
94
+ * $0; never throws.
95
+ */
96
+ export declare function checkServedChampionDrift(root: string, deps: GenerationDeps): Promise<DriftCheck>;
97
+ export interface FlywheelStatus {
98
+ generations: number;
99
+ attempts: number;
100
+ lineage: LineageTelemetry;
101
+ plateau: PlateauReport;
102
+ mutation: MutationStat[];
103
+ axisEffectiveness: AxisStat[];
104
+ cumulativeBenchmarkDelta: number;
105
+ cumulativeHumanRelevanceDelta: number;
106
+ humanEvalHash: string | null;
107
+ served: ServedState;
108
+ champion: {
109
+ config: Record<string, number>;
110
+ hash: string | null;
111
+ };
112
+ }
113
+ /** Reconstruct the persisted lineage + telemetry for a status endpoint / CLI. */
114
+ export declare function flywheelStatus(root: string): FlywheelStatus;
115
+ //# sourceMappingURL=harness-flywheel-generations.d.ts.map