ref:6f15b9b0ce81c0c0873e6883c27334999ea51a94

fix: UploadPackV2 must not emit acks section when client sends done (#21)

Closes #37 ## Summary - When \`done\` is in the fetch request, skip the acknowledgments section entirely and respond with \`[shallow-info] + packfile\` directly. - Modern git (\`fetch-pack.c\` v2.53, line 1715-1723) transitions from \`FETCH_SEND_REQUEST\` → \`FETCH_GET_PACK\` — bypassing \`FETCH_PROCESS_ACKS\` — whenever \`send_fetch_request\` returned \`done_sent=1\`. \`FETCH_GET_PACK\` expects \`packfile\` (or optional shallow-info / wanted-refs / packfile-uris) and dies with \`expected 'packfile', received 'acknowledgments'\` if we lead with acks. - Previous fix (PR #19) always-emit-acks-when-haves-were-sent was wrong; client is only in PROCESS_ACKS when it has NOT sent \`done\`. ## Why existing tests missed it Every test that exercised \`done\` happened to use \`done + 0 haves\`, which hits the \`build_acknowledgments(_repo, [], _) -> <<>>\` branch and correctly omits acks. The hephaestus trace showed 6 haves + \`done\` in the same request. ## Regression coverage - \`upload_pack_v2_negotiation_test.exs\` — state-machine test driving \`UploadPackV2.feed\` with \`done + unknown haves\`, asserting response's first pkt-line is \`packfile\`, never \`acknowledgments\`. - Real-git-client multi-round test seeding 300 unrelated side commits on the client to force the negotiator past \`MAX_IN_VAIN=256\`, reproducing the hephaestus scenario end-to-end (tagged \`:slow\`). - Three pre-existing tests that baked in the wrong acks-after-done invariant updated to assert the correct protocol shape. ## Test plan - [x] \`mix test --include integration --include slow\` — 800/800 pass - [x] \`mix format --check-formatted\` - [x] \`mix credo --strict\` — no new warnings introduced - [ ] After deploy to anvil.fangorn.io, verify \`git fetch\` in hephaestus succeeds (final acceptance test)
SHA: 6f15b9b0ce81c0c0873e6883c27334999ea51a94
Author: Anvil <noreply@anvil.fangorn.io>
Date: 2026-04-19 17:51
Parents: 176c8af
5 files changed +274 -59
Type
CHANGELOG.md +16 −0
@@ -7,6 +7,22 @@
## [Unreleased]
### Fixed
- **UploadPackV2: omit `acknowledgments` section when the client sends
`done`.** Real `git fetch` (v2.53) was failing against production
with `fatal: expected 'packfile', received 'acknowledgments'`. Per
`fetch-pack.c`: when `send_fetch_request` writes a `done\n` pkt-line
it returns `done_sent=1`, and the client state machine transitions
directly from `FETCH_SEND_REQUEST` to `FETCH_GET_PACK` — bypassing
`FETCH_PROCESS_ACKS`. `FETCH_GET_PACK` expects `[shallow-info]
[wanted-refs] [packfile-uris] packfile` and rejects any leading
acknowledgments section. The server now skips the acks section
entirely whenever `done` is in the request. Regression coverage: a
state-machine-level test in `UploadPackV2NegotiationTest` and a
real-git-client multi-round test that forces `done` alongside a
non-empty haves batch by driving the negotiator past `MAX_IN_VAIN`.
### Added
- **Full protocol-v2 capability coverage for UploadPackV2.** Capability