ADR-002 decided never to transcode because the server is a headless, GPU-less Xeon, and its measurements still hold. The problem is scope: "this server cannot transcode" became "Vellum does not transcode", and only the first is forced by the hardware. Intel Quick Sync has been in most desktop CPUs since 2015, NVENC is in every recent Nvidia card, and VAAPI covers AMD and Intel on Linux. Capping the product at the hardware in one closet is the wrong constraint, and the gap is visible: a client that cannot decode HEVC gets nothing, and in Chrome that is not even a clean refusal - the video never decodes and audio plays over a blank frame. So the engine is added and its registration is gated: - registered only when usable transcode hardware AND ffmpeg are both present - off by default; direct play stays the default and the preference - consulted only when no other engine can direct-play the file, which is exactly the case that returns "no engine can play this" today - hardware-accelerated only. No CPU fallback: that is the stall ADR-002 measured, and it fails slowly, which is worse than refusing. This deployment is unchanged. On a GPU-less Xeon the engine is never registered and the path is never reached, so ADR-002 holds as written. ADR-002's status now points here rather than being deleted, per the rule that a changed decision gets a new record instead of an edited one. |
||
|---|---|---|
| .. | ||
| ADR-001-sqlite-with-a-seam.md | ||
| ADR-002-direct-play-only.md | ||
| ADR-003-mark-missing-never-delete.md | ||
| ADR-004-modular-monolith.md | ||
| ADR-005-one-item-access-chokepoint.md | ||
| ADR-006-table-backed-job-queue.md | ||
| ADR-007-fixed-width-timestamps.md | ||
| ADR-008-optional-hardware-transcoding.md | ||
| README.md | ||
Decisions
Each record captures a decision that was expensive to reverse, the alternatives that were rejected, and the reasoning. They exist so nobody has to re-litigate a settled question — and so an agent does not "improve" something that was deliberate.
Read the ones relevant to what you are changing.
| ADR | Decision | Rejected alternatives |
|---|---|---|
| 001 | SQLite, with a storage seam for later | PostgreSQL; fixing the write lock in code instead |
| 002 | Direct play only, never transcode | On-demand CPU transcode; remux; pre-transcode |
| 003 | A vanished file is marked, never deleted | Delete on scan; auto-purge; keep it invisible |
| 004 | One binary with enforced package boundaries | Microservices; unenforced conventions |
| 005 | One item-access chokepoint, internal/catalog |
Leave the duplication; a shared helper; a generic repository |
| 006 | The job queue is a table | Redis or a broker; in-memory goroutines |
| 007 | Fixed-width timestamps via core.Stamp |
Integer nanoseconds; six/nine digits; sorting in Go |
| 008 | Transcoding is optional, hardware-gated, off by default | Keeping never-transcode as a product rule; CPU fallback; transcode by default |
Writing a new one
Add ADR-00N-short-title.md with Status, Date, Context, Decision,
Alternatives Considered (with the reason each was rejected) and
Consequences. Do not delete an ADR when a decision changes — write a new one
that supersedes it, so the history stays readable.
Two things make an ADR worth writing: the decision would be expensive to reverse, and a reasonable person could have chosen differently. If neither is true, a comment in the code is the right place.