| name | ml-metaoptimization |
|---|---|
| description | Use when running a continuous ML training improvement campaign using WandB Sweeps on SkyPilot/Vast.ai. Keywords: metaoptimization, hyperparameter sweep, architecture search, WandB, SkyPilot, Vast.ai, campaign, continuous improvement. |
Run a continuous ML metaoptimization campaign as a deterministic state machine across reinvocations. Each iteration: agents propose a WandB sweep search space, the sweep runs on Vast.ai via SkyPilot with parallel agents, results are analyzed against a direction-aware baseline, and learnings carry forward to the next iteration. The campaign is fully file-driven via ml_metaopt_campaign.yaml.
This skill does NOT produce code patches, architecture changes, or algorithm optimizations. Those belong to the code-optimization skill (built on repo-audit-refactor-optimize). This skill exclusively sweeps hyperparameters, architectures, and ML configuration against an existing codebase. Agents design search spaces; WandB searches them; SkyPilot provisions compute.
Project contract: the target project must have a training entrypoint that reads hyperparameters from wandb.config and logs metrics via wandb.log(...). No other coupling. The skill is framework-agnostic at the interface level, though PyTorch Lightning projects satisfy this contract naturally.
The state machine is persistent across reinvocations via .ml-metaopt/state.json. This skill is not a self-scheduling daemon. It persists state, exits, and resumes when a host runtime or user invocation re-enters it.
Target runtime: Codex-ready goal-style orchestration with a host orchestrator, control-agent subagents, and CLI worker subagents.
Named models below refer to host-exposed model classes. Model selection is deterministic, not discretionary:
strong_reasoner: resolution orderclaude-opus-4.6, thengpt-5.4general_worker: resolution orderclaude-sonnet-4, thengpt-5.4
Within each family, prefer the highest available version: any opus >= 4.6 is preferred over claude-opus-4.6, and any gpt >= 5.4 is preferred over gpt-5.4, if exposed by the runtime.
When fallback is needed, record the requested model and resolved model in orchestration metadata (for example, the handoff/launch metadata visible to the host), not as persistent state fields.
No strong_coder class — v4 does not dispatch code-writing workers.
{project_root}/
ml_metaopt_campaign.yaml
AGENTS.md
.ml-metaopt/
preflight-readiness.json
state.json
handoffs/
worker-results/
executor-events/
tasks/
final_report.md (written on COMPLETE)
.ml-metaopt/preflight-readiness.json is the readiness artifact produced by metaopt-preflight. LOAD_CAMPAIGN reads this artifact after campaign validation passes; if it is missing, stale (hash mismatch), or failed, metaopt-load-campaign recommends BLOCKED_CONFIG with next_action set to run or re-run metaopt-preflight.
metaopt-hydrate-state manages the AGENTS.md resume hook. On initialization it creates AGENTS.md if absent and appends the marked block. Hydrated terminal paths emit remove_agents_hook; pre-hydration BLOCKED_CONFIG from load validation or preflight does not have a hook to remove.
- Never ask the user for campaign-defining inputs. Read
ml_metaopt_campaign.yaml; if invalid, transition toBLOCKED_CONFIG. - Orchestrator never calls WandB API or SkyPilot CLI directly. All remote execution goes through
skypilot-wandb-workervia directives only. LOCAL_SANITYenforces a 60-second hard timeout — not configurable. The smoke-test directive is blocking: execute it, read.ml-metaopt/executor-events/smoke-test-iter-<N>.json, then re-entergate_local_sanitybefore applying the transition. The smoke test must exit0before the timeout; timeout or non-zero exit meansFAILED, no remediation loop.- max_budget_usd is always enforced. The budget limit (max 100 USD) must be configured in
ml_metaopt_campaign.yaml. Budget is checked on everypoll_sweepcall. If exceeded, terminate all jobs and transition toBLOCKED_CONFIG. - On crash recovery, reconnect to existing sweep. Never launch a new sweep if
current_sweep.sweep_idexists in state.HYDRATE_STATEdetects this and reconnects.
Each machine state is governed by exactly one control agent. The orchestrator invokes the governing control agent as a subagent, reads the handoff it writes to .ml-metaopt/handoffs/, and applies it. See references/control-protocol.md for the full protocol.
| Machine State(s) | Governing Control Agent | Phase(s) |
|---|---|---|
LOAD_CAMPAIGN |
metaopt-load-campaign |
validate |
HYDRATE_STATE |
metaopt-hydrate-state |
hydrate |
IDEATE, WAIT_FOR_PROPOSALS |
metaopt-background-control |
plan_background_work, gate_background_work |
SELECT_AND_DESIGN_SWEEP |
metaopt-select-design |
plan_select_design, finalize_select_design |
LOCAL_SANITY |
metaopt-remote-execution-control |
gate_local_sanity |
LAUNCH_SWEEP |
metaopt-remote-execution-control |
plan_launch |
WAIT_FOR_SWEEP |
metaopt-remote-execution-control |
poll_sweep |
ANALYZE |
metaopt-remote-execution-control |
analyze |
ROLL_ITERATION |
metaopt-iteration-close-control |
plan_roll_iteration, gate_roll_iteration |
digraph machine {
"LOAD_CAMPAIGN" [shape=box];
"HYDRATE_STATE" [shape=box];
"IDEATE" [shape=box];
"WAIT_FOR_PROPOSALS" [shape=diamond];
"SELECT_AND_DESIGN_SWEEP" [shape=box];
"LOCAL_SANITY" [shape=diamond];
"LAUNCH_SWEEP" [shape=box];
"WAIT_FOR_SWEEP" [shape=diamond];
"ANALYZE" [shape=box];
"ROLL_ITERATION" [shape=box];
"COMPLETE" [shape=doublecircle];
"BLOCKED_CONFIG" [shape=doublecircle];
"BLOCKED_PROTOCOL" [shape=doublecircle];
"FAILED" [shape=doublecircle];
"LOAD_CAMPAIGN" -> "BLOCKED_CONFIG" [label="invalid config"];
"LOAD_CAMPAIGN" -> "HYDRATE_STATE" [label="valid"];
"HYDRATE_STATE" -> "IDEATE";
"HYDRATE_STATE" -> "BLOCKED_CONFIG" [label="missing worker"];
"IDEATE" -> "WAIT_FOR_PROPOSALS";
"WAIT_FOR_PROPOSALS" -> "IDEATE" [label="need more proposals"];
"WAIT_FOR_PROPOSALS" -> "SELECT_AND_DESIGN_SWEEP" [label="threshold met"];
"SELECT_AND_DESIGN_SWEEP" -> "LOCAL_SANITY";
"LOCAL_SANITY" -> "LAUNCH_SWEEP" [label="pass"];
"LOCAL_SANITY" -> "FAILED" [label="crash or timeout"];
"LAUNCH_SWEEP" -> "WAIT_FOR_SWEEP";
"WAIT_FOR_SWEEP" -> "WAIT_FOR_SWEEP" [label="running"];
"WAIT_FOR_SWEEP" -> "ANALYZE" [label="completed"];
"WAIT_FOR_SWEEP" -> "FAILED" [label="all crashed"];
"WAIT_FOR_SWEEP" -> "BLOCKED_CONFIG" [label="budget exceeded"];
"ANALYZE" -> "ROLL_ITERATION";
"ROLL_ITERATION" -> "COMPLETE" [label="stop condition"];
"ROLL_ITERATION" -> "BLOCKED_CONFIG" [label="budget exhausted"];
"ROLL_ITERATION" -> "IDEATE" [label="next iteration"];
}Four worker types:
Background ideation workers (metaopt-ideation-worker): Propose WandB sweep search spaces. Each proposal includes a sweep config with parameter distributions and search method. Dispatched during IDEATE by metaopt-background-control.
Selection workers (metaopt-selection-worker): Pick the best proposal from the frozen pool, refine it, and design the final WandB sweep configuration. Invoked during SELECT_AND_DESIGN_SWEEP from the task file staged by metaopt-select-design; it is not a launch_requests slot.
Analysis workers (metaopt-analysis-worker): Read WandB best run results, compare against baseline using direction-aware comparison, update baseline if improved, extract learnings. Dispatched during ANALYZE by metaopt-remote-execution-control.
Execution worker (skypilot-wandb-worker): Directive-dispatched only — not a slot-based worker. Creates WandB sweeps, launches SkyPilot agents on Vast.ai, polls sweep status, enforces watchdog and budget. See references/backend-contract.md.
| Lane | Worker | Model Class |
|---|---|---|
| ideation | metaopt-ideation-worker |
general_worker |
| selection | metaopt-selection-worker |
strong_reasoner |
| analysis | metaopt-analysis-worker |
strong_reasoner |
| execution (directive) | skypilot-wandb-worker |
general_worker |
These files define the contract surface. Follow persisted state and canonical handoff output first; use these references to validate behavior, not to invent behavior from prose:
references/dependencies.mdbefore validating campaign inputsreferences/contracts.mdbefore reading or writing state or resultsreferences/control-protocol.mdbefore applying control-agent handoffsreferences/state-machine.mdbefore executing transitions or resuming from statereferences/worker-lanes.mdbefore dispatching any workerreferences/dispatch-guide.mdbefore dispatching any workerreferences/backend-contract.mdbefore any remote execution action
Use ml_metaopt_campaign.example.yaml as the canonical campaign example.
Read references/context-window-guide.md before your first turn. It tells you exactly which files to read, when, and which to skip to stay within your context budget.
TL;DR: read 4 reference docs once at startup, then only state.json (machine_state field) + the latest handoff JSON per turn. Never re-read the campaign YAML (it is denormalized into the LOAD_CAMPAIGN handoff). Estimated budget: ~10 000 tokens for a 5-iteration campaign.
The orchestrator may:
- Invoke the governing control agent for the current machine state as a subagent, read the resulting handoff, and apply it per
references/control-protocol.md - Read campaign and state files for protocol validation only
- Apply control-agent
state_patchupdates to.ml-metaopt/state.json - Execute each entry in
launch_requestsfrom the handoff for slot-based workers (ideation and analysis lanes) before applying any state transition - Invoke
metaopt-selection-workerfrom.ml-metaopt/tasks/select-design-iter-<N>.mdand read.ml-metaopt/worker-results/select-design-iter-<N>.json - Execute
run_smoke_testdirectives by dispatchingskypilot-wandb-worker - Execute
launch_sweepdirectives by dispatchingskypilot-wandb-worker - Execute
poll_sweepdirectives by dispatchingskypilot-wandb-worker - Treat executor directives with missing result files as blocking; after writing the result file, re-enter the governing control phase before applying any provisional non-null transition from the emit-only handoff
- Execute
remove_agents_hookdirectives (remove marked block fromAGENTS.md) - Execute
delete_state_filedirectives (delete.ml-metaopt/state.json) - Execute
emit_final_reportdirectives (write.ml-metaopt/final_report.md) - Execute
emit_iteration_reportdirectives (write iteration summary)
The orchestrator must delegate all semantic decisions to control agents and workers.
| Mistake | Fix |
|---|---|
| Calling WandB API or SkyPilot CLI from the orchestrator | Use skypilot-wandb-worker via directive only |
Launching a new sweep when current_sweep.sweep_id exists in state |
Reconnect to the existing sweep in HYDRATE_STATE |
Letting LOCAL_SANITY run longer than 60 seconds |
The 60-second timeout is hardcoded — terminate the process |
Exceeding max_budget_usd without stopping |
Budget is checked on every poll_sweep — stop all jobs and BLOCKED_CONFIG |
| Asking user for sweep parameters | Read proposals from current_proposals — if empty, stay in IDEATE |