Files
Nomarchy/agent/LOOP.md
Bernardo Magri 274ffc25e1
All checks were successful
Check / eval (push) Successful in 3m1s
docs(agent): model & token economy rules for the loop
Plan/reason/review on the strong model (Fable 5); delegate mechanical,
fully-specified subtasks (grep sweeps, table reconciliation, repeated
edits, log summarization) to cheaper models (haiku/sonnet) via subagent
model overrides — the strong model writes the spec and owns the commit.
Plus narrow-reading and terse-writing rules, and a cheaper --model
allowance for low-stakes headless iterations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 18:07:32 +01:00

7.6 KiB
Raw Blame History

The loop — autonomous iteration protocol

How an AI agent works on Nomarchy unattended. One iteration = pick one task, do it, verify it, commit it, record it. The protocol is runner-agnostic; the same iteration works under any of:

  • Interactive /loop in a Claude Code session in this repo — the agent self-paces iterations until stopped.
  • Headless (claude -p, cron/systemd-timer) — one invocation runs one iteration (or a small fixed number) and exits.
  • A fresh manual session — a human says "do a loop iteration"; the files below carry all the state, so any session can pick up where the last left off.

All loop state lives in this directory, git-tracked. There is no state outside the checkout (the distro's own philosophy, applied to its agents).

The files

File Role Who writes it
GOALS.md North star + quality bars + non-goals Human (agents propose edits)
BACKLOG.md Prioritized task queue (NOW/NEXT/LATER/PROPOSED/DECISIONS) Both — see its header rules
JOURNAL.md Append-only iteration log Agents
MEMORY.md Curated durable lessons/gotchas Agents (curated, not append-only)
HARDWARE-QUEUE.md Pending on-hardware checks only Bernardo can run Agents append, human checks off
CONVENTIONS.md Repo/design conventions to follow while coding Human (agents propose edits)

Model & token economy

Spend expensive tokens on judgment, not mechanics.

  • Plan and reason on the strong model. Orientation, task selection, design, debugging, Nix eval semantics, verification judgment, and anything that would land in a commit unreviewed stay with the top-tier model running the loop (Fable 5).
  • Delegate mechanical subtasks to cheaper models. When a subtask is fully specified and needs no design judgment — grep/audit sweeps, README-option-table reconciliation, a repeated edit applied across files, summarizing long logs or check output — hand it to a subagent with a model override: haiku for search/summarize/audit, sonnet for routine well-specified edits. The strong model writes the spec, reviews the result, and owns the commit. Only delegate when writing the spec is cheaper than doing the work — a spawned agent starts cold and must re-derive context.
  • Read narrowly. Step 0's list is the whole orientation read (the last 35 entries of the journal, never the full file). Read large files by section, don't re-read what's already in context, and tail build/check logs instead of dumping them.
  • Write tersely. Journal entries follow the template and no more; commit bodies state what/why/tier, not a narrative.
  • Headless runners may run whole low-stakes iterations (QA sweeps, docs-drift passes) on a cheaper --model; iterations touching modules/ or pkgs/ behavior keep the strong model.

One iteration, step by step

0. Orient

  1. Read GOALS.md, CONVENTIONS.md, MEMORY.md, the last 35 entries of JOURNAL.md, and BACKLOG.md.
  2. git pull --ff-only (skip silently if offline). Confirm you are on main with a clean tree. A dirty tree you didn't create → stop and report; never stash or discard someone else's work.
  3. Sanity baseline: if the last journal entry reports a red nix flake check, or you have any reason to suspect breakage, run nix flake check --no-build first. A red baseline preempts the backlog — fixing it is this iteration's task.

1. Pick exactly one task

  • Take the topmost actionable item: NOW before NEXT; never LATER unless NOW and NEXT are empty or all blocked.
  • Actionable means: not [blocked:hw] (those wait in HARDWARE-QUEUE.md), not [human] (decisions), and small enough to finish + verify in one iteration. If the top item is too big, split it in BACKLOG.md (that edit is part of the iteration) and take the first slice.
  • Never implement anything from PROPOSED — those await human triage.
  • If nothing is actionable, do a QA sweep instead: run the full check suite, hunt drift (README option tables vs the live nomarchy.* surface, template drift, dead code), deepen a VM test, or research and write up a PROPOSED item. An iteration that only improves the backlog is a valid iteration. If even that yields nothing, journal it and stop — do not manufacture churn.

2. Work

  • Keep the diff focused on the task. Unrelated fixes you trip over become PROPOSED/NOW entries, not scope creep.
  • Mechanical, fully-specified sub-steps go to a cheaper model (see Model & token economy above); design and review stay here.
  • Follow CONVENTIONS.md. Match the surrounding hand-formatting; never run a formatter.
  • New gotcha discovered the hard way → one line in MEMORY.md now, while it's fresh.

3. Verify — the ladder

Climb as high as the change warrants and your environment allows; record the tier reached in the commit body and journal entry.

Tier What When required
V0 nix flake check --no-build (+ bash -n / py_compile for scripts) Every change, no exceptions
V1 Build the touched output: system.build.toplevel, the HM generation, the ISO, or the package Anything beyond docs/comments
V2 VM: a checks.* runNixOSTest (add one if the change is guardable), or boot tools/test-live-iso.sh / tools/test-install.sh Behavioral changes — services, boot, installer, session
V3 Real hardware Cannot be done by the agent → append to HARDWARE-QUEUE.md with exact test steps

The honesty rule governs: a visual/interactive change verified only to V1 is not done — it ships as "V1-verified, V2/V3 pending" with the pending check queued. Prefer adding a permanent checks.* test over a one-off manual VM poke when the behavior is testable headlessly (see MEMORY.md for the reusable recipes).

4. Commit + push

  • Style: match the log — feat(scope): …, fix(scope): …, test(scope): …, docs(scope): …. Body explains what/why + the verification tier reached and what remains.
  • Include the agent/ bookkeeping updates (backlog/journal/memory/queue) in the same commit as the change they describe.
  • Commit directly on main and git push (Bernardo's standing workflow).
  • Never: force-push; touch the v1 branch or any branch/tag you didn't create; commit secrets or binaries; run nix flake update unless the task is explicitly a lock bump; delete themes, wallpapers, or user-facing assets without the backlog saying so.

5. Record

  1. Mark the task in BACKLOG.md (move to its ✓ line or delete, per that file's rules).
  2. Append a JOURNAL.md entry (template in that file).
  3. Queue any V3 checks in HARDWARE-QUEUE.md.

6. Pace (self-paced runners only)

Under /loop, continue to the next iteration while tasks remain actionable and checks stay green. Stop the loop when: nothing is actionable, the same task has failed twice (journal the failure analysis and mark the item [stuck]), or a [human] decision blocks everything remaining.

Stop-and-escalate conditions (any runner)

Write a journal entry + a BACKLOG note, then stop, when:

  • A fix would require touching v1, force-pushing, or a nixpkgs release jump.
  • The working tree contains uncommitted work you didn't create.
  • A task turns out to need a design decision Bernardo hasn't made → move it to Decisions in BACKLOG.md with the options laid out.
  • Two consecutive iterations failed on the same task ([stuck]).
  • Anything would delete or rewrite user data, git history, or the state file schema in a non-backward-compatible way.