docs(skill): simplify again after the code review, and demand a trigger for defensive branches

The full loop ran simplify (step 5) before Codex's code review (step 6) and never
again, so nothing walked back what the review added. Adversarial review is asked to
find what could go wrong, so its output is a list of arguments for more code and it
is never asked whether the branch it wants is reachable — accepting objections only
ratchets the diff upward. Step 6 now ends by re-running step 5's checks over the
hunks the review touched.

The other half was earlier than the review. The standing rule already said complexity
is bought only by a measurement, but it said it about performance, so a branch added
for robustness did not visibly fall under it. It now covers hypothetical hosts as
well as hypothetical performance: name the input that reaches a defensive branch and
the image it happens on, or delete it and let the case fail visibly. Step 3's
attack-your-own-plan list asks the same question before any code exists, which is
where it is cheapest to answer.

The case study in references/simplify.md is #3013: 25 lines of code at review, 10
merged. A pure-bash fallback written at implement time for images shipping
with-contenv but not s6-dumpenv — reasoned from the two binaries living in different
s6 packages, never demonstrated on a real image, and defending a case that would have
degraded to the pre-fix behaviour anyway — plus the helper function and second reset
that existed only to serve it. Deleting the fallback deleted all of it. The review's
own objections were correct and cost two tokens on an existing line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
alexbelgium
2026-08-24 14:11:35 +02:00
parent 9e5405fadc
commit d9b92f4e70
3 changed files with 38 additions and 4 deletions

View File

@@ -29,8 +29,8 @@ Triage first, then one of two paths:
resolve bot comments.
- **Full loop** — performance/RAM/CPU work, diagnosis, anything changing a shipped default,
ladder levels 4-6, or an explicit Codex-check request: scope → measure → plan → Codex reviews
the plan → implement → simplify → Codex reviews the code → PR → resolve comments → verify in
production → report.
the plan → implement → simplify → Codex reviews the code → **simplify again** → PR → resolve
comments → verify in production → report.
Escalate mid-flight if a light task grows — touches a default, needs a new script or service, or
reveals a deeper problem.
@@ -42,7 +42,10 @@ where every add-on solves a problem the same way is worth more than a locally ni
design. Prefer reusing or extending over adding a parallel implementation, and when you must add
something new, spell it the way the rest of the repo spells it (naming, option names, script
numbering, file layout). Complexity is bought only by a **measurement** showing a concrete,
user-visible cost on a real host — never by reasoning about hypothetical performance.
user-visible cost on a real host — never by reasoning about hypothetical performance, and never by
reasoning about a hypothetical *host* either. A defensive branch is complexity like any other: name
the input that reaches it and the image or host where that happens, or delete it and let the case
fail visibly instead.
**Repo layout.** `alexbelgium/hassio-addons`; each add-on is a top-level directory. This skill is
checked in at `.claude/skills/hassio-addon-workflow/` (canonical copy). Set the skill root once,
@@ -113,6 +116,9 @@ Attack your own plan before implementing:
- What is the **blast radius** if the assumption underneath it is wrong?
- What am I **inferring** that I could instead **detect at runtime** or **record explicitly**?
Highest-yield question here — see `references/evidence.md`'s failure-mode section.
- For every branch that exists **only to survive something going wrong**: what input reaches it,
on which image? Go and look. A fallback for a configuration you cannot find an instance of is
not robustness, it is a second code path nobody will ever exercise or notice rotting.
Full loop only, before writing code: get Codex's independent read on the plan. Spawn a subagent
whose prompt includes the path `references/codex-review.md` and tells it to follow that file's
@@ -140,11 +146,18 @@ justify the divergence in the PR body — but never at the cost of an isolation
numbered script, not an edit. Case studies of what happens when this check is skipped:
`references/simplify.md`.
## 6. Codex attacks the code (full loop only)
## 6. Codex attacks the code, then simplify what the review added (full loop only)
Same delegated invocation, pointed at `git diff origin/master...HEAD` plus your reasoning per
hunk. Details in `references/codex-review.md`.
Then run step 5's checks again over the hunks the review changed. Adversarial review only ever
argues *for* another branch — that is its job — so accepting objections ratchets the diff upward,
and nothing else in the loop walks it back down. For each accepted objection: is the case it
defends one you have now demonstrated, or one you have merely been told about? Taking a
correctness objection often deletes the code that made it necessary, and a fix that collapses back
to fewer lines than you started the review with is the normal outcome, not a suspicious one.
## 7. Open the PR
CI gates on a PR: **`CHANGELOG.md` updated** (hard fail), the **HA add-on linter**

View File

@@ -39,3 +39,10 @@ codex exec --model gpt-5.6-sol --sandbox read-only --skip-git-repo-check \
**Codex agrees with confident premises.** It has confirmed a wrong conclusion stated too
confidently, and separately caught a genuine methodology error in the same review. Treat its
confirmations with the same scepticism as its objections — especially about the build.
**Its objections ratchet complexity upward.** An adversarial reviewer is asked to find what could
go wrong, so its output is a list of arguments for more code; it is never asked whether the branch
it wants is reachable. Separate "this is wrong" from "this is undefended" before you write
anything: the first is a bug and you fix it, the second is a claim about some host, and it needs
the same demonstration you would demand of a measurement. That is what step 6's second simplify
pass is for.

View File

@@ -13,6 +13,15 @@ Being able to build the complicated thing is not a reason to.
- A resolution cap shipped as a **new init script writing an s6 envdir** — the wrong mechanism
entirely (ladder level 4). Renaming the option to the env var the service already reads
(level 1) would have worked, and the new script did not.
- A `.templates/ha_entrypoint.sh` fix went to review at 25 lines of code and merged at 10. Two
sources of the excess, and neither was caught by the loop: a **pure-bash fallback** written at
implement time for images shipping `with-contenv` but not `s6-dumpenv` — reasoned from the two
binaries living in different s6 packages, never demonstrated on any real image, and the case it
defended would have degraded to the pre-fix behaviour anyway — and a **helper function plus a
second reset** that existed only to serve that fallback. Deleting the fallback deleted all of
it. The rest of the review's objections were correct and cost two tokens on an existing line.
It took the maintainer asking "is this the simplest way possible?" to run the pass that step 6
now requires.
## Checks worth running against your own diff
@@ -25,6 +34,11 @@ Being able to build the complicated thing is not a reason to.
A removal that depends on a host default still needs the same verification as an addition.
- **Is the fix bigger than the thing it fixes?** That is a smell, not a rule — but it usually
means the problem was framed one level too deep.
- **For each defensive branch: what input reaches it, on which image or host?** Go and check,
the way you would check a measurement. If you cannot produce the case, delete the branch — the
situation then fails the way it already fails today, visibly, instead of through a second path
that is never exercised and silently rots as the base images move. Write down in the PR body
what you cut and why, so the next person does not re-add it from the same reasoning.
- **How does this fail in three years**, when the base image, Electron, or upstream has moved?
Code that reads a documented knob keeps working. Code that reaches into private internals
does not.