Files
tapir/docs/sprints/audio-upload-followups-2026-10.md
T
mathiasandClaude Sonnet 5.5 7b7da650d4
CI / Lint / Test / Vet (push) Successful in 31s
CI / Build & Import (push) Successful in 10s
CI / Deploy via GitOps (push) Successful in 5s
docs(sprint): v0.31.1 and v0.32.0 shipped; what is observed live and what waits for an authenticated look
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 00:34:59 +02:00

7.2 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: v0.31.1 and v0.32.0 shipped 2026-10-03/04, the owner-held items below remain open). 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).

Status (2026-10-04)

Shipped and deployed: v0.31.1 (PR #55, merge e037c6b) and v0.32.0 (PR #58, merge 85f4cc3; pod tapir-597dbfccc-ng78q 1/1, 0 restarts, image 85f4cc3). Each went through an adversarial review round by agent with the verdicts recorded on the PR (v0.31.1: BLOCK then APPROVE; v0.32.0: BLOCK, BLOCK, APPROVE, APPROVE; every block was a real defect, fixed test-first).

Live evidence: a foreign-origin POST to /account/delete and to /uploads/{id}/delete is refused (403) on production, a same-origin one reaches the login redirect (302).

Not observed live (needs an authenticated session, so it is the owner's look):

  • #37: the home page shows a "Recordings" link; a finished recording offers a confirm-then-delete control that removes it; a queued or transcribing one offers none.
  • #52: a transcript served by the cloud fallback shows the "audio left the homelab" note; a local one does not. A reconciled (two-engine) all-local transcript must NOT show it.
  • #50: the old pod of the next rollout should end Completed, not Error (the v0.31.1 rollout replaced a pod that ran the pre-fix image, and the v0.32.0 pod was gone before it could be read).

Still open (owner's call, milestone 11): #36, #38, #45, #46 remainder, #56 (slow-drip bodies hold a receive slot), #57 (shutdown logging, second signal). Decisions D4-D6, D8-D10 above are unchanged.

Deferred and filed from the reviews: #56, #57, #59 (start-up exits when the identity provider's OIDC discovery returns 503 (seen once at 19:40 on 2026-10-03; the pod restarted itself and recovered)).