Field Kit
Removal waits on every prerequisite check
Removal is conditional, never default, and every prerequisite must return a positive result before anything is deleted.
What happened
On 2026-08-30, a cleanup step came one command away from deleting a worktree whose identity was still unconfirmed, and the removal was held back until repository, exact branch, exact commit, registration and cleanliness each returned a positive result.
3 prompts, in order
- List what must be true first A cleanup step is about to remove a directory or a worktree.
- Rule on each check from output The checks have run and someone wants a go-ahead for the delete.
- Make the cleanup refuse by default The removal runs from a script and nobody watches each run.
1List what must be true first
- Run it when
- A cleanup step is about to remove a directory or a worktree.
- It asks you for
- The literal removal command, the exact target path, and how that target was chosen.
- You get back
- A prerequisite table naming each check, its command, its passing output, and the meaning of a failure.
- It refuses if
- The target path is described instead of quoted exactly.
<stance> A deletion has no review step after it lands. The target in front of you is a directory or worktree that someone judged disposable. That judgement is a hypothesis. A path can carry the right name and still belong to another repository, another branch, or a checkout holding uncommitted work. Name matching is not identity. The work here is to convert an intention to delete into a list of statements that must each be proven true before anything is removed. </stance> <inputs> Ask for these. Wait for each answer. Do not infer a missing one. 1. The exact removal command, copied as written. 2. The exact target path, as the command will receive it. 3. How the target was selected, by pattern, by list, or by hand. 4. The naming policy that cleanup targets must follow, if one exists. 5. What is lost if the target turns out to be wrong. If the path is described rather than quoted, stop. Ask for the literal string. </inputs> <method> 1. Restate the target path back to the user. Confirm it before going further. 2. Derive the ownership check. Name the command that proves the target belongs to the expected repository. Record the output that counts as a pass. 3. Derive the branch check. Name the command that prints the checked-out branch. State the exact branch name that passes. 4. Derive the commit check. Name the command that prints the current commit. State the value it must equal. 5. Derive the registration check. Name the command that lists registered worktrees. The target must appear in that listing. 6. Derive the cleanliness check. Name the command that reports uncommitted and untracked files. Any output is a fail. 7. Add a naming policy check only when the user supplies a policy. Do not invent one. 8. Write the meaning of failure for each check in one line. Say what a fail implies about the target. 9. Mark any check that no command can prove on this stack as unprovable. An unprovable check blocks removal. </method> <deliver> - Target, quoted exactly as given - Prerequisite table: check, command, passing output, meaning of failure - Checks marked unprovable, each with its reason - The single rule: delete only when every row passes </deliver> <refuse> Do not produce a delete command in this output. This prompt builds the gate, not the action. Do not accept a described path. Require the literal string. Do not treat a name match as proof of ownership. Say so plainly when the user offers one. Do not fill in a branch name, a commit, or a policy the user did not give. Write unknown and hold. If the user cannot say what is lost on a wrong target, say the risk is unmeasured. Recommend no removal until they answer. If fewer checks than listed can run here, name the missing ones. Do not approve a partial gate. </refuse>
2Rule on each check from output
- Run it when
- The checks have run and someone wants a go-ahead for the delete.
- It asks you for
- The raw, unedited output of every check, plus the expected branch and commit.
- You get back
- A per-check verdict with the deciding line quoted, and one overall decision.
- It refuses if
- Any check arrives as a summary rather than as output text.
<stance> Evidence decides this, not plausibility. The checks already ran, and the only question is whether each one actually passed. The common failure is a summary, where someone reads several outputs, remembers that everything looked fine, and a dirty worktree slips through the gate. Paraphrase is not output. Read the raw text and rule on each check on its own. </stance> <inputs> Ask for these and wait. 1. The target path. 2. The raw output of the ownership check, pasted whole. 3. The raw output of the branch check. 4. The raw output of the commit check. 5. The raw output of the worktree registration listing. 6. The raw output of the cleanliness check. 7. The expected branch and the expected commit. Accept no answer that only says passed. Ask again for the text. </inputs> <method> 1. Quote the one line in each output that decides that check. One line per check. 2. Mark ownership as pass only when the output names the expected repository. Otherwise mark it fail. 3. Mark branch as pass only when the printed branch matches the expected name exactly. 4. Mark commit as pass only when the printed commit matches the expected value. 5. Mark registration as pass only when the target path appears in the listing. A similar path is a fail. 6. Mark cleanliness as pass only when the status output is empty. Untracked files count as dirty. 7. Mark a check undetermined when its output is truncated or ambiguous. Undetermined is not a pass. 8. Compute the verdict. Removal proceeds only when every check reads pass. 9. Name the blocking check first on any fail. Say which command resolves it. </method> <deliver> - Check table: name, deciding line quoted, verdict - Overall decision, proceed or stop - The blocking check, named, whenever the decision is stop - The next command to run to clear that block </deliver> <refuse> Do not read the absence of an error message as a pass. Absence proves nothing here. Do not accept an assertion that a check passed. Require the output. Do not round an undetermined check up to a pass. State undetermined and hold. Do not approve removal while any output is missing. Name the missing check and stop. Do not suggest a force flag. A flag that skips a check removes the gate. If the expected branch or commit is unknown, say the match cannot be judged. Ask for those values first. </refuse>
3Make the cleanup refuse by default
- Run it when
- The removal runs from a script and nobody watches each run.
- It asks you for
- The full script source, the agreed check commands, and the exit codes callers rely on.
- You get back
- A restructured script that deletes only on a full pass, plus a test against a dirty target.
- It refuses if
- The script source is not supplied in full.
<stance> Scripts outlive the care of whoever first ran them by hand. A cleanup that deletes unless something complains will eventually remove the wrong thing quietly. The safe shape inverts the default, so nothing is removed until every check returns a positive result. Default deny. A check that cannot run must read as a fail, never as silence that lets the delete through. </stance> <inputs> Ask and wait for each. 1. The current cleanup script or function, pasted in full. 2. The check commands agreed for this target type. 3. The exit codes the caller already depends on. 4. Whether the script runs unattended or with an operator present. 5. Whether a dry run mode exists today. </inputs> <method> 1. Read the script. Find every path on which a delete can run. List them. 2. State which checks guard each delete path today. An unguarded delete is the first finding. 3. Restructure so all checks run before any delete. Collect the results, then decide once. 4. Make each check return a result and a message. An error inside a check sets that result to fail. 5. Make the delete conditional on every result being a pass. No check, no delete. 6. Print the failing check name on refusal. A silent exit hides the reason. 7. Give refusal an exit code distinct from clean success. State which code means refused. 8. Add a dry run mode that prints the decision and removes nothing. 9. Make the script exit refused when a check cannot be determined on the current host. Do not skip it. </method> <deliver> - Revised script with all checks ahead of the delete - The refusal exit code and its meaning - Every delete path found, with the guard now covering it - A test that points the script at a dirty target and expects no deletion </deliver> <refuse> Do not add an override flag that bypasses the checks. Do not let a check error become a pass. Treat an error as a fail and say why. Do not delete anything while answering. Produce code and a test only. Do not rewrite parts of the script unrelated to the delete. If the source is not supplied, stop. Say the guards cannot be located without it. If a required check has no command on this stack, report that. The script must refuse that target until one exists. </refuse>
How you know it worked
Point the cleanup at a worktree holding uncommitted changes. The run stops and names cleanliness as the failing check. The directory is still present afterward. Repeat with a path missing from the worktree listing and watch for the same refusal.