test: walker reachability property test (epic #215 T1) #34
feat/walker-invariant-property-test
into main
Phase 1 / T1 of Anvil #215 (the post-hephaestus-incident hardening epic).
A property test that asserts the walker’s pack response is exactly the BFS-reachable set from any want list, on a random commit graph. This catches the entire class of walker bugs that hit us in May 2026 (the gitlink-reachability regression where 1043 reachable objects were silently pruned).
What it does
`stream_data` generator produces graph specs:
- 1-3 commit chains of length 1-6
- random number of branches advertised as tips
- optional merge commit joining the tips
- optional gitlink (`mode: "160000"`) entry whose SHA equals either the root commit or the current tip — the exact shape that broke fangorn/hephaestus
For each spec, build the in-memory repo, run `UploadPackV2.feed/2` for the wants, extract the pack body, and assert `MapSet.new(walker_shas) == MapSet.new(bfs_reachable)`.
Proof it actually works
Temporarily removing the `mode: "160000"` head from `collect_single_tree_entry` (the fix from PR #33) makes the property fail on its very first iteration:
``` Failed with generated values (after 0 successful runs): Clause: spec <- graph_spec() Generated: %{… inject_gitlink?: true, gitlink_target: :self, …}
walker missed reachable SHAs: ["087267660a…", "209f7076a8…", "523efd8ba8…", "57c3b2d098…", … 8 SHAs total …] ```
With the fix in place, 80 random iterations pass in ~100 ms.
Test plan
- `mix test test/ex_git_objectstore/protocol/upload_pack_v2_walker_property_test.exs` — 1 property, 80 iterations, 0 failures
- `mix test` — 945 tests, 0 failures (full suite, 51 excluded)
- `mix format –check-formatted` clean
- `mix credo –strict` clean on touched files
- Demonstrated to fail on the May 2026 hephaestus regression shape
Adds
`{:stream_data, "~> 1.1", only: :test}` to deps.
Tracks
Epic #215 / REQ-GIT-076.