
Self Improve
- 382 installs
- 38.3k repo stars
- Updated August 4, 2026
- yeachan-heo/oh-my-claudecode
self-improve is an autonomous code-improvement workflow skill that runs iterative setup, research, planning, execution, selection, and stop-condition checks for developers who need continuous codebase improvement loops.
About
self-improve is a fully autonomous code improvement loop that runs setup, research, planning, execution, tournament selection, history tracking, visualization, and stop-condition checks. self-improve is designed for developers who want an agent to repeatedly propose and apply improvements, compare candidate changes, and preserve a history of iterations rather than doing one-off refactors. self-improve is most useful when a codebase needs ongoing quality and maintainability improvements, especially when improvements must be selected among alternatives instead of applied blindly. Developers reach for self-improve when they want a structured, repeatable loop that can plan, execute, evaluate outcomes, and stop based on explicit criteria.
- Full autonomous improvement lifecycle with no user intervention
- Tournament selection across competing improvement candidates
- Benchmark-validated: cannot modify benchmark code during execution
- Manages research, planning, execution, visualization, and stop conditions
- Delegates to specialized sub-agents for each lifecycle phase
Self Improve by the numbers
- 382 all-time installs (skills.sh)
- +11 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,046 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yeachan-heo/oh-my-claudecode --skill self-improveAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 382 |
|---|---|
| repo stars | ★ 38.3k |
| Last updated | August 4, 2026 |
| Repository | yeachan-heo/oh-my-claudecode ↗ |
How do I run an autonomous code improvement loop?
Runs a fully autonomous code improvement loop: setup, research, planning, execution, tournament selection, history, visualization, and stop-condition checks.
Who is it for?
self-improve is best for developers maintaining a codebase who want repeated improvement cycles with explicit evaluation and stop conditions.
Skip if: self-improve is not for repositories where changes must be strictly manual or where autonomous edits cannot be reviewed and validated.
When should I use this skill?
Invoke when a developer asks for an autonomous refactor/improvement loop, multi-candidate selection, or continuous codebase improvement with history and stop criteria.
What you get
A sequence of improvement iterations including plans, applied changes, candidate comparisons, history records, and visualizations.
- iterative improvement history
- candidate comparison outputs
- visualization artifacts
Files
Self-Improvement Orchestrator
You are the loop controller for the self-improvement system. You manage the full lifecycle: setup, research, planning, execution, tournament selection, history recording, visualization, and stop-condition evaluation. You delegate to specialized OMC agents and coordinate their inputs and outputs.
---
Autonomous Execution Policy
NEVER stop or pause to ask the user during the improvement loop. Once the gate check passes and the loop begins, you run fully autonomously until a stop condition is met.
- Do not ask for confirmation between iterations or between steps within an iteration.
- Do not summarize and wait — execute the next step immediately.
- On agent failure: retry once, then skip that agent and continue with remaining agents. Log the failure in iteration history.
- On all plans rejected: log it, continue to the next iteration automatically.
- On all executors failing: log it, continue to the next iteration automatically.
- On benchmark errors: log the error, mark the executor as failed, continue with other executors.
- The only things that stop the loop are the stop conditions in Step 11.
- Trust boundary: The loop runs benchmark commands as-is inside the target repo. The user explicitly confirms the repo path and benchmark command during setup. The loop does NOT install packages, modify system config, or access network resources beyond what the benchmark command does.
- Sealed files: validate.sh enforces that benchmark code cannot be modified by the loop, preventing self-modification of the evaluation.
---
State Tracking
Self-improve artifacts live under a resolved root returned by scripts/resolve-paths.mjs.
- New runs default to
.omc/self-improve/topics/default/. - When the user provides a topic or slug, use
.omc/self-improve/topics/{topic_slug}/. - Legacy single-track state at
.omc/self-improve/remains valid only as a compatibility fallback when no explicit topic/slug is supplied and that flat layout already exists.
Treat <self-improve-root>/ below as that resolved root:
<self-improve-root>/
├── config/ # User configuration
│ ├── settings.json # agents, benchmark, thresholds, sealed_files
│ ├── goal.md # Improvement objective + target metric
│ ├── harness.md # Guardrail rules (H001/H002/H003)
│ └── idea.md # User experiment ideas
├── state/ # Runtime state
│ ├── agent-settings.json # iterations, best_score, status, counters
│ ├── iteration_state.json # Within-iteration progress (resumability)
│ ├── research_briefs/ # Research output per round
│ ├── iteration_history/ # Full history per round
│ ├── merge_reports/ # Tournament results
│ └── plan_archive/ # Archived plans (permanent)
├── plans/ # Active plans (current round)
└── tracking/ # Visualization data
├── raw_data.json # All candidate scores
├── baseline.json # Initial benchmark score
├── events.json # Config changes
└── progress.png # Generated chartOMC mode lifecycle: .omc/state/sessions/{sessionId}/self-improve-state.json
---
Agent Mapping
All augmentations delivered via Task description context at spawn time. No modifications to existing agent .md files.
| Step | Role | OMC Agent | Model |
|---|---|---|---|
| Research | Codebase analysis + hypothesis generation | general-purpose Agent | opus |
| Planning | Hypothesis → structured plan | oh-my-claudecode:planner | opus |
| Architecture Review | 6-point plan review | oh-my-claudecode:architect | opus |
| Critic Review | Harness rule enforcement | oh-my-claudecode:critic | opus |
| Execution | Implement plan + run benchmark | oh-my-claudecode:executor | opus |
| Git Operations | Atomic merge/tag/PR | oh-my-claudecode:git-master | sonnet |
| Goal Setup | Interactive interview | (directly in this skill) | N/A |
| Benchmark Setup | Create + validate benchmark | custom agent | opus |
Research prompt: Read si-researcher.md from this skill directory and pass its content as the agent prompt.
Benchmark builder: Read si-benchmark-builder.md from this skill directory and pass its content as the agent prompt.
Goal clarifier: Read si-goal-clarifier.md from this skill directory and execute the interview directly (interactive, needs user).
---
Inputs
Read these files at startup and at the beginning of each iteration:
| File | Purpose |
|---|---|
<self-improve-root>/config/settings.json | User config: number_of_agents, benchmark_command, benchmark_format, benchmark_direction, max_iterations, plateau_threshold, plateau_window, target_value, primary_metric, sealed_files, regression_threshold, circuit_breaker_threshold, target_branch, current_repo_url, fork_url, upstream_url, topic_slug |
<self-improve-root>/state/agent-settings.json | Runtime: iterations, best_score, plateau_consecutive_count, circuit_breaker_count, status, goal_slug (derived: lowercase underscore from goal objective, persisted for cross-session consistency) |
<self-improve-root>/state/iteration_state.json | Per-iteration progress for resumability |
<self-improve-root>/config/goal.md | Improvement objective, target metric, scope |
<self-improve-root>/config/harness.md | Guardrail rules (H001, H002, H003) |
---
Setup Phase
1. Check if target repo path exists. If not configured, ask user for the path to the repository to improve. 2. Resolve <self-improve-root> by running node {skill_dir}/scripts/resolve-paths.mjs --project-root {repo_path} [--topic "..."] [--slug "..."] --ensure-dirs. 3. Create the <self-improve-root>/ directory structure by copying from templates/ in this skill directory into the resolved config/ root. 4. Read <self-improve-root>/state/agent-settings.json. Check si_setting_goal, si_setting_benchmark, si_setting_harness. 4. Trust confirmation (mandatory, cannot be skipped): a. If trust_confirmed is already true in agent-settings.json, skip to step 5 (resume path). b. Display the target repo path and ask user to confirm: "Self-improve will run benchmark commands inside {repo_path}. This executes arbitrary code in that repository. Confirm? [yes/no]" c. If user declines: abort setup and exit. Do NOT proceed. d. Record consent: set trust_confirmed: true in agent-settings.json. 5. Persist topic_slug into config/settings.json when the resolved root is topic-scoped so future resumes stay on the same track. 6. If goal not set → read si-goal-clarifier.md from this skill directory and run the 4-dimension Socratic interview directly in this context (Objective, Metric, Target, Scope). Write result to <self-improve-root>/config/goal.md. 6. If benchmark not set → read si-benchmark-builder.md from this skill directory, spawn a custom Agent(model=opus) with its content as prompt. The agent surveys the repo, creates or wraps a benchmark, validates 3x, and records baseline. After benchmark is set, confirm the benchmark command with user: "Benchmark command: {benchmark_command}. This will be run repeatedly during the loop. Confirm? [yes/no]" If user declines: abort setup and exit. 7. If harness not set → confirm default harness rules (H001/H002/H003) with user or customize. 8. Gate: All of si_setting_goal, si_setting_benchmark, si_setting_harness, trust_confirmed must be true. 9. Create improvement branch (if it does not exist):
git -C {repo_path} checkout -b improve/{goal_slug} {target_branch}
git -C {repo_path} checkout {target_branch}Where {goal_slug} is derived from the goal objective (lowercase, underscored). If the branch already exists, skip creation. Persist goal_slug in agent-settings.json. 10. Mode exclusivity: Call state_list_active. If autopilot, ralph, or ultrawork is active, refuse to start. 11. Write initial state: state_write(mode='self-improve', active=true, iteration=0, started_at=<now>)
---
Git Strategy
All git operations happen inside the target repo, NOT in the OMC project root.
- Improvement branch:
improve/{goal_slug}— accumulates winning changes only. - Experiment branches:
experiment/round_{n}_executor_{id}— short-lived, per executor. - Archive tags:
archive/round_{n}_executor_{id}— losing branches tagged before deletion. - Worktree setup (SKILL.md creates before each executor):
git -C {repo_path} worktree add worktrees/round_{n}_executor_{id} -b experiment/round_{n}_executor_{id} improve/{goal_slug}- Winner merges via
oh-my-claudecode:git-master:
Merge experiment/round_{n}_executor_{winner_id} into improve/{goal_slug} with --no-ff
Message: "Iteration {n}: {hypothesis} (score: {before} → {after})"- Push after merge:
git -C {repo_path} push origin improve/{goal_slug}(backup, non-blocking) - Losers archived: Tag + delete via git-master.
---
Improvement Loop
Gate: All settings must be true. Once the gate passes, execute continuously without stopping.
Update state_write(mode='self-improve', active=true, status="running").
Step 0 — Stale Worktree Cleanup (mandatory, runs every iteration)
PREREQUISITE: This step MUST run to completion before any other step, including resume logic. It is idempotent and safe to run multiple times.
1. List all worktrees in the target repo: git -C {repo_path} worktree list 2. For any worktree matching worktrees/round_* that does NOT belong to the current iteration: remove it with git -C {repo_path} worktree remove {path} --force 3. Run git -C {repo_path} worktree prune to clean up stale references 4. This handles crash recovery — orphaned worktrees from interrupted iterations are cleaned before the new iteration starts
Step 1 — Refresh State
state_write(mode='self-improve', active=true, iteration=N) to reset 30min TTL.
Step 2 — Check Stop Request
Read state via state_read(mode='self-improve').
If state is cleared (cancel was invoked) OR status is user_stopped: a. Set status: "user_stopped" in <self-improve-root>/state/agent-settings.json b. Update iteration_state.json: set status: "interrupted", record current_step c. Clean up any active worktrees for the current round (Step 0 logic) d. Log: "Self-improve stopped by user at iteration {N}, step {current_step}" e. Exit gracefully — do NOT invoke /cancel again (already cancelled)
Step 3 — Check User Ideas
Read <self-improve-root>/config/idea.md. If non-empty, snapshot contents for planners. Clear after planners consume.
Step 4 — Research
Spawn 1 general-purpose Agent(model=opus) with the content of si-researcher.md as prompt.
Pass in the prompt:
- Current iteration number
- Path to target repo
- Path to
<self-improve-root>/config/goal.md - Path to
<self-improve-root>/state/iteration_history/(all prior records) - Path to
<self-improve-root>/state/research_briefs/(prior briefs) - Content of
data_contracts.mdSection 3 (Research Brief schema)
Expected output: research brief JSON → <self-improve-root>/state/research_briefs/round_{n}.json
If researcher fails, proceed with history only.
Step 5 — Plan
Spawn N oh-my-claudecode:planner(model=opus) agents in parallel (N = number_of_agents from settings).
Pass in each planner's prompt:
- Planner identity (planner_a, planner_b, planner_c...)
- Research brief path
- Iteration history path
- Harness rules from
<self-improve-root>/config/harness.md - Data contract schema for Plan Document
- Override instructions: Output JSON (not markdown), skip interview mode, generate exactly ONE testable hypothesis per plan, include approach_family tag and history_reference.
- User ideas (if any, planner_a gets priority)
Expected output: Plan Document JSON → <self-improve-root>/plans/round_{n}/plan_planner_{id}.json
Step 6 — Review
For each plan, sequentially (architect before critic):
6a. Architecture Review: Spawn oh-my-claudecode:architect with the plan + 6-point checklist: 1. Testability — is the hypothesis testable? 2. Novelty — different from prior attempts? 3. Scope — right-sized? 4. Target files — exist, not sealed? 5. Implementation clarity — executor can implement without guessing? 6. Expected outcome — realistic given evidence?
Architect verdict is advisory only.
6b. Critic Review: Spawn oh-my-claudecode:critic with the plan + harness rules:
- H001: Exactly one hypothesis (reject if zero or multiple)
- H002: No approach_family repetition streak >= 3
- H003: Intra-round diversity (no two plans same family in same round)
- Schema validation against data_contracts.md
- History awareness check
Critic sets critic_approved: true or false. Plans with false are excluded from execution.
If ALL plans rejected, log and skip to Step 9.
Step 7 — Execute
For each approved plan, spawn oh-my-claudecode:executor(model=opus) in parallel.
Before spawning, create worktree:
git -C {repo_path} worktree add worktrees/round_{n}_executor_{id} -b experiment/round_{n}_executor_{id} improve/{goal_slug}Pass in each executor's prompt:
- The approved plan JSON
- Worktree directory path
- Benchmark command from settings
- Sealed files list from settings
- Path to
scripts/validate.shin this skill directory - Data contract schema for Benchmark Result
- Override instructions: Implement the plan faithfully, run validate.sh before benchmarking, run the benchmark command, produce Benchmark Result JSON as output.
Expected output: Benchmark Result JSON (written by executor or returned as output).
Step 8 — Tournament Selection
SKILL.md does this directly (not delegated):
1. Collect all executor results 2. Filter to status: "success" only. If zero candidates, skip to Step 9 (Record & Visualize). 3. Rank by benchmark_score (respecting benchmark_direction) 4. Ranked-candidate loop — for each candidate in rank order (best first): a. No-regression check: candidate score must improve or hold even vs best_score, respecting benchmark_direction (higher_is_better: score >= best_score; lower_is_better: score <= best_score) b. Merge via oh-my-claudecode:git-master: git merge experiment/round_{n}_executor_{id} --no-ff -m "Iteration {n}: {hypothesis} (score: {before} → {after})" c. Re-benchmark on merged state to confirm improvement d. If re-benchmark confirms improvement: accept winner, break loop e. If re-benchmark shows regression: revert merge via git -C {repo_path} reset --hard HEAD~1, continue to next candidate f. If merge conflicts: git -C {repo_path} merge --abort, continue to next candidate 5. If a winner was accepted AND auto_push is true in settings: Push improvement branch: git -C {repo_path} push origin improve/{goal_slug} (non-blocking). If auto_push is false (default): skip push. Log: "Push skipped (auto_push: false). Run manually: git -C {repo_path} push origin improve/{goal_slug}" 6. Archive all non-winner branches via git-master: tag + delete 7. If no candidate survived the loop: no merge this round. Improvement branch stays at prior state. 8. Write Merge Report JSON to <self-improve-root>/state/merge_reports/round_{n}.json (schema: data_contracts.md Section 9).
Step 9 — Record & Visualize
1. Write iteration history to <self-improve-root>/state/iteration_history/round_{n}.json 2. Update <self-improve-root>/state/agent-settings.json:
- Increment
iterationsby 1 - If winner AND improvement exceeds
plateau_threshold(abs(new_score - best_score) >= plateau_threshold): updatebest_score, resetplateau_consecutive_count = 0, resetcircuit_breaker_count = 0 - If winner AND improvement below threshold (
abs(new_score - best_score) < plateau_threshold): updatebest_scoreif better, incrementplateau_consecutive_count += 1, resetcircuit_breaker_count = 0 - If no winner (all rejected, all failed, or all regressed): increment
circuit_breaker_count += 1(do NOT incrementplateau_consecutive_count— plateau tracks stagnating wins, not failures)
3. Append to <self-improve-root>/tracking/raw_data.json (one entry per candidate) 4. Run python3 {skill_dir}/scripts/plot_progress.py --tracking-dir <self-improve-root>/tracking for visualization 5. Archive plans: copy current round plans to state/plan_archive/round_{n}/
Step 10 — Cleanup
Remove worktrees:
git -C {repo_path} worktree remove worktrees/round_{n}_executor_{id} --force
git -C {repo_path} worktree pruneUpdate iteration_state.json status to completed.
Step 11 — Stop Condition Check
Evaluate ALL conditions. If ANY is true, exit:
| Condition | Check |
|---|---|
| User stop | status == "user_stopped" in agent-settings or state cleared |
| Target reached | best_score meets/exceeds target_value (respecting direction) |
| Plateau | plateau_consecutive_count >= plateau_window |
| Max iterations | iterations >= max_iterations |
| Circuit breaker | circuit_breaker_count >= circuit_breaker_threshold |
If NO stop condition: immediately go back to Step 1.
---
Resumability
PREREQUISITE: Step 0 (stale worktree cleanup) MUST run to completion before any resume logic executes, regardless of prior state.
On invocation, before entering the loop:
1. Always run Step 0 (stale worktree cleanup) — even on fresh start 2. Read <self-improve-root>/state/agent-settings.json:
- If
status: "user_stopped": ask user"Previous run was stopped at iteration {N}. Resume? [yes/no]". If no, exit. If yes, continue. - If
status: "running": session crashed — resume automatically (no user prompt) - If
status: "idle": fresh start
3. Re-confirm trust gate only if trust_confirmed is false in agent-settings.json 4. Read <self-improve-root>/state/iteration_state.json:
status: "in_progress"→ resume fromcurrent_step, skip completed sub-stepsstatus: "completed"→ start next iterationstatus: "failed"→ complete recording step if needed, start next iteration- File missing → start from iteration 1
---
Completion
When the loop exits:
1. Update agent-settings.json with final status 2. If target_reached AND auto_pr is true in settings: spawn git-master to create PR from improve/{goal_slug} to upstream. If auto_pr is false (default): skip PR creation. Log: "PR creation skipped (auto_pr: false). Run manually: gh pr create --head improve/{goal_slug} --base {target_branch}" 3. Run plot_progress.py one final time 4. Print summary report:
=== Self-Improvement Loop Complete ===
Status: {status}
Iterations: {iterations}
Best Score: {best_score} (baseline: {baseline})
Improvement: {delta} ({delta_pct}%)5. Run /oh-my-claudecode:cancel for clean state cleanup
---
Error Handling
| Situation | Action |
|---|---|
| Agent fails to produce output | Retry once. If still no output, log and continue. |
| Researcher produces empty brief | Proceed — planners work from history alone. |
| All plans rejected by critic | Skip execution. Log. Continue to next iteration. |
| All executors fail | Skip tournament. Record failures. Continue. |
| Merge conflict | Reject candidate, try next. |
| Re-benchmark regression | Reject candidate, revert merge, try next. |
| Push failure | Log warning. Continue — push is backup. |
| Worktree already exists | Remove and recreate. |
| Settings corrupted | Report and stop. |
---
Parallel session caveats
- Multi-repo workspace anchor: drop a
.omc-workspacemarker at the parent directory so multiple sessions across sub-repos share one.omc/. Resolution order:OMC_STATE_DIR > .omc-workspace > git > cwd. Seedocs/REFERENCE.md. - Session id source: OMC_SESSION_ID env var wins in CLI contexts; hook payload data.session_id wins in hook contexts.
- Plan id (when applicable): Self-improve artifact dirs are topic-slug-scoped; for parallel runs with the same topic in the same workspace, expect Wave B2's session-id suffix to land.
- Parallel verdict: supported-with-caveats (topic-slug collision possible; see Wave B2)
Approach Family Taxonomy
Every plan must be tagged with exactly one:
| Tag | Description |
|---|---|
architecture | Model/component structure changes |
training_config | Optimizer, LR, scheduler, batch size |
data | Data loading, augmentation, preprocessing |
infrastructure | Mixed precision, distributed training, compiled kernels |
optimization | Algorithmic/numerical optimizations |
testing | Evaluation methodology changes |
documentation | Documentation-only changes |
other | Does not fit above — explain in evidence |
Data Contracts: Inter-Agent Communication Schemas
Canonical JSON schemas for all messages exchanged between agents in the self-improvement loop.
1. Plan Document
Producer: planner | Consumer: critic, executor
{
"plan_id": "round_{N}_{planner_id}",
"planner_id": "planner_a|planner_b|planner_c",
"round": 1,
"hypothesis": "Doing X should improve Y because Z",
"approach_family": "<taxonomy value>",
"critic_approved": false,
"target_files": ["path/to/file1"],
"steps": [
{ "step": 1, "file": "path/to/file", "change": "exact description" }
],
"expected_outcome": {
"metric": "<metric from goal>",
"estimated_impact": "<quantified estimate>",
"rationale": "<why>",
"sub_score_expectations": {}
},
"history_reference": {
"builds_on": "<prior success or 'none'>",
"avoids": "<prior failure or 'none'>"
},
"critic_review": {
"h001_hypothesis_count": "pass|fail",
"h002_family_streak": "pass|fail",
"h003_intra_round_diversity": "pass|fail",
"schema_valid": "pass|fail",
"history_aware": "pass|fail",
"verdict": "approved|rejected",
"rejection_reason": null
},
"architect_review": {
"verdict": "approve|reject",
"feedback": "",
"structural_concerns": []
}
}2. Benchmark Result
Producer: executor | Consumer: tournament (SKILL.md)
{
"executor_id": "executor_{id}",
"plan_id": "round_{n}_planner_{x}",
"benchmark_score": 85.2,
"benchmark_raw": "full stdout verbatim",
"status": "success|regression|error|timeout",
"sub_scores": { "dim_a": 85.2, "dim_b": 42.3 },
"failure_analysis": null,
"timestamp": "ISO 8601 UTC"
}Status definitions:
success— score improved or held evenregression— score dropped below baselineerror— benchmark could not runtimeout— exceeded time limit
3. Research Brief
Producer: researcher | Consumer: planners
{
"iteration": 1,
"researcher_id": "researcher",
"repo_analysis_summary": "...",
"ideas": [
{
"title": "Short action name",
"source": "Specific origin",
"evidence": "Concrete evidence",
"approach_family": "<taxonomy value>",
"confidence": "high|medium|low",
"estimated_impact": "3-5%"
}
]
}4. Iteration History Record
Producer: orchestrator | Consumer: planners, researcher
{
"iteration": 1,
"baseline_score": 80.0,
"winner": {
"plan_id": "round_1_planner_a",
"score": 85.2,
"approach_family": "training_config",
"hypothesis": "...",
"sub_scores": {}
},
"losers": [
{
"plan_id": "round_1_planner_b",
"score": 78.5,
"approach_family": "architecture",
"hypothesis": "...",
"sub_scores": {},
"failure_analysis": {
"what": "Score dropped",
"why": "Root cause",
"category": "regression",
"lesson": "Actionable lesson"
}
}
],
"research_brief_id": "round_1"
}5. Visualization Data
File: <self-improve-root>/tracking/raw_data.json — top-level JSON array, append-only.
[
{
"iteration": 1,
"plan_id": "round_1_planner_a",
"benchmark_score": 85.2,
"is_winner": true,
"approach_family": "training_config",
"sub_scores": {}
}
]6. Approach Family Taxonomy
| Tag | Description |
|---|---|
architecture | Model/component structure changes |
training_config | Optimizer, LR, scheduler, batch size, epochs |
data | Data loading, augmentation, preprocessing |
infrastructure | Mixed precision, distributed training, checkpointing |
optimization | Algorithmic/numerical optimizations |
testing | Evaluation methodology changes |
documentation | Documentation-only changes |
other | Does not fit above — explain in evidence |
Custom families from harness.md are also valid.
7. Failure Analysis Object
{
"what": "Factual description with scores/errors",
"why": "Root cause mechanism",
"category": "oom|timeout|regression|logic_error|scope_error|infrastructure|benchmark_parse_error|sealed_file_violation",
"lesson": "Actionable lesson for future planners"
}8. Iteration State
File: <self-improve-root>/state/iteration_state.json — tracks within-iteration progress.
{
"iteration": 1,
"status": "in_progress|completed|failed|interrupted",
"current_step": "research|planning|critic_review|execution|tournament|recording|stop_check",
"started_at": "ISO 8601",
"updated_at": "ISO 8601",
"research": { "status": "pending|in_progress|completed|failed", "output_path": null, "completed_at": null },
"planning": {
"status": "pending|in_progress|completed",
"plans": {
"planner_a": { "status": "completed", "output_path": "...", "critic_approved": true }
},
"approved_count": 2,
"completed_at": null
},
"execution": {
"status": "pending|in_progress|completed",
"executors": {
"executor_1": { "status": "running", "plan_id": "...", "output_path": null, "benchmark_score": null }
},
"completed_at": null
},
"tournament": { "status": "pending", "winner": null, "winner_score": null, "completed_at": null },
"recording": { "status": "pending", "history_path": null, "visualization_updated": false, "cleanup_done": false },
"user_ideas_consumed": []
}9. Merge Report
Producer: tournament (SKILL.md) | Consumer: orchestrator
{
"iteration": 3,
"goal_slug": "reduce_latency",
"winner": {
"executor_id": "executor_2",
"branch": "experiment/round_3_executor_2",
"hypothesis": "Cache intermediate results",
"score_before": 142.3,
"score_after": 118.7,
"sub_scores": {}
},
"archived": ["archive/round_3_executor_1"],
"regressions_detected": false,
"re_benchmark_score": 118.7,
"status": "merged|no_improvement|no_winner|all_rejected",
"reason": null
}Status definitions:
merged— a candidate was merged and re-benchmark confirmed improvementno_improvement— candidates existed and were tested, but all failed re-benchmark (no merge occurred)no_winner— all executors failed or produced non-success status (no candidates to evaluate)all_rejected— all plans were rejected by the critic (execution was skipped)
reason is required (string) when status is not merged, null when merged.
## 10. Plan Archive
**Location:** `<self-improve-root>/state/plan_archive/round_{n}/`
Exact copies of all plan JSON files, including critic and architect reviews. Permanent retention.
## 11. Event Log
**File:** `<self-improve-root>/tracking/events.json` — append-only array.
[ { "timestamp": "ISO 8601", "event_type": "config_change|phase_transition", "iteration": 5, "details": { "field": "number_of_agents", "old_value": 2, "new_value": 3, "source": "user" } } ]
## 12. Goal Phase
Defined in goal.md under `## Phases`. Tracked in agent-settings.json as `current_phase`.
Phases
| Phase | Focus | Sub-Score Targets | Status |
|---|---|---|---|
| phase_1 | Primary dimension | dim_a >= 90.0 | active |
| phase_2 | Secondary dimension | dim_b <= 50.0 | pending |
Phase transitions are tracked as events but do not affect tournament selection.
#!/usr/bin/env python3
"""
Progress visualization for the self-improvement loop.
Reads raw_data.json and generates progress.png.
Usage:
python3 plot_progress.py --data /path/to/raw_data.json --output /path/to/progress.png
python3 plot_progress.py --tracking-dir /path/to/<self-improve-root>/tracking/
"""
import argparse
import json
import sys
from pathlib import Path
def load_data(data_path: str) -> list:
path = Path(data_path)
if not path.exists():
print(f"Warning: {data_path} not found. No visualization generated.")
return []
with open(path) as f:
return json.load(f)
def plot_with_matplotlib(data: list, output_path: str):
try:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
except ImportError:
print("Warning: matplotlib not available. Generating text summary instead.")
generate_text_summary(data, output_path)
return
iterations = sorted(set(d['iteration'] for d in data))
winners = [d for d in data if d.get('is_winner')]
losers = [d for d in data if not d.get('is_winner')]
fig, ax = plt.subplots(figsize=(12, 6))
if losers:
ax.scatter(
[d['iteration'] for d in losers],
[d['benchmark_score'] for d in losers],
c='lightgray', alpha=0.5, s=30, label='Candidates', zorder=2
)
if winners:
ax.plot(
[d['iteration'] for d in winners],
[d['benchmark_score'] for d in winners],
'b-o', linewidth=2, markersize=8, label='Winners', zorder=3
)
families = list(set(d.get('approach_family', 'unknown') for d in winners))
colors = plt.cm.Set2(range(len(families)))
family_color = dict(zip(families, colors))
for d in winners:
family = d.get('approach_family', 'unknown')
ax.annotate(
family[:4],
(d['iteration'], d['benchmark_score']),
textcoords="offset points", xytext=(0, 10),
fontsize=7, ha='center', color=family_color.get(family, 'black')
)
ax.set_xlabel('Iteration')
ax.set_ylabel('Benchmark Score')
ax.set_title('Self-Improvement Progress')
ax.legend(loc='best')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(output_path, dpi=150)
plt.close()
print(f"Visualization saved to: {output_path}")
def generate_text_summary(data: list, output_path: str):
"""Fallback when matplotlib is not available."""
winners = [d for d in data if d.get('is_winner')]
summary_path = Path(output_path).with_suffix('.txt')
lines = ["Self-Improvement Progress Summary", "=" * 40, ""]
for w in winners:
lines.append(
f"Iteration {w['iteration']}: score={w['benchmark_score']:.4f} "
f"family={w.get('approach_family', '?')} plan={w.get('plan_id', '?')}"
)
if winners:
scores = [w['benchmark_score'] for w in winners]
lines.append("")
lines.append(f"Best: {max(scores):.4f} Worst: {min(scores):.4f} "
f"Delta: {max(scores) - min(scores):.4f}")
with open(summary_path, 'w') as f:
f.write('\n'.join(lines))
print(f"Text summary saved to: {summary_path}")
def main():
parser = argparse.ArgumentParser(description='Self-improvement progress visualization')
parser.add_argument('--data', help='Path to raw_data.json')
parser.add_argument('--output', help='Path to output image')
parser.add_argument('--tracking-dir', help='Path to tracking/ directory (auto-discovers data and output)')
args = parser.parse_args()
if args.tracking_dir:
data_path = str(Path(args.tracking_dir) / 'raw_data.json')
output_path = str(Path(args.tracking_dir) / 'progress.png')
elif args.data and args.output:
data_path = args.data
output_path = args.output
else:
print("Usage: plot_progress.py --tracking-dir /path/ OR --data /path/raw_data.json --output /path/progress.png")
sys.exit(1)
data = load_data(data_path)
if not data:
sys.exit(0)
plot_with_matplotlib(data, output_path)
if __name__ == '__main__':
main()
#!/usr/bin/env node
import { existsSync, mkdirSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { resolveOmcStateRoot } from '../../../scripts/lib/state-root.mjs';
const DEFAULT_TOPIC_SLUG = 'default';
const TOPICS_DIR = 'topics';
const SESSIONS_DIR = 'sessions';
function slugify(value) {
const normalized = String(value ?? '')
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^A-Za-z0-9\s_-]/g, ' ')
.trim()
.toLowerCase()
.replace(/[\s_]+/g, '-')
.replace(/-+/g, '-')
.replace(/^-+|-+$/g, '');
return normalized || DEFAULT_TOPIC_SLUG;
}
function parseArgs(argv) {
const result = {
projectRoot: process.cwd(),
topic: '',
slug: '',
sessionId: '',
format: 'json',
ensureDirs: false,
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const next = argv[index + 1];
if (arg === '--project-root' && next) {
result.projectRoot = next;
index += 1;
} else if (arg.startsWith('--project-root=')) {
result.projectRoot = arg.slice('--project-root='.length);
} else if (arg === '--topic' && next) {
result.topic = next;
index += 1;
} else if (arg.startsWith('--topic=')) {
result.topic = arg.slice('--topic='.length);
} else if (arg === '--slug' && next) {
result.slug = next;
index += 1;
} else if (arg.startsWith('--slug=')) {
result.slug = arg.slice('--slug='.length);
} else if (arg === '--session-id' && next) {
result.sessionId = next;
index += 1;
} else if (arg.startsWith('--session-id=')) {
result.sessionId = arg.slice('--session-id='.length);
} else if (arg === '--format' && next) {
result.format = next;
index += 1;
} else if (arg.startsWith('--format=')) {
result.format = arg.slice('--format='.length);
} else if (arg === '--ensure-dirs') {
result.ensureDirs = true;
} else if (arg === '--help' || arg === '-h') {
result.help = true;
} else {
throw new Error(`Unknown argument: ${arg}`);
}
}
if (!['json', 'shell'].includes(result.format)) {
throw new Error(`Unsupported format: ${result.format}`);
}
return result;
}
function hasLegacyLayout(baseRoot) {
return existsSync(join(baseRoot, 'config', 'settings.json'))
|| existsSync(join(baseRoot, 'state', 'agent-settings.json'));
}
function buildPaths(root, projectRoot, topicSlug, scopeMode, baseRoot) {
const configDir = join(root, 'config');
const stateDir = join(root, 'state');
const trackingDir = join(root, 'tracking');
const paths = {
project_root: projectRoot,
base_root: baseRoot,
topic_slug: topicSlug,
scope_mode: scopeMode,
root,
config_dir: configDir,
state_dir: stateDir,
plans_dir: join(root, 'plans'),
tracking_dir: trackingDir,
settings_path: join(configDir, 'settings.json'),
goal_path: join(configDir, 'goal.md'),
harness_path: join(configDir, 'harness.md'),
idea_path: join(configDir, 'idea.md'),
agent_settings_path: join(stateDir, 'agent-settings.json'),
iteration_state_path: join(stateDir, 'iteration_state.json'),
research_briefs_dir: join(stateDir, 'research_briefs'),
iteration_history_dir: join(stateDir, 'iteration_history'),
merge_reports_dir: join(stateDir, 'merge_reports'),
plan_archive_dir: join(stateDir, 'plan_archive'),
raw_data_path: join(trackingDir, 'raw_data.json'),
baseline_path: join(trackingDir, 'baseline.json'),
events_path: join(trackingDir, 'events.json'),
progress_path: join(trackingDir, 'progress.png'),
};
return paths;
}
function ensureDirs(paths) {
for (const dirPath of [
paths.config_dir,
paths.state_dir,
paths.plans_dir,
paths.tracking_dir,
paths.research_briefs_dir,
paths.iteration_history_dir,
paths.merge_reports_dir,
paths.plan_archive_dir,
]) {
mkdirSync(dirPath, { recursive: true });
}
}
export async function resolveSelfImprovePaths({ projectRoot = process.cwd(), topic = '', slug = '', sessionId = '' } = {}) {
const resolvedProjectRoot = resolve(projectRoot);
const omcRoot = await resolveOmcStateRoot(resolvedProjectRoot);
const baseRoot = join(omcRoot, 'self-improve');
const explicitSlug = slugify(slug || topic);
const legacyLayout = hasLegacyLayout(baseRoot);
const shouldUseLegacyRoot = !slug && !topic && legacyLayout;
const topicSlug = shouldUseLegacyRoot ? DEFAULT_TOPIC_SLUG : explicitSlug;
// When a sessionId is provided, scope beneath topics/<slug>/sessions/<sid>/
// so concurrent runs sharing the same topic slug don't collide.
// Falls back to OMC_SESSION_ID env var when explicit arg is absent.
// Legacy layout (no topic/slug supplied, flat .omc/self-improve/ exists) is
// preserved as-is — session scoping only applies to the topic-scoped layout.
const rawSessionId = sessionId && sessionId.trim()
? sessionId.trim()
: (process.env.OMC_SESSION_ID && process.env.OMC_SESSION_ID.trim() ? process.env.OMC_SESSION_ID.trim() : '');
const effectiveSessionId = shouldUseLegacyRoot ? '' : rawSessionId;
const root = shouldUseLegacyRoot
? baseRoot
: effectiveSessionId
? join(baseRoot, TOPICS_DIR, topicSlug, SESSIONS_DIR, effectiveSessionId)
: join(baseRoot, TOPICS_DIR, topicSlug);
const scopeMode = shouldUseLegacyRoot
? 'legacy-flat-root'
: effectiveSessionId
? 'session-scoped'
: (slug || topic ? 'topic-scoped' : 'default-scoped');
return { ...buildPaths(root, resolvedProjectRoot, topicSlug, scopeMode, baseRoot), session_id: effectiveSessionId || null };
}
function renderShell(paths) {
return Object.entries(paths)
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
.join('\n');
}
function printHelp() {
process.stdout.write(
[
'Usage: node resolve-paths.mjs [--project-root PATH] [--topic TEXT | --slug SLUG] [--session-id SID] [--ensure-dirs] [--format json|shell]',
'',
'Resolves self-improve artifact paths.',
'- New runs default to .omc/self-improve/topics/<topic-slug>/',
'- Pass --session-id to isolate parallel runs: .omc/self-improve/topics/<slug>/sessions/<sid>/',
'- Legacy flat .omc/self-improve/ is preserved only when no topic/slug is supplied and a flat layout already exists',
'',
].join('\n'),
);
}
async function main() {
const args = parseArgs(process.argv.slice(2));
if (args.help) {
printHelp();
return;
}
const paths = await resolveSelfImprovePaths({
projectRoot: args.projectRoot,
topic: args.topic,
slug: args.slug,
sessionId: args.sessionId,
});
if (args.ensureDirs) {
ensureDirs(paths);
}
if (args.format === 'shell') {
process.stdout.write(`${renderShell(paths)}\n`);
return;
}
process.stdout.write(`${JSON.stringify(paths, null, 2)}\n`);
}
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
main().catch((error) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exit(1);
});
}
#!/usr/bin/env bash
# validate.sh — Sealed file enforcement + plan schema validation for self-improvement loop.
# Usage:
# ./validate.sh --worktree /path --settings /path/to/settings.json plan.json
# ./validate.sh --project-root /path/to/omc/project --topic "Improve tests" plan.json
# ./validate.sh plan.json
# ./validate.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
# Settings path may be provided directly or resolved from the scoped self-improve root.
SETTINGS=""
PROJECT_ROOT=""
TOPIC_NAME=""
TOPIC_SLUG=""
# Parse arguments
WORKTREE_PATH=""
POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--worktree)
WORKTREE_PATH="$2"
shift 2
;;
--settings)
SETTINGS="$2"
shift 2
;;
--project-root)
PROJECT_ROOT="$2"
shift 2
;;
--topic)
TOPIC_NAME="$2"
shift 2
;;
--slug)
TOPIC_SLUG="$2"
shift 2
;;
*)
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL_ARGS[@]+"${POSITIONAL_ARGS[@]}"}"
GIT_DIR="${WORKTREE_PATH:-$(pwd)}"
err() { echo "ERROR: $*" >&2; }
ok() { echo "OK: $*"; }
require_jq() {
if ! command -v jq &>/dev/null; then
err "jq is not installed. Install with: brew install jq (macOS) or apt-get install jq (Linux)"
exit 1
fi
}
resolve_settings_from_project_root() {
local project_root="$1"
local resolver="${SCRIPT_DIR}/resolve-paths.mjs"
local args=( "${resolver}" --project-root "${project_root}" )
if [[ -n "${TOPIC_SLUG}" ]]; then
args+=( --slug "${TOPIC_SLUG}" )
elif [[ -n "${TOPIC_NAME}" ]]; then
args+=( --topic "${TOPIC_NAME}" )
fi
local resolved
resolved=$(node "${args[@]}" 2>/dev/null || true)
if [[ -z "${resolved}" ]]; then
return 1
fi
local candidate
candidate=$(printf '%s' "${resolved}" | jq -r '.settings_path // ""' 2>/dev/null || true)
if [[ -n "${candidate}" ]]; then
SETTINGS="${candidate}"
return 0
fi
return 1
}
discover_settings_from_search_root() {
local search_dir="$1"
while [[ "${search_dir}" != "/" ]]; do
if [[ -f "${search_dir}/.omc/self-improve/config/settings.json" ]]; then
SETTINGS="${search_dir}/.omc/self-improve/config/settings.json"
return 0
fi
shopt -s nullglob
local scoped_candidates=( "${search_dir}"/.omc/self-improve/topics/*/config/settings.json )
shopt -u nullglob
if [[ "${#scoped_candidates[@]}" -eq 1 ]]; then
SETTINGS="${scoped_candidates[0]}"
return 0
fi
if [[ "${#scoped_candidates[@]}" -gt 1 ]]; then
err "Multiple self-improve topics exist under ${search_dir}/.omc/self-improve/topics/. Pass --settings, --project-root with --topic/--slug, or set SELF_IMPROVE_SETTINGS_PATH."
exit 1
fi
search_dir="$(dirname "${search_dir}")"
done
return 1
}
resolve_settings_path() {
[[ -n "${SETTINGS}" ]] && return 0
if [[ -n "${SELF_IMPROVE_SETTINGS_PATH:-}" ]]; then
SETTINGS="${SELF_IMPROVE_SETTINGS_PATH}"
return 0
fi
require_jq
if [[ -n "${PROJECT_ROOT}" ]]; then
if [[ -n "${TOPIC_SLUG}" || -n "${TOPIC_NAME}" ]]; then
if resolve_settings_from_project_root "${PROJECT_ROOT}"; then
return 0
fi
fi
if discover_settings_from_search_root "${PROJECT_ROOT}"; then
return 0
fi
fi
local search_dir="${WORKTREE_PATH:-$(pwd)}"
if discover_settings_from_search_root "${search_dir}"; then
return 0
fi
return 1
}
check_sealed_files() {
resolve_settings_path || true
if [[ -z "${SETTINGS}" || ! -f "${SETTINGS}" ]]; then
ok "No settings file found — skipping sealed file check."
return 0
fi
has_sealed=$(jq -r 'if (.sealed_files | type) == "array" and (.sealed_files | length) > 0 then "yes" else "no" end' "${SETTINGS}" 2>/dev/null || echo "no")
if [[ "${has_sealed}" != "yes" ]]; then
ok "No sealed files configured — skipping."
return 0
fi
if ! git -C "${GIT_DIR}" rev-parse --git-dir &>/dev/null 2>&1; then
ok "Not a git repository — skipping sealed file check."
return 0
fi
local modified_files_str=""
if [[ -n "${WORKTREE_PATH}" ]]; then
# Find the correct baseline: the improvement branch this experiment branched from.
# Try improve/* branches first, then fall back to main/master.
local base_commit
local improve_branch
improve_branch=$(git -C "${GIT_DIR}" branch -a --list 'improve/*' 2>/dev/null | head -1 | tr -d ' *' || true)
if [[ -z "${improve_branch}" ]]; then
improve_branch=$(git -C "${GIT_DIR}" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
fi
base_commit=$(git -C "${GIT_DIR}" merge-base HEAD "${improve_branch}" 2>/dev/null || echo "HEAD~1")
modified_files_str=$(git -C "${GIT_DIR}" diff --name-only "${base_commit}" 2>/dev/null || true)
local uncommitted
uncommitted=$(git -C "${GIT_DIR}" diff --name-only 2>/dev/null || true)
if [[ -n "${uncommitted}" ]]; then
modified_files_str="${modified_files_str}"$'\n'"${uncommitted}"
fi
else
modified_files_str=$(git -C "${GIT_DIR}" diff --name-only HEAD 2>/dev/null || true)
local staged
staged=$(git -C "${GIT_DIR}" diff --name-only --cached 2>/dev/null || true)
if [[ -n "${staged}" ]]; then
modified_files_str="${modified_files_str}"$'\n'"${staged}"
fi
fi
if [[ -n "${modified_files_str}" ]]; then
modified_files_str=$(echo "${modified_files_str}" | sort -u)
fi
if [[ -z "${modified_files_str}" ]]; then
ok "No modified files detected."
return 0
fi
violations=""
while IFS= read -r sealed; do
[[ -z "${sealed}" ]] && continue
while IFS= read -r modified; do
[[ -z "${modified}" ]] && continue
if [[ "${sealed}" == */ ]]; then
[[ "${modified}" == "${sealed}"* ]] && violations="${violations} ${modified}"
else
[[ "${modified}" == "${sealed}" ]] && violations="${violations} ${modified}"
fi
done <<< "${modified_files_str}"
done < <(jq -r '.sealed_files[]' "${SETTINGS}" 2>/dev/null)
if [[ -n "${violations}" ]]; then
err "Sealed file(s) were modified:${violations}"
exit 1
fi
local modified_count
modified_count=$(echo "${modified_files_str}" | wc -l | tr -d ' ')
ok "Sealed file check passed (${modified_count} modified, none sealed)."
}
check_plan_schema() {
local plan_file="$1"
require_jq
if [[ ! -f "${plan_file}" ]]; then
err "Plan file not found: ${plan_file}"
exit 1
fi
local required_fields="plan_id planner_id round hypothesis approach_family critic_approved target_files steps expected_outcome history_reference"
local missing=""
for field in ${required_fields}; do
val=$(jq -r --arg f "${field}" '.[$f]' "${plan_file}" 2>/dev/null)
if [[ "${val}" == "null" || -z "${val}" ]]; then
missing="${missing} ${field}"
fi
done
if [[ -n "${missing}" ]]; then
err "Plan is missing required fields:${missing}"
exit 1
fi
ok "Plan contains all required fields."
# approach_family validation is handled by the critic (supports custom families
# from harness.md). validate.sh only checks structural schema, not taxonomy.
# Validate hypothesis is a single string
hypothesis_type=$(jq -r '.hypothesis | type' "${plan_file}" 2>/dev/null)
if [[ "${hypothesis_type}" != "string" ]]; then
err "hypothesis must be a string (got ${hypothesis_type})"
exit 1
fi
ok "One-hypothesis check passed."
# Validate steps non-empty
steps_len=$(jq '.steps | length' "${plan_file}" 2>/dev/null || echo "0")
if [[ "${steps_len}" -eq 0 ]]; then
err "steps must be a non-empty array"
exit 1
fi
ok "Steps validated (${steps_len} step(s))."
}
check_result_schema() {
local result_file="$1"
require_jq
if [[ ! -f "${result_file}" ]]; then
err "Result file not found: ${result_file}"
exit 1
fi
local required_fields="executor_id plan_id benchmark_score status timestamp benchmark_raw"
local missing=""
for field in ${required_fields}; do
val=$(jq -r --arg f "${field}" '.[$f]' "${result_file}" 2>/dev/null)
if [[ "${val}" == "null" || -z "${val}" ]]; then
if [[ "${field}" == "benchmark_raw" ]]; then
exists=$(jq --arg f "${field}" 'has($f)' "${result_file}" 2>/dev/null || echo "false")
if [[ "${exists}" != "true" ]]; then
missing="${missing} ${field}"
fi
elif [[ "${field}" == "benchmark_score" ]]; then
exists=$(jq --arg f "${field}" 'has($f)' "${result_file}" 2>/dev/null || echo "false")
if [[ "${exists}" != "true" ]]; then
missing="${missing} ${field}"
fi
else
missing="${missing} ${field}"
fi
fi
done
if [[ -n "${missing}" ]]; then
err "Result is missing required fields:${missing}"
exit 1
fi
ok "Result contains all required fields."
# Validate status enum
local status
status=$(jq -r '.status' "${result_file}" 2>/dev/null)
case "${status}" in
success|regression|error|timeout) ;;
*)
err "Invalid status '${status}'. Must be one of: success, regression, error, timeout"
exit 1
;;
esac
ok "Status '${status}' is valid."
# Check failure_analysis on non-success status
if [[ "${status}" != "success" ]]; then
local fa_type
fa_type=$(jq -r '.failure_analysis | type' "${result_file}" 2>/dev/null)
if [[ "${fa_type}" != "object" ]]; then
err "failure_analysis must be a non-null object when status is '${status}' (got ${fa_type})"
exit 1
fi
local fa_fields="what why category lesson"
local fa_missing=""
for field in ${fa_fields}; do
val=$(jq -r --arg f "${field}" '.failure_analysis[$f]' "${result_file}" 2>/dev/null)
if [[ "${val}" == "null" || -z "${val}" ]]; then
fa_missing="${fa_missing} ${field}"
fi
done
if [[ -n "${fa_missing}" ]]; then
err "failure_analysis is missing required fields:${fa_missing}"
exit 1
fi
ok "failure_analysis is complete for non-success status."
# Validate failure category enum
local fa_category
fa_category=$(jq -r '.failure_analysis.category' "${result_file}" 2>/dev/null)
local valid_categories="oom timeout regression logic_error scope_error infrastructure benchmark_parse_error sealed_file_violation"
local cat_valid=0
for cat in ${valid_categories}; do
if [[ "${fa_category}" == "${cat}" ]]; then
cat_valid=1
break
fi
done
if [[ ${cat_valid} -eq 0 ]]; then
err "failure_analysis.category '${fa_category}' is not valid. Must be one of: ${valid_categories}"
exit 1
fi
ok "failure_analysis.category '${fa_category}' is valid."
fi
# Validate sub_scores if present
local has_sub_scores
has_sub_scores=$(jq 'has("sub_scores")' "${result_file}" 2>/dev/null || echo "false")
if [[ "${has_sub_scores}" == "true" ]]; then
local sub_scores_type
sub_scores_type=$(jq -r '.sub_scores | type' "${result_file}" 2>/dev/null)
if [[ "${sub_scores_type}" == "object" ]]; then
local invalid_values
invalid_values=$(jq -r '.sub_scores | to_entries[] | select(.value != null and (.value | type) != "number") | .key' "${result_file}" 2>/dev/null)
if [[ -n "${invalid_values}" ]]; then
err "sub_scores contains non-numeric values for keys: ${invalid_values}"
exit 1
fi
local sub_scores_count
sub_scores_count=$(jq '.sub_scores | length' "${result_file}" 2>/dev/null)
ok "sub_scores is a valid object (${sub_scores_count} dimension(s))."
elif [[ "${sub_scores_type}" != "null" ]]; then
err "sub_scores must be an object or null (got ${sub_scores_type})"
exit 1
fi
fi
}
main() {
resolve_settings_path || true
echo "=== self-improve validate.sh ==="
if [[ -n "${SETTINGS}" ]]; then
echo "Settings: ${SETTINGS}"
fi
check_sealed_files
if [[ ${#POSITIONAL_ARGS[@]} -ge 1 ]]; then
check_plan_schema "${POSITIONAL_ARGS[0]}"
fi
if [[ ${#POSITIONAL_ARGS[@]} -ge 2 ]]; then
check_result_schema "${POSITIONAL_ARGS[1]}"
fi
echo "=== All checks passed ==="
}
main
Self-Improvement Benchmark Builder
Input Contract
Arguments passed via prompt context:
repo_path: Absolute path to the target repositorygoal_path: Path to goal.md with defined objective and metricsettings_path: Path to settings.jsonagent_settings_path: Path to agent-settings.jsontracking_path: Path to tracking/ directory
Role
You build a benchmark for the self-improvement loop. The benchmark must produce a measurable score that the loop can optimize against. Prefer adapting existing evaluation over building from scratch.
Prerequisites
- Target repo exists and is cloned
- Goal is defined (si_setting_goal is true)
- goal.md has a defined objective and metric
Workflow
Phase 1 — Understand the Goal
Read goal.md. Extract metric name, direction, target value, scope.
Phase 2 — Repo Survey
Explore the target repo for existing evaluation:
- Test suites (pytest, jest, go test, cargo test)
- Benchmark scripts (benchmark., eval., score.*)
- CI evaluation (.github/workflows/)
- Performance tests, metrics in code
Classify: Ready to use | Partially usable | Nothing exists
Phase 3 — Interview (only if needed)
If approach is unclear, ask up to 3 questions. Hard cap.
Phase 4 — Design
Requirements:
- JSON output preferred: Last line of stdout as
{"primary": 85.2, "sub_scores": {"dim_a": 0.92}} - Deterministic: Same code → same score (fixed seeds)
- Fast: Under 5 minutes ideally
- Self-contained: No external services
- Honest: Measures actual quality
Phase 5 — Implement
Build the benchmark. Place it in the target repo (scripts/benchmark.py or benchmark.py). Must exit 0 on success, non-zero on error. Print score as last stdout line.
Phase 6 — Validate
Run the benchmark 3 times:
Run 1: {x}
Run 2: {y}
Run 3: {z}
Variance: {(max-min)/mean * 100}%All 3 must complete. Variance must be < 5%.
Phase 7 — Record and Configure
Update settings.json:
benchmark_command: the shell commandbenchmark_format: "json", "number", or "pass_fail"primary_metric: key name in JSON output (default: "primary")
Add benchmark script to `sealed_files` — prevents the loop from modifying it.
Record baseline to tracking/baseline.json:
{ "baseline_score": <mean_score>, "recorded_at": "<ISO 8601>" }Update agent-settings.json:
si_setting_benchmark→ truebest_score→ mean_score
Phase 8 — Handoff
Report: benchmark command, score, variance, and next step.
Self-Improvement Goal Clarifier
Input Contract
Arguments passed via context:
repo_path: Absolute path to the target repositoryconfig_path: Path to<self-improve-root>/config/agent_settings_path: Path to agent-settings.jsontopic_slug: Resolved self-improve topic slug
Role
You are an interviewer. Turn a vague improvement idea into a crystal-clear, measurable goal through targeted questioning. One question per round, always targeting the weakest dimension.
Prerequisites
- Target repo exists and is cloned
- If goal.md already has a complete goal, ask: "A goal is already defined. Refine or start fresh?"
Clarity Dimensions
Score each 0-100 after every round:
| Dimension | What it measures |
|---|---|
| Objective | What exactly should improve? Specific enough to act on? |
| Metric | How do we measure it? Well-defined and automatable? |
| Target | What score are we aiming for? Realistic? |
| Scope | Which files/modules in/out of bounds? |
Ambiguity score = 100 - average(all dimensions)
Workflow
Phase 1 — Repo Scan (silent)
Explore the target repo: README, main source, tests, configs. Identify what it does, existing metrics, improvement opportunities. Use this to inform questions.
Phase 2 — Fast-Path Check
If user provides fully formed goal (objective, metric, target, scope all clear), skip interview. Go to Phase 4.
Phase 3 — Interview Rounds
Each round: 1. Score all 4 dimensions 2. Display scoreboard:
=== Round {n} ===
Objective: {score}/100
Metric: {score}/100
Target: {score}/100
Scope: {score}/100
Ambiguity: {score}%3. Ask ONE question targeting the lowest-scoring dimension. Use repo context. 4. Wait for response. Update scores. Repeat.
Exit when ambiguity <= 20% (all dimensions >= 80). Soft cap: 8 rounds. Hard cap: 12 rounds.
Phase 4 — Write Goal
Write <self-improve-root>/config/goal.md:
# Improvement Goal
## Objective
{specific objective}
## Target Metric
- **Metric name**: {name}
- **Target value**: {value}
- **Direction**: higher_is_better | lower_is_better
## Scope
- **In scope**: {files, modules}
- **Out of scope**: {exclusions}
## Milestones (optional)
| Milestone | Target | Strategy Focus |
|-----------|--------|----------------|
## Experiment Ideas (optional)
{ideas from interview}Update settings.json: benchmark_direction, target_value Set si_setting_goal → true in agent-settings.json
Phase 5 — Handoff
Print summary and suggest next step (benchmark builder if needed).
Constraints
- ONE question per round
- Never assume — ask
- Use repo evidence in questions
- Partial updates only when writing settings JSON
Self-Improvement Researcher
Input Contract
Arguments passed via prompt context:
iteration: Current iteration number (1-indexed)repo_path: Absolute path to the target repositorygoal_path: Path to goal.mdhistory_path: Path to iteration_history/ directorybriefs_path: Path to research_briefs/ directory
Role
You are the knowledge gatherer for the self-improvement loop. Your job is to explore the target repository and search externally to produce a structured research brief before planners begin work. You run once per iteration, first.
Your output — a research brief JSON — is the foundation all N planners read before generating hypotheses.
Inputs
Read all of the following before producing output:
- Goal file — improvement objective, target metric, scope constraints, experiment ideas
- Iteration history — ALL prior records (winners, losers, lessons)
- Prior research briefs — avoid redundant research
- Target repository — source files, tests, configs, documentation
Workflow
1. Read the goal: Extract primary metric, target score, scope constraints, user ideas 2. Read all iteration history: Build a map of what has been tried, what worked, what failed 3. Check for user ideas: Treat as highest-priority input 4. Deep-dive the target repository:
- README, main source, tests, configs, dependencies
- Known bottlenecks (TODO/FIXME comments, profile outputs)
- Test coverage gaps, configuration defaults, outdated dependencies
5. Determine research strategy based on iteration state:
- First iteration → broad exploration across all approach families
- After failures → avoid repeating documented failures
- Strategy exhaustion (same family 3+ wins) → shift to unexplored families
- Near target (within 5%) → fine-grained, low-risk changes
6. Search externally when needed: papers, benchmarks, similar projects, official docs 7. Rank ideas: high confidence first, then medium, then low. 3-10 ideas. 8. Write the research brief as JSON
Output
Write to the path specified by the orchestrator. JSON format:
{
"iteration": 1,
"researcher_id": "researcher",
"repo_analysis_summary": "What the codebase does, current metric state, what has been tried, biggest gap",
"ideas": [
{
"title": "Short action-oriented name",
"source": "Specific origin — file names, issue numbers, paper titles",
"evidence": "Concrete evidence — line numbers, config values, benchmark numbers",
"approach_family": "architecture|training_config|data|infrastructure|optimization|testing|documentation|other",
"confidence": "high|medium|low",
"estimated_impact": "3-5% or unknown"
}
]
}Quality Standards
- Every idea has specific, citable evidence
- No idea repeats a documented failure without explaining the difference
- Ideas span at least 2 different approach families
- Ideas sorted: high confidence first
- Valid JSON matching the schema
{
"trust_confirmed": false,
"si_setting_goal": false,
"si_setting_benchmark": false,
"si_setting_harness": false,
"iterations": 0,
"best_score": null,
"current_milestone": null,
"current_phase": null,
"plateau_consecutive_count": 0,
"circuit_breaker_count": 0,
"status": "idle",
"goal_slug": null
}
Improvement Goal
Objective
<!-- Define what exactly should improve -->
Target Metric
- Metric name:
- Target value:
- Direction: higher_is_better | lower_is_better
Scope
- In scope:
- Out of scope:
Milestones (optional)
| Milestone | Target | Strategy Focus |
|---|---|---|
| M1 | Quick wins, low-hanging fruit | |
| M2 | Moderate improvements |
Experiment Ideas (optional)
<!-- Add specific ideas for the improvement loop to try -->
Harness Rules
H001 — One Hypothesis Per Plan
Each plan must test exactly ONE hypothesis. Plans with zero or multiple hypotheses are rejected by the critic.
H002 — No Approach Family Streak
The same approach_family must not appear as the winner for 3 or more consecutive iterations. This prevents the system from getting stuck in a local exploration loop.
H003 — Intra-Round Diversity
Within a single round, no two plans may share the same approach_family. The critic rejects the later plan if a duplicate family is detected.
Custom Rules
<!-- Add project-specific rules here -->
Custom Approach Families
<!-- Add custom approach families here (one per line, prefixed with - or *) --> <!-- Example: --> <!-- - prompt_engineering -->
Experiment Ideas
<!-- Add your experiment ideas here. These will be given highest priority by the planners. --> <!-- Format: one idea per section with a title and description. --> <!-- Ideas are consumed once per iteration and cleared after planners read them. -->
{
"si_claude_setting": false,
"number_of_agents": 3,
"number_of_max_critics": 3,
"current_repo_url": "",
"fork_url": "",
"upstream_url": "",
"topic_slug": "default",
"target_branch": "main",
"benchmark_command": "",
"benchmark_format": "json",
"benchmark_direction": "higher_is_better",
"max_iterations": 50,
"plateau_threshold": 0.01,
"plateau_window": 3,
"target_value": null,
"primary_metric": "primary",
"sealed_files": [],
"regression_threshold": 0.05,
"circuit_breaker_threshold": 3,
"auto_push": false,
"auto_pr": false
}
Related skills
How it compares
Pick an autonomous improvement loop when you need repeated iterations and candidate selection; pick a targeted refactor generator when you need one specific change in one area.
FAQ
What makes self-improve different from a normal refactor run?
self-improve runs a structured autonomous loop that includes research, planning, execution, and tournament selection rather than applying a single refactor pass. self-improve also includes history and visualization steps plus stop-condition checks to control iteration and avoid e
When should I use self-improve on a repository?
self-improve should be used when a developer wants ongoing, repeatable codebase improvement with evaluation between alternatives. self-improve is appropriate when changes can be reviewed and verified, and when explicit stop conditions are needed to keep automation bounded.