Add POSTMORTEM.md: every real issue found, how, fix, and regression prevention
CI / build-and-test (push) Successful in 48s

A detailed, permanent record covering the whole arc of this project's
development so far -- requested explicitly, as detailed as possible, to
prevent regression for ever. Seven sections:

1. Performance findings: the file-index O(n^2) treap rewrite, pack_write's
   O(n^2) dedup + latent hash-collision correctness bug, and the mount
   table's O(n^2) (confirmed, deliberately not fixed, with the reasoning).
2. Environment/tooling limitations: TSan's categorical block (every
   workaround actually tried and ruled out, not just the ones that
   worked), and both variants of the ASan/UBSan sandbox-startup flake.
3. Project professionalization: version API, SPDX, pkg-config, SECURITY.md,
   CHANGELOG.md, the Gitea-not-GitHub migration, and the CODE_OF_CONDUCT
   decision.
4. Git identity correction: the filter-branch rewrite, the tag-object
   tagger field it missed (found only by a full object-database sweep,
   not by re-reading git log), and the exhaustive re-verification.
5. Data-integrity fault injection: test_crash_consistency.c, the real bug
   in its own oracle (128 false failures before a progress side-channel
   fixed it), and the two real overlay.c bugs found while building the
   write-failure test (silent journal failure, torn-record poisoning of
   later records).
6. CI as a second, independent reviewer: the memory leak detect_leaks=0
   had been masking locally, and the set -e scripting bug -- including
   the mistake made while fixing it (reintroducing the same bug in a new
   shape), the follow-on bash-variable-blowup bug, the newly-discovered
   TSan segfault variant, and the retry-budget gap, each confirmed by
   direct reproduction under bash -eo pipefail, not reasoned about.
7. Distilled lessons: ten patterns extracted from the above, written to
   be applied to a new problem, not just recognized in this one.

Every figure cited was checked against BENCH.md/CLAUDE.md's own numbers
before writing this, not reproduced from memory.

Cross-referenced from CLAUDE.md's "Repository status", README.md's
"Contributing" section, and CHANGELOG.md -- including two entries CHANGELOG
itself was missing (the set -e CI fix from the previous commit, and this
document), the exact class of gap POSTMORTEM.md section 3 already
describes happening once before with the v0.1.0 tag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqJpkdJ6Njnt1pw3CbghzB
This commit is contained in:
2026-09-14 21:02:58 +00:00
co-authored by Claude Sonnet 5
parent c414a27e44
commit 7661c94105
4 changed files with 746 additions and 2 deletions
+31
View File
@@ -67,6 +67,37 @@ pushed to any remote — there is no public release yet, only a local one.
fluke), not just this project's own — same root cause
(`personality(ADDR_NO_RANDOMIZE)` blocked by seccomp) confirmed both
generically and against this project's actual test suite.
- **`POSTMORTEM.md`**: a detailed, permanent record of every real bug and
environment issue found across this project's development, how each was
actually diagnosed (including false starts), the final fix, and the
regression-prevention artifact for each — written so a future session
doesn't have to re-diagnose something already answered there.
### Fixed (CI)
- **`.gitea/workflows/ci.yml`'s ThreadSanitizer step was failing CI for an
environment reason, but the actual bug was in the CI script, not the
code under test.** Gitea Actions' `run:` steps execute under `set -e`;
an unwrapped `OUT=$("./binary" 2>&1)` aborts the whole script on the
first nonzero exit *before* the step's own classification logic (which
exists specifically to not fail the build over the already-documented
TSan-can't-start-here limitation) ever runs. Fixed by wrapping every
such invocation in `if cmd; then RC=0; else RC=$?; fi`. Fixing this
surfaced two more real issues, both also fixed: capturing unbounded
flake output into a bash variable (rather than a file with a bounded
read-back) caused a genuine misclassification at scale, and a second,
previously undocumented segfault variant of the same TSan-can't-start
flake (confirmed via hitting multiple unrelated binaries at random, not
a per-binary bug). The ASan/UBSan step's retry budget was also raised
from 3 to 5 after a real 3-in-a-row flake exhaustion was observed during
this same verification work. See `POSTMORTEM.md` §6.2 for the full
diagnosis, including the exact reproduction technique used to confirm
each fix (not just reasoned about).
- A `-Wunused-result` warning on `tests/test_crash_consistency.c`'s
intentionally-ignored `write()` return value built clean locally (a
`(void)` cast) but still warned on the Gitea runner's own gcc — a real
toolchain configuration difference, confirmed by reproducing clean
locally with identical flags first. Fixed with an actual conditional
branch on the return value instead, which is portably honored.
## [0.1.0] - 2026-09-14