run
SkillFiles & storageSustained metric-improvement loop with atomic commits, auto-rollback, and experiment logging. Iterates with specialist agents, commits atomically, auto-rolls back on regression. Accepts a program.md file path. Supports --resume, --team, --colab, --codex, --researcher, --architect, --journal, --hypothesis.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the run skill
What this skill tells your AI
The instructions your AI receives, as published by borda/ai-rig in plugins/cc_research/skills/run/SKILL.md and read by ahel’s review.
Sustained metric-improvement loop — reads program.md, iterates specialist ideation agents, commits atomically, auto-rolls back on regression. For long-running automated improvement campaigns.
NOT for: methodology validation before run (use /research:judge); hypothesis generation (use research:scientist agent); one-off feature work (use /develop:feature).
Campaign mode only:
MAX_ITERATIONS: 50 (hard cap); DEFAULT 20 when max_iterations unset in program.md; program.md may raise up to 50; values above 50 clamped to 50 with a warning
MAX_CODEX_RUNS: 10 (cost ceiling for --codex Phase 2c — disable Codex once exceeded)
STUCK_THRESHOLD: 5 consecutive discards → escalation
GUARD_REWORK_MAX: 2 attempts before revert
VERIFY_TIMEOUT_SEC: 120 (local), 300 (--colab)
COLAB_KNOWN_HW: H100, L4, T4, A100
SUMMARY_INTERVAL: 10 iterations
DIMINISHING_RETURNS_WINDOW: 5 iterations < 0.5% each → warn user and suggest stopping
STATE_DIR: .experiments/state/<run-id>/ (timestamped dir per run — see .claude/rules/foundry-artifact-lifecycle.md)
SENTINEL_SLUG_FORMULA: |
eval "$(bash "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/git_slugs.sh")"
# Sentinel path: ${TMPDIR:-/tmp}/claude-commit-auth-${REPO_SLUG}-${BRANCH_SLUG} # tmpdir-exempt: user-shell-boundary
# Bash state is lost between tool calls — re-source git_slugs.sh at each use site; it is the only authorized slug form.
Agent strategy mapping (agent_strategy in config → ideation agent to spawn):
agent_strategy | Specialist agent | When to use |
|---|---|---|
auto | heuristic | Default — infer from metric_cmd keywords |
perf | foundry:perf-optimizer | latency, throughput, memory, GPU utilization |
code | foundry:sw-engineer | coverage, complexity, lines, coupling |
ml | research:scientist | accuracy, loss, F1, AUC, BLEU |
arch | foundry:solution-architect | coupling, cohesion, modularity metrics |
Auto-inference keyword heuristics (when agent_strategy: auto or omitted; checked against ## Goal text AND metric command):
Precedence order (first match wins; ML keywords beat test-framework keywords). ML-specific compound terms (not bare tokens) required — prevents over-triggering on eval/train/val as common words:
- contains
accuracy,loss(paired withtrain_loss/val_loss/eval_loss),f1_score,auc_roc,auroc,train_step,val_acc,eval_loss,epoch,gradient,tensor,overfit,generaliz,regulariz,validation,dropout,weight_decay,lr_schedule,cross_val,precision,recall, OR explicit--scientistflag →ml→research:scientist - contains
time,latency,bench,throughput,memory→perf→foundry:perf-optimizer - contains
pytest,coverage,complexity→code→foundry:sw-engineer - no keyword match →
perf(default fallback) — WARN: print⚠ No keyword match — defaulting to 'perf' strategy. If this is an ML task, set agent_strategy: ml in program.md.Log resolved agent + reason in state.jsonstrategy_resolution.
Bare tokens eval, train, val (without compound suffix) do NOT trigger ml routing — too common in non-ML contexts (test eval scripts, training-environment configs, validator command names).
Stuck escalation sequence (at STUCK_THRESHOLD consecutive discards):
-
Switch agent type. Rotation by current strategy:
Current strategy Next strategy Escalation agent codemlresearch:scientistmlperffoundry:perf-optimizerperfcodefoundry:sw-engineerarchcodefoundry:sw-engineer(fallbackfoundry:solution-architectif sw-engineer unavailable)autoinfer from resolved strategy follow rotation row for whichever concrete strategy autoheuristics resolved to at Step R3 (e.g.auto→ resolvedml→ nextperf→foundry:perf-optimizer) -
Spawn 2 agents parallel, competing strategies; each writes full analysis to
.experiments/state/<run-id>/stuck-escalation-<i>-<agent-type>.md, returns ONLY compact JSON envelope. Use this spawn prompt verbatim (substitute<run-id>,<i>, and strategy):Stuck-escalation handoff — iteration <i> after STUCK_THRESHOLD consecutive discards. Read `.experiments/state/<run-id>/state.json` for goal, best_metric, baseline, config. Read `.experiments/state/<run-id>/experiments.jsonl` for full iteration history. Read `.experiments/state/<run-id>/diary.md` for qualitative context (what was tried, why reverted). Read `.experiments/state/<run-id>/context-<i>.md` for current iteration's context block. Continue from the last completed iteration (do NOT restart from iteration 0). Write your full analysis and proposed change to `.experiments/state/<run-id>/stuck-escalation-<i>-<your-strategy>.md`. Write a resume point to `.experiments/state/<run-id>/resume.json`: {iteration: <i>, strategy: "<your-strategy>", proposed_change: "<one-line description>"}. Return ONLY: {"strategy":"<your-strategy>","description":"...","files_modified":[...],"confidence":0.N,"file":".experiments/state/<run-id>/stuck-escalation-<i>-<your-strategy>.md"}Consolidation: pick whichever returns delta ≥ 0.1% AND guard pass; if both qualify, pick higher delta.
-
Stop, report progress, surface to user — no blind looping
- Key boundary: end of each Phase 8 in R5 iteration loop — JSONL record appended and
state.jsonupdated. Overwrite each iteration; contract always reflects latest in-progress state. Long metric-improvement loops are the primary auto-compact risk. - Preserve at each boundary: RUN_ID (TMPDIR key), STATE_DIR path, program.md path, current iteration#, best metric, best-commit SHA, experiments.jsonl path.
- Clear at R1 start (stale prior run) and after R6/R7 campaign completion.
Agent Resolution
Agent resolution: load and follow the protocol below. Contains: foundry check + fallback table. If foundry not installed: use table to substitute each foundry:X with general-purpose. Agents this skill uses: foundry:sw-engineer, foundry:linting-expert, foundry:perf-optimizer, foundry:solution-architect, research:scientist.
# loads: compaction-contract.md
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
_RESEARCH_SHARED=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/resolve_shared.py" 2>/dev/null) # timeout: 5000
[ -z "$_RESEARCH_SHARED" ] && { echo "! Plugin path resolution failed — ensure research plugin installed and CLAUDE_PLUGIN_ROOT set, or invoke from project root."; exit 1; }
echo "$_RESEARCH_SHARED" > "${TMPDIR:-/tmp}/research-shared-${CSID}" # cold resolve — every later site reads this sentinel instead of re-running python
cat "$_RESEARCH_SHARED/agent-resolution.md"
CLAUDE_SKILL_DIR resolution — constants block provides default plugins/cc_research/skills/run (source-tree path). Resolve to installed path before use:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
CLAUDE_SKILL_DIR=$(ls -td ~/.claude/plugins/cache/borda-ai-rig/research/*/skills/run 2>/dev/null | head -1)
[ -z "$CLAUDE_SKILL_DIR" ] && CLAUDE_SKILL_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/plugins/cc_research/skills/run"
echo "$CLAUDE_SKILL_DIR" > "${TMPDIR:-/tmp}/research-run-skill-dir-${CSID}"
Default Mode (Steps R1–R7)
Triggered by run <goal|file.md>.
Task tracking: create tasks R0–R7 at start. If no --researcher/--architect, mark R0 skipped. If --codex active, create task R5b: Codex co-pilot (iter ?/max) status pending.
Step R0: Hypothesis pre-phase (--researcher / --architect)
If no --researcher/--architect, skip to R1.
Flag combination note: every oracle self-annotates feasibility (feasible/blocker/codebase_mapping are part of the oracle schema — no separate annotation spawn). --researcher alone, --architect alone, and both together are all valid; both together adds architectural hypotheses alongside the research ones.
Follow modes/hypothesis-pipeline.md:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLAUDE_SKILL_DIR < "${TMPDIR:-/tmp}/research-run-skill-dir-${CSID}" 2>/dev/null || CLAUDE_SKILL_DIR=""
cat "$CLAUDE_SKILL_DIR/modes/hypothesis-pipeline.md" # timeout: 5000
Per-iteration hypothesis selection (when --researcher/--architect set, inside R5 loop): pop next from RESEARCH_QUEUE. Append to Phase 2 prompt: "Focus this iteration on testing this hypothesis: <hypothesis text>."
Per-iteration journal hook (inside R5, after Phase 7): if --journal active, append entry to <RUN_DIR>/journal.md after EVERY iteration — regardless of outcome. Entry format: protocol.md (companion file, same skill dir). # loads: protocol.md Journals record kept and reverted iterations so ideation agent learns failed approaches.
Per-iteration checkpoint write (after Phase 7): if --researcher/--architect active, append one line to <RUN_DIR>/checkpoint.json per schema in protocol.md (companion file, same skill dir): {iteration, hypothesis_id, metric_before, metric_after, status: "passed"|"rolled_back"}.
Step R1: Load / build config
--resume flag detection: if --resume in args, extract optional program.md path. Jump to ## Resume Mode. Rest of R1 and R2–R7 skipped.
--hypothesis <path> parsing: if --hypothesis in args, extract path token following it. Verify file exists: [ -f "$HYPOTHESIS_PATH" ]. If not found: print ! --hypothesis <path>: file not found and stop. If found: set hypothesis_override = true. In R5 Phase 2 (Propose change), replace oracle-generated hypothesis with loaded file content — prepend to ideation agent prompt: "Use this pre-specified hypothesis as your starting hypothesis for iteration N: . Validate, refine, and implement it. Do not generate a new hypothesis from scratch."
Auto-detect: first non-flag arg ends in .md → parse as program file. Otherwise → text goal.
Clarification prompt (.md file only): after extracting .md path, inspect next token (before -- flags):
- Absent or starts with
--→clarification_prompt = null - Quoted string (starts/ends with
") → extract asclarification_prompt, strip quotes - Bare unquoted token (no
--, no") → accept asclarification_prompt; print:ℹ clarification set to "<token>" (tip: quote multi-word hints — e.g. "/research:run program.md \"focus on sort\" --codex")
After clarification extraction, remaining non-flag tokens (not starting --) are unrecognized. For each, print:
⚠ Unrecognized argument "<token>" — ignored.
Known positional args: <program.md path> [clarification]
Known flags: --resume <program.md>, --team, --compute=local|colab|docker, --colab[=HW], --codex, --researcher, --architect, --journal, --hypothesis <path>, --scientist, --codemap, --no-codemap, --keep "<items>"
If you meant to override the algo, edit the ## Config block in your program.md (algo: sort) and update ## Metric to match.
If you meant to set a clarification hint, pass it as a quoted string: "/research:run program.md \"sort improvements\" --codex"
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# runs under bash — zsh never populates ${BASH_REMATCH[1]}, so --keep "..." was silently resolving empty
python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/extract-keep-flag.py" research-run "$ARGUMENTS" # timeout: 5000 — parses --keep, clears a stale contract, persists for Phase 8
Unsupported flag check: load and follow the protocol below. Supported flags for this skill: --resume, --team, --compute, --colab, --codex, --researcher, --architect, --journal, --hypothesis, --scientist, --codemap, --no-codemap, --keep.
# loads: unsupported-flag-protocol.md
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r _RESEARCH_SHARED < "${TMPDIR:-/tmp}/research-shared-${CSID}" 2>/dev/null || _RESEARCH_SHARED="" # warm read (Check 41)
cat "$_RESEARCH_SHARED/unsupported-flag-protocol.md"
Codemap auto-detection — structural blast-radius context for modules the experiment edits; on by default when codemap installed + index found. --no-codemap opts out; --codemap is strict (fail if unavailable).
# timeout: 5000
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
# writes true/false to research-run-codemap-enabled-${CSID}; strict mode exits 1 (already printed ! BLOCKED) if unavailable
CODEMAP_RAW=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/codemap-flag.py" research-run "$ARGUMENTS") || exit 1
loads: codemap-gates.md
When CODEMAP_RAW ≠ off:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r _RESEARCH_SHARED < "${TMPDIR:-/tmp}/research-shared-${CSID}" 2>/dev/null || _RESEARCH_SHARED="" # warm read (Check 41)
cat "$_RESEARCH_SHARED/codemap-gates.md"
Follow Gate A and Gate B.
If argument is a .md file — read and parse with these rules:
- Find each
## <Section>heading (case-insensitive). - Extract first fenced code block following that heading.
- Parse block as
key: valuelines; multi-value = indented- valueitems. Paths with spaces: wrap in double quotes. - Missing required fields (
commandunder## Metric/## Guard) → stop with error. agent_strategy: auto(or omitted) → apply keyword heuristics from<constants>to## Goaltext and metric command.targetunder## Metric:direction: higher→ stop when metric ≥ target;direction: lower→ stop when metric ≤ target. Iftargetomitted, run untilmax_iterations.- Unrecognized keys/headings → warn once, ignore.
## Notesand# Program:title never parsed — human-only. (# Campaign:accepted as alias.)
If argument is text — auto-detect metric_cmd/guard_cmd from goal string and codebase scan (same as P-P1, non-interactive). config.json not read.
--colab[=HW] parsing: --colab (no =) → compute = "colab", colab_hw = null. --colab=<value> → compute = "colab", colab_hw = <value> (uppercased). Unknown <value> (not in {H100, L4, T4, A100}) → print "⚠ Unknown Colab hardware '<value>' — proceeding with default GPU. Known: H100, L4, T4, A100", set colab_hw = null. --compute=colab (no HW) → compute = "colab", colab_hw = null.
colab_hw in ## Config sets hardware preference (H100, L4, T4, A100); CLI --colab=HW overrides.
Generate run-id = $(date -u +%Y-%m-%dT%H-%M-%SZ). Assign immediately:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
RUN_ID=$(date -u +%Y-%m-%dT%H-%M-%SZ)
RUN_DIR=".experiments/${RUN_ID}" # hypothesis pipeline + journal outputs (per <constants> note)
STATE_DIR=".experiments/state/${RUN_ID}" # per-iteration artifacts (state.json, experiments.jsonl, diary.md)
mkdir -p "$RUN_DIR" "$STATE_DIR" # timeout: 5000 — both dirs created before any Write to either
echo "$RUN_ID" > "${TMPDIR:-/tmp}/research-run-id-${CSID}" # persist for Phase 8 contract write (Check 41: fresh shell)
Note: STATE_DIR (.experiments/state/${RUN_ID}/) is per-iteration artifact dir — distinct from RUN_DIR. Both coexist; see <constants> block.
Create run directory:
.experiments/state/<run-id>/
state.json ← iteration count, best metric, status
experiments.jsonl ← one line per iteration
diary.md ← human-readable research diary (hypothesis → outcome → decision)
Convert program_file to absolute path: realpath "$PROGRAM_FILE" — Resume Mode matches on absolute path.
Write initial state.json (program_file = absolute path to .md or null for text goal):
{
"run_id": "<run-id>",
"goal": "<goal>",
"config": {},
"program_file": "<absolute path to program.md, or null>",
"iteration": 0,
"best_metric": null,
"best_commit": null,
"status": "initializing",
"started_at": "<ISO timestamp>",
"clarification_prompt": null,
"colab_hw": null,
"sandbox_mode": "local"
}
Note: status is "initializing" until all R2 precondition checks pass — resume treats "initializing" as failed-init, not active run. Update to "running" at end of R2 (after all checks pass).
Step R2: Precondition checks
Run all checks before touching code. Fail fast with clear message:
- Clean git:
git status --porcelain→ must be empty. If dirty: print dirty files and stop. - Not detached HEAD:
git rev-parse --abbrev-ref HEAD→ must not beHEAD. - Metric command numeric: run
metric_cmdonce; parse stdout for float. If no float: show output and stop. - Guard passes: run
guard_cmdonce; must exit 0. If fails: show output and stop. --colabcheck: verifymcp__colab-mcp__runtime_execute_codeavailable. If not, print setup instructions (see Colab MCP section) and stop. If--colab=HW(colab_hwnon-null): print:Hardware requested: --colab=<colab_hw>. Ensure your Colab notebook running with <colab_hw> GPU.--codexcheck: distinguish the installed-and-enabled bridge target from absence.claudenot onPATH→ print⚠ 'claude' CLI not in PATH — bridge availability cannot be verified.and stop. Ifclaude plugin listlacksbridge@borda-ai-rig, print⚠ bridge@borda-ai-rig not installed. Install it from the Borda AI Rig marketplace.and stop. If it is disabled, print⚠ bridge@borda-ai-rig is disabled. Enable it and reload plugins.and stop.compute: dockercheck: rundocker psvia Bash (timeout: 5000). If non-zero: print⚠ Docker daemon not running. Start Docker Desktop and retry.and stop.- Flag conflict: if
--colaband--compute=dockerboth active: print⚠ --colab and --compute=docker are mutually exclusive. Use one or the other.and stop. --colab+--codexcompatibility note (non-blocking): if both flags active, printℹ --colab + --codex active: Codex Phase 2c will receive colab_hw context so generated code can target the right GPU (H100/T4 bf16 vs fp16). Phase 5 metric verification runs through Colab MCP as usual.and continue. Passcolab_hwto Codex spawn prompt (Phase 2c — seemodes/codex-copilot.md).--journalprerequisite: verify--researcher/--architectalso set. If neither: print⚠ --journal requires --researcher or --architect — omit --journal or add a hypothesis pipeline flag.and stop.
--codex-delegation warning (non-blocking): codex-delegation.md ships inside this plugin's own skills/_shared/, so R7 needs no other plugin installed. Verify it resolves:
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r _RESEARCH_SHARED < "${TMPDIR:-/tmp}/research-shared-${CSID}" 2>/dev/null || _RESEARCH_SHARED="" # warm read (Check 41)
[ -f "$_RESEARCH_SHARED/codex-delegation.md" ] || echo "⚠ codex-delegation.md not found under $_RESEARCH_SHARED — R7 Codex delegation will be skipped; reinstall the research plugin."
Set CODEX_DELEGATION_AVAILABLE=true if found, false otherwise. Continue regardless.
Initialize sandbox + timeout variables (after all checks pass — constants YAML block not auto-exported to bash; assign explicitly with ${VAR:-default} to honour environment overrides; ADV-L15 / ADV-M20):
SANDBOX_NETWORK="${SANDBOX_NETWORK:-none}" # override via program.md Config or environment variable
# Verify timeout — 120s local, 300s Colab per <constants>; bash overrides via VERIFY_TIMEOUT_SEC env var
if [ "${compute:-local}" = "colab" ]; then
VERIFY_TIMEOUT_SEC="${VERIFY_TIMEOUT_SEC:-300}"
else
VERIFY_TIMEOUT_SEC="${VERIFY_TIMEOUT_SEC:-120}"
fi
VERIFY_TIMEOUT_MS=$((VERIFY_TIMEOUT_SEC * 1000))
# Ideation Agent() calls are synchronous — no mid-flight poll; after each returns, check its output file and mark timed_out (⏱) if empty.
Initialize sandbox_mode:
compute: docker(daemon check passed in step 7) →sandbox_mode = "docker". Print:sandbox: Docker daemon reachable — sandbox mode active- All other cases (
compute: local,compute: colab) →sandbox_mode = "local"
Update state.json status to "running" — write only after ALL checks above pass. Resume treats "initializing" as failed-init and skips such runs.
Step R3: Select ideation agent
Apply agent_strategy mapping from <constants>. If auto, apply keyword heuristics to metric_cmd. Log selected agent to state.json.
Step R4: Establish baseline (iteration 0)
Run metric_cmd and guard_cmd. Parse metric value. Append to experiments.jsonl:
{
"iteration": 0,
"commit": "<HEAD sha>",
"metric": 0.0,
"delta": 0.0,
"guard": "pass",
"status": "baseline",
"description": "baseline",
"agent": null,
"confidence": null,
"timestamp": "<ISO>",
"files": []
}
Update state.json: best_metric = <baseline>, best_commit = <HEAD sha>.
Print: Baseline: <metric_cmd key> = <value>.
Write initial diary header to .experiments/state/<run-id>/diary.md:
# Research Diary — <goal>
**Run**: <run-id>
**Started**: <ISO timestamp>
**Baseline**: <metric_key> = <baseline value>
---
Then proceed to R5.
Step R5: Iteration loop
# REPO_SLUG / BRANCH_SLUG: source the single authorized slug form (see <constants>)
eval "$(bash "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/git_slugs.sh")" # timeout: 3000
COMMIT_SENTINEL="${TMPDIR:-/tmp}/claude-commit-auth-${REPO_SLUG}-${BRANCH_SLUG}" # tmpdir-exempt: user-shell-boundary
touch "$COMMIT_SENTINEL" # timeout: 3000
# trap doesn't survive across Bash calls — commit-guard.js hook (foundry-owned) handles protection instead
Dependency —
commit-guard.js(requiresfoundryplugin): the commit-sentinel dance above (touch at R5, re-touch each phase,rmat cleanup) is enforced by foundry'scommit-guard.jsPreToolUsehook. That hook ships with thefoundryplugin only — research does not bundle it. Standalone install (foundry absent): the sentinel touches become inert andgit commitproceeds unguarded. The sentinel logic is still safe to run (touch/rmon a temp file are harmless no-ops without the hook); it simply provides no protection. If you rely on atomic-commit guarding duringresearch:run, installfoundry.
Sentinel liveness: touch $COMMIT_SENTINEL after each Phase 8 result write to extend monitoring window — do NOT rely solely on sentinel touched at loop start; slow iterations exceed 15-min TTL. Re-derive slug per SENTINEL_SLUG_FORMULA from <constants> (bash state lost between calls).
--team mode: If --team active, follow modes/team.md and execute Phases A–D in place of standard iteration loop below.
export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
IFS= read -r CLAUDE_SKILL_DIR < "${TMPDIR:-/tmp}/research-run-skill-dir-${CSID}" 2>/dev/null || CLAUDE_SKILL_DIR=""
cat "$CLAUDE_SKILL_DIR/modes/team.md" # timeout: 5000
--team + --hypothesis combination: combinable. Team mode uses provided hypothesis path and skips oracle/hypothesis-generation phase — hypothesis_override = true applies inside team.md Phase A same as solo mode.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 27
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
run-borda- Source
- github.com/borda/ai-rig