synergyspec-selfevolving 1.4.0 → 2.0.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.
- package/README.md +31 -18
- package/dist/commands/learn.d.ts +12 -1
- package/dist/commands/learn.js +151 -11
- package/dist/commands/self-evolution-episode.d.ts +177 -0
- package/dist/commands/self-evolution-episode.js +423 -0
- package/dist/commands/self-evolution.d.ts +12 -190
- package/dist/commands/self-evolution.js +114 -866
- package/dist/core/archive.d.ts +0 -1
- package/dist/core/archive.js +0 -58
- package/dist/core/artifact-graph/instruction-loader.d.ts +2 -4
- package/dist/core/artifact-graph/instruction-loader.js +3 -31
- package/dist/core/fitness/loss.d.ts +5 -5
- package/dist/core/fitness/loss.js +4 -4
- package/dist/core/project-config.d.ts +2 -0
- package/dist/core/project-config.js +28 -0
- package/dist/core/self-evolution/candidate-fitness.d.ts +23 -1
- package/dist/core/self-evolution/candidate-fitness.js +31 -5
- package/dist/core/self-evolution/candidates.d.ts +0 -9
- package/dist/core/self-evolution/critic-agent.d.ts +150 -0
- package/dist/core/self-evolution/critic-agent.js +487 -0
- package/dist/core/self-evolution/edits-contract.d.ts +53 -0
- package/dist/core/self-evolution/edits-contract.js +89 -0
- package/dist/core/self-evolution/episode-orchestrator.d.ts +197 -0
- package/dist/core/self-evolution/episode-orchestrator.js +534 -0
- package/dist/core/self-evolution/episode-store.d.ts +266 -0
- package/dist/core/self-evolution/episode-store.js +573 -0
- package/dist/core/self-evolution/evolution-switches.d.ts +1 -1
- package/dist/core/self-evolution/evolution-switches.js +5 -10
- package/dist/core/self-evolution/evolving-agent.d.ts +162 -0
- package/dist/core/self-evolution/evolving-agent.js +449 -0
- package/dist/core/self-evolution/host-harness.d.ts +1 -2
- package/dist/core/self-evolution/host-harness.js +1 -2
- package/dist/core/self-evolution/index.d.ts +9 -6
- package/dist/core/self-evolution/index.js +18 -6
- package/dist/core/self-evolution/line-diff.d.ts +60 -0
- package/dist/core/self-evolution/line-diff.js +130 -0
- package/dist/core/self-evolution/policy/fs-safe.d.ts +19 -0
- package/dist/core/self-evolution/policy/fs-safe.js +89 -0
- package/dist/core/self-evolution/policy/index.d.ts +13 -0
- package/dist/core/self-evolution/policy/index.js +13 -0
- package/dist/core/self-evolution/policy/policy-store.d.ts +217 -0
- package/dist/core/self-evolution/policy/policy-store.js +774 -0
- package/dist/core/self-evolution/policy/reject-buffer.d.ts +48 -0
- package/dist/core/self-evolution/policy/reject-buffer.js +168 -0
- package/dist/core/self-evolution/promote.d.ts +1 -1
- package/dist/core/self-evolution/promote.js +6 -33
- package/dist/core/self-evolution/promotion.js +1 -2
- package/dist/core/self-evolution/reward-agent.d.ts +234 -0
- package/dist/core/self-evolution/reward-agent.js +564 -0
- package/dist/core/self-evolution/scope-gate.d.ts +66 -0
- package/dist/core/self-evolution/scope-gate.js +107 -0
- package/dist/core/self-evolution/success-channel.js +2 -2
- package/dist/core/self-evolution/tool-evolution.js +2 -13
- package/dist/core/self-evolution/verdict.d.ts +8 -5
- package/dist/core/self-evolution/verdict.js +4 -7
- package/dist/core/templates/workflows/learn.d.ts +3 -2
- package/dist/core/templates/workflows/learn.js +18 -16
- package/dist/core/templates/workflows/self-evolving.d.ts +6 -4
- package/dist/core/templates/workflows/self-evolving.js +62 -172
- package/dist/dashboard/data.d.ts +25 -51
- package/dist/dashboard/data.js +68 -180
- package/dist/dashboard/react-client.js +458 -503
- package/dist/dashboard/react-styles.js +3 -3
- package/dist/dashboard/server.js +23 -17
- package/dist/ui/ascii-patterns.d.ts +7 -15
- package/dist/ui/ascii-patterns.js +123 -54
- package/dist/ui/welcome-screen.d.ts +0 -14
- package/dist/ui/welcome-screen.js +16 -35
- package/package.json +1 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import {
|
|
3
|
+
// Orchestrator (the three-agent episode runner).
|
|
4
|
+
captureMainArm as captureMainArmImpl, runEpisode as runEpisodeImpl, resumeEpisode as resumeEpisodeImpl,
|
|
5
|
+
// 版本账本 ledger + 否决缓冲 reject-buffer (read-only views + manual rollback).
|
|
6
|
+
readPolicyLedger, readRejectBuffer, currentPolicyVersion, rollbackPolicyVersion, appendRejectBufferEntry,
|
|
7
|
+
// Canonical target registry.
|
|
8
|
+
lookupCanonicalTarget, listCanonicalTargets, DESIGN_ARTIFACT_TARGET_ID, } from '../core/self-evolution/index.js';
|
|
9
|
+
import { generateLearnReport } from '../core/learn.js';
|
|
10
|
+
import { validateExplicitTrajectoryHandle } from '../core/learn/trajectory-discovery.js';
|
|
11
|
+
import { validateChangeExists } from './workflow/shared.js';
|
|
12
|
+
/**
|
|
13
|
+
* The 主智能体 MAIN AGENT arm is graded from a learn report exactly the way the
|
|
14
|
+
* `learn` command grades it (the orchestrator REUSES that grading; it never
|
|
15
|
+
* re-runs the tests). The default target is the design artifact-template — the
|
|
16
|
+
* opinionated design-only evolution default — but any registered canonical
|
|
17
|
+
* target id is accepted via `--target`.
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_EPISODE_TARGET = DESIGN_ARTIFACT_TARGET_ID;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// CLI registration
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Attach the loop-v2 `episode` + `policy` subcommands to the parent
|
|
25
|
+
* `self-evolution` command. Called once from {@link registerSelfEvolutionCommand}.
|
|
26
|
+
*/
|
|
27
|
+
export function attachSelfEvolutionEpisodeCommands(parent) {
|
|
28
|
+
const episode = parent
|
|
29
|
+
.command('episode [change]')
|
|
30
|
+
.description('Run ONE loop-v2 episode (self-evolution as in-context RL) for a change: build the 主智能体 MAIN AGENT arm from the change\'s learn report, rerun the CRITIC AGENT(基线智能体 baseline agent)arm, score advantage with the 奖励智能体 REWARD AGENT, roll back the 策略 POLICY on a bad advantage, then let the 演进智能体 EVOLVING AGENT take ONE bounded edit.')
|
|
31
|
+
.option('--change <name>', 'the completed change to run the episode for (or positional)')
|
|
32
|
+
.option('--target <id>', `canonical target id to evolve (default ${DEFAULT_EPISODE_TARGET})`)
|
|
33
|
+
.option('--no-baseline', 'skip the CRITIC AGENT(基线智能体 baseline agent)arm for this episode')
|
|
34
|
+
.option('--transcript <path>', 'Explicit transcript .jsonl to grade (bypasses change-window discovery; Claude transcript store only)')
|
|
35
|
+
.option('--session-id <id>', 'Explicit Claude session id to grade (bypasses change-window discovery; Claude transcript store only)')
|
|
36
|
+
.option('--json', 'output the full RunEpisodeResult JSON')
|
|
37
|
+
.action(async (changeArg, options) => {
|
|
38
|
+
const changeName = options.change ?? changeArg;
|
|
39
|
+
const result = await runEpisodeCommand({
|
|
40
|
+
changeName,
|
|
41
|
+
target: options.target,
|
|
42
|
+
// commander sets `baseline` to false when --no-baseline is passed.
|
|
43
|
+
noBaseline: options.baseline === false,
|
|
44
|
+
transcript: options.transcript,
|
|
45
|
+
sessionId: options.sessionId,
|
|
46
|
+
json: options.json,
|
|
47
|
+
}, { repoRoot: process.cwd() });
|
|
48
|
+
process.exitCode = result.exitCode;
|
|
49
|
+
});
|
|
50
|
+
episode
|
|
51
|
+
.command('resume <episodeId>')
|
|
52
|
+
.description('Re-enter a partially-run loop-v2 episode at its recorded stage and idempotently finish the remaining steps (decision → 演进智能体 EVOLVING AGENT → close).')
|
|
53
|
+
.option('--json', 'output the full ResumeEpisodeResult JSON')
|
|
54
|
+
.action(async (episodeId, options) => {
|
|
55
|
+
const result = await runResumeEpisodeCommand({ episodeId, json: options.json }, { repoRoot: process.cwd() });
|
|
56
|
+
process.exitCode = result.exitCode;
|
|
57
|
+
});
|
|
58
|
+
const policy = parent
|
|
59
|
+
.command('policy')
|
|
60
|
+
.description('Inspect or manually roll back the 策略 POLICY lineage (版本账本 ledger + 否决缓冲 reject-buffer) for loop-v2 targets');
|
|
61
|
+
policy
|
|
62
|
+
.command('show')
|
|
63
|
+
.description('READ-ONLY: print the 版本账本 ledger (versions, actions, Δ stats, predictions) and the 否决缓冲 reject-buffer for the target(s). Replaces the read-only role of the trajectory command.')
|
|
64
|
+
.option('--target <id>', 'restrict to a single canonical target id (default: all registered targets with a lineage)')
|
|
65
|
+
.option('--json', 'output { targets: [{ targetId, head, ledger, rejectBuffer }] } JSON')
|
|
66
|
+
.action(async (options) => {
|
|
67
|
+
const result = await runPolicyShowCommand({ target: options.target, json: options.json }, { repoRoot: process.cwd() });
|
|
68
|
+
process.exitCode = result.exitCode;
|
|
69
|
+
});
|
|
70
|
+
policy
|
|
71
|
+
.command('rollback')
|
|
72
|
+
.description('Manual 策略 POLICY rollback: restore the previous version (recorded as a new monotonic head, git-revert style) and append a `human-reject` 否决缓冲 reject-buffer entry. Requires --yes.')
|
|
73
|
+
.requiredOption('--target <id>', 'the canonical target id whose lineage to roll back')
|
|
74
|
+
.option('--reason <text>', 'why the version is being rejected (recorded on the 否决缓冲 entry)')
|
|
75
|
+
.option('--yes', 'required: confirm the manual rollback')
|
|
76
|
+
.option('--json', 'output JSON')
|
|
77
|
+
.action(async (options) => {
|
|
78
|
+
const result = await runPolicyRollbackCommand({ target: options.target, reason: options.reason, yes: options.yes, json: options.json }, { repoRoot: process.cwd() });
|
|
79
|
+
process.exitCode = result.exitCode;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Programmatic entrypoint for `self-evolution episode`. Exported so tests can
|
|
84
|
+
* drive the full episode flow with an injected orchestrator seam (no real agent
|
|
85
|
+
* spawn).
|
|
86
|
+
*/
|
|
87
|
+
export async function runEpisodeCommand(args, opts) {
|
|
88
|
+
const stdout = opts.stdout ?? ((l) => console.log(l));
|
|
89
|
+
const stderr = opts.stderr ?? ((l) => console.error(l));
|
|
90
|
+
const generateReport = opts.generateReport ??
|
|
91
|
+
((changeName) => generateLearnReport({ projectRoot: opts.repoRoot, changeName }));
|
|
92
|
+
const captureMainArm = opts.captureMainArm ?? captureMainArmImpl;
|
|
93
|
+
const runEpisode = opts.runEpisode ?? runEpisodeImpl;
|
|
94
|
+
const targetId = args.target ?? DEFAULT_EPISODE_TARGET;
|
|
95
|
+
// Validate the target up front so a typo fails loud (exit 1) rather than deep
|
|
96
|
+
// in the orchestrator with a bare lineage-init error.
|
|
97
|
+
if (!lookupCanonicalTarget(targetId)) {
|
|
98
|
+
return failEpisode(args.json, stdout, stderr, `Unknown canonical target: ${targetId}`);
|
|
99
|
+
}
|
|
100
|
+
// USER-TYPED handle flags fail LOUD on a miss (mirrors the learn /
|
|
101
|
+
// evolve-from-edits commands) BEFORE the env is mutated.
|
|
102
|
+
const handleError = await validateExplicitTrajectoryHandle({
|
|
103
|
+
projectRoot: opts.repoRoot,
|
|
104
|
+
transcriptPath: args.transcript,
|
|
105
|
+
sessionId: args.sessionId,
|
|
106
|
+
});
|
|
107
|
+
if (handleError) {
|
|
108
|
+
return failEpisode(args.json, stdout, stderr, handleError);
|
|
109
|
+
}
|
|
110
|
+
// The change must exist (active) so the orchestrator has a real changeDirPath.
|
|
111
|
+
let changeName;
|
|
112
|
+
try {
|
|
113
|
+
changeName = await validateChangeExists(args.changeName, opts.repoRoot);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
return failEpisode(args.json, stdout, stderr, err instanceof Error ? err.message : String(err));
|
|
117
|
+
}
|
|
118
|
+
const changeDirPath = path.join(opts.repoRoot, 'synergyspec-selfevolving', 'changes', changeName);
|
|
119
|
+
// Build the 主智能体 MAIN AGENT arm from the change's learn report (the SAME
|
|
120
|
+
// grading the `learn` command uses; the orchestrator never re-grades). The
|
|
121
|
+
// explicit trajectory handle reaches the discovery layer via env (kept in this
|
|
122
|
+
// action layer so the injected-generateReport test seam stays untouched).
|
|
123
|
+
const prevTranscriptEnv = process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT;
|
|
124
|
+
const prevSessionEnv = process.env.SYNERGYSPEC_SELFEVOLVING_SESSION_ID;
|
|
125
|
+
if (args.transcript)
|
|
126
|
+
process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT = args.transcript;
|
|
127
|
+
if (args.sessionId)
|
|
128
|
+
process.env.SYNERGYSPEC_SELFEVOLVING_SESSION_ID = args.sessionId;
|
|
129
|
+
let mainArm;
|
|
130
|
+
try {
|
|
131
|
+
const report = await generateReport(changeName);
|
|
132
|
+
mainArm = await captureMainArm({ repoRoot: opts.repoRoot, changeName, report });
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
return failEpisode(args.json, stdout, stderr, `failed to build main arm for "${changeName}": ${err instanceof Error ? err.message : String(err)}`);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
restoreEnv('SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT', args.transcript, prevTranscriptEnv);
|
|
139
|
+
restoreEnv('SYNERGYSPEC_SELFEVOLVING_SESSION_ID', args.sessionId, prevSessionEnv);
|
|
140
|
+
}
|
|
141
|
+
// Run the episode. The CRITIC AGENT(基线智能体 baseline agent)arm is gated by
|
|
142
|
+
// the orchestrator's own shouldRunCriticAgent ledger read (it skips when the
|
|
143
|
+
// lineage has < 2 versions or last action refused), so the orchestrator owns
|
|
144
|
+
// the skip decision. `--no-baseline` is surfaced as `skipBaseline` on the
|
|
145
|
+
// options the command forwards to the (injectable) runEpisode seam so a caller
|
|
146
|
+
// can request the skip explicitly; the field is additive (the base
|
|
147
|
+
// RunEpisodeOptions ignores unknown keys), so this never alters the real
|
|
148
|
+
// orchestrator's behavior when it is unaware of the flag.
|
|
149
|
+
let outcome;
|
|
150
|
+
try {
|
|
151
|
+
const episodeOptions = {
|
|
152
|
+
repoRoot: opts.repoRoot,
|
|
153
|
+
targetId,
|
|
154
|
+
changeName,
|
|
155
|
+
changeDirPath,
|
|
156
|
+
mainArm,
|
|
157
|
+
...(args.noBaseline ? { skipBaseline: true } : {}),
|
|
158
|
+
};
|
|
159
|
+
outcome = await runEpisode(episodeOptions);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
return failEpisode(args.json, stdout, stderr, err instanceof Error ? err.message : String(err));
|
|
163
|
+
}
|
|
164
|
+
if ('busy' in outcome && outcome.busy) {
|
|
165
|
+
if (args.json) {
|
|
166
|
+
stdout(JSON.stringify({ exitCode: 0, busy: outcome }, null, 2));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
stdout(`Episode not started for ${targetId}: ${outcome.reason}`);
|
|
170
|
+
}
|
|
171
|
+
return { exitCode: 0, busy: outcome };
|
|
172
|
+
}
|
|
173
|
+
const ran = outcome;
|
|
174
|
+
if (args.json) {
|
|
175
|
+
stdout(JSON.stringify({ exitCode: 0, result: ran }, null, 2));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
printEpisodeSummary(ran, targetId, changeName, stdout);
|
|
179
|
+
}
|
|
180
|
+
return { exitCode: 0, result: ran };
|
|
181
|
+
}
|
|
182
|
+
function printEpisodeSummary(result, targetId, changeName, stdout) {
|
|
183
|
+
stdout(`episode ${result.episodeId} (target ${targetId}, change ${changeName})`);
|
|
184
|
+
stdout(` advantage: ${result.advantage === null ? 'n/a' : result.advantage.toFixed(3)} ` +
|
|
185
|
+
`(baseline arm ${result.baselineSkipped ? 'skipped' : 'rerun'})`);
|
|
186
|
+
stdout(` decision: ${result.decision}`);
|
|
187
|
+
stdout(` evolution: ${describeEvolution(result.evolution)}`);
|
|
188
|
+
stdout(` new policy version: ${result.newPolicyVersion === null ? 'n/a' : `v${result.newPolicyVersion}`}`);
|
|
189
|
+
}
|
|
190
|
+
function describeEvolution(evolution) {
|
|
191
|
+
if (evolution === null)
|
|
192
|
+
return 'not spawned (no decision branch reached the 演进智能体 EVOLVING AGENT)';
|
|
193
|
+
switch (evolution.kind) {
|
|
194
|
+
case 'not-spawned':
|
|
195
|
+
return `not spawned (${evolution.reason})`;
|
|
196
|
+
case 'refused':
|
|
197
|
+
return `refused (${evolution.reason})`;
|
|
198
|
+
case 'evolved':
|
|
199
|
+
return `evolved → v${evolution.ledgerEntry.version}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function restoreEnv(name, applied, previous) {
|
|
203
|
+
if (!applied)
|
|
204
|
+
return;
|
|
205
|
+
if (previous === undefined)
|
|
206
|
+
delete process.env[name];
|
|
207
|
+
else
|
|
208
|
+
process.env[name] = previous;
|
|
209
|
+
}
|
|
210
|
+
function failEpisode(json, stdout, stderr, message) {
|
|
211
|
+
if (json) {
|
|
212
|
+
stdout(JSON.stringify({ exitCode: 1, error: message }, null, 2));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
stderr(`episode failed: ${message}`);
|
|
216
|
+
}
|
|
217
|
+
return { exitCode: 1, error: message };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Programmatic entrypoint for `self-evolution episode resume <id>`. Exported for
|
|
221
|
+
* tests; the orchestrator seam is injectable.
|
|
222
|
+
*/
|
|
223
|
+
export async function runResumeEpisodeCommand(args, opts) {
|
|
224
|
+
const stdout = opts.stdout ?? ((l) => console.log(l));
|
|
225
|
+
const stderr = opts.stderr ?? ((l) => console.error(l));
|
|
226
|
+
const resumeEpisode = opts.resumeEpisode ?? resumeEpisodeImpl;
|
|
227
|
+
let result;
|
|
228
|
+
try {
|
|
229
|
+
result = await resumeEpisode({ repoRoot: opts.repoRoot, episodeId: args.episodeId });
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
+
if (args.json) {
|
|
234
|
+
stdout(JSON.stringify({ exitCode: 1, error: message }, null, 2));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
stderr(`episode resume failed: ${message}`);
|
|
238
|
+
}
|
|
239
|
+
return { exitCode: 1, error: message };
|
|
240
|
+
}
|
|
241
|
+
if (args.json) {
|
|
242
|
+
stdout(JSON.stringify({ exitCode: 0, result }, null, 2));
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
stdout(`resumed episode ${result.episodeId}`);
|
|
246
|
+
stdout(` from stage: ${result.resumedFrom}`);
|
|
247
|
+
stdout(` now at stage: ${result.stage}`);
|
|
248
|
+
stdout(` evolution: ${describeEvolution(result.evolution)}`);
|
|
249
|
+
}
|
|
250
|
+
return { exitCode: 0, result };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Programmatic entrypoint for `self-evolution policy show`. READ-ONLY: reads the
|
|
254
|
+
* 版本账本 ledger + 否决缓冲 reject-buffer for the target(s) and renders them.
|
|
255
|
+
* Never mutates anything.
|
|
256
|
+
*/
|
|
257
|
+
export async function runPolicyShowCommand(args, opts) {
|
|
258
|
+
const stdout = opts.stdout ?? ((l) => console.log(l));
|
|
259
|
+
const stderr = opts.stderr ?? ((l) => console.error(l));
|
|
260
|
+
// Resolve the target list. A named --target must be registered; otherwise show
|
|
261
|
+
// every registered canonical target (those with no lineage render head=null).
|
|
262
|
+
let targetIds;
|
|
263
|
+
if (args.target !== undefined) {
|
|
264
|
+
if (!lookupCanonicalTarget(args.target)) {
|
|
265
|
+
const message = `Unknown canonical target: ${args.target}`;
|
|
266
|
+
if (args.json)
|
|
267
|
+
stdout(JSON.stringify({ error: message }, null, 2));
|
|
268
|
+
else
|
|
269
|
+
stderr(message);
|
|
270
|
+
return { exitCode: 1, targets: [], error: message };
|
|
271
|
+
}
|
|
272
|
+
targetIds = [args.target];
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
targetIds = listCanonicalTargets().map((t) => t.id);
|
|
276
|
+
}
|
|
277
|
+
const targets = [];
|
|
278
|
+
try {
|
|
279
|
+
for (const targetId of targetIds) {
|
|
280
|
+
const ledger = await readPolicyLedger(opts.repoRoot, targetId);
|
|
281
|
+
const rejectBuffer = await readRejectBuffer(opts.repoRoot, targetId);
|
|
282
|
+
const head = await currentPolicyVersion(opts.repoRoot, targetId);
|
|
283
|
+
// Skip targets with NEITHER a lineage NOR a reject-buffer when showing
|
|
284
|
+
// ALL targets (keep the default view focused on live lineages). A named
|
|
285
|
+
// --target is always shown, even when empty.
|
|
286
|
+
if (args.target === undefined && ledger.length === 0 && rejectBuffer.length === 0) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
targets.push({ targetId, head, ledger, rejectBuffer });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
294
|
+
if (args.json)
|
|
295
|
+
stdout(JSON.stringify({ error: message }, null, 2));
|
|
296
|
+
else
|
|
297
|
+
stderr(`policy show failed: ${message}`);
|
|
298
|
+
return { exitCode: 1, targets: [], error: message };
|
|
299
|
+
}
|
|
300
|
+
if (args.json) {
|
|
301
|
+
stdout(JSON.stringify({ targets }, null, 2));
|
|
302
|
+
return { exitCode: 0, targets };
|
|
303
|
+
}
|
|
304
|
+
if (targets.length === 0) {
|
|
305
|
+
stdout('No policy lineage recorded yet.');
|
|
306
|
+
return { exitCode: 0, targets };
|
|
307
|
+
}
|
|
308
|
+
for (const view of targets) {
|
|
309
|
+
stdout(renderPolicyTargetView(view));
|
|
310
|
+
}
|
|
311
|
+
return { exitCode: 0, targets };
|
|
312
|
+
}
|
|
313
|
+
function renderPolicyTargetView(view) {
|
|
314
|
+
const lines = [];
|
|
315
|
+
lines.push(`# ${view.targetId} (head ${view.head === null ? 'uninitialized' : `v${view.head}`})`);
|
|
316
|
+
lines.push('## 版本账本 ledger');
|
|
317
|
+
if (view.ledger.length === 0) {
|
|
318
|
+
lines.push('- (no versions)');
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
for (const entry of view.ledger) {
|
|
322
|
+
const delta = entry.deltaStats
|
|
323
|
+
? ` Δ ${entry.deltaStats.filesChanged}f +${entry.deltaStats.linesAdded}/-${entry.deltaStats.linesRemoved}`
|
|
324
|
+
: '';
|
|
325
|
+
const prediction = entry.prediction
|
|
326
|
+
? ` predicts ${entry.prediction.metric} ${entry.prediction.direction} by ${entry.prediction.checkBy}`
|
|
327
|
+
: '';
|
|
328
|
+
const advantage = entry.advantage !== undefined ? ` advantage=${entry.advantage.toFixed(3)}` : '';
|
|
329
|
+
const reason = entry.reason ? ` (${entry.reason})` : '';
|
|
330
|
+
lines.push(`- v${entry.version} ${entry.action} ${entry.at}${delta}${prediction}${advantage}${reason}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
lines.push('## 否决缓冲 reject-buffer');
|
|
334
|
+
if (view.rejectBuffer.length === 0) {
|
|
335
|
+
lines.push('- (no rejections)');
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
for (const entry of view.rejectBuffer) {
|
|
339
|
+
const advantage = entry.advantage === null ? 'n/a' : entry.advantage.toFixed(3);
|
|
340
|
+
const files = entry.editSummary.files.join(', ') || '(none)';
|
|
341
|
+
lines.push(`- ${entry.at} ${entry.reason} v${entry.fromVersion}→v${entry.toVersion} advantage=${advantage} files=[${files}]`);
|
|
342
|
+
if (entry.textualGradientTried) {
|
|
343
|
+
lines.push(` gradient: ${entry.textualGradientTried.slice(0, 200)}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return lines.join('\n');
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Programmatic entrypoint for `self-evolution policy rollback`. Manually rolls
|
|
351
|
+
* the 策略 POLICY lineage back to the previous version (recorded as a NEW
|
|
352
|
+
* monotonic head, git-revert style) and appends a `human-reject` 否决缓冲
|
|
353
|
+
* reject-buffer entry so the next 演进智能体 EVOLVING AGENT step sees the rejected
|
|
354
|
+
* direction. Requires --yes.
|
|
355
|
+
*/
|
|
356
|
+
export async function runPolicyRollbackCommand(args, opts) {
|
|
357
|
+
const stdout = opts.stdout ?? ((l) => console.log(l));
|
|
358
|
+
const stderr = opts.stderr ?? ((l) => console.error(l));
|
|
359
|
+
const now = opts.now ?? (() => new Date());
|
|
360
|
+
const fail = (code, message) => {
|
|
361
|
+
if (args.json)
|
|
362
|
+
stdout(JSON.stringify({ exitCode: code, error: message }, null, 2));
|
|
363
|
+
else
|
|
364
|
+
stderr(`policy rollback failed: ${message}`);
|
|
365
|
+
return { exitCode: code, error: message };
|
|
366
|
+
};
|
|
367
|
+
if (!args.yes) {
|
|
368
|
+
return fail(2, '--yes is required: policy rollback restores the previous version of your local policy file(s).');
|
|
369
|
+
}
|
|
370
|
+
if (!lookupCanonicalTarget(args.target)) {
|
|
371
|
+
return fail(1, `Unknown canonical target: ${args.target}`);
|
|
372
|
+
}
|
|
373
|
+
const head = await currentPolicyVersion(opts.repoRoot, args.target);
|
|
374
|
+
if (head === null) {
|
|
375
|
+
return fail(1, `No policy lineage for ${args.target}; nothing to roll back.`);
|
|
376
|
+
}
|
|
377
|
+
if (head < 1) {
|
|
378
|
+
return fail(1, `Policy lineage for ${args.target} is at v0; there is no earlier version to roll back to.`);
|
|
379
|
+
}
|
|
380
|
+
const toVersion = head - 1;
|
|
381
|
+
const reason = (args.reason ?? '').trim() || 'human: manual rollback to the previous policy version';
|
|
382
|
+
let entry;
|
|
383
|
+
try {
|
|
384
|
+
// Restore the previous version (recorded as a new monotonic head). The manual
|
|
385
|
+
// rollback path has no per-arm advantage, so none is recorded on the ledger.
|
|
386
|
+
entry = await rollbackPolicyVersion({
|
|
387
|
+
repoRoot: opts.repoRoot,
|
|
388
|
+
targetId: args.target,
|
|
389
|
+
episodeId: `manual-rollback-${now().toISOString().replace(/[^0-9]/g, '').slice(0, 14)}`,
|
|
390
|
+
toVersion,
|
|
391
|
+
});
|
|
392
|
+
// Append the 否决缓冲 reject-buffer entry so the rejected direction stays as
|
|
393
|
+
// evidence (the rolled-back files themselves are 产物即弃).
|
|
394
|
+
await appendRejectBufferEntry(opts.repoRoot, {
|
|
395
|
+
schemaVersion: 1,
|
|
396
|
+
at: now().toISOString(),
|
|
397
|
+
episodeId: entry.episodeId ?? 'manual-rollback',
|
|
398
|
+
targetId: args.target,
|
|
399
|
+
// fromVersion = the version restored TO; toVersion = the rejected head.
|
|
400
|
+
fromVersion: toVersion,
|
|
401
|
+
toVersion: head,
|
|
402
|
+
advantage: null,
|
|
403
|
+
rewardMain: null,
|
|
404
|
+
rewardBaseline: null,
|
|
405
|
+
textualGradientTried: '',
|
|
406
|
+
editSummary: { files: [], linesAdded: 0, linesRemoved: 0, rationaleExcerpt: reason },
|
|
407
|
+
reason: 'human-reject',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
return fail(1, err instanceof Error ? err.message : String(err));
|
|
412
|
+
}
|
|
413
|
+
if (args.json) {
|
|
414
|
+
stdout(JSON.stringify({ exitCode: 0, entry, toVersion }, null, 2));
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
stdout(`Rolled back ${args.target} to v${toVersion} (recorded as new head v${entry.version}).`);
|
|
418
|
+
stdout(`Recorded a human-reject 否决缓冲 reject-buffer entry: ${reason}`);
|
|
419
|
+
stdout('Restored your LOCAL policy file(s) — effective immediately, no rebuild/republish.');
|
|
420
|
+
}
|
|
421
|
+
return { exitCode: 0, entry, toVersion };
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=self-evolution-episode.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { type AggregationOptions, type CanonicalCandidate, type CanonicalCandidateSource, type CanonicalCandidateStatus, type PromotionReportArtifact, type StaticGateResult, type
|
|
2
|
+
import { type AggregationOptions, type CanonicalCandidate, type CanonicalCandidateSource, type CanonicalCandidateStatus, type PromotionReportArtifact, type StaticGateResult, type PromotionApplyResult, type RollbackResult } from '../core/self-evolution/index.js';
|
|
3
3
|
import { type LearnReport } from '../core/learn.js';
|
|
4
4
|
export declare function registerSelfEvolutionCommand(program: Command): void;
|
|
5
5
|
/**
|
|
@@ -41,34 +41,19 @@ export interface RunProposeCanonicalArgs {
|
|
|
41
41
|
/** If true, emit JSON output. */
|
|
42
42
|
json?: boolean;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* (proposal-only) — for cron/CI runs with no host agent. Ignored when
|
|
47
|
-
* `editsInput` is present. When both are false/omitted, the candidate carries
|
|
48
|
-
* the placeholder diff and a human supplies the change before the gate.
|
|
49
|
-
*/
|
|
50
|
-
agent?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Population-based generation: draft N competing variant candidates (clamped
|
|
53
|
-
* 1-5; default 1) for the single surviving group, each on a distinct
|
|
54
|
-
* improvement angle, so the GA outer loop (`evolve`) can select the best.
|
|
55
|
-
* Requires `agent` (divergence is prompt-side; the host `--from-edits` path
|
|
56
|
-
* carries one payload). Ignored/treated as 1 otherwise.
|
|
57
|
-
*/
|
|
58
|
-
variants?: number;
|
|
59
|
-
/**
|
|
60
|
-
* HOST-AGENT path (preferred). Candidate edits the host code agent already
|
|
61
|
-
* authored. When present, the CLI validates + packages these (frozen + target
|
|
62
|
-
* scope) instead of spawning the proposer agent, and `agent` is ignored.
|
|
44
|
+
* HOST-AGENT path. Candidate edits the host code agent already authored. When
|
|
45
|
+
* present, the CLI validates + packages these (frozen + target scope).
|
|
63
46
|
* Requires exactly one surviving hint group (narrow via --target /
|
|
64
|
-
* --threshold-key) so the single payload is not misattributed.
|
|
47
|
+
* --threshold-key) so the single payload is not misattributed. When absent the
|
|
48
|
+
* candidate carries the placeholder diff and a human supplies the change
|
|
49
|
+
* before the gate.
|
|
65
50
|
*/
|
|
66
51
|
editsInput?: HostEditsInput;
|
|
67
52
|
/**
|
|
68
53
|
* Aggregation thresholds. Self-evolution is gradient descent: ONE forward
|
|
69
54
|
* pass (a change's trajectory + pass-rate) is a complete loss signal, so
|
|
70
|
-
*
|
|
71
|
-
* diversity requirement) to act on one change. Omitted → conservative
|
|
55
|
+
* the host evolve-from-edits path passes single-change thresholds (all
|
|
56
|
+
* min-occurrences = 1, no diversity requirement) to act on one change. Omitted → conservative
|
|
72
57
|
* cross-change defaults (recurrence required).
|
|
73
58
|
*/
|
|
74
59
|
aggregationOptions?: AggregationOptions;
|
|
@@ -76,10 +61,6 @@ export interface RunProposeCanonicalArgs {
|
|
|
76
61
|
export interface RunProposeCanonicalOptions {
|
|
77
62
|
/** Project root used to resolve the candidate base dir. */
|
|
78
63
|
repoRoot: string;
|
|
79
|
-
/** Test seam: spawn used by the proposer agent (defaults to node's spawn). */
|
|
80
|
-
proposerSpawn?: typeof import('node:child_process').spawn;
|
|
81
|
-
/** Test seam: proposer agent binary override. */
|
|
82
|
-
proposerBinary?: string;
|
|
83
64
|
/** Override for stdout (defaults to console.log). */
|
|
84
65
|
stdout?: (line: string) => void;
|
|
85
66
|
/** Override for stderr (defaults to console.error). */
|
|
@@ -124,9 +105,8 @@ export declare function parseHostEditsInput(raw: string): HostEditsInput;
|
|
|
124
105
|
*
|
|
125
106
|
* SAFETY:
|
|
126
107
|
* - Never writes outside `<repoRoot>/.synergyspec-selfevolving/self-evolution/candidates/`.
|
|
127
|
-
* - Generation is
|
|
128
|
-
*
|
|
129
|
-
* the placeholder for a human to complete.
|
|
108
|
+
* - Generation is the host-agent `--from-edits` path (validate + package); when
|
|
109
|
+
* absent, diff.patch is the placeholder for a human to complete.
|
|
130
110
|
*/
|
|
131
111
|
export declare function runProposeCanonical(args: RunProposeCanonicalArgs, opts: RunProposeCanonicalOptions): Promise<RunProposeCanonicalResult>;
|
|
132
112
|
export interface RunPromoteArgs {
|
|
@@ -146,7 +126,7 @@ export interface RunPromoteResult {
|
|
|
146
126
|
}
|
|
147
127
|
/**
|
|
148
128
|
* Programmatic entrypoint for `self-evolution promote <id>` — the close-the-loop
|
|
149
|
-
* apply/rollback. Exported so tests +
|
|
129
|
+
* apply/rollback. Exported so tests + the host evolve-from-edits path can drive it directly.
|
|
150
130
|
*/
|
|
151
131
|
export declare function runPromoteCommand(args: RunPromoteArgs, opts: {
|
|
152
132
|
repoRoot: string;
|
|
@@ -175,95 +155,6 @@ export declare function runRejectCommand(args: RunRejectArgs, opts: {
|
|
|
175
155
|
stderr?: (l: string) => void;
|
|
176
156
|
now?: () => Date;
|
|
177
157
|
}): Promise<RunRejectResult>;
|
|
178
|
-
export interface RunTrajectoryArgs {
|
|
179
|
-
targetId: string;
|
|
180
|
-
/** Cap the number of prior candidates shown (default in buildOptimizationTrajectory: 6). */
|
|
181
|
-
maxEntries?: number;
|
|
182
|
-
json?: boolean;
|
|
183
|
-
}
|
|
184
|
-
export interface RunTrajectoryResult {
|
|
185
|
-
exitCode: number;
|
|
186
|
-
error?: string;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Programmatic entrypoint for `self-evolution trajectory <targetId>` — a
|
|
190
|
-
* READ-ONLY view of the scored optimization-trajectory block the headless
|
|
191
|
-
* proposer receives, so a HOST code agent (which authors edits via
|
|
192
|
-
* `--from-edits` and never sees that prompt) can read the same prior-candidate
|
|
193
|
-
* loss/verdict history before authoring. Reuses the exact builder/renderer the
|
|
194
|
-
* proposer uses. Never mutates anything.
|
|
195
|
-
*/
|
|
196
|
-
export declare function runTrajectoryCommand(args: RunTrajectoryArgs, opts: {
|
|
197
|
-
repoRoot: string;
|
|
198
|
-
stdout?: (l: string) => void;
|
|
199
|
-
stderr?: (l: string) => void;
|
|
200
|
-
}): Promise<RunTrajectoryResult>;
|
|
201
|
-
export interface RunAutoEvolveArgs {
|
|
202
|
-
/**
|
|
203
|
-
* One or more completed changes to learn from and evolve. A SINGLE change is
|
|
204
|
-
* enough; pass several to let a signal recurring across them be the trigger.
|
|
205
|
-
*/
|
|
206
|
-
changeNames: string[];
|
|
207
|
-
/** Default true; false (via --no-auto) runs the pipeline but stops before applying. */
|
|
208
|
-
auto?: boolean;
|
|
209
|
-
requireProven?: boolean;
|
|
210
|
-
/**
|
|
211
|
-
* Minimum occurrences a signal must reach to be proposed. Default 1 — so a
|
|
212
|
-
* single change CAN evolve. Raise it (and pass several `changeNames`) to
|
|
213
|
-
* require a signal to RECUR across changes before it evolves. Neither
|
|
214
|
-
* single-change nor multi-change is mandatory.
|
|
215
|
-
*/
|
|
216
|
-
minOccurrences?: number;
|
|
217
|
-
/** Force-propose only the aggregated signal with this exact thresholdKey. */
|
|
218
|
-
thresholdKey?: string;
|
|
219
|
-
evolveTarget?: string;
|
|
220
|
-
freezeTarget?: string;
|
|
221
|
-
json?: boolean;
|
|
222
|
-
}
|
|
223
|
-
export interface RunAutoEvolveOptions {
|
|
224
|
-
repoRoot: string;
|
|
225
|
-
stdout?: (l: string) => void;
|
|
226
|
-
stderr?: (l: string) => void;
|
|
227
|
-
now?: () => Date;
|
|
228
|
-
/** Injected for tests; forwarded to the proposer agent. */
|
|
229
|
-
proposerSpawn?: typeof import('node:child_process').spawn;
|
|
230
|
-
proposerBinary?: string;
|
|
231
|
-
}
|
|
232
|
-
export interface AutoEvolveReport {
|
|
233
|
-
exitCode: number;
|
|
234
|
-
changeNames: string[];
|
|
235
|
-
/** Mean per-change loss (functional ⊕ health) from learn; null when unmeasurable. */
|
|
236
|
-
loss: number | null;
|
|
237
|
-
/** Mean RAW code-health penalty across the change(s); null when no health signal. */
|
|
238
|
-
healthPenalty?: number | null;
|
|
239
|
-
hintCount: number;
|
|
240
|
-
hintsPaths: string[];
|
|
241
|
-
proposed: string[];
|
|
242
|
-
gated: {
|
|
243
|
-
candidateId: string;
|
|
244
|
-
passed: boolean;
|
|
245
|
-
}[];
|
|
246
|
-
promoted: {
|
|
247
|
-
candidateId: string;
|
|
248
|
-
targetIds: string[];
|
|
249
|
-
files: string[];
|
|
250
|
-
}[];
|
|
251
|
-
skipped: {
|
|
252
|
-
candidateId: string;
|
|
253
|
-
reason: string;
|
|
254
|
-
}[];
|
|
255
|
-
error?: string;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* ONE-BUTTON auto-evolve: learn → hints → propose(--agent) → gate → promote, in
|
|
259
|
-
* one motion. Auto-applies the gate-passing winner per target onto the canonical
|
|
260
|
-
* template (no per-change human approval), honoring the per-target switch + the
|
|
261
|
-
* oracle/gate freeze, and snapshotting every write for rollback.
|
|
262
|
-
*
|
|
263
|
-
* Exported + fully injectable (proposer spawn, clock, io) so it is unit-testable
|
|
264
|
-
* without a real `claude` binary.
|
|
265
|
-
*/
|
|
266
|
-
export declare function runAutoEvolve(args: RunAutoEvolveArgs, opts: RunAutoEvolveOptions): Promise<AutoEvolveReport>;
|
|
267
158
|
export interface RunEvolveFromEditsArgs {
|
|
268
159
|
/** The change's learn hints.json to aggregate into a single signal/target. */
|
|
269
160
|
fromLearn: string;
|
|
@@ -285,7 +176,7 @@ export interface RunEvolveFromEditsArgs {
|
|
|
285
176
|
requireProven?: boolean;
|
|
286
177
|
/** Mutually exclusive with this host path; presence is a hard error. */
|
|
287
178
|
agent?: boolean;
|
|
288
|
-
/** Non-interactive confirmation; required to run (
|
|
179
|
+
/** Non-interactive confirmation; required to run (one-button host-authored apply). */
|
|
289
180
|
yes?: boolean;
|
|
290
181
|
json?: boolean;
|
|
291
182
|
}
|
|
@@ -402,73 +293,4 @@ export interface RunPromotionReportResult {
|
|
|
402
293
|
* - Exit code 0 on success, non-zero when the candidate cannot be loaded.
|
|
403
294
|
*/
|
|
404
295
|
export declare function runPromotionReportCommand(args: RunPromotionReportArgs, opts: RunPromotionReportOptions): Promise<RunPromotionReportResult>;
|
|
405
|
-
export interface RunEvolveOuterLoopArgs {
|
|
406
|
-
/** Restrict the loop to a single canonical target id. */
|
|
407
|
-
target?: string;
|
|
408
|
-
/** Replay a corpus to score candidates (requires `changeIds`). */
|
|
409
|
-
replay?: boolean;
|
|
410
|
-
/** The replay corpus (change ids). Only used when `replay` is true. */
|
|
411
|
-
changeIds?: string[];
|
|
412
|
-
/** Write a human-gated promotion-report.md for each selected best. */
|
|
413
|
-
write?: boolean;
|
|
414
|
-
/**
|
|
415
|
-
* Mark proven sibling variants that lost the ranking to `best` (same
|
|
416
|
-
* variantGroup, higher meanLoss) with verdict `outcompeted`, so the
|
|
417
|
-
* optimization-trajectory block shows them as negative examples. Never
|
|
418
|
-
* transitions status (rejected is human-owned); advisory metadata only.
|
|
419
|
-
*/
|
|
420
|
-
markOutcompeted?: boolean;
|
|
421
|
-
/** Per-target evolution policy overrides (supports all/none). */
|
|
422
|
-
evolveTarget?: string;
|
|
423
|
-
freezeTarget?: string;
|
|
424
|
-
json?: boolean;
|
|
425
|
-
/**
|
|
426
|
-
* ISO-8601 timestamp stamped onto appended fitness records. Defaults to the
|
|
427
|
-
* current time; supplied explicitly by tests for determinism.
|
|
428
|
-
*/
|
|
429
|
-
at?: string;
|
|
430
|
-
/**
|
|
431
|
-
* Injected replay re-runner (see {@link makeReplayRunChange}). Defaults to a
|
|
432
|
-
* live agent shell-out; tests pass a fake to drive the loop without an agent.
|
|
433
|
-
*/
|
|
434
|
-
runChange?: RunChangeFn;
|
|
435
|
-
}
|
|
436
|
-
export interface RunEvolveOuterLoopOptions {
|
|
437
|
-
repoRoot: string;
|
|
438
|
-
stdout?: (line: string) => void;
|
|
439
|
-
stderr?: (line: string) => void;
|
|
440
|
-
}
|
|
441
|
-
export interface TargetEvolveSummary {
|
|
442
|
-
targetId: string;
|
|
443
|
-
candidateIds: string[];
|
|
444
|
-
/** Skipped because the per-target policy froze this target. */
|
|
445
|
-
frozen: boolean;
|
|
446
|
-
/** Replay scoring outcomes (only present in --replay mode). */
|
|
447
|
-
scored?: CandidateReplayScore[];
|
|
448
|
-
/** Candidates ranked best-first by accumulated fitness. */
|
|
449
|
-
ranked: RankedCandidate[];
|
|
450
|
-
/** The selected best candidate id, or null when there are no candidates. */
|
|
451
|
-
best: string | null;
|
|
452
|
-
/** Path to the promotion report written for `best` (only with --write). */
|
|
453
|
-
promotionReportPath?: string;
|
|
454
|
-
/** Sibling variants marked `outcompeted` this run (only with --mark-outcompeted). */
|
|
455
|
-
outcompeted?: string[];
|
|
456
|
-
}
|
|
457
|
-
export interface RunEvolveOuterLoopResult {
|
|
458
|
-
exitCode: number;
|
|
459
|
-
targets: TargetEvolveSummary[];
|
|
460
|
-
error?: string;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Programmatic entrypoint for `self-evolution evolve` — the GA outer loop.
|
|
464
|
-
*
|
|
465
|
-
* Chains the previously-inert pieces into one live pass:
|
|
466
|
-
* groupCandidatesByTarget → (optional replay scoring that APPENDS fitness)
|
|
467
|
-
* → rankCandidatesForTarget → select best → human-gated promotion report.
|
|
468
|
-
*
|
|
469
|
-
* Invariants: frozen targets (per the resolved policy) are skipped; promotion
|
|
470
|
-
* is NEVER applied here (the report keeps its human-review gate); the oracle is
|
|
471
|
-
* never touched (replay only runs tests).
|
|
472
|
-
*/
|
|
473
|
-
export declare function runEvolveOuterLoopCommand(args: RunEvolveOuterLoopArgs, opts: RunEvolveOuterLoopOptions): Promise<RunEvolveOuterLoopResult>;
|
|
474
296
|
//# sourceMappingURL=self-evolution.d.ts.map
|