Files
tapir/docs/sprints/audio-upload-followups-2026-10.md
mathiasandClaude Sonnet 5.5 24d65b2085
CI / Lint / Test / Vet (push) Successful in 27s
CI / Build & Import (push) Successful in 22s
CI / Deploy via GitOps (push) Successful in 5s
docs(sprint): audio upload follow-ups sweep sprint plan, oath and decision list
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 20:50:34 +02:00

5.6 KiB

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, v0.32.0 - audio upload follow-ups.

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)

  1. #52 label a cloud-served transcript in plain words; document the empty-transcript trigger.
  2. #53 bound the LLM client's response body read.
  3. #51 tracker: save timeout, then app-clock heartbeat.
  4. #37 nav link to uploads and per-recording delete (queued/transcribing: refuse until it ends). (Decision D3.)
  5. #36 chat default model after measurement. (Decision D4.)
  6. #38 measurements (Berget length limit, large models on production iguana). (D5, D6.)
  7. #46 remainder of the hardening list after the items above are split out.
  8. #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).