Incident Records¶
What broke in this project, and what changed so it cannot break that way again. This page keeps debugging knowledge inside the codebase instead of in git history and one person's memory — the knowledge a team already paid for once should not have to be paid for twice.
See also: Usage · Contributing · Changelog.
Not an ADR, and not a lessons store¶
An ADR records a decision made before something went wrong. An incident record documents one made because something already did — different trigger, different audience. If this project adopts ADRs later, keep the two separate rather than merging them into one grab-bag.
This is also not a copy of a scaffolding tool's own "lessons learned" store. That kind of store answers "what should every future generated project inherit" — this page answers "what bit this codebase, and what did we do about it." Different audience, different lifetime.
Published on purpose — redact before you write¶
This page ships in the published docs site, same as Usage. An incident can carry
production detail (a hostname, a query, a customer-facing symptom) that should not reach a
public site — redact or generalize it before committing the entry, the way an external
postmortem omits internal specifics. If this project's site is genuinely public and entries
cannot stay redacted, exclude incidents.md via exclude_docs in mkdocs.yml — and remove its
Incidents entry from nav in the same change, or mkdocs build --strict fails — instead of
leaving it half-written.
Format¶
Five fields, in this order. Keep each to a few sentences — the entry stops getting written the day it takes an hour to fill in.
- Symptom — what was observed, not what was wrong.
- How it was found — the tool, command, or accident that surfaced it; "we were careful" does not belong here.
- Root cause — measured, not assumed.
- The change — what now makes this class of failure impossible, or loud instead of silent.
- How we'd know it regressed — the gate, test, or assertion that would catch it coming back.
TEMPLATE — replace with this project's first real incident¶
Delete this section once you have an entry of your own. It stays only so a reader can see what a filled-in entry looks like — an empty page teaches readers there is nothing to read here.
2026-08-16 — Reconfiguring the logger leaked a file handle every time¶
Symptom. logging.FileHandler objects accumulated across the process lifetime whenever
the logger was reconfigured (e.g. rotating to a new dated log file). Harmless in a short-lived
script; in a long-running service the leaked file descriptors build up.
How it was found. Not by the test suite — ResourceWarning is not shown by default in
Python. It surfaced only when the suite ran with -W always, while measuring something
unrelated. The defect was invisible by construction, not by anyone's oversight.
Root cause. The reconfiguration path called logger.handlers.clear() to detach the old
handler before attaching a new one. Clearing the list drops the Python reference; it does not
close the OS file descriptor, which stays open until garbage collection runs.
The change. Close every existing handler explicitly before detaching it, so the descriptor
is released immediately instead of at GC time. One call site — handlers.clear() appeared
exactly once in the whole codebase, so no sibling caller was left unfixed.
How we'd know it regressed. A test reconfigures the logger twice and asserts the first
handler's .stream is None after the second call — close() sets that attribute the moment the
descriptor is released, so the assertion does not depend on garbage-collection timing.
⚠️ Measured separately: promoting bare ResourceWarning to error: in pytest.ini does not
catch this alone — it is raised from __del__, an unraisable context pytest downgrades to
PytestUnraisableExceptionWarning, so both warning classes must be promoted together or the
suite still passes over a real leak.