diagnosing-bugs
Skill4 starsChanged 8 months agoUnlicenseView on GitHub
Use when facing a bug with no obvious cause, a performance regression, or 5+ test failures at once. Triggers on: "diagnose", "debug this", "something broken", "no idea why", "flaky", "slow since", "fix multiple failing tests".
We split this skill into the rules it gives an agent
41 rules. 7 are common, 29 are distinctive, and 5 are very specific to this project.
7
Common
Most of the 180 edited versions we read keep these.
29
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
When multiple failures exist — broken test suites, migration fallout, dependency upgrades, non-TDD development sessions — do not fix them …
- Distinctive: line 44
Goal: Ensure your failure map is trustworthy.
- Distinctive: line 48
No recent changes to fixtures, models, or shared code.
- Distinctive: line 75
If failures span unit, integration, and E2E — fix the fastest tier first.
- Distinctive: line 89
Run the full failing tier with serialized output:
- Distinctive: line 101
Read every traceback. Group failures by root cause, not by file:
- Distinctive: line 117
Before triaging all failures, determine which are caused by your changes vs pre-existing.
- Distinctive: line 137
Do this for the smallest plausible set of failures — don't run the full suite just to check relevance.
- Distinctive: line 166
Frequency fallback: If unclear, pick the test with the most common error signature.
- Distinctive: line 184
Remove only owned temporary instrumentation.
- Distinctive: line 188
After all offenders are fixed: commit the wave.
- Distinctive: line 190
Pattern fixes span multiple files: If the root cause is a shared pattern (e.g., the same callback in N templates, the same import in M …
- Distinctive: line 195
Commit strategy for cross-layer fixes: When a single logical fix touches files across multiple layers (backend → message bus → JavaScript → …
- Distinctive: line 217
Exception: If the fix touches entirely unrelated modules (e.g., fixing a payment bug AND a warehouse bug in the same session), use separate …
- Very specific: line 221
Feature parity audit: When you fix a bug or add a feature in one module/template/consumer, check if similar ones have the same fix.
- Distinctive: line 236
If a similar file is missing a handler that exists in the fixed file, it's likely a feature gap — not a bug yet, but it will become one …
- Distinctive: line 246
Run only the fixed offenders' tests:
- Very specific: line 256
Critical: always use --dist loadgroup with xdist.
- Distinctive: line 258
If any offender fails → fix immediately, re-verify.
- Distinctive: line 259
If all offenders pass → proceed to step 5.
- Distinctive: line 267
Run all previously-failing tests:
- Distinctive: line 306
Goal: Verify the required regression coverage on the cleaned candidate.
- Very specific: line 313
After the fires are out: if the triage revealed E2E-only assertions that lower tiers could carry (the usual reason triage was this slow), …
- Common: line 367
Property / fuzz loop: If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
- Common: line 368
Bisection harness: If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so …
- Common: line 369
Differential loop: Run the same input through old-version vs new-version (or two configs) and diff outputs.
- Common: line 376
Treat the loop as a product.
- Common: line 401
If you catch yourself reading code to build a theory before this command exists, stop — jumping straight to a hypothesis is the exact …
- Distinctive: line 405
When the server says "success" but the UI is wrong, the bug is almost always in one of these patterns:
- Distinctive: line 419
No error. Silent failure.
- Very specific: line 421
Check: handlers[data.type] returns undefined.
- Distinctive: line 440
When debugging a UI bug with server-side success, check these four patterns in order.
- Distinctive: line 446
When E2E tests fail mysteriously, check in order:
- Common: line 460
Run the loop. Watch it go red — the bug appears.
- Common: line 485
Do not proceed until you have reproduced and minimised.
- Distinctive: line 500
If the experiment is inconclusive, tighten the loop further (Phase 1) before forming new hypotheses.
- Distinctive: line 510
If the hypothesis is about internal state (data flow, timing, caching), add instrumentation to make the invisible visible.
- Very specific: line 571
If test_page_X expects a toast in container A and test_page_Y expects it in container B, one of them has the wrong selector.
- Distinctive: line 573
Check which matches the template's actual structure.
- Distinctive: line 576
If the test asserts content on an element that is actually an HTMX target (not a toast container), the test has the wrong selector.
- Distinctive: line 585
If the test expectation is wrong, fix the test, not the code.
“Common” is measured: kept by at least 6 in 10 of the 180 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Diagnosing Bugs
A discipline for hard bugs. Skip phases only when explicitly justified.
When exploring the codebase, read the project's domain glossary — CONTEXT.md unless the domain doc config points elsewhere — for a mental model of the modules involved, and check the ADRs covering the area you're touching, so a hypothesis doesn't contradict a decision already made.
Redact
This skill has you show commands, outputs and captured artifacts. Redact every secret first — write <REDACTED> in its place. Build loops against env vars, so the credential stays in the environment rather than in what you show. Captured artifacts carry auth headers: quote only the lines that carry the signal.
The whole skill in one breath: build a tight feedback loop — one command that is fast (seconds), deterministic, and red-capable (goes red on this exact bug) — then minimize the repro and test hypotheses one variable at a time. Record a representative permanent regression observed red for this bug's reason before production repair; verify it green and rerun the original scenario afterward. If 5+ failures exist at once, run Multi-Failure Triage first; otherwise start at Phase 1.
Multi-Failure Triage (when 5+ failures exist)
When multiple failures exist — broken test suites, migration fallout, dependency upgrades, non-TDD development sessions — do not fix them one-by-one with full-suite runs between each fix. That wastes feedback-loop time on tests you already know pass.
Full suite is a gate, not a feedback loop.
This procedure is tier-agnostic: it works for unit, integration, E2E, or any mix. Use the reference commands as templates — adapt to your project's tooling.
Output capture rule
| tee [filename] is the ONLY allowed method for capturing test output — tail, head, and bare >/>> are forbidden (test-run rule: testing-workflow skill; the general rule for every command whose output you read is construction-craft Critical rule 8). Triage-specific consequence: after running with | tee, read or grep the log file — never re-run the same pytest command just to inspect output. The file already has everything; re-running wastes a full-suite cycle.
Timeout guidelines
Set both timeout layers explicitly on every triage run — outer (harness budget) and inner (--timeout per test), outer > inner. The tier-by-tier floors live in the testing-workflow skill's timeout doctrine; for triage the floors that matter most are --timeout 600 for E2E and an outer budget of 1 hour — a run killed by a default 60s timeout reports on the runner, not the tests, and corrupts your failure map.
Step 0 — CALIBRATE (run once, before triage)
Goal: Ensure your failure map is trustworthy. Don't triage on corrupted data.
Skip 0a and 0b if all of the following are true:
- You just ran the full suite and the failure set matches what you expect.
- No recent changes to fixtures, models, or shared code.
- The failures span multiple unrelated modules (unlikely to be xdist contamination).
Skipping is safe because xdist contamination and flakes both produce inconsistent failure sets — if your set is stable across runs, the calibration step adds no value.
0a. Verify parallel stability (if you suspect xdist issues).
# Serial baseline
pytest tests/<tier>/ -n 1 --timeout 600 --tb=line -q | tee /tmp/serial_failures.log
# Parallel run
pytest tests/<tier>/ -n auto --dist loadgroup --timeout 600 --tb=line -q | tee /tmp/parallel_failures.logCompare failure sets. If they differ → xdist contamination detected. Tests that fail under parallelism but pass serially are likely sharing state. Add xdist_group markers to force them onto the same worker, then re-calibrate. Do not proceed to triage until serial and parallel failure sets match.
0b. Quarantine flakes.
pytest tests/<tier>/ --timeout 600 --count=3 -qTests that fail inconsistently are flakes, not bugs. Quarantine them (xfail, skip, or fix separately). They inflate the failure count and corrupt your cascade map. A 50%-flake test masquerades as a real failure half the time.
0c. Split by tier (if multiple tiers are failing).
If failures span unit, integration, and E2E — fix the fastest tier first.
- Unit tests → run on host, feedback in seconds. Fix these first — fastest ROI.
- Integration tests → run in Docker, feedback in tens of seconds. Keep the container warm.
- E2E tests → run in Docker with browser, slowest. Fix last.
Fixing unit tests often collapses the E2E failure count dramatically. Don't touch E2E until faster tiers are green.
Step 1 — MAP (one full run, serialized)
Goal: Build a reliable failure map. One expensive run, done right.
Run the full failing tier with serialized output:
# With xdist (recommended)
pytest tests/<tier>/ -n auto --dist loadgroup --timeout 600 --json-report -q | tee /tmp/failures_round_0.log
# Without xdist
pytest tests/<tier>/ --timeout 600 --json-report -q | tee /tmp/failures_round_0.logSave the output as failures_round_0.json. You'll diff against this file in later rounds.
Read every traceback. Group failures by root cause, not by file:
| Grouping signal | Likely root cause |
| Same exception type across files | Shared import, fixture, or model change |
| Same module in traceback | Broken signal handler, service, or utility |
AttributeError / ImportError cluster | Renamed/moved function or missing migration |
AssertionError with same expected value | Changed behavior or hardcoded expectation |
| Client sees wrong UI but server logs show success | Client-side state not updated, or HTMX/server-swap overwrote injected content |
| Async message ordering (client sends X, server processes Y first) | Race condition in queue/sync logic |
| Test passes serially but fails parallel, or vice versa | Timing dependency masked by execution order |
Expect 38 failures → ~8-12 actual problems. One fix cascades through its cluster.
Quick relevance check (before deep triage)
Before triaging all failures, determine which are caused by your changes vs pre-existing. This prevents wasting effort on unrelated bugs.
Method: git stash + targeted re-run.
# Run only the failures most likely related to your changes
# (same module, same consumer, same template family)
git stash
pytest tests/<related_tier>/ -k "test_a or test_b" -n 1 --timeout 300 -q | tee /tmp/before.log
git stash pop
pytest tests/<related_tier>/ -k "test_a or test_b" -n 1 --timeout 300 -q | tee /tmp/after.log
diff /tmp/before.log /tmp/after.log- Same fail before and after → pre-existing. Skip for this wave.
- Passed before, fails after → regression. High priority.
- Failed before, passes now → your fix already worked (maybe from an earlier commit in this session).
Do this for the smallest plausible set of failures — don't run the full suite just to check relevance. A 3-test targeted run takes 2 minutes; a full E2E run takes 5+.
Why this matters: In a session with 24 failures, 20 may be pre-existing. Spending 20 minutes investigating pre-existing bugs instead of fixing the 4 you actually caused is a 5:1 waste ratio.
Step 2 — HYPOTHESIZE (categorize + rank)
Goal: Identify which fixes will cascade the furthest. This is hypothesis formation — you're guessing at root causes. The procedure's job is to make each guess cheap to test.
2a. Group failures.
Primary grouping: module (file/directory). Secondary grouping: error signature (same exception type + same root traceback frame).
2b. Rank groups by failure count descending.
The group with the most failures has the highest coupling — fixing its root cause resolves the most downstream tests.
2c. Pick the "offender candidate" per group.
Within each ranked group, identify the test whose setup/fixture/code path appears in the most other failures' tracebacks. Heuristics, in order:
- Shared fixture: Which fixture is referenced across the most failing tests?
- Shared error frame: Which traceback frame (file:line) appears most often?
- Shared module import: Which module is imported by the most failing tests and could be the source?
- Frequency fallback: If unclear, pick the test with the most common error signature.
2d. Produce an ordered list.
Wave N offenders: [offender_1 (module A, 12 failures), offender_2 (module B, 8 failures), ...]Step 3 — FIX WAVE
Goal: Repair the ranked offender candidates as vertical slices within the wave.
Work each offender through this sequence:
- Record a representative permanent regression observed red for this bug's reason on unfixed code before production repair. Reuse an existing permanent test if its observed failure reaches the actual boundary; a throwaway loop alone is not that record.
- Apply the bounded repair, verify the permanent regression green, and rerun the original scenario before moving to the next offender.
- Remove only owned temporary instrumentation; preserve unrelated instrumentation and work. Verify actual removal, then rerun the permanent regression, original scenario and appropriate checks on the cleaned candidate as in Phase 5.
- One commit per offender (or one commit per module if offenders are related).
- Do not fix scattered non-offenders. Resist the urge to "knock out easy ones" — they're hardest to cluster and you'll dig rabbit holes.
After all offenders are fixed: commit the wave.
Pattern fixes span multiple files. If the root cause is a shared pattern (e.g., the same callback in N templates, the same import in M modules), fix all instances in one commit. One commit per pattern, not per file. The pattern is the offender; the files are just locations.
Commit strategy for cross-layer fixes. When a single logical fix touches files across multiple layers (backend → message bus → JavaScript → templates), use one commit for the entire fix, not one commit per file.
Reason: splitting across commits creates intermediate states where half the fix is deployed. The backend sends a message that no template handles. The template has a handler that no backend produces. These intermediate states are broken and confusing in git history.
Commit message format:
fix(scope): describe the logical fix
Files changed:
- apps/x/consumer.py (add handler)
- static/js/y.js (fix callback ordering)
- templates/z.html (add toast, skip HTMX for temp)
- tests/e2e/test_*.py (fix expectation)
Root cause: one sentence explaining why the fix works.Exception: If the fix touches entirely unrelated modules (e.g., fixing a payment bug AND a warehouse bug in the same session), use separate commits.
Feature parity audit. When you fix a bug or add a feature in one module/template/consumer, check if similar ones have the same fix.
# Find all files that implement the same handler/callback pattern
grep -rl "<handler_name>" templates/ static/ apps/
# Find all files that handle the same message type
grep -rl "<message_type>" apps/ static/For each match:
- Does it have the same handler/callback/method?
- Does it handle the same message types?
- Does it have the same error handling?
If a similar file is missing a handler that exists in the fixed file, it's likely a feature gap — not a bug yet, but it will become one when someone uses that code path. Fix it in the same commit.
Step 4 — VERIFY WAVE (targeted run)
Goal: Confirm each fix in isolation before checking cascades.
Run only the fixed offenders' tests:
# With xdist
pytest tests/<tier>/ -k "test_a or test_b or test_c" -n auto --dist loadgroup --timeout 600 -x
# Without xdist
pytest tests/<tier>/ -k "test_a or test_b or test_c" --timeout 600 -xCritical: always use --dist loadgroup with xdist. Without it, grouped tests land on different workers and shared-state tests explode.
- If any offender fails → fix immediately, re-verify. Do not proceed to step 5.
- If all offenders pass → proceed to step 5.
Step 5 — CASCADE CHECK (diff-based)
Goal: See what turned green from the wave, detect regressions, and plan the next wave.
Run all previously-failing tests:
# --lf uses .pytest_cache to replay last-failing tests
pytest tests/<tier>/ --lf -n auto --dist loadgroup --timeout 600 --json-report -q | tee /tmp/failures_round_N.logSave as failures_round_N.json. Diff against the previous round:
| Status | Action |
| Newly green | Log cascade count per fix. This is architectural data — high cascade = high coupling. |
| Still red | Keep for next wave. |
| Newly red | REGRESSION. Fix immediately. Do not proceed to step 6 until regressions are resolved. |
If regressions found: fix → re-run step 4 for the affected module → re-run step 5 until clean.
Operational note: .pytest_cache must persist across Docker runs for --lf to work. Mount it as a volume:
volumes:
- ./.pytest_cache:/app/.pytest_cacheStep 6 — CONVERGENCE CHECK
Goal: Decide whether to continue batching or switch to precision work.
Count remaining failures:
- ≤ 5 remaining: Switch to the standard single-bug workflow (Phase 1-5 below). Batch overhead isn't worth it for isolated bugs. Use
pytest -xand fix one at a time. - > 5 remaining: Go back to Step 2 with the remaining failures. Repeat the wave cycle.
Step 7 — PROJECT REGRESSION GATE
Goal: Verify the required regression coverage on the cleaned candidate.
Use testing-workflow and the project's check policy to choose proportionate affected-tier checks and broader gates. Run required cross-cluster checks between waves; reserve whole-product E2E/full suites for the project/risk conditions that require them, not automatically after every cluster. Explicit repository full-suite mandates still apply. Missing or failed required coverage blocks completion.
- Required checks green: Done. Commit.
- Any new red: Diagnose the new failure as a separate bug using the single-bug workflow; do not weaken its assertion.
After the fires are out: if the triage revealed E2E-only assertions that lower tiers could carry (the usual reason triage was this slow), schedule the restore-test-pyramid skill — see the tier-strategy section of testing-workflow.
Anti-patterns
| Anti-pattern | Why it fails | |
| Fix one failure, run full suite, repeat | Wastes time per iteration on known-passing tests | |
| "Knock out scattered ones for momentum" | Scattered failures are hardest to cluster — you'll dig rabbit holes when a shared fixture is the real culprit | |
| Skip required affected checks between clusters | Cross-cluster regressions accumulate; use the project's proportionate checks between waves and its required broader gates | |
| Assume N failures = N fixes | Clusters collapse the count. Diagnose first, count later | |
| Start triage without calibrating xdist | Parallelism-induced failures look like real bugs. You'll chase ghosts. | |
Forget --dist loadgroup with xdist | Grouped tests land on different workers → nondeterministic failures → debugging the wrong problem | |
Not persisting .pytest_cache in Docker | --lf resets every run → every "targeted" run becomes a full collection → time savings vanish | |
Using tail, head, or bare >/>> on test output | Destroys insight → user cannot kill a bad run early, must wait for it to end just to confirm what they suspected. ` | tee` is the ONLY allowed method. |
| Assuming server logs tell the whole story | Client-side bugs (JS errors, DOM mutations, WebSocket frame ordering) are invisible in server logs. If the server says "success" but the UI is wrong, the bug is between the server response and the rendered DOM. |
When to fall through to single-bug workflow
Once triage reduces failures to ≤5, switch to the standard single-bug workflow (Phase 1-5 below). The multi-failure approach is for the initial blast radius; the single-bug loop is for precision work.
Phase 1 — Build a feedback loop
This is the skill. Everything else is mechanical. If you have a tight pass/fail signal for the bug — one that goes red on this bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you.
Spend disproportionate effort here. Be aggressive. Be creative. Refuse to give up.
Ways to construct one — try them in roughly this order
- Failing test at whatever seam reaches the bug — unit, integration, e2e.
- Curl / HTTP script against a running dev server.
- CLI invocation with a fixture input, diffing stdout against a known-good snapshot.
- Headless browser script (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network.
4.5. WebSocket frame capture. If the bug involves real-time communication
(WebSockets, SSE, channel layers), capture the actual frames:
- Playwright:
page.on("websocket", ws => ws.on("framesent", ...)) - Chrome DevTools: Network tab → WS → copy as HAR
This is how you prove the server sent one message type but the client
received (or ignored) a different one. Without this, you're guessing
at message ordering.
4.6. Browser console capture. Add temporary
console.logto JavaScript, capture via Playwright:page.on("console", msg => logs.append(msg.text)). This is the JS equivalent of server logging — and just as necessary when the bug lives in the browser. 4.7. DOM state inspection. Usepage.evaluate(() => document.querySelector(...).innerHTML)orpage.query_selector()to check what's actually in the DOM at the failure point. If an element "should" be visible but isn't, this tells you whether it was never rendered, was removed by another script, or is hidden via CSS.
- Playwright:
- Replay a captured trace. Save a real network request / payload / event log to disk; replay it through the code path in isolation.
- Throwaway harness. Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call.
- Property / fuzz loop. If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
- Bisection harness. If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can
git bisect runit. - Differential loop. Run the same input through old-version vs new-version (or two configs) and diff outputs.
- HITL bash script. Last resort. If a human must click, drive them with
scripts/hitl-loop.template.shso the loop is still structured. Captured output feeds back to you.
Build the right feedback loop, and the bug is 90% fixed.
Tighten the loop
Treat the loop as a product. Once you have a loop, tighten it:
- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.)
- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".)
- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.)
A 30-second flaky loop is barely better than no loop; a 2-second deterministic one is tight — a debugging superpower.
Non-deterministic bugs
The goal is not a clean repro but a higher reproduction rate. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable.
When you genuinely cannot build a loop
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do not proceed to hypothesise without a loop.
Completion criterion — a tight loop that goes red
Phase 1 is done when the loop is tight and red-capable: you can name one command — a script path, a test invocation, a curl — that you have already run at least once (paste the invocation and its output), and that is:
- Red-capable — it drives the actual bug code path and asserts the user's exact symptom, so it can go red on this bug and green once fixed. Not "runs without erroring" — it must be able to catch this specific bug.
- Deterministic — same verdict every run (flaky bugs: a pinned, high reproduction rate, per above).
- Fast — seconds, not minutes.
- Agent-runnable — you can run it unattended; a human in the loop only via
scripts/hitl-loop.template.sh.
If you catch yourself reading code to build a theory before this command exists, stop — jumping straight to a hypothesis is the exact failure this skill prevents. No red-capable command, no Phase 2.
Client-side async flow bugs (HTMX + WebSockets + JS callbacks)
When the server says "success" but the UI is wrong, the bug is almost always in one of these patterns:
Pattern A: Callback fires before modal/render completes.
- Symptom: Element should be visible but isn't. Server logs show success.
- Cause: A callback (HTMX
afterSwap, JS event handler, WebSocketonmessage) triggers a DOM mutation that overwrites content just injected by another callback. - Fix: Check callback ordering. Does X fire before Y completes? Add a guard: "only call the success handler after confirming the modal is still visible."
Pattern B: Missing handler for a message type the server sends.
- Symptom: Server sends a confirmation message but nothing happens in UI. No error. Silent failure.
- Cause: The client's message dispatcher has no handler for this type.
Check:
handlers[data.type]returns undefined. - Fix: Add the missing handler. Often the same handler exists in a similar page — copy it.
Pattern C: HTMX swap overwrites injected content.
- Symptom: Modal appears, then disappears. Toast appears, then vanishes.
- Cause: An HTMX
afterSwapor callback triggershtmx.ajax(...)which replaces the target element's innerHTML, erasing your injected content. - Fix: Skip the HTMX swap when you've just injected modal/content. Use
a guard flag like
if (!data.is_temporary_creation)or similar.
Pattern D: WebSocket message arrives but client is offline.
- Symptom: Server sends a success message but UI doesn't update until next user action. Or: offline queue replays old state.
- Cause: Client was offline when the message arrived; it's now online but the message was consumed by the queue, not the UI handler.
- Fix: Check an
offlineflag in the response. Handle queued messages differently from real-time messages.
When debugging a UI bug with server-side success, check these four patterns in order. They account for ~80% of "server says OK but UI is wrong" bugs in HTMX + WebSocket apps.
E2E debugging checklist (Playwright)
When E2E tests fail mysteriously, check in order:
- Duplicate IDs — Playwright strict mode fails on
locator("#id")resolving to 2+ elements. Search rendered HTML forid="...". - CSP violations — Capture console:
page.on("console", lambda m: msgs.append(...)); filter for "Content Security Policy". Inline styles/scripts blocked if'unsafe-inline'missing or hashes present. - Silent JS failure — Empty console = script blocked (CSP) or syntax error before execution. Dump
page.content()and verify<script>tags present. - Template vars in static JS —
{{ var|safe }}in.jsfiles renders as literal{{ var|safe }}. Use inline<script>config or data attributes. - JSON serialization — Python lists render as
[\'item\'](single quotes) in Django templates. Usejson.dumps()in view context. - CSRF origin check —
Origin checking failed - null does not match. Ensure test settings remove CSRF middleware AND add127.0.0.1toCSRF_TRUSTED_ORIGINS. - Pytest settings module —
pyproject.tomlDJANGO_SETTINGS_MODULEmust beconfig.settings_test, notconfig.settings.
Save rendered HTML on failure: page.content() to file for offline inspection.
Phase 2 — Reproduce + minimise
Run the loop. Watch it go red — the bug appears.
Confirm:
- The loop produces the failure mode the user described — not a different failure that happens to be nearby. Wrong bug = wrong fix.
- The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against).
- You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it.
- Test environment is what you think it is. If the bug involves settings, feature flags, or runtime mode, verify them at the point of failure. Lazy-loaded settings can cache values before fixtures override them. A test that "should" run in one mode but actually runs in another will produce wrong navigation items, wrong URLs, and confusing failures.
Minimise
Once it's red, shrink the repro to the smallest scenario that still goes red. Cut inputs, callers, config, data, and steps one at a time, re-running the loop after each cut — keep only what's load-bearing for the failure.
Why bother: a minimal repro shrinks the hypothesis space in Phase 3 (fewer moving parts left to suspect) and becomes the clean regression test in Phase 5.
Preserve boundary correspondence. Record the original trigger, runtime conditions and user-visible assertion. Keep the minimised repro at the same boundary or demonstrate that its narrower seam still carries the same causal path and symptom. Recheck that correspondence after each cut: a green helper, a mocked substitute that bypasses the failing boundary, or a wrong failure is not representative coverage. For example, successful server logic alone cannot prove a reconnect/DOM failure is covered.
Name a coverage gap when the representative permanent regression cannot be established. State the missing boundary, required evidence or access, and the next bounded owner action. Keep any narrower test's limited result, but do not claim verified completion or replace an unavailable original scenario with a pretend browser/server demonstration.
Done when every remaining element is load-bearing — removing any one of them makes the loop go green — and the repro still represents the original failure.
Do not proceed until you have reproduced and minimised.
Phase 3 — Hypothesise
With a tight, minimised, red loop — now you can form hypotheses.
Plan scoped, reversible diagnostic changes within existing authority before changing code. Restore those changes after measurement and retain experiment receipts separately from final production repair. Record the permanent regression on genuinely unfixed code before that final repair; do not repair first and undo it merely to fabricate retroactive red, or relabel a production fix as an experiment afterward.
For each hypothesis:
- State it plainly. "I think X causes Y because Z."
- Design an experiment that distinguishes this hypothesis from the most likely alternative.
- Run the experiment — modify the loop, add instrumentation, change one variable.
- Record the result — hypothesis confirmed, refuted, or inconclusive.
If the experiment is inconclusive, tighten the loop further (Phase 1) before forming new hypotheses.
Escalation rule: after ~3 refuted hypotheses, stop generating new ones. Write up what has been ruled out (each hypothesis + the experiment that killed it) and present it to the user — the ruled-out list is real progress, and the user often holds the missing fact. Continuing to guess past this point is rabbit-holing.
Completion criterion
Phase 3 is done when you have one confirmed hypothesis — an experiment that rules out the most likely alternative. Not "I think it's probably X" — "I changed X and the bug went away; I changed X back and the bug returned."
Phase 4 — Instrument (skip only if Phase 3 already confirmed with concrete data)
If the hypothesis is about internal state (data flow, timing, caching), add instrumentation to make the invisible visible.
- Server logs — targeted, not blanket. Log the specific variable or transition you're testing.
- Asserts — insert temporary assertions at the suspected boundary.
- Profilers — if the bug is performance-related, use the right tool (cProfile, py-spy, Django debug toolbar).
- Traces — if the bug crosses service boundaries, use distributed tracing.
- WebSocket frame capture — for real-time communication bugs, capture
actual frames sent/received. Playwright:
page.on("websocket"). Without this you can't distinguish "server didn't send" from "client didn't receive" from "client received but ignored". - Browser console capture — add
console.logto JS, capture viapage.on("console", ...). The JS equivalent of server logging. - DOM state snapshots —
page.evaluate(() => el.innerHTML)at the failure point. Tells you whether an element was never rendered, was removed by another script, or is CSS-hidden.
Record which temporary instrumentation this session owns, including its files, hooks and log paths. Preserve unrelated instrumentation and work. Remove only the owned additions after measurement; verify actual removal and rerun the appropriate loop on the cleaned candidate. If cleanup happens before repair, the genuine bug should still reproduce. Phase 5 requires cleaned-candidate verification after the final repair too.
Completion criterion
Phase 4 is done when instrumentation has confirmed the hypothesis with concrete data. Not "the logs look suspicious" — "the log shows variable X is None at line Y, which matches the hypothesis."
Phase 5 — Permanent regression + repair
Record a representative permanent regression observed red for this bug's reason on unfixed code before production repair. Convert the minimised repro into the project's normal test collection, or reuse an existing permanent regression. Run it and retain the actual failing assertion/output; an exploratory loop or a failure from broken imports/setup is not sufficient.
Apply the repair. Verify the same permanent regression green, then rerun the original scenario to check the user's symptom at its original boundary.
Recovery paths when it doesn't go to plan:
- Loop stays red → the hypothesis was wrong or incomplete. Back to Phase 3; the failed fix is itself an experiment result — record what it rules out.
- Loop goes green but other tests break → the hypothesis was incomplete: the "fix" changed behavior beyond the bug. Back to Phase 2 with the new failure folded into the repro — do not patch the newly-broken tests to match.
Then:
- Preserve the permanent regression. Keep its assertion and normal collection path; retain the pre-repair red and post-repair green evidence.
- Run proportionate project checks. Delegate affected tiers, broader gates and execution to testing-workflow and the project's policy. Run full suites when explicitly required; do not require whole-product E2E/full suites after every cluster. Missing or failed required coverage remains a gap.
- Remove only owned temporary instrumentation. Use the session's ownership record; preserve unrelated instrumentation, logs and work. Verify actual removal in the files/hooks/diff, not just a promise to clean up.
- Reverify the cleaned candidate. Rerun the permanent regression and original scenario after cleanup, plus the appropriate project checks. Earlier green evidence does not prove the cleaned candidate.
- Document the root cause. If the bug was non-obvious, add a comment at the fix site explaining the root cause and the fix. Not "fixed bug" — "X was None because Y; added guard at Z."
Completion criterion
Phase 5 is done when:
- The feedback loop goes green on the fix.
- A representative permanent regression was observed red for the bug's reason before repair and is now green.
- The original scenario has been rerun and the user's symptom is resolved.
- Required proportionate project checks pass; explicit full-suite mandates remain satisfied.
- Actual removal of owned temporary instrumentation is verified; unrelated instrumentation and work are preserved.
- The permanent regression, original scenario and appropriate checks have been rerun on the cleaned candidate.
- The root cause is documented.
Before applying the fix, verify the test expectation is correct
A failing test doesn't always mean the code is wrong. Sometimes the test has incorrect expectations. Check:
- Does another test assert the same thing differently?
- If
test_page_Xexpects a toast in container A andtest_page_Yexpects it in container B, one of them has the wrong selector. Check which matches the template's actual structure.
- If
- Does the element the test checks exist and have the right purpose?
- If the test asserts content on an element that is actually an HTMX target (not a toast container), the test has the wrong selector.
- Did the test pass in a commit before the bug was introduced?
git log --oneline -p -- tests/e2e/test_xxx.py— if the test was added in the same commit as the feature, its expectations were written alongside untested code. Trust the implementation pattern from related tests over the new test's assertions.
If the test expectation is wrong, fix the test, not the code. A test with wrong expectations is a bug in the test, not in the application.
Reference
See Feedback Loops for the full catalogue of feedback-loop construction strategies.
Other versions194 projects use a version of this.
Word for word · 0
None found.
Edited · 150
- mattpocock/skills★ 303k30% same
- QuivrHQ/quivr★ 40k31% same
- GreyDGL/PentestGPT★ 16k28% same
- ArcReel/ArcReel★ 5.5k30% same
- withcoral/coral★ 4.9k30% same
- bestofjs/bestofjs★ 3.1k30% same
- moeru-ai/xsai★ 66431% same
- chrisdoc/hevy-mcp★ 52330% same
- mxyhi/ok-skills★ 50630% same
- aws/graph-explorer★ 48931% same
140 more not listed
Wrote their own version · 44
- vinvcn/mattpocock-skills-zh-CN★ 5.3ksame name
- getsentry/sentry-react-native★ 1.8ksame name
- getsentry/sentry-dart★ 877same name
- nodetool-ai/nodetool★ 588same name
- stevesolun/ctx★ 583same name
- devcxl/mattpocock-skills-zh★ 508same name
- OyadotAI/oya-browser★ 351same name
- jellydn/my-ai-tools★ 123same name
- WenWuZhiDao/mattpocock-skills-zh★ 27same name
- rafaelob/pragmatic-agent-skills★ 23same name
34 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use setup-ts-deep-modules, loop-me and 7 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
liufeng1976Skillsetup-ts-deep-modules
Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files. User-invoked.
Also in 33 projects that use this
RoentekSkillloop-me
Grill me about specs for the workflows I want to build, within this workspace.
Also in 46 projects that use this
LesaniSkillwriting-beats
Shape an article as a journey of beats, choose-your-own-adventure style. The user picks a starting beat from the raw material, you write only that beat, then offer options for where to pivot next, beat by beat, until the article reaches a natural end. Use when the user has raw material and wants to assemble it as a narrative rather than an argument.
Also in 49 projects that use this
ryotaroh180105Skillclaude-handoff
Hand the current conversation off to a fresh background agent that picks up the work immediately.
Also in 33 projects that use this
FabienDehopreSkilldecision-mapping
Turn a loose idea into a sequenced map of investigation tickets, then drive them to resolution one at a time.
Also in 6 projects that use this
LesaniSkillwriting-shape
Take a markdown file of raw material and shape it into an article through a conversational session — drafting candidate openings, growing the piece paragraph by paragraph, arguing about format (lists, tables, callouts, quotes) at each step. Use when the user has a pile of notes, fragments, or a rough draft and wants help turning it into something publishable.
Also in 47 projects that use this
- +1 more from Lesani
In this repository73 other files it gives its agents.
- agent/AGENTS.mdAGENTS.md
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- git-guardrails-claude-codeSkill
- setup-project-skillsSkill
- skill-creatorSkill
- websearchSkill
- writing-great-skillsSkill
- codebase-designSkill
- construction-craftSkill
- critical-partnerSkill
- data-intensiveSkill
- django-disciplineSkill
- docker-disciplineSkill
- documentation-lifecycleSkill
- domain-driven-designSkill
- enterprise-patternsSkill
- git-disciplineSkill
- legacy-codeSkill
- production-readinessSkill
- refactoring-passSkill
- resolving-merge-conflictsSkill
- tddSkill
- testing-workflowSkill
- ui-design-directionSkill
- webapp-testingSkill
- writing-for-agentsSkill
- django-allauthSkill
- djangoSkill
- dockerSkill
- drfSkill
- full-calendarSkill
- giteaSkill
- htmxSkill
- http-status-codesSkill
- hyperscriptSkill
- langchainSkill
- litestarSkill
- prestashopSkill
- prSkill
- pykeSkill
- python-asyncSkill
- tablerSkill
- cleanroom-rewriteSkill
- councilSkill
- court-jesterSkill
- domain-modelingSkill
- fix-prSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- handoffSkill
- humanifySkill
- implementSkill
- implement-specSkill
- improve-codebase-architectureSkill
- ponytail-auditSkill
- ponytail-debtSkill
- prototypeSkill
- qaSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

