# Handoff — STA Onda 2.A complete + Onda 2.B brainstorming paused mid-scope-decision

**Data:** 2026-04-27
**Worktree:** `/Users/luizpenha/monetarie/.worktrees/sta-paridade`
**Status na pausa:** Onda 2.A 100% complete em main + retry-cap fix landed (PR #52). Onda 2.B brainstorming paused mid-scope-decision (3 opções A/B/C apresentadas, recommendation = B).
**Branch on resumption:** detached HEAD on `origin/main` `d60faec9` (cleanup completed; branch local `sta/handoff-2026-04-27-onda2b-pause` será deletada após este handoff mergear)
**Action on resumption:** decidir A/B/C → cortar branch `sta/onda2-status-and-queries` off main → Task 0 Manual Verification + plan + TDD.

> **READ THIS DOC FIRST in the next session, before any code or commands.** It captures every Onda 2.A merged PR, every relevant Manual STA citation, the 3 scope options for Onda 2.B with full reasoning, the existing infrastructure, the resumption checklist, the decision tree, and the explicit out-of-scope cuts.

---

## TL;DR (one paragraph)

**Onda 2.A is fully complete and production-wired.** 6 PRs landed in main between 2026-04-26 and 2026-04-27: Onda 2.A core (PR #30), Worker chunked-upload wiring (PR #34), Operations.resume_upload/3 (PR #36), ranged-resume mid-download (PR #43), caller-wiring bundle for the two unwired APIs (PR #46), and the upload-attempts retry-cap fix (PR #52). Onda 2.A delivered: chunked uploads with `posicao_upload` reconciliation; ranged downloads with `If-Match` + `X-Content-Hash` integrity verification; admin-driven `resume_upload` recovery; in-flight resume on transient mid-download drops; and a working retry-cap (the `attempt_changeset` increment bug surfaced and fixed). **Next up: Onda 2.B status & queries.** A 3-option scope decision was being negotiated when this handoff was written. Recommendation: **(B) trim** — ship StateCode + change_status + Worker wiring (~250 LOC, 1 PR), defer query_protocol + advanced_query to a future 2.B-2 sub-PR when a real caller exists. Pattern: Manual Verification gate applies; Task 0 inventory pre-collected in this doc to save reading time.

---

## What's in main (chronologically) — Onda 2.A track

| # | Merge SHA | PR | Type | Subject |
|---|---|---|---|---|
| 1 | `301503ec` | #24 | feat | **Onda 1** parent (B1+B2+B3+B4 wire-format fixes + ICP-Brasil chain) |
| 2 | `3af17964` | #30 | feat | **Onda 2.A core** — chunked upload + ranged download + ETag/X-Content-Hash. 33 new tests. |
| 3 | `ed03ce39` | #34 | feat | **Onda 2.A follow-up #1** — Outbound.Worker chunked-upload wiring + bytes_uploaded persistence. 6 new tests. |
| 4 | `100e5154` | #36 | feat | **Onda 2.A follow-up #3** — `Operations.resume_upload/3` public restart-after-crash entry point. 6 new tests. |
| 5 | `99e6e57c` | #43 | feat | **Onda 2.A follow-up #2** — `Client.download_file_streamed/2` + Operations resume state machine for mid-stream drops. 11 new tests. |
| 6 | `1c10b7fa` | #46 | feat | **Onda 2.A caller-wiring bundle** — Inbound.Worker threads `:expected_size`; new admin endpoint `POST /api/admin/outbound_files/:id/resume_upload`; OutboundFile schema datetime alignment side-fix. 7 new controller tests. |
| 7 | `fbf4dac2` | #52 | fix | **upload_attempts retry-cap bug fix** — `attempt_changeset` switched from `update_change` (no-op) to `get_field` + `put_change`. 1 new test. |

**Origin/main HEAD at handoff write:** `d60faec9` (PR #53 from sisbajud track; STA-orthogonal).

### Combined Onda 2.A test surface delta

- `client_test.exs`: baseline 35 → **59** (+24 tests, 6 baseline failures preserved — pre-existing `Soap.parse_response` noise unrelated to our work)
- `operations_test.exs`: baseline ~30 → **58** (+28 tests, 0 failures)
- `outbound_test.exs`: 9 baseline `:utc_datetime`/usec failures resolved as side-effect of PR #46 schema alignment + new tests added → **38/0**
- `outbound_file_test.exs`: 7/0 (2 assertions adjusted for usec semantic equality post-schema-fix)
- New `outbound_files_controller_test.exs`: **7/0**
- `application_test.exs`: 3/0 unchanged

---

## Onda 2.A is design-complete + production-wired

| Sub-component | Status |
|---|---|
| Chunked upload with §5.6 `Content-Range` + `posicao_upload` reconciliation | ✅ in main + production-wired (Outbound.Worker via PR #34) |
| Ranged download with §6.4 `Range` + `If-Match` + `X-Content-Hash` | ✅ in main + production-wired (Inbound.Worker via PR #46) |
| Streaming download for mid-stream resume capture | ✅ in main + production-wired (Inbound.Worker via PR #46) |
| Resume mid-stream state machine (Operations.download_with_retry `:expected_size`) | ✅ in main + production-wired (Inbound.Worker via PR #46) |
| Restart-after-crash for upload (Operations.resume_upload/3) | ✅ in main + production-wired (admin endpoint via PR #46) |
| Upload retry safety cap (`upload_attempts` counter) | ✅ functional (PR #52 fix) |

**No Onda 2.A items remain.** Onda 2.B is fresh-scope work from the parent rollout plan.

---

## Onda 2.B — Brainstorming pause point (the resumption fork)

The user invoked brainstorming for Onda 2.B and I presented 3 scope options. The user requested this handoff before answering, so the choice is the resumption point.

### Onda 2.B per parent rollout plan (`sta/docs/plans/2026-04-26-sta-conformance-rollout.md` §Sub-PR 2.B)

5 components, ~400 LOC, ~2 days estimated:

1. **`StateCode` module** — enum mapping for the 14 state codes per Manual Tabela 3 + classifications `terminal?/1`, `success?/1`, `failure?/1`. Replaces string-comparison logic in workers. Spec already drafted in rollout plan §Sub-PR 2.B (lines 691-726).
2. **Batched `change_status`** — extend the existing `:confirm_receipt` build clause in `Client` to accept `%{protocols: [list], situation: :rec | :a_rec}` and emit `<Protocolos>p1;p2;...</Protocolos>`. Rename clause to `:change_status` for clarity; keep `:confirm_receipt` as a thin backward-compat wrapper.
3. **`Client.query_protocol/2`** — `GET /staws/arquivos?tipoConsulta=PROT&nivelDetalhe={RES|BAS|COMPL}&protocolos={p1;p2}&origemResposta={RESP|PROT_RESP}` per Manual §8.2.
4. **`Client.advanced_query/1`** — `GET /staws/arquivos?tipoConsulta=AVANC&...` per Manual §8.3 (multi-parameter date+state+situation+ordering filters).
5. **Worker wiring** — `Inbound.Worker` + `Outbound.Worker` switch from string-comparing `status_desc` to `StateCode.terminal?/success?/failure?`.

### The 3 scope options I presented

| Opt | Scope | LOC | Trade-off |
|---|---|---|---|
| **(A) Full** | All 5 items per rollout plan | ~500 | Mirrors Onda 2.A pattern: ship API + caller-wiring follow-up. Items 3+4 land as orphan API (no caller) until a 2.B-2 wires them. |
| **(B) Trim** ← **RECOMMENDED** | 1 + 2 + 5 only | ~250 | Applies PR #46 lesson-learned — don't ship API without caller. Items 3+4 deferred to 2.B-2 OR pulled into Onda 2.C lifecycle tracking when actually needed. |
| **(C) Full + admin endpoints** | All 5 + admin endpoints exposing 3+4 from day 1 | ~600 | Zero dead API in a single PR. More surface; admin auth pipeline still has `# Future: Add admin authentication here` TODO so endpoints would inherit current `:admin_api` pipeline as-is. |

### Why (B) is the recommendation

1. **PR #46 lesson-learned.** Items 3+4 in (A) repeat the exact orphan-API pattern that Operations.resume_upload/3 fell into post-PR #36 — public function with no caller for ~1 day until PR #46 wired it. Worth avoiding.
2. **YAGNI.** Items 3+4 are admin/diagnostics primitives. No production code path in STA currently calls them. The rollout plan's reference to "query_protocol" was speculative for future Onda 2.C lifecycle tracking — and 2.C will probably want richer integration (e.g., periodic state polling) than a raw endpoint can provide.
3. **Smaller blast radius.** ~250 LOC vs ~500-600 means easier review, faster touchstone runs, lower risk of regression in the 6-PR Onda 2.A surface we just completed.
4. **No technical lock-in.** When 2.B-2 (or 2.C) actually needs query_protocol/advanced_query, they can be added then with the real caller in the same PR — better testing, no orphan window.

### Counter-arguments (in case user picks A or C)

- **(A) is the rollout plan as written.** If the user wants strict adherence, ship all 5. The (B) deferral creates a 2.B-2 sub-PR that's not in the canonical rollout plan numbering — minor inconvenience.
- **(C) eliminates the orphan API window entirely** but adds admin-controller scope. Pattern is sound (mirrors PR #46 OutboundFilesController). Larger PR but cleanest end state.

**No wrong answer.** The user's call.

---

## Manual Verification gate — pre-collected (for whichever option is picked)

The Pattern: Manual Verification gate (`sta/docs/plans/2026-04-26-sta-conformance-rollout.md#pattern-manual-verification`) requires Task 0 inventory for any sub-PR introducing new wire-format claims. Onda 2.B introduces several. To save reading time in next session, key facts are pre-collected here against `sta/Manual_STA_Web_Services.pdf` v1.5 jul/2022.

### `[VERIFIED §X.Y]` claims

| # | Claim | Manual section / Tabela | Notes |
|---|---|---|---|
| 1 | `PUT /staws/arquivos/situacao` for status change | §7.1 | Body: `<Parametros><Protocolos>p1;p2</Protocolos><Situacao>{A_REC\|REC}</Situacao></Parametros>` |
| 2 | `Content-Type: application/xml` REQUIRED on §7 PUT | §7.1 Atenção | Distinct from §5.3.1/§6.1/§6.4 endpoints which forbid Content-Type |
| 3 | §7 returns HTTP 204 No Content on success | §7.1 | |
| 4 | §7 errors: 400 only (XML body) | §7.1 errors table | |
| 5 | `<Protocolos>` in §7 is **semicolon-separated**, not array | §7.1 informações importantes #1 | Mirrors `<RangesRecebidos>` shape from §5.3.1 |
| 6 | Two `Situacao` enum values: `A_REC` (a receber), `REC` (recebido) | §7.1 informações importantes #2 | |
| 7 | `GET /staws/arquivos?tipoConsulta=PROT&nivelDetalhe={...}&protocolos={...}&origemResposta={...}` for query by protocols | §8.2.1 + Tabela 1 | |
| 8 | `Content-Type` MUST NOT be in request for §8.1, §8.2, §8.3 | §8.1.1, §8.2.1, §8.3 Atenção | Same as §6.1/§6.4 |
| 9 | `nivelDetalhe` enum: `RES`, `BAS`, `COMPL` (Resumido / Básico / Completo) | §8 + Tabela 1 + Tabela 2 | |
| 10 | `origemResposta` enum: `RESP` (apenas respostas), `PROT_RESP` (origem e resposta); default = origem only | Tabela 1 | |
| 11 | `protocolos` query param is semicolon-separated | Tabela 1 | |
| 12 | `GET /staws/arquivos?tipoConsulta=AVANC&...` for advanced query — same response format as §8.2 | §8.3.1 | |
| 13 | `situacaoTransmissao` query param: `REC` or `A_REC` (optional, default = both) | Tabela 2 | |
| 14 | `qtdMaxResultados` cap is 100 | Tabela 2 | |
| 15 | `ordenacao` enum: `PROT_ASC`, `PROT_DESC`, `DH_ASC`, `DH_DESC`. Default = `DH_DESC` | Tabela 2 | |
| 16 | `dataHoraCriacaoFim` requires `dataHoraCriacaoInicio`; same for `dataHoraInicio`/`dataHoraFim` (atualização) | Tabela 2 | |
| 17 | `sistemas` query param semicolon-separated, max 100 sistemas | Tabela 2 | |
| 18 | 14 state codes per Tabela 3: 1, 2, 5, 10, 15, 20, 25, 30, 35, 45, 55, 65, 70, 75 | Tabela 3 | |
| 19 | Response shape `<Resultado><Arquivo>...` with `<EstadoAtual><Codigo>{n}</Codigo>` | §8.2.1 sample response | |
| 20 | §8.3 Atenção: prefer §8.1 over `tipoConsulta=AVANC&situacaoTransmissao=A_REC` because manual receipts may break advanced query results | §8.3 Atenção | Document this as a runtime warning if anyone uses `:advanced_query` with `situacao: :a_rec` |

### `[STUDY-CONJECTURE]` items (Manual silent — conservative read applied)

| # | Topic | Manual silence | Decision |
|---|---|---|---|
| 1 | `Situacao` value when querying via `:list_available_files` (already implemented) — current code returns enum value as XML string. Onda 2.B should align this with the new `StateCode` mapping. | Manual sample at §8.1 shows `<SituacaoAtual><Codigo>3</Codigo><Descricao>A receber</Descricao></SituacaoAtual>` — but Tabela 3's `codigoEstado` enum doesn't have a `3` mapped to "A receber". Tabela 3 codes are 1/2/5/10/15/20/25/30/35/45/55/65/70/75. | The `<SituacaoAtual><Codigo>` in §8.1 uses a DIFFERENT enum than Tabela 3's `EstadoAtual` codes. This is a known confusion-point. Pin in Task 0 of Onda 2.B. |
| 2 | Default value of `qtdMaxResultados` when not supplied in §8.3 | Tabela 2 says max 100; doesn't state default | Default to 100 (the max). Document as conservative read. |
| 3 | `dependencia` — should it be the institution's main SISBACEN code or a specific dependência code? | Tabela 2 says "Código Sisbacen de uma dependência da instituição" | Make it caller-supplied, document expectation. Don't hardcode. |
| 4 | `dataHoraInicio` (atualização) vs `dataHoraCriacaoInicio` (criação) — when does the application care about each? | Manual distinguishes them but doesn't recommend one for typical use cases | Both are caller-supplied. Document the difference in docstring. |
| 5 | What happens when `protocolos` list contains a protocol that doesn't exist? | Manual silent | Handle gracefully — empty `<Resultado>` for that protocol; no error. Test with Bypass. |

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

| # | Topic | Why operator confirmation needed |
|---|---|---|
| 1 | Do we expect Monetarie-side admin UI to call `query_protocol` directly? | If yes, change scope to (C) full+admin from day 1. If no, scope (B) trim is correct. |
| 2 | Does the existing `Inbound.Worker` need to switch its decision logic to `StateCode` immediately, or can the wiring lag the StateCode introduction by a sub-PR? | Per rollout plan, Worker wiring is part of Onda 2.B. Per (B) trim recommendation, it stays. But user could decide to pure-ship StateCode + change_status without Worker wiring if more conservative. |

### Misdiagnoses anticipated (none yet — but watch for these)

The Onda 2.A core Task 0 caught 4 misdiagnoses. Onda 2.B may catch:
- Whether `<EstadoAtual>` and `<SituacaoAtual>` are actually the same field (they're NOT — see [STUDY-CONJECTURE] #1 above).
- Whether the existing `:list_available_files` builder correctly emits no `Content-Type` header (the request is already a GET; verify in Task 0).
- Whether `:confirm_receipt` builder currently sends `Content-Type: application/xml` (it should — §7.1 requires it; it currently does per `lib/sta_connector/sta/client.ex:887-897`).

---

## Existing infrastructure — what's already there

What you DON'T need to write:

### Already in `lib/sta_connector/sta/client.ex`

- `Client.list_available_files/2` — wraps §8.1 (`:list_available_files`). Onda 2.B does NOT need to reimplement this.
- `Client.confirm_receipt/2` — wraps §7 with single-protocol + REC only. Onda 2.B EXTENDS to multi-protocol + situation enum.
- `build_rest_request(:list_available_files, ...)` — emits the §8.1 GET URL.
- `build_rest_request(:confirm_receipt, ...)` — emits the §7 PUT XML. **THIS IS THE CLAUSE TO RENAME** to `:change_status` (per rollout plan).
- `parse_rest_response(:list_available_files, ...)` — parses §8.1 response with `<Arquivo>` blocks. Reuse the parsing for `:query_protocol` if response shape allows (§8.2 sample shows similar `<Arquivo>` shape but with extra `<EstadoAtual>` block).
- `find_header/2`, `parse_x_content_hash/1`, `maybe_add_header/3` — existing helpers.

### Already in `lib/sta_connector/sta/types.ex`

- `Types.FileInfo` — used by §8.1; check if §8.2/8.3 responses need an extended shape with `EstadoAtual` field.
- `Types.PosicaoUpload` — irrelevant for Onda 2.B.
- `Types.DownloadResult`, `Types.UploadResult` — irrelevant for Onda 2.B.

### Already in `lib/sta_connector/sta/client_behaviour.ex`

- `@callback list_available_files/2` and `@callback confirm_receipt/2` — both already defined. Onda 2.B EXTENDS or REPLACES with new callback shapes.

### Already in workers

- `lib/sta_connector/inbound/worker.ex` — currently does NOT use textual status_desc compares (its retry logic is via `RetryEntry` DB rows), but per rollout plan it should switch to `StateCode` for any place that does compare states.
- `lib/sta_connector/outbound/worker.ex` — same.

### Already in router

- `pipeline :admin_api` runs `SsoAuth` plug. New admin endpoints (option C) inherit auth as-is.

---

## Pre-existing baseline gotchas (NOT introduced by us; do NOT investigate)

### `client_test.exs` — 6 `Soap.parse_response/2` failures

Pre-date Onda 1. These reference a `StaConnector.Sta.Soap` module whose parse_response logic doesn't match test expectations. Treated as baseline noise.

### Inbound `poller_test.exs` / `supervisor_test.exs` / `router_test.exs` — 21/36 failures

Pre-existing DB-setup gaps. Verified by stashing changes and re-running on identical baseline (PR #46 commit message documents this).

### `mix test` (suite-wide) is broken

Don't run bare `mix test`. The suite-wide failures are noise from the gotchas above + the long-tail of integration tests with infrastructure dependencies.

---

## Hard rules (CARRIED OVER from prior handoffs — re-state in PR body)

1. **STA scope only.** Touches outside `sta/*` are forbidden. 4 parallel Claude sessions running in this monorepo (efinanceira, pix, sisbajud, this).
2. **Never modify `core/`, `pix/`, `spb/`, `npc/`, `clst/`, `backoffice/`, `mobile/`, `infra/`, `docs/` (root), `MEMORY.md` (root), `CLAUDE.md` (root).**
3. **`mix format` ONLY on touched files.** Never bare `mix format` (would contaminate diff with 70+ baseline-drifted files).
4. **Run tests per-file**, never full suite. Touchstone test files for STA:
   - `test/sta_connector/sta/client_test.exs` (59/6)
   - `test/sta_connector/sta/operations_test.exs` (58/0)
   - `test/sta_connector/files/outbound_file_test.exs` (7/0)
   - `test/sta_connector/application_test.exs` (3/0)
   - `test/sta_connector/outbound_test.exs` (38/0)
   - `test/sta_connector_web/controllers/api/admin/outbound_files_controller_test.exs` (7/0)
5. **Pattern: Manual Verification mandatory** for sub-PRs introducing new BCB-facing wire-format claims. Onda 2.B qualifies.
6. **Push + PR só com auth explícita do usuário.** Stop after committing + ask.
7. **Worktree branch policy:** stay on the current branch until PR is merged; then cut new branch off `origin/main`.
8. **Decision policy when ambiguous:** (1) Plan vs código diverge → confiar no código. (2) Plan vs Manual diverge → confiar no Manual, fix-commit como misdiagnosis. (3) Manual silencia → `[STUDY-CONJECTURE]` + default + env-tunable + document. (4) Manual ambíguo → `[NEEDS-OPERATOR-CONFIRM]` + conservative + flag in PR body.

---

## Resumption checklist (commands to verify environment)

In the next session, run these in order — DO NOT skip any. If any fails, investigate before continuing.

```bash
# 1. Be in the right worktree
cd /Users/luizpenha/monetarie/.worktrees/sta-paridade
pwd                                                 # → /Users/luizpenha/monetarie/.worktrees/sta-paridade

# 2. State check
git fetch origin --prune
git status                                          # working tree should be clean
git log --oneline -5                                # should show recent merges including PR #52 fbf4dac2

# 3. Determine current branch (will be sta/handoff-2026-04-27-onda2b-pause OR detached if handoff already merged)
git branch --show-current
gh pr view {THIS_HANDOFF_PR}# --json state,mergedAt 2>&1 | head -3
# If MERGED + still on branch → cleanup: git checkout --detach origin/main && git branch -d sta/handoff-2026-04-27-onda2b-pause

# 4. Compile baseline (5 warnings expected)
cd sta/backend
mix deps.get
mix compile 2>&1 | grep -c "^[[:space:]]*warning:"  # → 5

# 5. Touchstone tests (per-file, NOT suite-wide)
mix test test/sta_connector/sta/client_test.exs                          # 59 tests, 6 baseline failures
mix test test/sta_connector/sta/operations_test.exs                      # 58 tests, 0 failures
mix test test/sta_connector/files/outbound_file_test.exs                 # 7/7 PASS
mix test test/sta_connector/application_test.exs                         # 3/3 PASS
mix test test/sta_connector/outbound_test.exs                            # 38/38 PASS (post-PR #52)
mix test test/sta_connector_web/controllers/api/admin/outbound_files_controller_test.exs  # 7/7 PASS

# 6. Postgres dev (only if DB-touching work — Onda 2.B is mostly stateless)
docker ps --filter name=sta-connector-db-dev --format '{{.Names}}: {{.Status}}'
# If down: docker compose -f sta/docker-compose.dev.yml up -d db

# 7. Sanity — ICP-Brasil cert chain (Onda 1 leftover)
ls -la sta/backend/priv/certs/icp_brasil_ca_chain.pem
```

---

## Decision tree at session start

After running the resumption checklist above, branch on these states:

### State A — This handoff PR not yet merged

Branch on the worktree is `sta/handoff-2026-04-27-onda2b-pause`. Either wait for merge or proceed in parallel by cutting Onda 2.B work branch off `origin/main` (the handoff doc and the Onda 2.B work are independent — Onda 2.B doesn't touch the handoff file).

### State B — This handoff merged + worktree on detached HEAD or local main

Clean state. Cut new branch:

```bash
cd /Users/luizpenha/monetarie/.worktrees/sta-paridade
git fetch origin --prune
git checkout --detach origin/main          # if not already

# Pick one based on (A/B/C) decision:
git checkout -b sta/onda2-status-and-queries
```

### State C — Onda 2.B already started in another session / partial

Unlikely but check `gh pr list --state open --search "onda2-status"` first.

---

## Implementation roadmap (post-decision)

Once (A/B/C) is chosen, follow this exact sequence:

### Step 1 — Plan doc
- Path: `sta/docs/plans/2026-04-XX-sta-onda2b-status-and-queries.md`
- Sections: Task 0 Manual Verification (pre-collected above can be copied verbatim) → Architecture revisions → Pre-flight → Tasks → Acceptance → References
- Mirror format of `sta/docs/plans/2026-04-26-sta-onda2a-followup-2-ranged-resume.md`
- Commit as `docs(sta): Onda 2.B {chosen-scope} plan + Task 0 Manual Verification`

### Step 2 — Tasks (TDD per task, RED → GREEN → commit)

If **(B) trim chosen** — 3 tasks:

**Task 1: `StateCode` module + tests**
- Create `lib/sta_connector/sta/state_code.ex` with the spec from rollout plan §Sub-PR 2.B (lines 691-726).
- Create `test/sta_connector/sta/state_code_test.exs`.
- Tests: `from_int/1` happy + invalid; `to_int/1` happy + invalid; `terminal?/1` for each terminal code + 1 non-terminal; `success?/1` ditto; `failure?/1` ditto; in-progress codes (1, 2, 5, 10, 15, 20, 30, 70) classify as none of terminal/success/failure.
- Estimate: 30 min.

**Task 2: Batched `change_status` + tests**
- Modify `lib/sta_connector/sta/client.ex`:
  - Rename `:confirm_receipt` build_rest_request clause to `:change_status` (per rollout plan §Sub-PR 2.B line 683).
  - New parameter shape: `%{protocols: [String.t()], situation: :rec | :a_rec}`.
  - Map `:rec` → `"REC"`, `:a_rec` → `"A_REC"` in XML body.
  - Emit `<Protocolos>p1;p2;...</Protocolos>` per §7.1.
- Modify `lib/sta_connector/sta/client_behaviour.ex`: new `@callback change_status/2` (signature: `(%{protocols, situation}, opts) -> {:ok, :ok} | {:error, Error.t()}`).
- Keep `def confirm_receipt(protocol, opts)` as a thin wrapper: `change_status(%{protocols: [protocol], situation: :rec}, opts)`.
- Tests: extend `client_test.exs` with new describe block:
  - 1 protocol + REC → emits `<Protocolos>P1</Protocolos><Situacao>REC</Situacao>`
  - 3 protocols + REC → semicolon-joined
  - A_REC → emits `<Situacao>A_REC</Situacao>`
  - Asserts `Content-Type: application/xml` is sent
  - 204 response → `{:ok, :ok}`
  - 400 response → mapped error
- Estimate: 1-2h.

**Task 3: Worker wiring (Inbound + Outbound)**
- Audit `lib/sta_connector/inbound/worker.ex` and `lib/sta_connector/outbound/worker.ex` for any `status_desc` string compares. If found, replace with `StateCode.terminal?/success?/failure?` decisions. Onda 2.B rollout plan acceptance criterion: "Workers no longer string-compare status descriptions."
- Possible scenario: Worker may NOT currently string-compare states (their retry logic is RetryEntry-based). In that case, this task becomes a "future-readiness" change — wire StateCode usage into worker's status-update functions so future Onda 2.C state-polling can leverage them.
- Tests: extend existing worker tests if any; or create minimal regression test if none exist (Inbound.Worker has no `worker_test.exs` per PR #46 handoff).
- Estimate: 1-2h.

If **(A) full chosen** — add 2 more tasks:

**Task 4: `Client.query_protocol/2` + tests**
- New build_rest_request clause + parse_rest_response clause.
- New behaviour callback.
- Tests: emits correct query string with all 3 nivelDetalhe values; emits semicolon-joined protocols; parses single-protocol response; parses multi-protocol response with `<Respostas>` block (§8.2 sample); 400 response → mapped error.
- Estimate: 2-3h.

**Task 5: `Client.advanced_query/1` + tests**
- New build_rest_request clause (uses URI.encode_query for the multi-param URL).
- Reuse parse_rest_response from Task 4 (§8.3 says "idêntica à resposta da consulta por protocolos").
- New behaviour callback.
- Tests: each query parameter emits correctly; date format ISO 8601 enforced; ordering enum mapped; situacaoTransmissao enum mapped; default ordenacao = DH_DESC.
- Estimate: 2-3h.

If **(C) full + admin chosen** — add 2 more tasks beyond (A):

**Task 6: Admin endpoint `GET /api/admin/sta/protocols`**
- New `Api.Admin.StaQueryController` (separate from existing FilesController and OutboundFilesController to keep the 3 schemas/concerns distinct).
- Route: `GET /api/admin/sta/protocols?ids={p1,p2}&detail={res|bas|compl}`.
- Wires `Client.query_protocol/2`. Returns JSON-serialized response.
- Operations injection seam (mirror PR #46 OutboundFilesController pattern).
- Tests: 4 tests covering happy / detail-level enum / not-found / error-from-Client.
- Estimate: 2-3h.

**Task 7: Admin endpoint `GET /api/admin/sta/queries/advanced`**
- Same controller; new action `:advanced`.
- Route: `GET /api/admin/sta/queries/advanced?date_from=...&date_to=...&state=...&situation=...&order_by=...`.
- Wires `Client.advanced_query/1`.
- Tests: 4 tests covering happy / each filter / not-found / error-from-Client.
- Estimate: 2-3h.

### Step 3 — Touchstone validation
- Same 6 touchstone files as listed under Hard rules.
- Confirm no regressions; all should be unchanged from baseline counts above.
- Confirm `mix compile` 5 baseline warnings preserved.
- Guard rail: `git diff $(git merge-base origin/main HEAD)..HEAD --name-only | grep -v "^sta/"` empty.

### Step 4 — Handoff doc + push/PR
- Write `sta/docs/handoff/2026-04-XX-sta-onda2b-{chosen-scope}.md`.
- Commit.
- Stop. Ask for explicit push + PR auth.

---

## Out of scope (will NOT do in 2.B regardless of A/B/C)

1. **Onda 2.C scope** — HTTP 410 protocol-expired auto-renew, HTTP 429 with `Retry-After`, 48h protocol expiry, 3 separate rate limiter buckets (transfers / queries / status_changes). Rollout plan reserves these for Sub-PR 2.C.
2. **Onda 2.D scope** — `/senhaws/senha` password management endpoints, separate hosts (www9.bcb.gov.br for homol, www3.bcb.gov.br for prod). Sub-PR 2.D.
3. **Onda 3 scope** — leiautes corpus (44 leiautes, 9 categorias).
4. **Onda 4 scope** — identifier mapping (CCS→PCCS001 etc.) and deletions in `core/backend/lib/monetarie/regulatory/sta_delivery.ex` (CRITICALLY — touching `core/` violates the parallel-sessions rule; deferred to Onda 4 only).
5. **Worker.init auto-recovery** for upload-side resume. Per Onda 2.A core handoff F1 risk note, admin-endpoint approach was preferred (PR #46 implemented this).
6. **`worker_test.exs` for Inbound.Worker** — pre-existing testing gap, documented in PR #46 handoff. Could be a separate hygiene PR.
7. **Cross-process / cross-restart download resume persistence** — out-of-scope since PR #43.
8. **Admin auth enforcement** — `:admin_api` pipeline still has the "Future: Add admin authentication here" TODO. New admin endpoints (option C) would inherit current state. Proper auth is its own future PR.
9. **Real BCB smoke test** — deferred to operator post-merge whenever Onda 2.B lands.
10. **Telemetry events for the new endpoints** — Wave 11 P2 metrics framework would be the home for these; deferred.

---

## Newly-visible follow-up work surfaced from PR #52

- The `OutboundFile.attempt_changeset` fix unblocked the `mark_uploading` increment behavior. Side-effect: previously-passing tests that asserted `upload_attempts == 0` after a mark_uploading call would now fail. Search confirmed: no such tests exist in the codebase. But if any production caller was incidentally relying on the broken-zero counter, that would now break. Low risk because the broken behavior was clearly a bug, but worth a smoke test on the next deploy.

---

## References

### Plan docs (the canonical truth)
- **Parent rollout plan:** `sta/docs/plans/2026-04-26-sta-conformance-rollout.md` — §Sub-PR 2.B at lines 670-737. Pattern: Manual Verification gate at §pattern-manual-verification.
- **Onda 2.A core plan:** `sta/docs/plans/2026-04-26-sta-onda2a-resilient-transfer.md`
- **Onda 2.A follow-up #2 plan:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-2-ranged-resume.md`
- **Onda 2.A caller-wiring plan:** `sta/docs/plans/2026-04-26-sta-onda2a-followup-caller-wiring.md`

### Handoff docs (chronological — most recent first)
- **THIS HANDOFF:** `sta/docs/handoff/2026-04-27-sta-onda2b-brainstorming-pause-and-onda2a-complete.md`
- **PR #46 caller-wiring handoff:** `sta/docs/handoff/2026-04-26-sta-onda2a-followup-callers-wiring.md`
- **PR #43 ranged-resume handoff:** `sta/docs/handoff/2026-04-26-sta-onda2a-followup-2-ranged-resume.md`
- **Pre-PR #43 status handoff:** `sta/docs/handoff/2026-04-26-sta-onda2a-followups-status-and-next-session.md`

### Manual STA references (Manual de utilização dos Web Services do STA v1.5 jul/2022)
- `sta/Manual_STA_Web_Services.pdf` (canonical PDF)
- `/tmp/manual_sta.txt` (extracted text, regenerable via `pdftotext -layout sta/Manual_STA_Web_Services.pdf /tmp/manual_sta.txt`)
- §7 Alteração de situação (lines 758-823 in extracted text)
- §8 Consulta a transferências de arquivos (lines 825-1089)
- §8.1 Consulta arquivos disponíveis (lines 867-973)
- §8.2 Consulta por protocolos (lines 977-1043)
- §8.3 Consulta avançada (lines 1045-1090)
- Tabela 1 — Parâmetros §8.2 (lines 1299-1330)
- Tabela 2 — Parâmetros §8.3 (lines 1331-1430)
- Tabela 3 — 14 state codes (lines 1431-1466)
- Tabela 4 — Parâmetros §8.1 (lines 1467-1490, already used by `:list_available_files`)

### Merged PRs (chronological)
| PR | Merge SHA | Date | Title |
|---|---|---|---|
| #24 | `301503ec` | 2026-04-26 | Onda 1 wire-format + ICP-Brasil chain |
| #30 | `3af17964` | 2026-04-26 | Onda 2.A core (chunked upload + ranged download) |
| #34 | `ed03ce39` | 2026-04-26 | Worker chunked-upload wiring |
| #36 | `100e5154` | 2026-04-26 | Operations.resume_upload/3 |
| #43 | `99e6e57c` | 2026-04-26 | Streaming download + ranged-resume mid-download |
| #46 | `1c10b7fa` | 2026-04-26 | Caller-wiring bundle + OutboundFile schema fix |
| #52 | `fbf4dac2` | 2026-04-27 | upload_attempts retry-cap fix |

### RFCs the Manual references
- RFC 7233 (HTTP Range Requests)
- RFC 7232 (HTTP Conditional Requests — ETag, If-Match, If-Unmodified-Since, Last-Modified)
- RFC 7617 (HTTP Basic auth)
- RFC 5754 (SHA-2 algorithm family)
- ISO 8601 (date format)

---

**End of handoff.** All context preserved. Next session: read this top-to-bottom, run resumption checklist, pick (A/B/C), follow implementation roadmap.
