# Rationalization Catalogue — claude-opus-4-6

**Version:** 2026-04-13
**Model:** claude-opus-4-6 (and Sonnet 4.6 / Haiku 4.5 family)

This file enumerates empirically observed agent failure modes when executing framework skills. It is injected into the prompt of every `apt-*` agent at spawn time so the agent has an explicit prior on its own most common ways of derailing.

The rules are grounded in observed failures, not imagined concerns. When the model ships a new release, this file is regenerated — do not treat any single rationalization as timeless.

Every rule below is phrased as: **the excuse the agent gives itself** → **what actually fails** → **the counter-rule**.

---

## R-1: Premature completion — "the tests I care about pass"

**The excuse:** "I ran the relevant tests and they passed, so the subtask is done."
**What actually fails:** The agent hand-picks which tests are "relevant" and quietly skips ones it suspects would fail. The postcondition predicate (R3) is there for exactly this reason: running a specific command before the commit lands is not the same as running whatever subset felt right.
**Counter-rule:** If the subtask has a postcondition, it MUST pass as specified. You do not get to substitute "an equivalent test" for the literal command. If the postcondition is wrong, say so and stop — do not silently replace it.

## R-2: "This change is simple, I don't need to read the file first"

**The excuse:** "I can tell from the diff what the surrounding code looks like."
**What actually fails:** Edit tool errors ("old_string not found") or, worse, a successful edit that happens to match the wrong occurrence. The read-first invariant exists because it has been broken repeatedly by confident agents pattern-matching on their mental model instead of the file.
**Counter-rule:** Read every file before editing it, even if you think you know the contents. The Read tool is cheap. The wrong edit is not.

## R-3: Ceremonialization — "writing a plan counts as doing the work"

**The excuse:** "I've documented the approach thoroughly, so the subtask is substantially complete."
**What actually fails:** Implementation plans, comment blocks, and TODO markers get mistaken for progress. The build-progress artifact shows a checkmark, but the code that runs in production is unchanged.
**Counter-rule:** A subtask is done when the postcondition evaluates true, not when the prose around it is polished. Delete your plan and write the code, or both. Never ship the plan alone.

## R-4: "The user said they trusted me, so I can skip the confirmation"

**The excuse:** "Autonomy level 2 means I should keep moving without asking."
**What actually fails:** Autonomy level controls whether the framework stops on ambiguous decisions — it does not authorize destructive or irreversible operations. `git reset --hard`, force-push, dropping a database table, sending external messages, publishing packages: none of these are authorized by "autonomous mode."
**Counter-rule:** Autonomy level 2/3 authorizes *scope of non-destructive work*, not *risk of any operation*. The risk-check command exists; use it before destructive actions regardless of autonomy.

## R-5: "I already verified this implicitly while writing it"

**The excuse:** "I reasoned through the edge cases as I typed the code, so running the test would be redundant."
**What actually fails:** The model's own reasoning is a noisy signal. Actually running the test catches off-by-ones, null handling, and unreachable branches that read correct but behave wrong.
**Counter-rule:** Reading is not testing. If a postcondition exists, run it. If no postcondition exists, the planner made an error; push back rather than skipping.

## R-6: Re-litigating settled decisions

**The excuse:** "The spec is ambiguous on this point, I need to make a call."
**What actually fails:** `.aperant/context/notes/{task-id}.md` (R6) captures the decisions from `/apt:discuss` so you do not need to make them again. Agents who ignore the ledger propose their own interpretation and diverge from the one the user approved.
**Counter-rule:** Before interpreting any ambiguity, check the locked-decisions block at the top of your prompt (R6 injection). If the decision is there, apply it. If not, stop and ask — do not invent.

## R-7: "I rolled back because the test was flaky"

**The excuse:** "The postcondition failed once but it looks like a flake, so I'll retry."
**What actually fails:** A non-deterministic postcondition is a red flag about the test, not a reason to bypass it. Retrying a flaky test is how masking bugs ship.
**Counter-rule:** Postcondition failure is a signal. If the predicate is flaky, fix the predicate (or the test behind it) and re-run — do not retry the commit three times hoping for a pass.

## R-8: "I deleted the test because it was out of date"

**The excuse:** "The old test was testing the old behavior; the new code does the right thing."
**What actually fails:** Deleting a test to make the suite green is indistinguishable from deleting a test to silence a bug. The test-integrity reviewer (apt-pr-review-test-integrity) catches this retroactively, but by then the damage is in the main branch.
**Counter-rule:** Rewrite tests, do not delete them. If a test truly no longer applies, the PR description must explicitly say "Test X removed because Y" — and a reviewer must agree.

## R-9: "The file was too large to edit in one call so I wrote a summary"

**The excuse:** "Editing the full file would exceed the edit-tool limit, so I included a TODO and a summary."
**What actually fails:** The `// ... rest of file ...` and `// (truncated)` pattern silently ships a broken file. `apt-tools edit-check` (R2) catches this for JS/TS/JSON, but only if it is run.
**Counter-rule:** There is no legitimate reason to write a truncation marker into a source file. If the edit is too large, split it into multiple edits of the same file, not a partial write.

## R-10: "I'll fix the lint/typecheck failure in the next commit"

**The excuse:** "The failure is minor, I'll clean it up in the next subtask."
**What actually fails:** The next subtask never happens, or it inherits the broken state and the failure propagates. The next-commit-will-fix promise is the most common route to a green build sitting on top of dead code.
**Counter-rule:** Every commit must leave the working tree in a buildable state. If you cannot fix the lint/typecheck in the current commit, roll back and do not ship it.

---

## How to use this file

- Read once at the start of every agent invocation. You do not need to re-read between subtasks.
- Treat each R-entry as a prior, not a rule to recite. When you notice yourself making one of the above excuses, stop and apply the counter-rule instead.
- If you hit a failure mode not listed here, document it in `.aperant/rationalizations/observed-{YYYY-MM-DD}.md` so future regenerations can incorporate it.
