# /afk Safety Rules

Binding for both the orchestrator (the shell loop) and the inner agent (claude/codex). Violating any of these aborts the loop with a blocker comment on the active issue.

## Repository Layout Invariants

- The **primary checkout** stays on `main` at all times. Never `git checkout`, `git switch`, or `git branch -m` inside it.
- All work happens in **worktrees** under `.red/tmp/workers/{id}/{N}-a{n}/worktree/` (inside the primary checkout but gitignored) on the deterministic issue branch `afk/{N}-{slug}`.
- The worktree branch is mirrored to `origin/afk/{N}-{slug}` for remote backup. One issue has one head branch across workers, so a ghost requeue collides structurally instead of creating a silent parallel attempt. The orchestrator pushes the worktree branch via the PR's admin-merge (unlocked) or direct merge + push of the locked branch (locked); it does not push `main` directly.

## Git Operations

**Allowed in primary checkout:**
- `git fetch`.
- `git worktree add|remove|list`.
- `git push --force-with-lease` (mirroring only: `git push origin -u HEAD:refs/heads/afk/{N}-{slug} --force-with-lease` to push the worktree branch onto the remote-tracked `afk/*` namespace, and per-worktree post-commit hook `git push origin HEAD --force-with-lease`, both per the Per-Issue Loop Worktree section below).
- A guarded post-Landing `git merge --ff-only` may advance a clean, on-trunk primary whose local tip is a strict ancestor of the fetched remote tip. A dirty or diverged primary is never promoted.
- Read-only: `git status`, `git log`, `git diff`, `git show`, `git branch`.

**Allowed in worktree:**
- `git add`, `git commit`, `git mv`, `git status`, `git diff`, `git log`, `git show`.

**Forbidden everywhere, no exceptions:**
- `git reset` (any flavour)
- `git rebase` in the worktree or inner agent (orchestrator-only integration step is excepted above).
- `git clean` (any flavour)
- `git restore`, `git checkout -- <path>`, `git checkout .`
- `git stash` — push, pop, drop, all banned
- `git branch -D`, `git branch -d -f`
- `git push --force` or any bare `--force` flag in the worktree or inner agent (orchestrator-only mirroring step is excepted above).
- Any command with `--hard`, `--no-verify` anywhere
- Switching branches inside any checkout
- Rewriting history of any branch, ever

**SSH-only remotes:**
- Refuse to start if `git remote -v` shows any `https://` URL.
- Never auto-rewrite remotes. The user fixes them manually.

## Dirty Primary Checkout

AFK never stages or commits the primary checkout. A dirty primary remains
untouched through boot, claim, the worker, feedback and backpressure
gates, and Landing. In particular, AFK never creates a pre-merge snapshot
commit on boot or claim. A worker crash or failed gate therefore leaves the
operator's index, working tree, and commit history unchanged.

Landing does not need that snapshot: the unlocked path merges remotely through
a PR, while the direct path fetches, integrates, merges, pushes, and rolls back
inside an isolated landing worktree. The direct path captures its rollback
anchor immediately before `merge --no-ff`; a rejected push resets only that
disposable worktree. Never `git stash`, `git restore`, or discard dirty primary
state.

## Merge Conflicts

One self-resolve attempt: re-enter the inner agent with the conflict diff in the handoff file Notes. If the inner can't resolve cleanly:

1. Abort the merge in the isolated landing worktree.
2. Comment the conflict diff on the issue.
3. Re-label `ready-for-human`, remove `running`.
4. Move to the next issue. The primary checkout was never the merge target and needs no cleanup.

## Worktree Lifecycle

- Created from `origin/{pinned}` after `git fetch` (where `{pinned}` is the resolved base branch: lock > pin > main).
- Before fresh work starts, list open PRs and adopt any whose body closes `#{N}` or whose head is the deterministic/legacy AFK branch for `#{N}`. Cross-link every matching PR on the issue. A fresh branch is allowed only when no open attempt exists (or after an explicit commented supersede + PR close).
- Branch name: `afk/{N}-{slug}` (literal `afk/` + issue number + slug). Worker/runner provenance belongs in the PR body and handoff record, not the ref. Slug is the issue title lowercased, non-alphanumerics → `-`, truncated to 40 chars.
- Removed only after successful merge **and** push. Never remove a worktree with uncommitted changes — that loses work.
- If cleanup fails (e.g. worktree busy), leave it in place and print the path for manual recovery.

## `.red/` Discipline

- `.red/tmp/` is gitignored. The orchestrator guarantees this in the worktree before writing handoff files.
- `.red/wiki/` is gitignored if the project uses the LLM-wiki pattern. The inner agent must never `git add` it.
- `.red/agents/`, `.red/CONTEXT.md`, and other tracked `.red/` content are normal source. Treat them like any other file.

## Heartbeat and State Files

- The periodic issue-thread heartbeat (`:one:` … `:four:` via `gh issue comment`) was retired in Slice D — there is no sub-shell to track or kill. `heartbeat_pid` in older state files is vestigial and ignored.
- State file writes are atomic: write to `.red/tmp/workers/{id}/{N}-a{n}/afk.state.json.tmp`, `mv` over the real path. Never partial writes.
- The monitor never writes. Only the orchestrator writes state.

## Signals and Shutdown

- `SIGINT` (Ctrl-C): finish the current `pnpm`/git command if mid-flight, write a "interrupted" comment on the active issue, leave the worktree in place, exit 130.
- `SIGTERM`: same as `SIGINT`.
- Never trap `SIGKILL` — let the OS do its thing.

There is no heartbeat sub-shell to reap on any of these paths since Slice D — the only cleanup work is releasing the in-flight claim, preserving the active worker directory, and removing the per-worker `worker.pid` (and the empty worker dir) on the EXIT trap.

## What "Blocker" Means

A blocker is recorded by:
1. Appending Notes to the handoff file (the inner does this).
2. Posting the Notes as a comment on the GitHub issue (the orchestrator does this).
3. Removing `running`, adding `ready-for-human`.
4. Worktree is preserved at the moment of blocker — the human investigates in place.

The loop continues with the next issue. Blockers are not fatal to the loop.

## What Aborts The Whole Loop

Only these:
- Hard preconditions failed at startup (HTTPS remote, no `gh` auth, no `main`).
- Both runners exhausted on the same issue.
- Uncaught error in the orchestrator shell (not from inner — those become blockers).

On abort: write final state, print recovery instructions. Worktrees in progress are left alone. (No heartbeat sub-shell to kill since Slice D.)
