Hardening an Overnight Fix Across a Monorepo
An engineer wants to leave a headless run going overnight in a large
monorepo: claude -p "find and fix every call site still using the deprecated retry helper across packages/api and packages/web, commit each fix", run in auto mode, unattended. They plan to check the
results in the morning.
- Identify three distinct gaps in this plan as described — one about what the run is allowed to touch, one about how it knows it's actually done, and one about the codebase's scale — and explain the concrete failure each gap could cause by morning.
- Propose a hardened version of the command and setup that closes all three gaps. Be specific about flags, settings, or scoping mechanisms, not just "add safety."
- The engineer says: "auto mode already reviews every action, so I don't need an allowlist too." Explain concretely why that reasoning is incomplete.
1. Three gaps and their failure modes:
- What it's allowed to touch. Nothing in the command scopes the run's tools. Left as-is, a run that hits an unexpected situation (a call site inside a migration file, a test that needs regenerating) can reach for any tool it has access to, including ones far outside "fix call sites and commit" — by morning that could mean commits well beyond the intended scope, or a run that touched files nobody wanted touched.
- How it knows it's done. "Fix every call site... commit each fix" is a one-off instruction with no verification target — Claude stops each fix when it looks fixed, with nobody there to notice a fix that compiles but changes behavior. By morning, some fraction of the "fixed" call sites could be subtly wrong with no evidence generated to catch it.
- The codebase's scale. This is a monorepo, and the command gives Claude no scoping beyond the two package names in the prompt. Without directory-level scoping, the run's search and read behavior can wander into unrelated packages' files, generated/vendored code, or simply cost far more context (and tokens) per fix than necessary — by morning, cost and noise are both higher than the two-package scope of the actual task warranted.
2. A hardened version:
Scope the run's tools and budget, and give it a real verification gate:
claude -p "find and fix every call site still using the deprecated \
retry helper in packages/api and packages/web. For each fix, run \
that package's test suite and only commit if it passes. Report any \
call site you could not fix with a passing test, do not commit it." \
--allowedTools "Edit,Bash(npm test *),Bash(git commit *),Bash(git diff *)" \
--max-turns 60 \
--max-budget-usd 5 \
--output-format json
This closes the three gaps: --allowedTools bounds what the run can
touch to editing and the specific test/commit commands it needs (not
an open-ended Bash allowance); --max-turns and --max-budget-usd
cap how far a run that goes wrong can go before something stops it;
and requiring a passing test suite per fix before committing turns
"looks fixed" into an actual pass/fail gate, with the report-only
instruction for anything that doesn't pass giving the engineer
morning-review evidence instead of an unverified commit. On the scale
gap: the repository's permissions.deny should already block reads of
generated/vendored paths (Read(./**/dist/**), etc.), and if this is
a task the team runs repeatedly, worktree.sparsePaths scoped to
packages/api and packages/web keeps any worktree the run creates
from checking out the rest of the monorepo.
3. Why "auto mode is enough" is incomplete:
Auto mode's classifier blocks categories of risky action — scope escalation, unknown infrastructure, hostile-content-driven behavior — but it is a probabilistic reviewer making a judgment call on each action, not a guarantee, and the docs are explicit that it reduces prompts without guaranteeing safety. An allowlist (and explicit deny rules) is a deterministic, pre-committed boundary that doesn't depend on a classifier correctly recognizing a specific action as risky in the moment — it simply doesn't let a non-allowlisted command run unattended at all. The two are complementary layers, not substitutes: auto mode catches things you didn't think to write a rule for, and the allowlist holds even if auto mode's judgment call on a specific action turns out to be wrong.
Share this question