The repo is maintained by agents from multiple vendors, so agent instructions move out of vendor-specific locations into shared, git-tracked markdown: - AGENTS.md is the new entry point for any harness; CLAUDE.md becomes a symlink to it (Claude Code keeps working unchanged). - Skill bodies relocate to agent/: VERIFICATION.md (the enforcement rules, ex .claude/skills/nomarchy), DELEGATION.md (capability tiers light/standard/frontier, scout/runner role contracts, token economy, parallel fan-out — consolidates the CLAUDE.md model table, LOOP.md's economy section, and skill §6.5 into one place; vendor model names survive only in the per-harness mapping table), THEME-DESIGN.md (ex .claude/skills/theme, which previously lacked frontmatter). - .claude/ shrinks to a thin Claude Code adapter: settings, subagent defs, and skill shims that route into agent/. - Maps updated: agent/README.md (instructions vs state vs adapters), docs/README.md, README.md layout tree. Cleanup: stale old_distro .gitignore entry dropped (dir long gone); local result*/__pycache__ artifacts removed; JOURNAL.md rotated (29 recent entries kept, 120 older moved to agent/JOURNAL-ARCHIVE.md, rotation rule documented in the header). Verification: V0 (docs/meta only) — nix flake check --no-build exit 0 with the tracked symlink; grep sweep confirms no vendor model names outside DELEGATION.md's mapping table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.9 KiB
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 harness- and vendor-agnostic; the same iteration works under any of:
- An interactive self-paced loop in any agent harness (e.g.
/loopin Claude Code) — the agent iterates until stopped. - Headless (a one-shot CLI invocation, 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) |
Instructions live next to the state: VERIFICATION.md (enforcement),
DELEGATION.md (tiers/roles/economy), GOALS.md, THEME-DESIGN.md.
Model & token economy
Spend expensive tokens on judgment, not mechanics. Tiers, roles, and the
full delegation rules are in DELEGATION.md; the loop-specific
habits:
- 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 frontier-tier model running the loop.
- Delegate mechanical subtasks down. 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
light/standard-tier subagent per
DELEGATION.md. The strong model writes the spec, reviews the result, and owns the commit. - Read narrowly. Step 0's list is the whole orientation read (the last 3–5 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/orpkgs/behavior keep the strong model.
One iteration, step by step
0. Orient
- Read
GOALS.md,CONVENTIONS.md,MEMORY.md, the last 3–5 entries ofJOURNAL.md, andBACKLOG.md. If the top task is product-shaped (UX, release bar, day-2 confidence), also read the matching section ofdocs/VISION.md— do not invent work from VISION; only execute BACKLOG items. Map of docs vs agent state:docs/README.md,agent/README.md. git pull --ff-only(skip silently if offline). Confirm you are onmainwith a clean tree. A dirty tree you didn't create → stop and report; never stash or discard someone else's work.- Sanity baseline: if the last journal entry reports a red
nix flake check, or you have any reason to suspect breakage, runnix flake check --no-buildfirst. 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 inHARDWARE-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.mdnow, 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
mainandgit push(Bernardo's standing workflow). - Never: force-push; touch the
v1branch or any branch/tag you didn't create; commit secrets or binaries; runnix flake updateunless the task is explicitly a lock bump; delete themes, wallpapers, or user-facing assets without the backlog saying so.
5. Record
- Mark the task in
BACKLOG.md(move to its ✓ line or delete, per that file's rules). - Append a
JOURNAL.mdentry (template in that file). - 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.