@evo-hq/pi-evo 0.4.5 → 0.5.0-alpha.10
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/package.json +1 -1
- package/skills/discover/SKILL.md +145 -9
- package/skills/discover/references/inline_instrumentation.js +45 -0
- package/skills/discover/references/inline_instrumentation.py +50 -0
- package/skills/discover/references/sdk_node.js +19 -0
- package/skills/discover/references/sdk_python.py +25 -0
- package/skills/{optimize → discover}/references/sizing-the-round.md +8 -0
- package/skills/infra-setup/SKILL.md +1 -2
- package/skills/optimize/SKILL.md +167 -8
- package/skills/optimize/workflows/evo-optimize.js +672 -0
- package/skills/report/SKILL.md +1 -1
- package/skills/subagent/SKILL.md +89 -7
package/skills/optimize/SKILL.md
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: optimize
|
|
3
|
-
description:
|
|
3
|
+
description: Drive structured autoresearch iteration after evo:discover and the baseline commit -- scan-subagent cross-cutting analysis between rounds, frontier-based parent selection, ideator dispatch on stall, verifier pre/post hooks, annotation discipline. Width is set via subagents=N (1 for serial workloads, larger for parallel); the loop's structural value applies at any width.
|
|
4
4
|
argument-hint: "[subagents=N] [budget=N] [stall=N]"
|
|
5
|
-
evo_version: 0.
|
|
5
|
+
evo_version: 0.5.0-alpha.10
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Run the `evo` optimization loop. Each round, the orchestrator writes structured briefs and spawns
|
|
8
|
+
Run the `evo` optimization loop. Each round, the orchestrator writes structured briefs and spawns subagents that execute within them. Each subagent is semi-autonomous: it reads the pointer traces, forms the concrete edit, runs experiments, and can iterate within its branch. Runs until interrupted or the stall limit is reached.
|
|
9
|
+
|
|
10
|
+
**This skill is the canonical loop for ALL post-discover work — including serial workloads.** If the workspace's resource profile forces width 1 (single GPU, single-process benchmark, etc.), you still invoke `/evo:optimize` -- just pass `subagents=1`. The loop's value is the STRUCTURE around each experiment (scan-subagent cross-cutting analysis between rounds, verifier pre/post hooks via the subagent skill, ideator spawning on stall, frontier reconciliation, stop-hook discipline), NOT just parallelism. Bypassing optimize because "I'm running serial work anyway" loses every piece of that structure -- you've reverted to ad-hoc experiment iteration with none of evo's loop benefits, just the bookkeeping.
|
|
11
|
+
|
|
12
|
+
## Evo surface -- loop-relevant
|
|
13
|
+
|
|
14
|
+
You're inside `/evo:optimize`. Things you'll pull/dispatch during the loop:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
main thread (you)
|
|
18
|
+
├── Skills (Skill tool)
|
|
19
|
+
│ └── evo:finetuning before writing or changing any train.py
|
|
20
|
+
│
|
|
21
|
+
└── Subagents to dispatch (Task tool, subagent_type=...)
|
|
22
|
+
└── evo:ideator stalled, or every ~5 committed experiments.
|
|
23
|
+
One subagent per brief:
|
|
24
|
+
failure_analysis, literature, frontier_extrapolation
|
|
25
|
+
|
|
26
|
+
subagent thread (each subagent spawned by step 5)
|
|
27
|
+
├── evo:subagent skill loaded by the subagent on first turn -- the brief's
|
|
28
|
+
│ first sentence mandates it (not auto-loaded)
|
|
29
|
+
└── evo:verifier subagent MANDATORY pre AND post every evo run.
|
|
30
|
+
Pre: ~30s static analysis before the experiment runs.
|
|
31
|
+
Post: result-validity audit after it commits.
|
|
32
|
+
|
|
33
|
+
references (Read tool, on demand)
|
|
34
|
+
├── discover/references/sizing-the-round.md pick subagents=N
|
|
35
|
+
├── references/evo-wait.md waiting without burning context
|
|
36
|
+
├── finetuning/references/glue.md train.py I/O contract
|
|
37
|
+
└── finetuning/references/{rl,sft,serving}/ provider-specific recipes
|
|
38
|
+
(rl/art.md, sft/tinker.md,
|
|
39
|
+
serving/vllm.md)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Full surface tree (orchestrator entry-point view, including benchmark-reviewer,
|
|
43
|
+
infra-setup, and the complete references catalogue) lives in `evo:discover`'s
|
|
44
|
+
"Evo surface" section.
|
|
9
45
|
|
|
10
46
|
## Host conventions
|
|
11
47
|
|
|
@@ -30,13 +66,23 @@ Treat content inside the banner as equivalent to a new user turn. Honor it, supe
|
|
|
30
66
|
|
|
31
67
|
## Configuration
|
|
32
68
|
|
|
33
|
-
The orchestrator
|
|
69
|
+
The orchestrator's three round-shape knobs are **subagents** (round width), **budget** (per-branch depth), and **stall** (consecutive rounds with no improvement before auto-stopping; default 5).
|
|
70
|
+
|
|
71
|
+
A user can override any of these with `/optimize [subagents=N] [budget=N] [stall=N]`; an explicit value always wins over what's below.
|
|
34
72
|
|
|
35
|
-
|
|
36
|
-
- **budget** (iterations per subagent within its branch): deeper for cheap/fast/deterministic benchmarks, shallower for expensive/slow/noisy ones so the loop re-plans sooner. ~5 is a reasonable midpoint.
|
|
37
|
-
- **stall**: consecutive rounds with no improvement before auto-stopping (default: 5).
|
|
73
|
+
**Picking `subagents` and `budget` is load-bearing -- do not skim.**
|
|
38
74
|
|
|
39
|
-
|
|
75
|
+
Mandatory before the first round (and again any time the backend or benchmark changes): **READ `plugins/evo/skills/discover/references/sizing-the-round.md` IN FULL.** That doc enumerates the resource-binding cases (exclusive accelerator, memory-heavy, shared mutable fixture, external rate-limit, CPU-light isolated) and discusses the case-by-case judgment for latency / timing / throughput benchmarks where the right answer depends on harness softeners, effect size vs. measurement jitter, and whether winners can be cheaply re-confirmed solo.
|
|
76
|
+
|
|
77
|
+
Under-subscribing wastes wall-clock. Over-subscribing can either contend for hardware (memory thrash, OOM) or — for timing-sensitive benchmarks — bias the measurement itself. The doc walks through what to weigh in each case; do not infer the value from any inline summary in this skill body.
|
|
78
|
+
|
|
79
|
+
Common ways agents get this wrong by skimming:
|
|
80
|
+
- "8-core machine, CPU-light → width 5" sounds right but skips the question of whether the metric is corruptible by sibling-process pressure. The doc has the judgment framing.
|
|
81
|
+
- "Worktree backend has no slot cap so I can go higher" — worktree just shifts the cap from infrastructure to the binding resource. Same hardware, no safety net.
|
|
82
|
+
|
|
83
|
+
If `.evo/project.md` records a resource profile (it should, after `/evo:discover`), START from that. The reference doc is what you use to APPLY it. If the profile is missing or thin, that's a discover-step bug — fix it (write a resource profile that names the binding resource explicitly) before continuing.
|
|
84
|
+
|
|
85
|
+
In your opening message, state the width/budget you chose AND a one-line reason that references the binding-resource framing FROM THE DOC (e.g. "width 1 — exclusive GPU; budget 8 — runs deterministic"; or "width 3 — CPU-light isolated, but harness reports stable jitter at this concurrency so promoting solo-confirm gate; budget 5"). If your reason doesn't connect to the doc's framing, go back and read it.
|
|
40
86
|
|
|
41
87
|
- **autonomous**: the keep-going loop. **Default: on** — evo is autoresearch; it runs unattended. Turn off for a run with `evo autonomous off`.
|
|
42
88
|
- **subagents-only**: gate orchestrator edits, pushing all edits through subagents. **Default: on**. Turn off for a run with `evo subagents-only off`.
|
|
@@ -56,6 +102,18 @@ evo defaults get subagents-only --json
|
|
|
56
102
|
|
|
57
103
|
As your **very first actions, before the loop**, resolve each and arm it: run `evo autonomous on` / `evo subagents-only on` when it resolves on, or `evo autonomous off` / `evo subagents-only off` when an explicit instruction or stored default turned it off. If a behavior resolves off — whether from the user's instruction this run or a stored default — say so in your opening message (e.g. "autonomous off — running one round at a time, as you asked") so it's never invisible.
|
|
58
104
|
|
|
105
|
+
**Orchestrator driver (Claude Code only).** evo can drive the loop two ways: the prose loop below (default, every host), or a deterministic **dynamic workflow** (Claude Code only, opt-in). Resolve which as part of your very first actions:
|
|
106
|
+
|
|
107
|
+
1. `evo host show` — must be `claude-code` for the workflow driver. If it prints `<not set>` (a pre-host workspace), determine your actual runtime from your own context (system prompt, env such as `CLAUDECODE=1`, self-identity): **only if you are genuinely Claude Code**, do the one-time host migration now (`evo host set claude-code`) and continue; if you are any other runtime, do NOT stamp the host here — leave it for Step 0.1 to record and use the prose loop. Any non-`claude-code` host uses the prose loop.
|
|
108
|
+
2. `evo config get default-orchestrator` — `workflow` selects the workflow driver; anything else (including unset) resolves to `prose`. An explicit user instruction this run still wins.
|
|
109
|
+
|
|
110
|
+
If host is `claude-code` **and** the value is `workflow` **and** the Workflow tool is available in this session, do NOT drive the loop turn-by-turn. Launch the bundled workflow once instead:
|
|
111
|
+
|
|
112
|
+
- Call the **Workflow** tool with `scriptPath: ${CLAUDE_PLUGIN_ROOT}/skills/optimize/workflows/evo-optimize.js` and `args: {pluginRoot: "${CLAUDE_PLUGIN_ROOT}", subagents: <N>, budget: <N>, stall: <N>}`, using the round sizing you resolved above. **Pass all four keys explicitly — never omit one.** For `stall`, use the user's `/optimize stall=N` override if given, else the default 5. (The workflow's stop condition is the stall limit, so a dropped `stall` silently reverts it to 5.)
|
|
113
|
+
- Report the returned `runId` and tell the user to watch progress with `/workflows`. The workflow runs the round loop itself (orient → mandatory scan + cross-history axis check → ideators on stall/periodic → briefs → fan-out + verify → collect → frontier-select → stall); you do **not** execute "The Loop" section below.
|
|
114
|
+
|
|
115
|
+
Otherwise — any non-`claude-code` host, `default-orchestrator` unset/`prose`, or the Workflow tool unavailable — ignore this and follow **The Loop** below as the canonical driver. The workflow is only an execution strategy over the same `evo` CLI; gates, frontier, dashboard, and recovery are identical either way.
|
|
116
|
+
|
|
59
117
|
**Autonomous mode.** Off lets you stop naturally at a turn boundary — finish a round, report, and stop. On arms the stop-nudge: at every turn boundary you are re-prompted to keep driving the loop until the **stall** limit is hit or the user interrupts. Without it, the loop does NOT force-continue across turn boundaries. To stop an autonomous run, the user runs `evo autonomous off` or `evo exit-optimize-mode`.
|
|
60
118
|
|
|
61
119
|
**Subagents-only mode.** Off, the orchestrator may edit files directly — the optimization protocol still pushes edits through subagents (you write briefs; they edit in their worktrees), but a one-off orchestrator edit is not blocked. On arms the deny-gate: orchestrator file-mutation tools (Edit/Write, mutating Bash) are denied on an alternating cadence — 1st violation blocked, 2nd allowed, 3rd blocked, and so on — each block nudging you to delegate the edit to a subagent. It is a nudge, not a hard block: an edit can still land on an even-numbered attempt. Subagent edits (sessions with an `exp_id`) are never gated. To lift it, the user runs `evo subagents-only off` or `evo exit-optimize-mode`.
|
|
@@ -297,6 +355,86 @@ Update notes with cross-cutting learnings:
|
|
|
297
355
|
evo set <exp_id> --note "key insight from round N"
|
|
298
356
|
```
|
|
299
357
|
|
|
358
|
+
### 6a. Pattern recognition across history (objective, not narrative)
|
|
359
|
+
|
|
360
|
+
Step 6 cross-cuts a single round. This step looks across ALL committed experiments in the run, not just this round's. The orchestrator's failure mode is tunnel vision -- iterating on the visible axis (whatever knob the recent rounds touched) while missing the orthogonal axis (the harness itself, the score definition, the environment, the input data, plumbing). The check is cheap, runs between rounds, and is the most reliable signal that you're on the wrong axis.
|
|
361
|
+
|
|
362
|
+
Four checks via `evo show` + `evo tree`:
|
|
363
|
+
|
|
364
|
+
1. **Score plateaus across structurally distinct hypotheses.** Read the `hypothesis` strings of committed experiments. If 3+ experiments with materially different hypotheses (not minor parameter sweeps of the same idea) all commit at the same score, the bottleneck is not where the hypotheses were aiming. The next move belongs on an axis none of those hypotheses touched.
|
|
365
|
+
|
|
366
|
+
2. **Repeated failure class.** Tally failure indicators across discarded + failed nodes in the run so far: `gate_failures` names, non-zero exit codes, shared error-message fragments in `benchmark_err.log`. If 2+ failures share a class, that class is structural -- fix the cause, rather than queuing more experiments that will hit the same wall.
|
|
367
|
+
|
|
368
|
+
3. **Internal-vs-benchmark delta.** Compare each evaluated node's *internal* indicators (progress signals the experiment's own process produces during a run -- intermediate test pass-rates, training loss, build success, agent self-report metrics, whatever the trace stream carries) to its *committed* benchmark score. Healthy internal signal + flat benchmark score = the experiment is optimizing something the benchmark does not reward. The fix is usually in the harness, output format, or score definition -- not in another hypothesis on the same axis.
|
|
369
|
+
|
|
370
|
+
4. **Annotate facts, not narratives.** Annotations via `evo annotate <exp_id>` and `evo set <exp_id> --note` should record what HAPPENED -- scores, exact error messages, surprising observations, sources used. Not what you hoped would happen, what you plan to try next, or how you feel about the result. Annotations get loaded into future decision context and into ideator briefs; narrative noise contaminates them. State facts; leave plans to TodoWrite or `evo set --note` on the round itself.
|
|
371
|
+
|
|
372
|
+
If any check surfaces a structural issue, the next round's subagent briefs should target the orthogonal axis the pattern identifies. Another iteration on a plateaued or systematically-failing axis produces another data point with the same conclusion.
|
|
373
|
+
|
|
374
|
+
### 6b. Periodically spawn ideators (in parallel)
|
|
375
|
+
|
|
376
|
+
The optimize loop's scan sub-agents (step 3) read the CURRENT round's evaluated experiments for failure patterns. They don't do deep cross-graph analysis or external literature scans -- that work belongs to the ideator skill (`evo:ideator`).
|
|
377
|
+
|
|
378
|
+
Spawn ideators in parallel when ANY of these triggers fire:
|
|
379
|
+
|
|
380
|
+
- **Periodic**: every N=5 committed experiments since the last ideator round
|
|
381
|
+
- **Stall**: best score unchanged for M=3 consecutive committed experiments (the stall counter from step 6)
|
|
382
|
+
- **Failure cluster**: M=3 consecutive discards with related root causes (use the `evo discards` output)
|
|
383
|
+
- **User-triggered**: a directive (`evo direct`) asks for fresh ideas
|
|
384
|
+
|
|
385
|
+
When a trigger fires, spawn three parallel **evo ideator subagents** via your host's Task tool -- one per brief:
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
Task(subagent_type="evo:ideator", prompt="workspace=<path>\nbrief=failure_analysis")
|
|
389
|
+
Task(subagent_type="evo:ideator", prompt="workspace=<path>\nbrief=literature")
|
|
390
|
+
Task(subagent_type="evo:ideator", prompt="workspace=<path>\nbrief=frontier_extrapolation")
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
| Brief | What it does |
|
|
394
|
+
|---|---|
|
|
395
|
+
| `failure_analysis` | Cross-graph clustering of discards/failures |
|
|
396
|
+
| `literature` | Web/arXiv scan for untried techniques in the workspace domain |
|
|
397
|
+
| `frontier_extrapolation` | Deeper variants of the steepest score gradient on the best path |
|
|
398
|
+
|
|
399
|
+
Each subagent runs the brief in its own context, appends proposals as JSONL lines to `.evo/run_<run_id>/ideator/proposals.jsonl` (single final write), and returns a JSON summary. See `plugins/evo/agents/ideator.md` for the full procedure each ideator follows.
|
|
400
|
+
|
|
401
|
+
Ideators take 5-10 min while the optimize loop's next round is typically 1-2 min away. If you fire and continue, proposals miss the next round's brief-writing every time. Two patterns work:
|
|
402
|
+
|
|
403
|
+
- **Block here briefly.** If the trigger was a STALL or FAILURE CLUSTER, the next round's quality depends on fresh ideas -- block until enough proposals land:
|
|
404
|
+
```bash
|
|
405
|
+
evo wait --for ideators --count 3 --timeout 900 # 15 min cap, fail-open
|
|
406
|
+
```
|
|
407
|
+
Exit 0 means the proposals are ready; exit 124 (timeout) means proceed with whatever's available -- proposals.jsonl may have partial results.
|
|
408
|
+
|
|
409
|
+
- **Fire and continue for periodic spawns** (every-N-commits trigger). The next round can run without proposals; the round after that will read them once they land. Use this when there's plenty of in-graph signal still to extract.
|
|
410
|
+
|
|
411
|
+
### 6c. Reconcile ideator proposals at brief-writing time
|
|
412
|
+
|
|
413
|
+
Before writing the next round's briefs (step 4 of the next iteration), check for new ideator proposals:
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
# Read proposals newer than the last round
|
|
417
|
+
test -f .evo/run_*/ideator/proposals.jsonl && \
|
|
418
|
+
tail -n +1 .evo/run_*/ideator/proposals.jsonl | \
|
|
419
|
+
jq -s --argjson cutoff "$LAST_ROUND_END_TS" \
|
|
420
|
+
'map(select(.generated_at > $cutoff))'
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
If you fired ideators in 6b WITHOUT blocking (periodic trigger) and they haven't landed yet, you can also wait here -- but only a short timeout, since brief-writing should not stall indefinitely:
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
evo wait --for ideators --count 1 --timeout 120 # 2 min cap, fail-open
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
For each new proposal:
|
|
430
|
+
|
|
431
|
+
1. Check the workspace graph -- has the proposed config already been tried? Use `evo discards --like "<keyword>"` to scan. If yes, skip.
|
|
432
|
+
2. Score each remaining proposal by `expected_score_uplift × confidence`. Confidence ranking: `frontier_extrapolation > failure_analysis > literature`, all else equal.
|
|
433
|
+
3. The top 1-2 proposals become objectives in the next round's briefs (step 4). Cite the proposal's `hypothesis` and `mechanism` in the brief's *Objective* field.
|
|
434
|
+
4. Leave the rest in the queue -- they may surface as winners after a few more rounds when the frontier shifts.
|
|
435
|
+
|
|
436
|
+
Proposals are advisory, not mandatory. If none look better than what step 3's scan sub-agents surfaced from in-graph signal, ignore them and proceed with the in-graph briefs. Ideator output complements, doesn't replace.
|
|
437
|
+
|
|
300
438
|
### 7. Continue or stop
|
|
301
439
|
|
|
302
440
|
**Continue** if:
|
|
@@ -317,6 +455,27 @@ On stop, print a final summary:
|
|
|
317
455
|
|
|
318
456
|
Go back to step 1.
|
|
319
457
|
|
|
458
|
+
## Polling discipline
|
|
459
|
+
|
|
460
|
+
When waiting on a long-running background process (a subagent's training subprocess, a long evaluation, a batch job), do NOT use `while true; do sleep N; tail file; done`. That loop never exits when the underlying process crashes -- the tail keeps reading the same dead file, the agent interprets "no growth" as "still working," and the agent blocks indefinitely.
|
|
461
|
+
|
|
462
|
+
Use `evo wait`. The CLI is the bounded, structured replacement:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
# wait until the training subprocess exits, OR its log stalls, OR the GPU goes idle,
|
|
466
|
+
# whichever first; 60-minute ceiling; structured JSON on stdout
|
|
467
|
+
evo wait --for process=$TRAIN_PID \
|
|
468
|
+
--for log-growth=$TRAIN_LOG \
|
|
469
|
+
--for gpu-idle \
|
|
470
|
+
--timeout 60m --stall-threshold 5m --json
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Multiple `--for` flags combine; the wait returns on the first matching condition. The JSON output's `exit_reason` and `triggered_by` identify which condition fired. Process / log-growth / gpu-* watches do not require an evo workspace context; the workspace-anchored watches (`--for experiments`, `--for ideators`) cover the ideator + commit waits described elsewhere in this skill.
|
|
474
|
+
|
|
475
|
+
Full surface, exit codes, JSON shape, examples: `references/evo-wait.md` (under `plugins/evo/skills/references/`).
|
|
476
|
+
|
|
477
|
+
If `evo wait` is not available for some reason (older CLI on PATH, sandbox constraint), fall back to a bounded poll loop that checks all three signals -- process liveness via `kill -0 $PID`, log growth via `wc -c` delta, GPU via `nvidia-smi --query-gpu=utilization.gpu` -- and exits on any one going negative. NEVER unbounded `while true`.
|
|
478
|
+
|
|
320
479
|
## Resetting the eval epoch
|
|
321
480
|
|
|
322
481
|
`evo infra event -m "<reason>" --breaking` bumps `current_eval_epoch` and blocks
|