The wrapper sits in front of both local engines; a fresh context there would send an auto
upload as language=sv and un-cancel the job, and no test noticed. Found by the PR #62 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The read is capped at 17 bytes and no valid language is longer than 4, so the parse
refuses anything the length check would; the mutant that removed it survived.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Form field (Swedish preselected, Other = auto) read before the file part and refused
before anything is spooled if outside the closed set; stored on the row (migration 021,
CHECK constraint, default sv for history); carried on the Job and the worker's context;
shown on the status page. Feature scenarios and the coverage map are updated, and
ADR-033/034 amended.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The other engine transcribes alone, then the cloud fallback if it fails and the job is
still live. A cancelled job never reaches the fallback (mutation-checked).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The request used to hard-code language=sv for every upload, so a recording in another
language was decoded as Swedish. The language now comes from the context; no language in
the context is still sv.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
sv and auto; an empty form value is sv, anything else is refused. LanguageFrom defaults to
sv so a caller that sets nothing behaves as before.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Skipping 'by', 'reconciled' and '+' as words everywhere let an alias with one of those
names pass as local. The two-engine shape is now split on its own separators and each alias
judged whole; a reconciler that is not local is pinned too. Hardening suggested by the third
PR #58 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The previous version kept only words containing '/' and dropped the rest, so a bare
TAPIR_TRANSCRIBE_MODEL alias in the composite produced a false 'all local' result. Found
by the PR #58 re-review; red tests for a bare alias, an alias with a space and a split
'berget / x' first.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The tracker comment said 'never a stalled job'; each transition can wait up to the timeout.
The delete refusal is judged at the moment of the attempt. Found by the PR #58 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The earlier entry posted to an app without uploads, where the route does not exist, and
stayed green with the route removed. This one seeds a finished row and goes red without the
gate. Found by the PR #58 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Only the 8-byte override was exercised; >= vs >, a huge default and a non-positive option
all stayed green. Also says why the error-body read ignores its error. Found by the PR #58
review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The worker fake ignored the heartbeat time, so the store-boundary half of the app-clock
fix had no assertion. Found by the PR #58 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every earlier test passed WithSaveTimeout; the worker passes nothing, so the 5 s default
was the only production value and a flip to 0, 1ms or 1h stayed green. Found by the PR #58
review; each flip is now red.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The two-engine path writes '<base> + <alt>, reconciled by <model>'. IsLocalModel is a
single-alias match, so the composite was 'not local' and the note claimed the audio left
the homelab, naming a local host as the provider. Found by the PR #58 review.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A 900-stream file cost ~570 MB RSS in each tool; with the cap the same file fails
in ~18 MB and is reported as unreadable.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Upload a recording (audio, or video with the camera off), get a private Swedish transcript, and chat about it.
- ADR-033: RLS-scoped private transcript store; ffprobe/ffmpeg intake; gateway transcription (iguana kb-whisper, berget fallback); in-process single-replica worker; chat over the transcript (local models only).
- ADR-034: optional two-engine mode (pianissimo + kb-whisper) reconciled by a LOCAL model; off unless TAPIR_RECONCILE_BASE_MODEL is set.
- ADR-035: real progress (phases, running/quiet/lost), phase logging, every judged spot stored, merged segments.
- Hardening from four adversarial reviews: errors logged and stored as kinds never text; per-engine deadlines; converted-length check; bounded queue; client hang-ups are not errors; strict local-alias check.
- Dockerfile: static ffmpeg/ffprobe 7.0.2 pinned by sha256 (#34). Built and inspected by hand; CI does not build the image on a PR, so its first CI build is this merge.
Uploads stay OFF until TAPIR_UPLOADS_ENABLED is set in the deployment.
Closes#33, #34, #42, #43, #44.
Co-authored-by: Mathias <mthbqv@gmail.com>
internal/web/views.templ:7 still imports
gitea.d-ma.be/mathias/tapir/internal/adapters/store
while go.mod declares
git.d-ma.be/mathias/tapir
Left behind by the gitea rename (infra ADR-0004). The old hostname still
resolves in DNS but no longer serves — HTTPS to it fails outright — so the
import is genuinely broken, not merely outdated.
Why nothing caught it: the COMMITTED internal/web/views_templ.go is correct,
so builds and CI use a good generated file and stay green. The rot is only in
the source. It surfaces the moment anyone runs `templ generate` — which
regenerates the broken import over the good output, and then the build fails
with
no required module provides package gitea.d-ma.be/mathias/tapir/internal/adapters/store
Found exactly that way while evaluating this repo for the estate-wide lint
config: regenerating templ as part of the check turned a green repo red, and
the cause was the source, not the check.
Source fixed; regenerating now reproduces the committed generated file
byte-for-byte, so views_templ.go is unchanged here. Verified: go build and
go vet pass.
Estate-wide consolidation pass. The canonical .golangci.yml
(infra ci-templates/golangci.yml, infra#309 pattern) enables the gofumpt
formatter; golangci-lint v2 default set enables NO formatter, which is why
this drift accumulated silently -- the same hole that let two files rot in
dma-cli until they were found by hand (dma-cli#12).
Mechanical and deterministic: `gofumpt -w .`, nothing hand-edited. Verified
per repo before commit -- gofumpt -l now reports 0, gofmt -l reports 0, and
go build + go vet both pass.
Clears this repo to adopt the canonical lint config in a later wave.