forge
Forge Core protocol plus Claude Code and Codex/manual drivers for task-driven looping with KPI guardrails. Tracks coverage/speed/quality with baselines, rotates strategies on stagnation, derives or records success criteria for the task itself, and uses fresh-context evaluation. Activates when user mentions "forge it", "forge loop", "quality loop", "kpi loop", "improvement loop".
The Forge — Core Protocol Plus Claude Code and Codex Drivers
A structured, task-driven improvement protocol with KPI guardrails. Forge tracks coverage/speed/quality with baselines and targets, derives or records success criteria for the task itself, evaluates with fresh-context audits, rotates strategies when stagnating, and records lessons across iterations.
Built on the Ralph Wiggum loop pattern (Geoff Huntley), informed by Karpathy's autoregressive philosophy, pi-autoresearch's measurement discipline, and SICA's compounding iteration approach.
Activation Triggers
- "forge it", "forge this", "forge loop"
- "quality loop", "kpi loop", "improvement loop"
- "codebase improvement loop"
- When user wants structured, KPI-driven autonomous improvement
Architecture
Forge Core
├── Protocol phases (A through H)
├── State format and KPI model
├── Strategy selection + stagnation logic
└── Fresh-context evaluation expectations
Claude Code driver
├── /forge command
├── /forge-cancel command
├── .claude/forge-state.SESSION.md
├── .claude/forge-loop.SESSION.local.md
└── Stop hook re-injects prompt on session exit
Each iteration (one OODA cycle):
├── A. ORIENT — Read forge-state, understand position + trends
├── B. MEASURE — Run tests, capture KPIs
├── C. EVALUATE — Every 3rd iteration: fresh-context reality-check subagent
├── D. DECIDE — Pick strategy + target from KPI gaps + findings
├── E. EXECUTE — Apply ONE focused transformation
├── F. VERIFY — Run tests, re-measure KPIs
├── G. RECORD — Update forge-state with deltas + lessons
└── H. COMPLETE — Task success + KPI guardrails satisfied? → FORGE_COMPLETE
Driver model
Forge has two layers:
- Forge Core — portable protocol, state model, KPI semantics, strategies, and completion rules
- Success contract — task objective plus explicit or derived completion checks
- Driver — runtime-specific integration that launches the loop, persists state, and handles pause/continue mechanics
Forge currently ships two first-class drivers:
- Claude Code — command, agent, and stop-hook integration are bundled here
- Codex —
forge-init,forge-continue,forge-cancel, andforge-statusmanage a manual loop with project-local state
Other environments can reuse Forge Core manually, but should not be described as officially supported unless they ship a real driver.
The Forge Protocol
A. ORIENT — Read State
Session ID: Each forge loop gets a unique session ID: MMDD-HHMM-SLUG where MMDD-HHMM is the current timestamp and SLUG is 2-3 words from the task (e.g., 0406-1530-djinnchat-primefix). Generate this on the first iteration and reuse it every subsequent iteration.
State file path (substitute your session ID for {sid}):
- Claude Code:
.claude/forge-state.{sid}.md - Codex:
.codex/forge/forge-state.{sid}.md
On first iteration: no state file exists yet. Generate the session ID, proceed to MEASURE, and create the state file during RECORD.
On subsequent iterations: read the state file matching your session ID. Never read or write a different session's state file — concurrent forge loops in the same repo are supported and must remain isolated.
- Parse baseline KPIs, targets, iteration history, current strategy
- Parse the success contract:
task: the primary requested workdone_when: explicit success override, if providedcompletion_checks: concrete checks derived from task + override
- If
completion_checksis empty, derive them before EXECUTE and persist them in forge-state - Check
stagnation_count— if >= 3, MUST rotate strategy - Review lessons from previous iterations (avoid repeating failures)
B. MEASURE — Capture Current KPIs
Run your test suite with coverage enabled and parse output for:
- coverage: percentage from cover summary
- speed_seconds: total test time
- tests: total test count
- failures: failure count
- warnings: compiler warning count (optional, first run)
Elixir: mix test --cover 2>&1
Python: pytest --cov --cov-report=term 2>&1
JavaScript: npm test -- --coverage 2>&1
Ruby: bundle exec rspec 2>&1 (with SimpleCov)
Go: go test -cover ./... 2>&1
First iteration: record as baseline in forge-state. Subsequent: compute deltas from previous iteration AND from baseline.
C. EVALUATE — Fresh-Context Reality Check
When: iteration 1 AND every 3rd iteration thereafter (1, 4, 7, 10...).
Spawn a fresh-context audit using an agent or persona available in your environment (for example a code reviewer, security auditor, or refactorer) with:
- ONLY the scope files/modules
- Prompt: "Audit [scope]. Report findings by severity (high/medium/low). No context about KPI targets or iteration state."
- NO forge state, NO iteration context, NO KPI targets — unbiased evaluation
Main agent reviews findings critically against its KPI targets. Record findings count by severity in forge-state.
Success model
Forge has two goal layers:
- Task success — what the requested work must make true
- KPI guardrails — tests, coverage, speed, quality, and no critical regressions
If the user gives only open text scope, derive concrete completion_checks from
that task and record them in forge-state.
If the user provides --done-when, treat that as the explicit override and
derive any additional clarifying completion_checks from it.
Do not mark Forge complete just because the KPIs improved. The task itself must be honestly done.
D. DECIDE — Pick Strategy
The Simplicity Criterion
All else being equal, simpler is better. When evaluating whether to keep a change:
- Improvement + ugly complexity → probably discard
- Improvement from DELETING code → definitely keep
- No metric change + simpler code → keep (that's a simplification win)
- Marginal gain from complexity → reject
This prevents complexity ratchet. Code removal for equivalent performance is always a win.
Available Strategies
| Strategy | When to use | Typical impact |
|---|---|---|
component-extraction | DRY violations, repeated patterns | Coverage + quality |
refactor-for-testability | Code hard to test (private, coupled) | Coverage |
coverage-push | Clear coverage gaps, uncovered modules | Coverage |
speed-optimization | async:false overuse, slow fixtures, redundant DB | Speed |
dead-code-removal | Unused code flagged by reality-check | Quality + coverage |
quality-polish | Naming, complexity, clarity issues | Quality |
design-system | Duplicated UI patterns, status badges | Quality + coverage |
simplification | Complex code that can be made simpler | Quality (+ coverage if tests improve) |
Selection Logic
-
Compute normalized KPI gap for each target:
- coverage_gap = (target - current) / target
- speed_gap = (current - target) / current (inverted — lower is better)
- quality_gap = high_findings / 5 (scale 0..1)
-
Largest gap gets priority in strategy selection:
- coverage_gap largest →
coverage-pushorrefactor-for-testability - speed_gap largest →
speed-optimization - quality_gap largest →
component-extractionordead-code-removal
- coverage_gap largest →
-
High findings from reality-check → immediate
component-extractionorrefactor-for-testability -
Stagnation (stagnation_count >= 3):
- Pick best historical delta strategy OR untried strategy
- Log: "Strategy '{current}' stagnating, switching to '{new}'"
- Reset stagnation_count
-
Never repeat a strategy that yielded negative deltas without changing approach
E. EXECUTE — ONE Focused Change
Each iteration does ONE thing well:
- Structural refactoring → use an available refactoring agent, or do the focused change directly
- Clarity/polish → use an available simplification/review agent, or do the focused change directly
- Coverage gaps → write tests + refactor for testability
- Speed optimization → convert sync to async tests, consolidate fixtures, reduce DB hits
- Dead code removal → delete unused code flagged by evaluation
- Design system → extract shared components (badges, cards, indicators)
- Simplification → delete dead code, reduce abstractions, flatten indirection
Use fresh-context agents when they are available and helpful; otherwise keep the change focused and do it directly.
F. VERIFY — Tests Must Be Green
Run tests — must be green before proceeding. If red: debug and fix within this iteration. Do NOT proceed to RECORD with failures. Re-measure with coverage to capture post-change KPIs.
G. RECORD — Update Forge State (THE Autoregressive Step)
Update the Forge state file for the current driver (using your session ID {sid}):
- Claude Code:
.claude/forge-state.{sid}.md - Codex:
.codex/forge/forge-state.{sid}.md
-
Append iteration entry with:
- Iteration number
- KPIs before and after (with deltas)
- Strategy used
- Actions taken (brief)
- Success contract progress or refinement if it changed
- Findings count (if evaluation ran)
- Lesson learned
-
Update strategy tracking:
- Add iteration to current strategy's history
- Record coverage_delta and speed_delta for the strategy
-
Stagnation detection:
- Coverage delta < 0.1% for 2 consecutive iterations → increment stagnation_count
- Any improvement > 0.1% → reset stagnation_count to 0
-
Git commit if tests green AND any improvement:
- Stage changed files (NOT forge-state, it's in .claude/)
- Commit with:
forge(N): [strategy] — [brief description]
-
Clean revert if tests red or KPIs regressed AND no commit:
- Revert only the files changed in the current iteration
- If you cannot identify that set safely, stop and leave unrelated local work untouched
- Record what was attempted in the iteration log (even failed attempts inform future decisions)
-
Ideas backlog — if the iteration surfaced promising but deferred opportunities:
- Add to the
ideaslist in forge-state frontmatter - On future iterations, review backlog for combination opportunities
- Add to the
H. COMPLETE — All Targets Met?
Check ALL conditions simultaneously:
- the recorded task success contract is satisfied
- coverage >= min_coverage target
- speed_seconds <= max_speed_seconds target
- failures == 0
- high_findings == 0 (from last evaluation)
If ALL met → output FORGE_COMPLETE on its own line
If not → exit normally (stop hook re-injects prompt for next iteration)
Stagnation Protocol
if stagnation_count >= 3:
1. Log: "Strategy '{current}' stagnating after {N} low-delta iterations"
2. Rank strategies by historical effectiveness (coverage_delta / iterations)
3. Pick: best historical strategy OR untried strategy
4. Reset stagnation_count to 0
5. Record lesson: "'{old}' exhausted after iterations [X,Y,Z], switching to '{new}'"
Getting Unstuck
When stagnation triggers (or when you run out of ideas within a strategy):
- Re-read scope files — fresh eyes find new angles
- Review the ideas backlog — deferred opportunities may be ripe now
- Combine near-misses — two changes that individually didn't help may compound
- Try the inverse — if adding X didn't help, try removing it (or vice versa)
- Think harder — don't stop and ask. Read related code, look for patterns, try more radical changes
- Simplification pass — can you delete code and maintain the same KPIs? That's a win
Forge State File Format
Driver defaults (substitute your generated session ID for {sid}):
- Claude Code:
.claude/forge-state.{sid}.md - Codex:
.codex/forge/forge-state.{sid}.md
---
session_id: "0406-1530-djinnchat-primefix" # MMDD-HHMM-SLUG, generated once on first iteration
scope: "description of scope"
success:
mode: "task-derived"
task: "build password reset flow"
done_when: null
completion_checks:
- "users can request a reset and use the token successfully"
- "failure paths are covered and tested"
baseline:
coverage: 92.99
speed_seconds: 81
tests: 21563
failures: 0
measured_at: "2026-03-20T14:30:00Z"
targets:
min_coverage: 95.0
max_speed_seconds: 40
quality: "moderate"
max_iterations: 20
current_strategy: "coverage-push"
stagnation_count: 0
strategies_tried:
- name: "coverage-push"
iterations: [1, 2]
coverage_delta: 0.8
speed_delta: -5
lessons:
- "async:true on LiveView tests saves ~3s per file"
ideas:
- "consolidate 3 similar fixture helpers into one parameterized function"
- "auth module has dead code paths from v1 migration"
---
## Iteration 1 — coverage-push
- Coverage: 92.99 -> 93.15 (+0.16%)
- Speed: 81s -> 79s (-2s)
- Tests: 21563 -> 21578 (+15)
- Actions: Added 15 tests for data_loaders.ex edge cases
- Reality-check: 2 high, 3 medium findings
- Lesson: "data_loaders has 7 identical try-rescue - extract, don't test each"
Quality Levels
| Level | High findings | Medium findings |
|---|---|---|
strict | 0 | 0 |
moderate | 0 | <= 3 |
lax | 0 | <= 5 |
Critical Rules
- ONE change per iteration — resist the urge to batch. Small steps compound.
- Never skip VERIFY — red tests mean the iteration failed. Fix before RECORD.
- Never fabricate KPIs — always parse from actual test runner output.
- Fresh-context evaluation — use an available isolated reviewer/audit pass to avoid anchoring bias.
- Lessons accumulate — read ALL previous lessons before DECIDE. Never repeat a documented failure.
- Commit on green — every improvement gets persisted to git.
- State file is sacred — it survives context compaction. Keep it accurate.
- Simpler is better — code deletion at same KPIs is always a win. Don't add complexity for marginal gains.
- Clean revert on failure — restore clean state before the next iteration. Never leave dirty files.
- Never stop to ask — if stuck, think harder. Re-read code, review backlog, combine near-misses, try the inverse.
- Task success comes first — KPIs are guardrails, not a substitute for actually finishing the requested work.
Support posture
- Claude Code support is first-class in this repo
- Codex support is first-class as a manual driver in this repo
- Other runtimes may reuse the protocol, but should not be described as officially supported unless they ship a real driver