loopdriver — The Dark Forge Loop
The Dark Forge (DFG) loop is an 8-phase agentic cycle that drives autonomous task execution. Phases 1–5 are implemented. Phases 0, 6, and 7 are defined but not called. This page covers every phase, the gap between the spec and the implementation, and how the loop connects to Zeus.
The 8-phase cycle
| Phase | Name | Status | Purpose |
|---|---|---|---|
| 0 | Recovery | Constant only — not called | Resume interrupted session from checkpoint |
| 1 | Preflight | Implemented (minimal) | Validate environment, triage issue backlog |
| 2 | Planning | Implemented (minimal) | Generate implementation plan per issue |
| 3 | Dispatch | Implemented (minimal) | Assign issues to coder agents, launch work |
| 4 | Collect | Implemented (minimal) | Gather coder outputs, review results |
| 5 | Merge | Implemented (minimal) | Synthesise final response, PR creation |
| 6 | Build | Constant only — not called | Code synthesis, lint gating, test runner integration |
| 7 | Deploy | Constant only — not called | Git push, PR creation, branch protection handling |
Driver.Run — single cycle
Driver.Run(ctx) executes one cycle (phases 1–5) and returns a
CycleResult with phase outcomes, issues processed, and a terminal action:
"continue", "done", "human_review", or
"error".
Driver.RunAutonomous(ctx) wraps Run() in a loop, draining the
issue backlog until exhausted, human_review is triggered, or
MaxCycles is reached.
Dispatch strategy is configurable: "interleaved" (default) plans issue 1 then
immediately dispatches it, then plans issue 2 and dispatches it. "batch" plans
all issues first, then dispatches all. Interleaved is canonical and reduces time-to-first-work.
// DriverConfig
type DriverConfig struct {
MaxCycles int // 0 = unlimited
DispatchStrategy string // "interleaved" (default) or "batch"
HumanApprovesPlans bool // pause at Phase 2 for human review
Autonomous bool // run continuously vs. single cycle
PMMode bool // enable tech-lead delegation validation
}
How loopdriver calls Zeus
loopdriver communicates with Zeus through the LLMInvoker interface.
In the shell, a thin zeusLLMInvoker struct wraps Zeus.Generate()
to satisfy the interface without creating a circular import.
CLILLMInvoker spawns a subprocess (intended for the original dfg
binary design). With the decision that dfg is not a separate binary — all DFG
functionality lives inside olympus — CLILLMInvoker is a dead code
path. zeusLLMInvoker is the only path that matters.
What the loop actually sends to Zeus
Each phase builds a prompt string via buildPhasePrompt(phase, issues) and calls
invoker.Invoke(ctx, prompt, workDir). Phase 2 now uses the
internal/orchestrator/planning package to produce validated structured JSON plans.
| Phase | Prompt source | Output |
|---|---|---|
| Preflight | buildPhasePrompt(PhasePreFlight, issues) |
JSON with issues_selected |
| Planning |
Batch: planning.BuildBatchPlanPrompt(issues)Interleaved: planning.BuildPlanPrompt(issue, intentHint)
|
Structured Plan JSON validated by planning.Validate();
written to .olympus/plans/<session>-<issue>.json
|
| Dispatch | Built from plan.Steps JSON (typed struct, not free text) |
Coder agent invocation |
| Collect | buildPhasePrompt(PhaseCollect, issues) | Results JSON |
| Merge | buildPhasePrompt(PhaseMerge, issues) | Final summary |
Aegis-3: Circuit Breaker
The loop now includes a per-task circuit breaker (internal/orchestrator/circuitbreaker).
After two consecutive failures on the same work unit, the circuit trips and surfaces an interactive
prompt to the operator:
⚡ Circuit open: this task has stalled 2 times with the same error.
Error: <last error>
[h] pause for human review [r] retry with a different approach [a] abort
- [h] Human review — saves circuit state to
.olympus/circuit/, writes a checkpoint (Mnemosyne), returnsErrHumanReviewRequired. Restart Olympus after addressing the underlying issue to resume from the checkpoint. - [r] Retry — resets the circuit and re-runs the phase with the prior error prepended as context.
- [a] Abort — skips this work unit and continues to the next issue.
In non-interactive (CI) environments, the prompt defaults to [h] automatically.
Recovery — spec vs. reality
The Phase 0 spec (embedded in the binary as
prompts/startup-phases/phase-0-recovery.md) describes:
- Read a structured JSON checkpoint from
~/.olympus/checkpoints/ - Validate git state: clean working tree, correct branch, no merge conflicts
- Resume from the exact phase that was active at crash time (enforced by StateMachine's recovery target)
- Emit a structured
RECOVERY_REPORTJSON before continuing
Reality: The PhaseRecovery = 0 constant is defined.
Driver.Run() starts at Phase 1 unconditionally. The checkpoint package
(internal/checkpoint) exists and maybeCheckpoint() fires at 80%
context threshold, but it saves a plain text snapshot — not a structured resume point.
The StateMachine's SetRecoveryTarget() method exists but is never called.
Build and Deploy — not yet implemented
The spec describes Phase 6 (Build) as a code synthesis phase: diff generation, test runner integration, lint gating, Hephaestus-backed patch application. Phase 7 (Deploy) handles git push, PR creation with branch protection awareness, and release tagging.
Both phases are defined as integer constants. Neither is called anywhere in
Driver.Run(). The validTransitions map in
StateMachine includes the transitions
5→6 and 6→7 already, ready for when the phases are implemented.
Note: Phase 7 Deploy currently means olympus deploys — git push, PR creation, not deploying user applications. The naming follows the DFG spec, not infrastructure deployment.
How users invoke the loop
The Dark Forge loop is not a separate binary. There is no dfg executable.
All loop functionality is embedded in the olympus binary under the
olympus orchestrate subcommand path:
# Single cycle on a task
olympus orchestrate run --task "implement issue #42"
# Drain the full issue backlog autonomously
olympus orchestrate run --autonomous
# Governance and environment checks (Phase 1 preflight)
olympus govern verify
# Resume from last checkpoint
olympus orchestrate resume
These commands do not yet exist — they are the target state. Today, the loop is invoked
programmatically via loopdriver.NewDriver(...).Run(ctx) or through the
/startup slash command in the TUI shell.