# Handoff — Onda 2.A caller-wiring bundle ready for PR

**Data:** 2026-04-26 (continuação)
**Worktree:** `/Users/luizpenha/monetarie/.worktrees/sta-paridade`
**Branch:** `sta/onda2a-followup-callers-wiring` (4 commits)
**PR target:** `main`
**Plan doc:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-caller-wiring.md`
**Awaiting:** explicit user authorization to push + open PR

---

## TL;DR

Bundles the two unwired public APIs from Onda 2.A follow-ups #2 and #3 into a single sub-PR — both ship in main since 2026-04-26 evening but had no caller in production.

- **Inbound side:** `Inbound.Worker` switches its 3 download call sites from `Client.download_file/2` to `Operations.download_with_retry/2` with `expected_size: protocol.file_size`. Mid-download transient failures now drive §6.4 ranged resume in production.
- **Outbound side:** new admin endpoint `POST /api/admin/outbound_files/:id/resume_upload` exposes `Operations.resume_upload/3` to operators. State-guarded (status="uploading", protocol_number set, bytes_uploaded > 0); validates file_path on disk; surfaces success/failure via 200/422/502 responses.

After this merges, **Onda 2.A is fully caller-wired in production** in addition to design-complete.

---

## Branch contents (4 commits)

| # | SHA | Type | Subject |
|---|---|---|---|
| 1 | `f2a8312a` | docs | Plan + scoping (Pattern: Manual Verification gate skipped — pure internal wiring) |
| 2 | `9ce72c57` | feat | `Inbound.Worker` threads `:expected_size` to enable mid-download resume |
| 3 | `d704dc07` | feat | Admin endpoint `POST /api/admin/outbound_files/:id/resume_upload` (+ `OutboundFile` schema datetime alignment side-fix) |
| 4 | (this commit) | docs | Handoff for caller-wiring bundle PR |

Branch cut from `origin/main` at `99e6e57c` (post PR #43 merge).

---

## Touchstones at PR creation

```
$ mix compile
5 baseline warnings (unchanged)

$ mix test test/sta_connector/sta/client_test.exs
59 tests, 6 failures   # 6 baseline Soap.parse_response failures preserved

$ mix test test/sta_connector/sta/operations_test.exs
58 tests, 0 failures

$ 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)

