9 Commits
Author SHA1 Message Date
mathias e6f508824b docs: add ADR-016 — Stage 0 gate revised to "me or a friend", behavioural
CI / Lint / Test / Vet (push) Successful in 19s
CI / Build & Import (push) Successful in 11s
Records the gate change: Stage 0 now passes when either the maintainer or an
onboarded friend returns unprompted in >=2 separate weeks. Behavioural (return
usage), not feedback-based, to resist politeness bias. Includes an honest
self-scrutiny note that this is a guardrail edit made while the original gate was
unmet — examined on that basis and proceeding because it broadens who supplies the
signal without softening the kind of signal required. Adds feedback-based-gate to
rejected alternatives.
2026-06-03 20:46:40 +00:00
mathias 17fad140a6 docs: add ADR-015 — per-user credentials envelope-encrypted in PG18
CI / Lint / Test / Vet (push) Successful in 10s
CI / Build & Import (push) Successful in 10s
Records the infra#88 spike decision: per-user OAuth tokens are runtime app-state,
not config, so they live envelope-encrypted in PG18 under RLS (key from 1P via the
existing read-only SA) rather than in the vault. Infra creds stay ESO/1Password —
two mechanisms because they're two different things. Includes the falsification
conditions (frequent rotation; estate audit policy; key-rotation cost) so the
choice is earned not assumed. Adds the two rejected candidates (vault-write SA;
Supabase) to the rejected-alternatives table. Full reasoning in the #88 decision
doc; build + reboot-validation in #89.
2026-06-03 20:29:50 +00:00
mathiasandClaude Opus 4.8 fb425cbf9a chore(decisions): reorder ADR-010 to numeric position
ADR-010 sat between ADR-008 and ADR-009. Moved ADR-009 (TBD) ahead of
ADR-010 (timedtext caption acquisition) so the file reads 001..014 in
numeric order. Pure structural move, no content change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:13:50 +02:00
mathias 672a0c8580 docs: add ADR-013 (delete semantics) and ADR-014 (429 handling + UX)
CI / Lint / Test / Vet (push) Successful in 10s
CI / Mirror to GitHub (push) Failing after 3s
CI / Build & Import (push) Successful in 10s
ADR-013 records the deliberate choice that account deletion is Tapir-side only
(cascade + secret purge), leaving the shared Dex identity intact — clean
re-registration, but a noted GDPR-shaped gap if Future C ever arrives.

ADR-014 specifies timedtext 429 handling: Retry-After-aware backoff, a single
per-egress-IP rate gate shared by the batch and click paths, and honest in-flight
UX (summarizing / queued-waiting / no-transcript) so a rate-limited fetch never
presents as a stuck spinner or error. Whisper stays deferred pending measurement
of the sustainable rate, which this work finally makes measurable.
2026-06-03 19:03:52 +00:00
mathias aa3f1631a6 docs(adr): ADR-012 — open Stage 1 (multi-user) with enforced RLS isolation
CI / Lint / Test / Vet (push) Successful in 8s
CI / Build & Import (push) Successful in 10s
CI / Mirror to GitHub (push) Has been skipped
Maintainer's call to open Stage 1 ahead of the Stage-0 gate. Non-negotiable:
multi-user ships WITH DB-enforced isolation (Postgres RLS, FORCE'd on the
owner role, per-request tapir.current_user_id) and a passing two-user isolation
test in the same slice — the VISION Stage-2 bar pulled forward, not deferred.
Replaces ADR-011's allowlist-of-one with per-subject users rows; adds
video_connections + subscriptions.
2026-06-03 15:06:23 +02:00
mathiasandClaude Opus 4.8 13663c220b docs(ui): add Stage-0 Web UI spec + ADR-011 (Dex authn, action signal, tapir.d-ma.be GitOps)
CI / Build & Import (push) Failing after 0s
CI / Mirror to GitHub (push) Has been skipped
CI / Lint / Test / Vet (push) Successful in 6s
Reader + watch/skip/save (instruments the Stage-0 'acts on a summary' metric),
HTMX+Templ over the existing store, Dex OIDC login with single-user allowlist
authz (authn now, tenancy deferred), deployed at tapir.d-ma.be via Flux GitOps
with ESO secrets and in-cluster postgres18. Build decomposed into 4 gated lanes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:30:41 +02:00
mathiasandClaude Opus 4.8 8b14ef4add feat(youtube): acquire captions via player/timedtext baseUrl (ADR-010)
CI / Lint / Test / Vet (push) Successful in 6s
CI / Build & Import (push) Failing after 1s
CI / Mirror to GitHub (push) Has been skipped
The Data API captions.download endpoint is owner-only: every subscription
video the user does not own returned HTTP 403, producing 0 summaries and a
~150-line error spew in the first live Stage-0 run. Captions-first (ADR-007)
is sound; only the acquisition mechanism was wrong.

FetchTranscript now resolves caption tracks from the InnerTube player
response (ANDROID client, unauthenticated) and GETs the chosen track's
timedtext baseUrl with a plain http.Client — no OAuth token, which can break
the endpoint. The srv3 XML, json3, and legacy <transcript> formats all parse;
non-asr tracks in a preferred language win. Watch-page ytInitialPlayerResponse
scrape is the fallback when InnerTube returns no tracks.

Degrade, don't error (explicit quick-fix): no captionTracks, empty baseUrl, a
non-200 fetch, or an unparseable body yield Source=none, not an error. Only
genuine transport faults error — this kills the spew. OAuth stays on
ListSubscriptions/NewVideos (Data API); only transcript fetch goes unauthed.

Validated live from koala: the ANDROID client returned working baseUrls and
real transcript text for public videos the run identity does not own.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:08:13 +02:00
mathias bc79167dfe docs: record rejected alternatives in DECISIONS.md
Adds a consolidated table of approaches considered and deliberately not taken
(Python, Supabase, living in the monolith, shared-lib lift, filesystem brain
package, reusing inbound oauth, global dedup table, Whisper-in-core, SaaS-now,
swarm-delegating the spike), each mapped to the ADR that settles it. Prevents a
later session from re-proposing settled rejections as fresh ideas.
2026-06-02 10:38:41 +00:00
mathias 7090fb1e40 docs: add architecture decision records (ADR-001..009)
Records the decisions from the S5 spike and the Full Grill as append-only ADRs:
Go not Python; no Supabase; standalone-first with brain as one sink; copy the
llm package; brain sink via HTTP brain-mcp; fresh outbound OAuth; captions-first
with STT deferred; Future C deferred behind the Stage 0 gate; trunk-based dev.
2026-06-02 10:31:00 +00:00