A follow-through audit of every retrospective, chronicle, plan and report in the Vaults counted
24 promised fixes: 15 live, 9 dead or never built. The worst mistake class, unverified claims
reaching a human, had been written up 11 separate times, and both of its proposed fixes were
still unbuilt. Meanwhile improvement-gate.sh, the hook that enforced "every run improves the
system that does the work", was retired 2026-07-28 and nothing ever replaced it.
The old version of this skill was not badly reasoned. Its nine lenses were sharp. It failed for one structural reason: every output was a ledger row, and a ledger row enforces nothing. A lesson written to a ledger that nothing reads at the moment of action is a gravestone with better formatting.
So version 3 inverts what this skill is for.
| v2 | v3 | |
|---|---|---|
| primary question | what did we learn | did the last learnings actually fire |
| output | ledger rows | installed mechanisms, or an explicit accepted-no-mechanism row |
| when learning happens | here, at end of run | at error time, by error-loop.py; here only for what error time cannot see |
| a finding with no mechanism | acceptable | REFUSED, or recorded as explicitly accepted with a reason |
This skill is no longer the primary learning mechanism, and must not be treated as one.
Error-time closing belongs to _meta/services/error-loop.py, which opens a defect the moment a
gate we own fails and refuses the next commit until a lesson or a waive is recorded. That catches
the single instance while the cause is still warm.
What error-time closing structurally CANNOT see is recurrence across runs: the same mistake in different clothes, three independent reinventions of one missing primitive, a guard that has been quietly dead for six weeks. That, and only that, is what this skill is for.
Proportionality is a hard rule, not a preference. Aria has named the failure out loud: "overengineering the fuck out of this". A retrospective longer than the work it describes is a defect. If phase 0 finds nothing dead and phase 2 finds no recurrence, the correct output is four lines saying so.
A run that adds a new promise while an old one lies dead has made the problem worse, so this phase runs first and cannot be skipped.
- Read the previous retrospective's
mechanisms[]and the last 20 rows of each ledger. - For each promised mechanism, resolve a verdict. LIVE requires two facts: the file exists AND
something invokes it. Prove the second with a grep, never by reading the file.
- wired into a
settings.jsonhook chain, or - a
jobctlregistry entry thatlaunchctl listactually shows loaded, or - a git hook, or
- a script another live script calls.
- wired into a
- Any mechanism whose file exists but which nothing invokes is DEAD, not live. Say so in those words. A file nobody runs is indistinguishable from a file nobody wrote.
- Any mechanism that was retired: find its replacement and verify the replacement is LIVE. A retirement with no live successor is a REGRESSION and outranks every new finding this run.
- Count: promised, LIVE, DEAD, NEVER BUILT. That ratio is the headline of the report.
OUTPUT: followthrough[], one row per prior mechanism with {name, verdict, invoked_by, evidence},
plus the four counts. A phase 0 with no counts did not run.
STOP CONDITION: if anything is DEAD or a REGRESSION, fixing or formally retiring it takes priority over every new lesson below. Report that and act on it first.
Reason from artifacts, never from recollection. Pull git log and the diff, reverted commits and
abandoned branches, failed test runs, guard refusals, escalations, receipts and verdicts, and the
error-loop.py entries opened this run.
Guard refusals are the highest-signal item in that list and the most commonly discarded. A hook that refused something recorded a mistake a machine could already see; that is a finished experiment, free of charge.
OUTPUT: a quotable evidence list. Anything not traceable to a commit, a log line, a receipt or a
recorded decision is marked inference, never finding.
Single instances were already handled at error time. This phase looks ACROSS runs, which is the one thing no error-time hook can do.
- For each finding, search the ledgers, chronicles and agentdb for prior instances. Search the CONCEPT from a second vocabulary, not just the words this run happened to use: the error code, the table name, the route, the domain noun. A negative grep is evidence about your pattern, not about the world.
- Count instances and list every date. Two or more is a recurrence and demands a mechanism.
- Ask the question that generalizes: are these one mistake in different clothes? Three independent reinventions of one primitive that has no name yet?
- Then run at most TWO further lenses, whichever bite for this run. Skip the rest; a lens with no
artifact is narration.
- surprise: where was my prediction most wrong?
prediction -> reality -> evidence. - belief-update: what do I believe less now, and what contradicted it?
- anomaly: what does not fit my explanation? Preserve it with a
preserve_until_condition; do NOT resolve an anomaly you had to reach for an explanation for. Anti-convergence is load-bearing: a weird result kept alive unexplained for three runs is worth more than a tidy story now. - abstraction: what did I build manually that is secretly a reusable primitive?
- surprise: where was my prediction most wrong?
OUTPUT: recurrences[] with {class, instances[], dates[], prior_promises[]} and at most two
lens artifacts.
This phase is why the skill exists. Everything above is input to it.
A recurrence leaves this phase in exactly one of two states. There is no third.
- INSTALLED, with all four fields filled and verified:
failure_class: what it refuses, stated as a class not an instanceinstall_path: the real file, and what invokes it, proven by greprefusal_condition: exactly when it says nofiring_test: the one-line command that makes it fire, RUN, with its output pasted
- ACCEPTED, explicitly:
{class, why_no_mechanism, who_accepted, revisit_when}. An honest accepted row is a good outcome. A silent omission is not.
Rules that hold without exception:
- Prose is never a mechanism. Adding a sentence to a CLAUDE.md, a skill, or a reference file does not count and must never be recorded as a fix. Every recurrence class in the audit that was "fixed" in prose recurred.
- Prefer the machine that refuses at the moment of action over the one that reports afterwards.
A gate at Stop is a retrospective wearing a hook's clothes; that is precisely how
improvement-gate.shdied. - Derive authority from something the caller does not control: a pinned precedent commit, a canon document, the deployed artifact, a prior measurement. A gate that grades work against an expectation the caller supplies can never fail.
- A new mechanism must COMPOSE with the existing ones, never short-circuit past them. Reaching
for
continuein a function that accumulates a verdict is the tell. A new fault state may displace the healthy state and nothing else. - Before recommending any checker, RUN it on the current tree. A gate that breaks the build on first run is worse than no gate.
- Break it on purpose in an isolated copy and confirm the checker catches it. An unfired guard is a guess.
Test discipline, measured 2026-09-03: of about 40 test suites examined, 4 had any receipt of ever catching a real defect, and roughly 36 were wired to nothing at all. So a test earns its keep only when BOTH hold: something runs it without a human remembering to, and it names the specific defect class it exists to catch, proven by failing against the broken state first. If you cannot name the observed defect, do not write the test.
OUTPUT: mechanisms[], every row INSTALLED with a pasted firing test or ACCEPTED with a reason.
A row that is neither is a defect in this report.
_meta/reports/retrospective-<date>.jsonaskernel.retrospective-result/v2, carryingfollowthrough[], the four counts,recurrences[],mechanisms[], and the lens artifacts.mechanisms[]is what the NEXT run's phase 0 audits, so name each one exactly as installed.- Ledger writes only where the row will be read at a moment that matters. A belief nothing consults is not worth a line.
agentdb learnfor anything that must fire during future work rather than at the next retrospective. Concrete nouns; a lesson nobody can recall is not stored.- Chronicle at the tier
_meta/services/chronicle-triage.shcomputes. Not the tier you feel. - Hard cap: if the report is longer than the work it describes, cut it. Nothing dead in phase 0 and no recurrence in phase 2 means the whole output is four lines saying exactly that.
OUTPUT: the report path, the four counts, and the count of mechanisms installed versus accepted.
- Ledger as gravestone. A learning written where nothing reads it at the moment of action. 24 promises, 9 dead. Fix: phase 3 or an explicit accepted row.
- Prose as fix. The rule gets a better sentence and recurs anyway. Every prose-only class in the audit recurred.
- Enforcement at Stop. The session has moved on and the nag competes with wrapping up.
improvement-gate.sh, retired with no successor. - Retirement without replacement. Three hooks retired 2026-07-28 on a real argument; nothing took over their function. Phase 0 step 4 exists for this.
- The guard nobody invokes. The file is perfect and no chain calls it. Phase 0 step 2 demands the grep for exactly this reason.
- Hardening that breaks the thing. A launcher hardened to a root-owned path that was never installed left an "automated" pipeline unschedulable for nine days while its own status read OK. Always prove the good case still runs after adding a gate.
- The report longer than the work. Buries the two facts that mattered and spends the reader's attention, the scarcest resource here.