# Handoff — Onda 2.A follow-up #2 (ranged resume mid-download) ready for PR

**Data:** 2026-04-26 (end of focused follow-up #2 session)
**Worktree:** `/Users/luizpenha/monetarie/.worktrees/sta-paridade`
**Branch:** `sta/onda2a-followup-2-ranged-resume` (5 commits, ready to push)
**PR target:** `main`
**Plan doc:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-2-ranged-resume.md`
**Awaiting:** explicit user authorization to push + open PR

---

## TL;DR (one paragraph)

Onda 2.A follow-up #2 (ranged resume on transient mid-download failure) is the **last documented Onda 2.A follow-up**. Manual Verification (Task 0) discovered that Manual STA Web Services v1.5 §6.2 explicitly endorses the resume use case ("Caso a conexão seja interrompida durante o recebimento, é possível retomá-lo... utilize a requisição descrita na seção 6.4") — the previous Onda 2.A handoff was overcautious in calling this ambiguous. Implementation: new `Client.download_file_streamed/2` primitive uses `Finch.async_request/3` to capture partial bytes on mid-stream drops; `Operations.download_with_retry/2` gains a `:expected_size`-opt-in resume state machine that drives `Client.download_part/2` with explicit `Range: bytes=N-{end}` + `If-Match: <etag>` for the missing tail. Both real mid-stream drops (`{:partial, ...}` from streaming primitive) AND short-body deliveries (`{:ok, %DownloadResult{content: short}}`) trigger resume. 412 / 416 / 501 from §6.4 reset state and restart from byte 0; max_retries exhausted returns `{:error, :max_retries_exceeded_during_resume}`. **Onda 2.A is truly complete after this merges.** Caller wiring (Outbound.Worker passing `expected_size`) is explicitly out of scope and could bundle with the upload-side `resume_upload/3` caller wiring (PR #36 was merged at 2026-04-26 20:57 — its public `resume_upload/3` API exists but no caller passes through to it yet).

---

## Branch contents (5 commits)

| # | SHA (HEAD ordering) | Type | Subject |
|---|---|---|---|
| 1 | `ede59cc1` | docs | Plan doc + Task 0 Manual Verification |
| 2 | `5a20151e` | feat | Client.download_file_streamed/2 with mid-stream failure capture (4 new tests) |
| 3 | `290a046c` | feat | Operations.download_with_retry/2 :expected_size resume state machine (2 new tests) |
| 4 | `3a493bb3` | test | Coverage for 412/416/501 + max_retries + multi-cycle (5 new tests) |
| 5 | (this commit) | docs | Handoff for follow-up #2 PR + unused-var warning fix |

Branch was cut from `origin/main` at `92e0be2e`. By time of handoff write, origin/main had advanced to at least `3678e633` (PR #40 SPB chore). All advances are SPB-scope or non-conflicting; rebase before merge if needed.

---

## Touchstones at PR creation

```
$ mix compile
5 baseline warnings — UNCHANGED from pre-Onda-1 state

$ mix test test/sta_connector/sta/client_test.exs
59 tests, 6 failures
# 4 net new (download_file_streamed/2 wire format describe block)
# 6 baseline failures = pre-existing Soap.parse_response noise (NOT introduced)

$ mix test test/sta_connector/sta/operations_test.exs
58 tests, 0 failures
# 7 net new (download_with_retry/2 with :expected_size resume describe block)

$ mix test test/sta_connector/files/outbound_file_test.exs
7 tests, 0 failures

$ mix test test/sta_connector/application_test.exs
3 tests, 0 failures

$ mix test test/sta_connector/outbound_test.exs --only describe:"set_bytes_uploaded/2 (Onda 2.A worker chunked-wiring follow-up)"
4 tests, 0 failures (33 excluded)
```

**Combined Onda 2.A follow-up #2 test surface:** 11 net new tests across Client + Operations.

**Guard rail:** `git diff $(git merge-base origin/main HEAD)..HEAD --name-only | grep -v "^sta/"` returns empty. Zero touches outside `sta/*`.

---

## Manual Verification gate (Pattern: Manual Verification)

Per `sta/docs/plans/2026-04-26-sta-conformance-rollout.md#pattern-manual-verification`, this sub-PR introduces wire-format claims (the streaming variant's request shape, the resume state machine's Range/If-Match assembly) and therefore the Pattern gate applies. Task 0 of `2026-04-26-sta-onda2a-followup-2-ranged-resume.md` covers the inventory.

### `[VERIFIED §X.Y]` (highlights)

- **§6.2** Resume mid-download is documented use case — overrides the previous handoff's "ambiguous" framing.
- **§6.4** `Range: bytes={inicio-fim}` requires both bounds explicit.
- **§6.4** `If-Match` and `If-Unmodified-Since` are optional.
- **§6.4** 206 response carries ETag + Last-Modified + X-Content-Hash (FULL FILE hash).

### `[STUDY-CONJECTURE]` items

1. **Open-ended `Range: bytes=N-`** — RFC 7233 standard but Manual silent. Conservative read: don't use; require caller `:expected_size`.
2. **Stitching consistency across changing ETags** — Manual silent; treat any 412 as restart-from-zero.
3. **Caller-supplied `:expected_size` wrong** — Manual silent on too-small/too-large. 416 path covers too-small; assembled body shorter than expected → hash mismatch → caller decides.
4. **Maximum resume attempts** — Manual silent. Reuse `:max_retries` (default 3); add hard cap at 1,000,000 against pathological non-progress (defensive only).

### `[NEEDS-OPERATOR-CONFIRM]`

None for this follow-up.

### Misdiagnoses caught

None. The previous Onda 2.A handoff suggested `bytes=N-` open-ended form was an open question; this is **not a misdiagnosis** but a clarification — Manual §6.4 is silent, conservative read applies.

---

## Out of scope (explicit cuts in PR body)

1. **Caller wiring for resume.** `Outbound.Worker` continues to call `download_with_retry/2` without `:expected_size`. Production callers must opt in by passing `:expected_size` (the data already comes from `FileInfo.file_size` returned by `list_available_files/1`). Wiring becomes a separate sub-PR — **could bundle with caller-wiring of `resume_upload/3` from PR #36** (which merged at 2026-04-26 20:57 but also has no caller).
2. **Parallel downloads (Manual §6.3)** — orthogonal feature; Onda 2.B/C scope.
3. **Open-ended Range form** — Manual silent; conservative read applied.
4. **Hash mismatch retry** — assembled body's hash mismatch returns `{:error, :hash_mismatch_after_resume}` (or the existing parse_error). No automatic retry.
5. **DB schema changes** — none. Migration `20260426150000_add_resilient_transfer_columns.exs` from PR #30 already provides `etag` / `last_modified` / `bytes_uploaded` columns that future caller-wiring can persist.
6. **Cross-process resume** — if a worker crashes mid-`download_with_retry/2`, no DB-persisted resume state. State is in-memory across attempts within a single call. Production might want a future "resume from persisted state on worker restart" feature.

---

## Status of Onda 2.A overall (post-this-merge)

| Sub-component | Status |
|---|---|
| Onda 2.A core (PR #30 merged `3af17964`) | ✅ in main |
| Follow-up #1 — Worker chunked-upload wiring (PR #34 merged `ed03ce39`) | ✅ in main |
| Follow-up #3 — `Operations.resume_upload/3` (PR #36 merged `100e5154` at 20:57) | ✅ in main |
| **Follow-up #2 — Ranged resume mid-download (this PR)** | ⏳ ready to push |

Once this PR merges, **Onda 2.A is design-complete**. Remaining work is caller-wiring (above) and Onda 2.B/C/D fresh-scope sub-PRs from the parent rollout plan.

---

## Suggested next move (post-merge)

**Recommended: caller-wiring sub-PR for both `download_with_retry(expected_size:)` AND `resume_upload/3`** — bundle the two unwired follow-ups in a single sub-PR. Estimated 1-2h. Touches:
- `Outbound.Worker.init/1`: detect `status: "uploading"` + `protocol_number` + `bytes_uploaded > 0` → call `Operations.resume_upload/3`. (OR admin endpoint as the safer alternative per Onda 2.A core handoff F1 risk note.)
- `Inbound.Worker.run/1`: pass `expected_size: file_info.file_size` to `Operations.download_with_retry/2` so resume kicks in on real mid-stream BCB drops.

**Counter-recommendations:**
- **Onda 2.B (status & queries)** — fresh sub-PR, ~2 days, ~400 LOC. Independent of any Onda 2.A wiring.
- **Onda 2.C (rate & lifecycle)** — has multiple `[STUDY-CONJECTURE]` items pending (HTTP 429, 48h expiry, bucket sizes).

---

## References

- **Manual canônico:** `sta/Manual_STA_Web_Services.pdf` v1.5 jul/2022 §6.1, §6.2, §6.4
- **Plan doc:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-2-ranged-resume.md`
- **Onda 2.A core plan:** `sta/docs/plans/2026-04-26-sta-onda2a-resilient-transfer.md`
- **Parent rollout plan:** `sta/docs/plans/2026-04-26-sta-conformance-rollout.md`
- **PR #30 (Onda 2.A core, MERGED):** https://github.com/FluxiqBR/monetarie/pull/30
- **PR #34 (Follow-up #1, MERGED):** https://github.com/FluxiqBR/monetarie/pull/34
- **PR #36 (Follow-up #3, MERGED):** https://github.com/FluxiqBR/monetarie/pull/36
- **Previous Onda 2.A handoff (now stale post-PR-#36 merge):** `sta/docs/handoff/2026-04-26-sta-onda2a-followups-status-and-next-session.md`

---

**End of handoff.** Awaiting explicit user authorization for `git push` + `gh pr create` per the standing rule "push+PR só com auth explícita do usuário".