$ mix test test/sta_connector_web/controllers/api/admin/outbound_files_controller_test.exs
7 tests, 0 failures   # NEW
```

**Bonus delta:** `test/sta_connector/outbound_test.exs` went from **9 baseline failures (all `:utc_datetime`/usec mismatch)** down to **1 failure** (newly-visible pre-existing logic bug — see below). The schema fix in commit `d704dc07` unblocks 8 previously-broken tests as a side effect.

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

---

## Pattern: Manual Verification — gate skipped

This sub-PR introduces **no new wire-format / endpoint / response-code / field-name / numeric-threshold claims** at the BCB-facing surface. Both changes are pure internal wiring of existing wire-formatted APIs:
- Inbound.Worker swap doesn't change BCB-facing wire formats (Range / If-Match / X-Content-Hash were pinned in PR #43).
- New admin endpoint is internal control-plane (admin-auth pipeline, JSON request/response). Underlying `Operations.resume_upload/3` had its wire format pinned in PR #36.

Mirrors the precedent of PR #34 + PR #36 which were also pure-wiring follow-ups with no Task 0 inventory. Documented in plan doc + PR body.

---

## Newly-visible pre-existing logic bug (NOT introduced; surfaced by schema fix)

The schema datetime alignment in commit `d704dc07` (changing `OutboundFile`'s `:utc_datetime` fields to `:utc_datetime_usec` to match the migration's column types) unblocked 8 previously-masked tests. One newly-visible failure surfaced:

**`StaConnector.OutboundTest "mark_uploading/1 updates status and increments attempts"`** at `test/sta_connector/outbound_test.exs:213` — asserts `updated.upload_attempts == 1`; actual is `0`.

Root cause: `OutboundFile.attempt_changeset/1` (line 243) does:
```elixir
|> change()
|> put_change(:status, "uploading")
|> put_change(:last_attempt_at, DateTime.utc_now())
|> update_change(:upload_attempts, &((&1 || 0) + 1))
```

`Ecto.Changeset.update_change/3` only fires the function when there's a PRIOR change for the field in the changeset — `change()` doesn't create one for `:upload_attempts`, so the update is a no-op. Should be `put_change(:upload_attempts, (file.upload_attempts || 0) + 1)`.

**Pre-existing:** would have surfaced earlier if the datetime error hadn't masked it. Not introduced by this PR. Documented as a follow-up.

---

## Out of scope (explicit cuts)

1. **`Inbound.Worker` unit test creation.** No `worker_test.exs` exists in this codebase — pre-existing gap; relies on Operations' own 58 tests + supervisor/poller integration tests for safety. Could become a follow-up.
2. **Worker.init auto-recovery for upload-side resume.** Per Onda 2.A core handoff F1 risk note, admin-endpoint approach was preferred. Auto-recovery deferred to a future PR if production demands it.
3. **Cross-process / cross-restart download resume persistence.** Onda 2.A follow-up #2 left this out-of-scope; remains out-of-scope here.
4. **Admin auth wiring.** The `:admin_api` pipeline runs `SsoAuth` but does not enforce; controller does not check `current_user`. The router still has the "Future: Add admin authentication" TODO. Out of scope.
5. **Fix for `attempt_changeset` upload_attempts no-op bug.** Pre-existing logic bug newly-visible after schema fix; documented above; follow-up scope.
6. **Pre-existing inbound test failures (poller_test / supervisor_test / router_test — 21/36 fail due to DB setup gaps).** Pre-date this commit (verified by stashing changes and re-running on baseline); not introduced.

---

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

| Sub-component | Status |
|---|---|
| Onda 2.A core (PR #30) | ✅ in main + caller-wired (Outbound.Worker chunked upload) |
| Follow-up #1 — Worker chunked-upload wiring (PR #34) | ✅ in main |
| Follow-up #3 — `Operations.resume_upload/3` (PR #36) | ✅ in main + ⏳ caller-wired by THIS PR (admin endpoint) |
| Follow-up #2 — Ranged resume mid-download (PR #43) | ✅ in main + ⏳ caller-wired by THIS PR (Inbound.Worker) |
| **This PR — caller-wiring bundle** | ⏳ ready to push |

After this merges, **Onda 2.A is design-complete AND production-wired**.

---

## Suggested next moves (post-merge)

**Recommended:**
1. **Onda 2.B (status & queries)** — fresh sub-PR, ~2 days, ~400 LOC. Pattern: Manual Verification mandatory (introduces wire-format claims). 14 status codes, query params, semicolon-batched protocols.
2. **Or fix the 1 newly-visible `attempt_changeset` upload_attempts bug** as a quick-win sub-PR (~15 min) before moving to Onda 2.B.

**Counter-options:**
- **Onda 2.C (rate & lifecycle)** — has multiple `[STUDY-CONJECTURE]` items pending (HTTP 429, 48h expiry, bucket sizes).
- **`worker_test.exs` for Inbound.Worker** — close the testing gap.
- **Worker.init auto-recovery for upload-side resume** — if admin-endpoint approach is operationally insufficient.

---

## References

- **This sub-PR plan:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-caller-wiring.md`
- **Onda 2.A core (PR #30, MERGED):** https://github.com/FluxiqBR/monetarie/pull/30
- **Follow-up #1 (PR #34, MERGED):** https://github.com/FluxiqBR/monetarie/pull/34
- **Follow-up #3 (PR #36, MERGED):** https://github.com/FluxiqBR/monetarie/pull/36
- **Follow-up #2 (PR #43, MERGED):** https://github.com/FluxiqBR/monetarie/pull/43
- **Onda 2.A core handoff F1 risk note** (recommended admin-endpoint over Worker.init auto-recovery): `sta/docs/handoff/2026-04-26-sta-onda2a-followups-status-and-next-session.md` §F1
- **Onda 2.A follow-up #2 handoff** (out-of-scope items + caller-wiring guidance): `sta/docs/handoff/2026-04-26-sta-onda2a-followup-2-ranged-resume.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".
