mix pd.bisect (PropertyDamage v0.2.0)
View SourceFind the first commit where a saved PropertyDamage failure starts reproducing.
Given a .pd failure file and a known-good commit, this drives git bisect
over the history between that commit and a known-bad one (HEAD by default),
replaying the saved failure at each candidate commit and classifying it from
the replay's exit code. It reports the first commit where the bug appears, then
restores the working tree.
This is an orchestrator over git bisect run and mix pd.replay; it adds no
engine logic of its own. Each per-commit replay runs in a fresh mix pd.replay
subprocess (fresh compile, fresh BEAM), so the result is honest across the
recompilation each checkout requires.
Usage
mix pd.bisect path/to/failure.pd --good <ref> [--bad <ref>] [--verbose]--good REF(required): a commit/tag/branch where the bug is known absent.--bad REF(defaultHEAD): a commit where the bug is known present.--verbose: pass--verbosethrough to eachmix pd.replay.
The failure file records its own model and adapter, so no --model /
--adapter flags are needed.
How a commit is classified
Bisect needs each commit sorted into good / bad / skip. mix pd.replay supplies
exactly that via its exit code:
- 0 — every command passed: the bug is fixed at this commit (good).
- 1 — a command failed its check or errored: the bug reproduces (bad).
- 125 — the replay could not run at all (the commit does not compile, the
model/adapter do not exist yet, the file fails to load, or the sequence is
branching): indeterminate, so
git bisectskips it rather than blaming it.
The 125 = skip case is load-bearing. Commits older than the bug often predate
the model/adapter modules entirely, so the replay cannot run there. Marking such
an ancestor "bad" would push the reported regression earlier than the truth and
corrupt the result; skipping keeps the search honest. If skipped commits cluster
at the good/bad boundary, git bisect reports a set of candidates rather than a
single commit, which this task surfaces as-is.
What this bisects (and why it is version-robust)
This replays the saved concrete shrunk sequence, not a re-generation from
the seed. That matters because the saved command structs are replayed verbatim,
so the bisect stays correct even across commits that changed generators,
command weights, or when: predicates (DR-023). Bisecting by seed would
silently produce a different sequence after any such drift and is therefore not
offered here. The only requirement is that the saved command structs still exist
as modules at each tested commit (commits where they do not are skipped, as
above).
Traps this task handles for you
- Tracked failure files vanish on checkout. If the
.pdlives inside the repo, it may not exist at thegoodcommit. The file is copied to a temp path outside the working tree before bisecting and replayed from there. - A dirty working tree breaks bisect. Uncommitted changes are detected up front; the task errors with a hint and never starts a bisect in that state.
- Leaving the repo mid-bisect.
git bisect resetalways runs at the end, on success, error, and exception, so the working tree returns to where it started.
Exit code
- 0 when a first bad commit (or a candidate set) is identified.
- non-zero only on an orchestration error: a dirty tree, an invalid
--good/--badref, a git failure, or a bisect that produced no verdict.
Examples
# Find where a saved failure first appears, from a known-good tag to HEAD
mix pd.bisect failures/currency-bug.pd --good v0.1.0
# Bound both ends explicitly
mix pd.bisect failures/currency-bug.pd --good abc1234 --bad def5678