$npx -y skills add UnpaidAttention/fable5-methodology --skill course-correctionRecognize mid-task that the current APPROACH is wrong and execute a disciplined stop-revert-replan instead of accumulating patches. Trigger this when any alarm fires during ongoing work: your last two changes each fixed the previous change's symptom; the design keeps growing spec
| 1 | # Course Correction |
| 2 | |
| 3 | The discipline of stopping. Failed approaches are cheap if abandoned early and ruinous if |
| 4 | defended. Code is cheap to rewrite from correct understanding; understanding is what the |
| 5 | failed attempt bought you — keep it, discard the code. |
| 6 | |
| 7 | ## Step 1: Recognize the alarms |
| 8 | |
| 9 | Treat ANY of these as an alarm, not background noise: |
| 10 | |
| 11 | 1. **Patch stacking** — change N exists to fix what change N-1 broke. |
| 12 | 2. **Growing exceptions** — "...except for POSTs, and except admin users, and except...". |
| 13 | 3. **Fighting the framework** — overriding internals, monkey-patching, copy-pasting library |
| 14 | code to force your approach through. |
| 15 | 4. **Spreading diff** — the "small fix" touches 8 files and you can't say where it stops. |
| 16 | 5. **Unexplainable state** — your honest answer to "why is this line here?" is "the error |
| 17 | went away". |
| 18 | 6. **Three strikes** — 3 failed attempts on the same failure. |
| 19 | 7. **Sunk-cost narration** — the thought "too far in to restart" is itself the strongest |
| 20 | signal that restarting is cheaper than continuing. |
| 21 | |
| 22 | Rule: the second workaround stacked on a first is an automatic alarm — the real cause is |
| 23 | upstream of both. |
| 24 | |
| 25 | ## Step 2: Freeze |
| 26 | |
| 27 | No further edits toward the current approach. Not one more "quick try". The next keystrokes |
| 28 | are documentation, not code. |
| 29 | |
| 30 | ## Step 3: Snapshot knowledge, not code |
| 31 | |
| 32 | Write down, in a note or message: |
| 33 | - The goal (re-read the original request — it has drifted in your memory). |
| 34 | - Approaches tried, and what each attempt REVEALED (the constraint you didn't know, the |
| 35 | behavior that surprised you). |
| 36 | - What is now known to be true / known to be false / still unknown. |
| 37 | |
| 38 | This snapshot is the asset the failed work purchased. Without it, the revert wastes everything. |
| 39 | |
| 40 | ## Step 4: Revert to last known-good |
| 41 | |
| 42 | `git stash`, `git checkout -- .`, or delete the scratch files. Keep the snapshot; discard the |
| 43 | patches. Exception: individual pieces that are independently correct and verified (a test you |
| 44 | wrote, a helper that works) may be kept — but only pieces you can justify in isolation. |
| 45 | |
| 46 | ## Step 5: Re-plan from discovered constraints |
| 47 | |
| 48 | The new plan MUST explain why the old approach failed. If it doesn't, you are about to repeat |
| 49 | the same approach with different syntax. Check the new plan against every constraint listed in |
| 50 | the snapshot before writing code. |
| 51 | |
| 52 | ## Step 6: Escalate when scope changed |
| 53 | |
| 54 | If the new plan changes scope, cost, or the deliverable itself, tell the user BEFORE executing — |
| 55 | one short paragraph: what didn't work, why (the discovered constraint), what you propose |
| 56 | instead. A clear "here's what I ruled out and why the approach must change" is a legitimate |
| 57 | deliverable; a fourth blind patch is not. |
| 58 | |
| 59 | ## Step 7: Correcting already-delivered mistakes |
| 60 | |
| 61 | If you discover a defect in work you already reported as done: say so immediately and plainly |
| 62 | ("the earlier fix misses case X — correcting now"), fix it, re-verify, re-report. Never |
| 63 | quietly fold the correction into the next unrelated diff. |
| 64 | |
| 65 | ## Worked example |
| 66 | |
| 67 | Task: add per-user rate limiting to an API. |
| 68 | |
| 69 | Attempt 1: decorator storing counters in a module-level dict. Test fails — counters reset |
| 70 | every deploy and are per-worker. Patch: move dict behind a lock. Still per-worker. Patch 2: |
| 71 | pickle counters to disk on shutdown. Now tests are flaky under parallel workers. |
| 72 | |
| 73 | Alarm fired: patch 2 exists to fix patch 1's symptom (patch stacking) — and honestly, the |
| 74 | disk-pickle line only exists because "the test went away". |
| 75 | |
| 76 | - Freeze. Snapshot: "Goal: per-user rate limit across 4 workers. Learned: state must be |
| 77 | shared across processes → in-process storage is disqualified, not fixable. Unknown: is |
| 78 | Redis available? (check lockfile — yes, used for sessions)." |
| 79 | - Revert both patches. Keep the rate-limit test — it's independently correct. |
| 80 | - New plan, which explains the failure: counters live in Redis (`INCR` + `EXPIRE`, atomic, |
| 81 | shared across workers). The old approach failed on a structural constraint, not a bug. |
| 82 | - Result: 15 lines, tests pass under parallel workers. Total cost of the revert: the snapshot |
| 83 | paragraph. Cost of NOT reverting: a pickle-based distributed-state system defended forever. |
| 84 | |
| 85 | ## Done when |
| 86 | |
| 87 | The dead-end code is reverted; the knowledge snapshot exists in writing; the new plan names the |
| 88 | d |