89 lines
5.6 KiB
Markdown
89 lines
5.6 KiB
Markdown
# Sprint: audio upload follow-ups (Sweep Sprint, 2026-10)
|
|
|
|
Method: Sweep Sprint (phase 1 parallel read-only research per issue: **done**; phase 2 sequential
|
|
execution, one issue per commit: **not started, waits for the owner's go**). Closing oath below.
|
|
|
|
Milestones: [v0.31.1 - security and robustness](https://git.d-ma.be/mathias/tapir/milestone/10),
|
|
[v0.32.0 - audio upload follow-ups](https://git.d-ma.be/mathias/tapir/milestone/11).
|
|
|
|
Context: v0.31.0 shipped audio upload (ADR-033/034/035) and is live in production behind auth at
|
|
`/uploads`, with the Berget cloud fallback on for audio (infra#522). A real 63.7 min upload
|
|
transcribed in 2m53s with no errors. The phase-1 investigation found two pre-existing or new
|
|
weaknesses an authenticated user can reach, so the first release is a hotfix.
|
|
|
|
## Oath (closing gates, every clause must hold; blocked = descoped and tracked, never "done")
|
|
|
|
| # | Clause | Evidence |
|
|
|---|--------|----------|
|
|
| SG1 | Every issue in the sprint is closed with a linked commit, or moved out with a stated reason | issue list of both milestones |
|
|
| SG2 | Each fix has a test that was seen red on main first, named after the mechanism it observes | red/green output in the issue |
|
|
| SG3 | `task check` AND `golangci-lint run ./...` exit 0 (checked by `$?`, not by grep); `task check` does not gate lint until #41 | gate output |
|
|
| SG4 | Control code (#48, #49, the upload cap in #47) has a recorded adversarial review verified by execution, verdict in the issue | gatekeeper verdict comment |
|
|
| SG5 | Docs current in the same commit: ADR-033/035 text, feature file, use-case coverage test | diff |
|
|
| SG6 | Each release tagged (v0.31.1, v0.32.0), pushed, deployed, and observed live (pod running the new image, no restart loop, a real upload still works) | `kubectl`/log + one upload |
|
|
| SG7 | Learnings written to the brain; deferred work filed as issues; decisions that are the owner's recorded as DECISION NEEDED | brain ids + issue numbers |
|
|
|
|
## Work order
|
|
|
|
### v0.31.1 (security and robustness; hotfix, do first)
|
|
|
|
1. **#41 lint gate.** `task check` swallows lint findings (`|| echo`). Make the gate fail on findings,
|
|
fix what it surfaces. First, so every later gate means something. (Decision D1.)
|
|
2. **#48 cross-origin POST protection.** `http.NewCrossOriginProtection()` around `Router()`.
|
|
Red: foreign-origin POST to `/account/delete`, `/uploads`, `/uploads/{id}/chat` is refused.
|
|
3. **#49 ffprobe/ffmpeg limits.** `-max_streams 32` + narrower probe output; probe stdout cap;
|
|
probe deadline. Three commits.
|
|
4. **#47 concurrent-upload cap (remaining item).** Bound the spool: reservation per accepted upload
|
|
against the 6Gi emptyDir so concurrent uploads cannot evict the pod.
|
|
5. **#50 SIGTERM interim.** Handle SIGTERM; record the interrupted job at once; fix the false
|
|
comment. Full drain recorded as a limitation. (Decision D2.)
|
|
|
|
Release v0.31.1: one adversarial review round over 2-5, then tag, push, observe.
|
|
|
|
### v0.32.0 (follow-ups)
|
|
|
|
6. **#52** label a cloud-served transcript in plain words; document the empty-transcript trigger.
|
|
7. **#53** bound the LLM client's response body read.
|
|
8. **#51** tracker: save timeout, then app-clock heartbeat.
|
|
9. **#37** nav link to uploads and per-recording delete (queued/transcribing: refuse until it
|
|
ends). (Decision D3.)
|
|
10. **#36** chat default model after measurement. (Decision D4.)
|
|
11. **#38** measurements (Berget length limit, large models on production iguana). (D5, D6.)
|
|
12. **#46** remainder of the hardening list after the items above are split out.
|
|
13. **#45** evaluate Berget as the reconciler: a question, not work. (D8.)
|
|
|
|
## DECISION NEEDED (owner's call; recommendation first)
|
|
|
|
| # | Question | Recommendation | Blocks |
|
|
|---|----------|----------------|--------|
|
|
| D1 | #41: should any linter be allowed to opt out of the gate? | No opt-out; fix findings | #41 |
|
|
| D2 | #50: full SIGTERM drain, or record as a limitation and ship the interim fix? | Limitation + interim; a drain costs 4-6 h and downtime on every deploy at current volume | #50 |
|
|
| D3 | #37: delete of a queued/transcribing recording | Refuse until it ends | #37 |
|
|
| D4 | #36: make a 31B model the chat default? | Only after measurement, conditional | #36 |
|
|
| D5 | #38: Berget length measurement needs a throwaway key | Yes, throwaway key | #38 |
|
|
| D6 | #38: load large models on production iguana | Yes, in a quiet window with the owner present | #38 |
|
|
| D7 | #46: where the ffmpeg tarball lives | Do nothing now; file an issue | none |
|
|
| D8 | #45: evaluate Berget as the reconciler | No; stay local-only | #45 |
|
|
| D9 | Who judges ADR-034's second blind check | The owner; do not reuse the one-off Opus authorisation | ADR-034 |
|
|
| D10 | When to enable reconcile in production | Needs `iguana/pianissimo` in the key pin and env vars; owner present | ADR-034 |
|
|
|
|
## Dropped or reduced, with reasons
|
|
|
|
- **#46 item 1 (`alis`/`dref` external reference):** checked, not a problem. Under the application's
|
|
flags ffmpeg skips the external track and writes a 78-byte WAV.
|
|
- **#46 item 2 / #47 item 2:** merged into #49.
|
|
- **#47 item 3 (full drain):** reduced to #50 plus a recorded limitation.
|
|
- **#46 `Beat` taking the tracker mutex:** incorrect in #46; only `logStill` does. Corrected in #51.
|
|
|
|
## Human-present items (do not run unattended)
|
|
|
|
- Production iguana model loads (#38, D6).
|
|
- Enabling reconcile in production (D10).
|
|
- Any key change or `apply` in infra (infra#523, infra#524 are open and not part of this sprint).
|
|
|
|
## Release plan
|
|
|
|
SemVer: v0.31.1 patch (fixes, no new behaviour); v0.32.0 minor (nav link, delete, cloud labelling).
|
|
Every push to tapir main redeploys the pod and fails any job in flight: push when no upload is
|
|
running (check the pod log first).
|