docs(adr): record ADR-020 recency-bounded auto-summarize + sparse honesty
CI / Lint / Test / Vet (push) Successful in 12s
CI / Build & Import (push) Successful in 10s

Document the architecture decision behind this bundle: bound auto-summarize to
a recency window (refines ADR-018; bounds load against the ADR-014 gate without
fetching harder), surface scarcity honestly, and collapse the un-summarized
back-catalogue in a single feed. Records the return-nudge as a deliberate
non-goal (it would contaminate the Stage-0 unprompted-return signal, ADR-016).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 14:04:37 +02:00
co-authored by Claude Opus 4.8
parent f29927f50d
commit 2cda62b3ad
+46
View File
@@ -697,6 +697,52 @@ the supported path.
---
## ADR-020 — Recency-bounded auto-summarize + honest sparse-state surface
**Status:** Accepted (2026-06-08). **Refines ADR-018** (auto-summarize) and **ADR-014**
(per-IP caption rate gate).
**Context.** Tapir is operational but sparse: at real subscription volume the maintainer's
account holds ~283 discovered videos, ~15 summarized, ~256 behind the respected per-IP caption
rate gate, ~12 no-captions. Two problems follow. (1) **Load:** ADR-018 auto-summarizes *every*
unseen video, so a large back-catalogue re-drives the whole queue through the gate every cycle —
self-inflicted 429s with no user value (nobody is waiting on a 6-month-old video). (2) **First
contact:** a new user sees a mostly-empty feed with no moving parts and a UI that implied
abundance/imminence ("fetching soon" ×256, "Run `tapir run`", "Summarize now"); the Stage-0 gate
is *return usage*, and the experience died at the first visit. Source: a UX heuristic review
(`docs/ux-review/UX-REVIEW-stage0-recency.md`).
**Decision.**
1. **Recency bound on auto-summarize.** In automatic mode the scheduler only summarizes videos
published within `TAPIR_AUTO_SUMMARIZE_WINDOW` (default ~7d). Older videos are still discovered
and listed but not auto-processed — they keep the manual "Summarize" affordance. An explicit
manual request bypasses the bound. `0` disables it (pre-recency behaviour). This bounds auto
*load*; it does not fetch harder — the gate (ADR-014) is untouched and the manual path still
serialises through it.
2. **Honest sparse-state surface.** Copy is reframed to surface scarcity truthfully, never to
look fuller: "N ready · M in queue · K no captions" (not "fetching soon"); a one-line "captions
are fetched slowly on purpose" note; "Summarize" (not "Summarize now"); the empty-connected
state stops printing an impossible CLI command.
3. **Feed IA = one list, noise-collapsed.** Summarized + recent un-summarized cards lead inline;
the older un-summarized back-catalogue collapses behind a single "Show N older videos"
disclosure; caption-less videos collapse to a one-line count instead of N dead cards. List is
ordered summarized-first, then `published_at DESC NULLS LAST`.
**Consequences.** The auto path's per-cycle fetch volume is bounded by recent uploads, not the
whole back-catalogue, so steady-state 429 pressure drops sharply. Older videos become explicitly
on-demand — a deliberate honesty trade (the user chooses to spend a scarce fetch on old content).
The single-replica assumption (ADR-018) is unchanged.
**Deliberately NOT done (premature until the Stage-0 loop is validated).** Return-nudges
(digest email / push) — a nudge contaminates the *unprompted*-return signal the gate measures
(ADR-016); building it now poisons the experiment. Also deferred: full-text search, channel
facets, read/unread, saved views — all need summary abundance to matter.
**Reversibility.** `TAPIR_AUTO_SUMMARIZE_WINDOW=0` restores summarize-every-unseen; the feed
collapse keys off the same window (`App.RecencyWindow=0` → everything inline).
---
## Rejected alternatives
Approaches considered during the 2026-06-02 planning + grill session and **deliberately not