Implementation Loop
Autonomous implementation loop. Each iteration gets fresh context, picks one task from the spec, implements it, runs feedback loops, commits, and exits. Progress lives in git history, not conversation memory.
Setup
These commands run automatically when the skill loads — output replaces each line below:
- Available specs: !
for d in docs/plans/*/; do { [ -f "$d/spec.md" ] || [ -f "$d/prd.md" ]; } && basename "$d"; done 2>/dev/null || true - Project files: !
ls package.json Makefile Cargo.toml go.mod pyproject.toml setup.py 2>/dev/null || true - Tests directory: !
ls tests/ 2>/dev/null || true
1. Select a spec
From the spec list:
- If the user passed a name (e.g.
/loop auth-system), usedocs/plans/auth-system/spec.md(fall back toprd.mdwhenspec.mdis missing — legacy plan dirs) - If there's exactly one spec, use it
- If there are multiple, ask the user which one to use
- If there are none, tell the user to run
/spec-createfirst
Store the selected spec path as SPEC_FILE for use in the prompt template.
2. Detect feedback loops
Map the detected project files to feedback commands:
package.json→npm run test,npm run typecheck,npm run lintMakefile→make test,make checkCargo.toml→cargo test,cargo checkgo.mod→go test ./...,go vet ./...pyproject.toml/setup.py→pytest,mypytests/directory in this repo →bash tests/test-skills.sh,bash tests/test-structure.sh
Build the feedback loop commands list from what's actually available.
3. Create the plans directory
mkdir -p docs/plans/<name>
4. Generate the prompt
Prefer the prompt generator script because it uses the shared loop engine's feedback detector:
bash skills/loop/loop/scripts/prompt.sh <name>
If running from an installed skill path, resolve {{SKILL_SCRIPTS}} as in
step 5 and run bash {{SKILL_SCRIPTS}}/prompt.sh <name>.
The script writes docs/plans/<name>/prompt.md, sources lib/feedback.sh,
and renders feedback commands from almanac_loop_feedback_markdown().
Use the template below only as a fallback when the script is unavailable.
Write docs/plans/<name>/prompt.md (e.g. docs/plans/auth-system/prompt.md) using the template below, filling in the detected feedback loops and the spec path:
# INPUTS
Pull @{{SPEC_FILE}} into your context.
You've been passed the last 10 LOOP commits (SHA, date, full message). Review these to understand what work has been done.
# TASK QUEUE
Before decomposing the spec, check whether an explicit queue exists. Detect in this order:
1. **Local ticket files.** If `docs/plans/<name>/issues/` contains `*.md` files, that directory is your queue. New tickets use `status: ready-for-agent|ready-for-human`; legacy tickets use `status: open` plus `type: AFK|HITL`.
2. **GitHub issues.** Else if `gh issue list --search 'label:"loop(<name>)" state:open'` returns at least one issue, that's your queue. (Use `--search`, not `--label` — the parenthesised label name breaks the `--label` filter.) New agent tickets also carry `ready-for-agent`; legacy tickets may have no readiness label.
3. **No queue.** Skip to TASK BREAKDOWN below and decompose the spec yourself.
If a queue is present:
- Pick the **lowest-numbered** agent-ready local ticket (or **oldest** agent-ready GitHub issue) whose blockers are all done or closed. Treat legacy `status: open` + `type: AFK` files and legacy GitHub queue issues without any readiness label as agent-ready. Never pick `ready-for-human` or legacy `type: HITL`.
- Its `## What to build` and `## Acceptance criteria` define your scope. The spec is reference; the slice/issue is authoritative.
- Do NOT decompose the spec again — TASK BREAKDOWN below is for the no-queue case only.
- If every queued task is blocked by something incomplete, output `<promise>ABORT</promise>`.
# TASK BREAKDOWN
(Run this section ONLY if TASK QUEUE found no queue. Otherwise the slice/issue you picked IS your task; skip ahead to EXPLORATION.)
Break down the spec into tasks.
Pick the smallest unit of work that pins one meaningful behavior. Don't outrun your headlights — but don't underrun them either.
- **Behavior changes** (new features, schema, business logic): one task = one behavior, written test-first.
- **Mechanical refactors** (renames, threading a parameter through callers, search-and-replace across many files): the whole refactor is ONE task. Batch all related edits across all affected files into a single commit. The existing test suite is the verification — don't split a rename into one commit per call site.
If you can't articulate a behavior the task pins, you're mid-refactor — bundle it.
# TASK SELECTION
If TASK QUEUE found a task, that's your task. Otherwise pick the next task from your TASK BREAKDOWN that hasn't been completed (check LOOP commits for completed work).
If all tasks are complete, output <promise>COMPLETE</promise>.
# EXPLORATION
Explore the repo and fill your context window with relevant information that will allow you to complete the task.
# EXECUTION
Follow the `implement` skill for this one selected task: verify readiness and blockers, implement at the agreed seam, run feedback loops, review the diff, and update queue state. The selected task is already resolved — do not choose another ticket.
# FEEDBACK LOOPS
Before committing, run ALL feedback loops. Fix any failures before proceeding.
{{FEEDBACK_COMMANDS}}
# COMMIT
Follow the `implement` skill's strict checkbox and queue-update protocol. Then make the git commit. The commit message must:
1. Start with `LOOP(<name>):` prefix (e.g. `LOOP(auth-system):`)
2. Include task completed + spec reference
3. Key decisions made
4. Files changed
5. Blockers or notes for next iteration
Keep it concise but informative for the next iteration.
# REPORT
After committing, append a self-report to `docs/plans/<name>/agent-reports.log`. The overseer reads recent reports each tick and may emit steering directives based on what you flag. Be honest — concerns and uncertainties are more useful than reassurance.
Append exactly this block (replace `<HEAD-sha>` with the SHA of the commit you just made, e.g. `git rev-parse HEAD`):
===== sha= ts= =====
concerns
- <anything about the code, tests, or approach that feels off; or "(none)">
errors
- <runtime errors, test failures, lint issues, or retries you hit; or "(none)">
uncertainties
- <spec ambiguities, missing context, or assumptions you made and want validated; or "(none)">
If the iteration was a CI fix or a steered iteration, mention that in concerns so the overseer has context.
# FINAL RULES
ONLY WORK ON A SINGLE TASK.
Replace {{FEEDBACK_COMMANDS}} with the detected commands as a markdown list
matching almanac_loop_feedback_markdown(), e.g.:
- `npm run test` to run the tests
- `npm run typecheck` to run the type checker
5. Tell the user how to run it
Print:
Loop ready for <name>.
Interactive CLI:
almanac loop
# or: bash {{SKILL_SCRIPTS}}/loop.sh
Single iteration (HITL):
bash {{SKILL_SCRIPTS}}/once.sh <name>
Autonomous (AFK):
bash {{SKILL_SCRIPTS}}/afk.sh <name> <iterations>
Example — run 10 iterations:
bash {{SKILL_SCRIPTS}}/afk.sh auth-system 10
Where {{SKILL_SCRIPTS}} is the absolute path to this skill's scripts. Resolve in this order:
~/.agents/skills/almanac/loop/scripts— set byalmanac install codexoralmanac install pi; use this in Codex or Pi.~/.claude/skills/almanac/loop/scripts— set byalmanac install claude-code; use this in Claude Code.$ALMANAC_HOME/skills/loop/loop/scripts— fallback when invoked outside an installed provider.
Print the literal provider install path in user-facing instructions (~/.agents/... for Codex/Pi, ~/.claude/... for Claude Code) so users can run the scripts directly.
Modes
AFK Mode (afk.sh)
Fully autonomous. Runs N iterations, each in a fresh agent context. Stops when:
- All tasks complete (
<promise>COMPLETE</promise>) - A task is blocked (
<promise>ABORT</promise>) - Iteration limit reached
.loop-stopfile exists in the working directory (graceful stop — see below)- Overseer detects HIGH drift (writes
.loop-stopautomatically — see Overseer below)
Provider selection: set LOOP_PROVIDER=codex or LOOP_PROVIDER=claude to force an agent. If unset, the scripts use Codex when running inside Codex, otherwise Claude Code when available, otherwise Codex.
Model override: set LOOP_MODEL (e.g. LOOP_PROVIDER=codex LOOP_MODEL=gpt-5.5 bash afk.sh <name> 10 or LOOP_MODEL=claude-opus-4-7 bash afk.sh <name> 10); unset uses the selected provider's default.
Thinking override: set LOOP_EFFORT to control model thinking level. Codex receives this as model_reasoning_effort; Claude Code receives it as --effort. Supported common values: low, medium, high, xhigh; Claude Code also supports max.
Codex output: Codex raw session output is quiet by default and written to docs/plans/<name>/loop-codex-*.log; the terminal shows concise agent progress messages, the final assistant message, and the log path. Set LOOP_CODEX_VERBOSE=1 to stream Codex's full session output.
Interactive launcher: run almanac loop (or loop.sh directly) to select spec, mode, provider, model, thinking level, iteration count, and overseer behavior from prompts. It delegates to once.sh or afk.sh with the corresponding LOOP_PROVIDER, LOOP_MODEL, LOOP_EFFORT, and LOOP_NO_OVERSEE environment values.
Auto-push: the overseer pushes any unpushed LOOP commits to origin at the start of each tick (default 15 min, configurable via LOOP_OVERSEE_INTERVAL). This batches commits so CI runs at overseer cadence rather than per-iteration — avoids clogging CI when iterations are minutes apart. End-of-loop also pushes as a safety net. Sets upstream automatically on first push and repairs mismatched upstreams such as origin/main.
Overseer: a parallel process wakes every LOOP_OVERSEE_INTERVAL seconds (default 900 = 15 min) and runs a sequential tick:
Push (shell). Pushes any local commits ahead of upstream. Logs to
docs/plans/<name>/overseer.log.Wait for CI (shell, only if step 1 actually pushed). Polls
gh run listeveryLOOP_CI_POLL_INTERVALseconds (default 30) for the run matching the pushedheadSha, blocking until status leavesin_progress|queued|waiting|requested|pending. Times out afterLOOP_CI_WAIT_TIMEOUTseconds (default 1800 = 30 min). Exits early on.loop-stop. While the overseer waits, main-loop iterations keep running — only the overseer thread is blocked.CI verdict (shell, no Claude call). Reads
gh run list --limit 1. Onconclusion=failure|cancelled|timed_out|action_required|startup_failure, writes.loop-ci-failed(run URL, ID, workflow name, branch, timestamp). Onconclusion=success, clears the marker. Also runs once at script start to pick up pre-existing failures from prior sessions or manual pushes.Drift review (selected agent call). Reviews recent
LOOP(<name>)commits, the tail ofdocs/plans/<name>/agent-reports.log(last ~8KB of agent self-reports — concerns, errors, uncertainties), and any task queue (slice files indocs/plans/<name>/issues/or open GitHub issues with theloop(<name>)label) against the spec. Detects:- Repeated tasks, off-spec work, ABORT loops, vague commits, scope creep, test rot, recurring concerns the agents aren't solving on their own.
- Queue overclaim — checkboxes flipped to
[x](orstatus: doneset, or issues closed) without the corresponding code in those commits. For each recently-flipped checkbox, the overseer reads the slice/issue criterion and the commits that flipped it, and judges whether the diff actually fulfills the criterion. If not, the steer directs the next iteration to roll back the checkbox / status / issue closure. - Queue staleness — criteria clearly fulfilled by recent commits but checkbox still
[ ].
Outputs
DRIFT_LEVEL: low|medium|high,REASON: …,STEER: …. On HIGH drift writes.loop-stop. WhenSTEERis non-none, writes the directive to.loop-steer.
Effective drift-review cadence is LOOP_OVERSEE_INTERVAL + (CI duration if pushed). Steps 2-3 silently no-op if gh is missing, the repo has no remote, or no run materialized for the pushed SHA.
Disable the whole overseer with LOOP_NO_OVERSEE=1 — that also disables overseer-cadence push, CI wait, CI monitoring, and steer; only the end-of-loop push remains.
Iteration prompt prefixes: at the start of each iteration, afk.sh may prepend up to two directives to the iteration prompt:
- Fix-CI — when
.loop-ci-failedexists. The spawned agent is told to skip new task work, read the marker, fetch logs viagh run view, repair, and commit withLOOP(<name>): fix CI — …. Persistent: cleared automatically by the next overseer tick once CI is green again. - Overseer steer — when
.loop-steerexists. The spawned agent is told the overseer reviewed recent reports + commits and emitted concrete advice (wrong assumption, scope correction, alternate approach, etc.). One-shot:afk.shremoves the file after consumption. The overseer can re-emit it next tick if the underlying issue persists.
Both can stack — a steered fix-CI iteration is valid.
Agent self-reports: the iteration prompt template instructs the spawned agent to append a structured block to docs/plans/<name>/agent-reports.log after committing — concerns, errors, uncertainties per iteration. This is the primary signal the overseer uses to decide whether to issue a steer beyond what the commits alone reveal. Agents are told to be honest — flagged uncertainties are more useful than reassurance.
HITL Mode (once.sh)
Single iteration with human in the loop. Runs one pass — you review the result before continuing. Good for:
- First iteration (sanity check)
- After an ABORT (diagnose and unblock)
- When you want to steer
Monitoring
While AFK mode runs, you can watch progress:
git log --grep="LOOP(auth-system)" --oneline
Each LOOP(<name>): commit message contains what was done and notes for the next iteration. The name prefix means multiple specs can run against the same repo without confusing each other's progress.
When to stop
- All tasks done → loop exits with "Loop complete" + auto-push.
- Something's wrong → loop exits with "Loop aborted" + auto-push.
- Graceful stop mid-run →
touch .loop-stopin the working directory. The loop exits at the start of the next iteration, pushes commits, removes the file. Use this instead of Ctrl+C — Ctrl+C skips the auto-push and may leave LOOP commits stranded locally. - You see bad commits → Ctrl+C, review, and
git pushmanually if you want to keep them. - Context is confused → kill it, fix the issue, restart (fresh context = fresh start).