test: walker reachability property test (epic #215 T1) #34

closed colechristensen cole.christensen@gmail.com wants to merge feat/walker-invariant-property-test into main
No CI

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.

Created May 14, 2026 at 06:49 UTC