run

SkillFiles & storage

Sustained 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.

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_strategySpecialist agentWhen to use
autoheuristicDefault — infer from metric_cmd keywords
perffoundry:perf-optimizerlatency, throughput, memory, GPU utilization
codefoundry:sw-engineercoverage, complexity, lines, coupling
mlresearch:scientistaccuracy, loss, F1, AUC, BLEU
archfoundry:solution-architectcoupling, 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 with train_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 --scientist flag → mlresearch:scientist
  • contains time, latency, bench, throughput, memoryperffoundry:perf-optimizer
  • contains pytest, coverage, complexitycodefoundry: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.json strategy_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):

  1. Switch agent type. Rotation by current strategy:

    Current strategyNext strategyEscalation agent
    codemlresearch:scientist
    mlperffoundry:perf-optimizer
    perfcodefoundry:sw-engineer
    archcodefoundry:sw-engineer (fallback foundry:solution-architect if sw-engineer unavailable)
    autoinfer from resolved strategyfollow rotation row for whichever concrete strategy auto heuristics resolved to at Step R3 (e.g. auto → resolved ml → next perffoundry:perf-optimizer)
  2. 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.

  3. 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.json updated. 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 as clarification_prompt, strip quotes
  • Bare unquoted token (no --, no ") → accept as clarification_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_RAWoff:

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:

  1. Find each ## <Section> heading (case-insensitive).
  2. Extract first fenced code block following that heading.
  3. Parse block as key: value lines; multi-value = indented - value items. Paths with spaces: wrap in double quotes.
  4. Missing required fields (command under ## Metric/## Guard) → stop with error.
  5. agent_strategy: auto (or omitted) → apply keyword heuristics from <constants> to ## Goal text and metric command.
  6. target under ## Metric: direction: higher → stop when metric ≥ target; direction: lower → stop when metric ≤ target. If target omitted, run until max_iterations.
  7. Unrecognized keys/headings → warn once, ignore.
  8. ## Notes and # 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:

  1. Clean git: git status --porcelain → must be empty. If dirty: print dirty files and stop.
  2. Not detached HEAD: git rev-parse --abbrev-ref HEAD → must not be HEAD.
  3. Metric command numeric: run metric_cmd once; parse stdout for float. If no float: show output and stop.
  4. Guard passes: run guard_cmd once; must exit 0. If fails: show output and stop.
  5. --colab check: verify mcp__colab-mcp__runtime_execute_code available. If not, print setup instructions (see Colab MCP section) and stop. If --colab=HW (colab_hw non-null): print: Hardware requested: --colab=<colab_hw>. Ensure your Colab notebook running with <colab_hw> GPU.
  6. --codex check: distinguish the installed-and-enabled bridge target from absence. claude not on PATH → print ⚠ 'claude' CLI not in PATH — bridge availability cannot be verified. and stop. If claude plugin list lacks bridge@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.
  7. compute: docker check: run docker ps via Bash (timeout: 5000). If non-zero: print ⚠ Docker daemon not running. Start Docker Desktop and retry. and stop.
  8. Flag conflict: if --colab and --compute=docker both active: print ⚠ --colab and --compute=docker are mutually exclusive. Use one or the other. and stop.
  9. --colab + --codex compatibility 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. Pass colab_hw to Codex spawn prompt (Phase 2c — see modes/codex-copilot.md).
  10. --journal prerequisite: verify --researcher/--architect also 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 (requires foundry plugin): the commit-sentinel dance above (touch at R5, re-touch each phase, rm at cleanup) is enforced by foundry's commit-guard.js PreToolUse hook. That hook ships with the foundry plugin only — research does not bundle it. Standalone install (foundry absent): the sentinel touches become inert and git commit proceeds unguarded. The sentinel logic is still safe to run (touch/rm on a temp file are harmless no-ops without the hook); it simply provides no protection. If you rely on atomic-commit guarding during research:run, install foundry.

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