Safe agent loops: a practical control checklist
An agent loop becomes useful when autonomy is tied to evidence and clear exits. The controls below do not guarantee safe code; they constrain what the loop can do and leave a reviewable record of each attempt.
Start with honesty
Safety is a control model, not a promise
A coding agent can misunderstand requirements, alter the wrong file, or propose a command with unintended effects. Keyword warnings alone cannot understand arbitrary shell behavior, and a sandbox does not prove that a change is correct. A practical safety model therefore combines several independent controls: constrained permissions, external proof, budgets, stop rules, human approval, and audit artifacts.
LoopLatch separates browser validation from local enforcement. The website blocks missing structural inputs and warns about risky terms, but it never executes a command or claims to validate shell safety. The locally generated harness is responsible for capability checks, worktree rules, typed worker results, timeouts, approvals, and stop semantics.
Draw the trust boundaries before granting autonomy
The task, done condition, verifier command, repository content, and model output can all influence local execution. Treat each as untrusted data. The browser must render and serialize user text without evaluating it. The local harness must parse structured output as real JSON, reject malformed results, and avoid interpolating model text directly into shell execution.
The website also has a privacy boundary: it should not receive repositories, secrets, task analytics, or hosted execution traffic. Static public content and templates can be delivered normally, while task content and generated archives stay in the loaded document until the user chooses to copy or download them.
- No repository upload or GitHub access is needed for prompt generation.
- No secret should be requested or placed in a shareable URL.
- No worker summary may override verifier failure.
- No warning acknowledgement should become permission for a future local action.
Let the verifier establish success
The verifier is the loop's independent success authority. Run it before each worker to avoid unnecessary edits and after each worker to prove the new state. Preserve complete logs for both runs. A zero exit code can end the loop; a worker statement such as “all tests pass” cannot.
A strong verifier measures the requested behavior and produces a stable exit result. If the task is a UI regression, a type check alone is insufficient. If the task is a formatting failure, a full browser suite may be wasteful. Match proof to the acceptance criterion, then add repository-required gates.
Fix least privilege instead of offering a dangerous toggle
A local coding loop normally needs to inspect the repository and write files in its workspace. It does not need a standing bypass of the Codex sandbox. LoopLatch fixes the worker at workspace-write and keeps auto-commit and auto-push off. Those values are constraints, not website preferences.
A sandbox limits capability; it does not make every allowed edit correct. Keep the repository itself isolated from unrelated credentials and production access, review the generated harness, and use a separate disposable environment when the task has unusual operational risk.
Use several stop conditions because failures differ
No single budget covers every unproductive state. Maximum iterations limits worker turns. A total timeout limits wall-clock exposure, including slow verifiers. A repeated-failure limit catches a stable error that is not improving. A no-diff stop catches an iteration that changed nothing. Capability, collision, dirty-worktree, invalid-result, and approval stops close other unsafe paths.
Use one total deadline for the complete loop rather than resetting the clock for every subprocess. On expiry, terminate the active child, allow a short cleanup grace period, record the timeout, and return a non-zero result. An exhausted budget is a stop, not evidence that the task succeeded.
| Stop | Protects against | Result |
|---|---|---|
| Maximum iterations | Unlimited worker turns | Exit non-zero when budget is exhausted |
| Total timeout | Slow or stuck workers and verifiers | Terminate, record, and exit non-zero |
| Repeated failure | Same normalized verifier failure | Stop at the configured consecutive limit |
| No diff | An iteration with no repository change | Stop when enabled |
| Invalid result | Missing or malformed structured worker output | Fail closed before using approval data |
Bind approval to one exact action
A risky action should never inherit permission from a template, a website warning, or a prior approval. When a worker needs approval, it should return a structured request containing an action ID, category, reason, and exact action. The harness displays those fields and asks a default-No question on an interactive terminal.
Approval applies to one exact subsequent worker turn and is cleared immediately, whether the action succeeds or fails. Decline, end-of-file, missing TTY, an action mismatch, or a second unapproved risky action stops the loop. This is deliberately inconvenient compared with a persistent allow-list because the decision remains specific and reviewable.
Preserve attribution in clean and reviewed-dirty worktrees
A clean worktree is the safest default because each patch can be attributed to the loop. If reviewed dirty work is allowed, confirmation should be explicit and interactive. The harness can capture a synthetic Git tree before and after the iteration without changing the real index, refs, branch, or commits, then write a binary patch for the delta.
Per-iteration artifacts should retain the worker prompt, Codex JSONL, typed result, verifier logs before and after, and the patch. Aggregate progress and decision files are useful summaries, but they should not replace immutable records. Auditability helps a reviewer understand what happened; it does not approve the change automatically.
Review this checklist before running a local loop
Use the checklist as a preflight, then inspect the generated repository-specific implementation. The correct commands and file boundaries depend on the target repository, so a generic website cannot perform the final review for you.
- The task names one coherent change and explicit non-goals.
- The verifier independently exercises the done condition.
- Workspace-write is sufficient; no sandbox bypass is present.
- Iteration, total timeout, repeated-failure, and no-diff behavior are visible.
- Existing target files cause a collision stop instead of an overwrite.
- Dirty-worktree handling preserves the real index and records attribution.
- Risky actions require exact one-use TTY approval.
- Every iteration retains structured output, verifier logs, and a patch.
- You will review the generated harness and final repository changes.
What to know before you start
Why are risky command checks warnings instead of blockers?
A browser keyword check cannot reliably parse or understand arbitrary shell behavior. It can call attention to risky terms, while structural validation and the local harness provide enforceable boundaries.
Why not allow auto-push after the verifier passes?
Verifier success proves only the configured check. It does not replace review, authorize a remote write, or prove that every side effect is acceptable. LoopLatch keeps unattended push off.
Can I run a loop on a dirty worktree?
The safe default is refusal. A reviewed-dirty mode can work only with explicit TTY confirmation and attribution that does not alter the real Git index or refs. Review the generated implementation before relying on it.