Bring the living docs current with the recency-bounded auto-summarize + sparse
honesty + feed IA bundle (ADR-020):
- requirements (BDD): summarize_mode.feature — auto now summarizes RECENT new
videos; added a scenario for older videos (listed, on-demand), recency note.
- architecture.md: summarization-mode + new list-surface paragraph; scheduler
diagram + two-path table + three-phase pass now show the recency pre-filter;
dropped stale "Summarize now".
- data-model.md: auto_summarize is recent-only, older on-demand.
- README.md: one-line recency note on the serve scheduler.
- ui-spec.md: appended the as-built ADR-020 row (supersedes earlier copy/sort).
- specs/{video-card-states,newest-first-ordering,scheduled-discovery}.md:
superseded/extended banners pointing at ADR-020 (kept as design records).
Docs-only; task check green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.1 KiB
Spec — Unify video-card states: one "Summarize now" verb, honest no-captions state
Superseded in part by ADR-020 (2026-06-08). The five card states still hold, but the copy changed: the nudge verb is now "Summarize" (not "Summarize now"), the rate-limited state reads "In queue" (not "Fetching soon…"), and the queued state reads "summarizing shortly" (not "waiting for the next run"). The list also now collapses older un-summarized and caption-less videos. See
DECISIONS.mdADR-020 andviews.templ(VideoCard) for the current copy; this doc is kept as the original design record.
Repo: tapir · Size: small, view-layer only (views.templ + a little CSS in
view.go; regenerate views_templ.go). No handler, store, or DB change. The two existing
handlers (/summarize, /retry-now) stay exactly as they are — only what the card shows
changes.
Why. The video card today presents two different verbs — "Try now" (.btn-retry, on
rate-limited videos) and "Summarize" (.btn-secondary, on pending videos) — for what the user
experiences as one intent: "summarize this video now." The user doesn't know or care about the
internal pipeline state (rate-limited vs. manual-queue); two differently-labelled, differently-
styled buttons leak that state machine into the UI as a choice. Also: a no-captions video
(TranscriptStatus == "none") currently falls into the else branch and wrongly shows a
"Summarize" button that, if clicked, tries and fails — there are no captions to fetch. That's
the confusing dead-end to remove.
In normal use the maintainer is in auto mode, so these buttons are exceptions, not the main path — videos summarize themselves. So the card should be status-first: the state is what the user reads constantly; the manual nudge is a small, quiet affordance for impatience, not a prominent call-to-action.
The honest per-state card model (footer of VideoCard)
Restructure the footer branch in VideoCard (in internal/web/views.templ) to these states.
The branch ORDER matters (summarized first, then terminal/no-action states, then actionable):
- Summarized — preview + provider chip + fallback badge + actions. No button. (unchanged)
- No captions (
r.TranscriptStatus == "none") — NEW branch. Quiet status text, e.g.No transcript available(use a muted.card-state/.chip-retry-style treatment, NOT a button). This is a terminal honest dead-end — the user can do nothing, so offer nothing. - Queued / requested (
r.SummarizeRequested) — "Queued · waiting for the next run". No button. (unchanged) - Rate-limited (
r.TranscriptStatus == "rate_limited") — quiet status (keep the "fetching soon" sense) + a quiet "Summarize now" button POSTing toretryNowURL(clears backoff then processes). Same quiet style as state 5. - Pending (else — discovered, not yet attempted) — quiet "Not summarized" + a quiet
"Summarize now" button POSTing to
summarizeURL(flips the queue flag then processes).
Unify the verb and the style
- One label everywhere a manual nudge is offered: "Summarize now" (states 4 and 5). Drop the "Try now" wording entirely.
- One quiet style for both: use the understated
.btn-retrypattern (small, pill, outline, transparent bg) — NOT.btn-secondary/.btn(heavier). Rename the CSS class to something state-neutral (e.g..btn-quietor.btn-summarize-now) so it no longer reads as "retry"-specific; keep the same visual. The point: the nudge is subtle, status is primary. - Keep both
<form>s posting to their respective existing handler URLs (retryNowURLfor rate-limited,summarizeURLfor pending) with the existing HTMX attributes (hx-post,hx-target=#video-{id},hx-swap=outerHTML) — only the button label/class change. The backend side-effect difference (clear-backoff vs. set-flag) stays invisible to the user, which is correct. - Drop the engineer-facing
title="Fetch transcript now through the shared rate gate"tooltip; if a hint is wanted, make it user-facing ("Summarize this one now").
Quietness check (the design intent)
The summary content and the per-state status are the card's primary information. The "Summarize now" button is a minor affordance. Do not make it a prominent solid-accent CTA — it must read as "you can nudge this if you're impatient", not "action required". Status text uses muted styling; the button uses the quiet outline style.
Tests
videocard_internal_test.go(exists): assert each of the 5 states renders the expected footer — summarized (no button), no-captions (status, NO button, nosummarize/retry-nowURL present), queued (no button), rate-limited ("Summarize now" → retry-now URL), pending ("Summarize now" → summarize URL). The key new assertion: anone-status video renders no action button and no POST URL.- Assert the label string "Try now" no longer appears anywhere in rendered output.
Out of scope
Handler/DB changes; the detail-page action buttons (watched/skipped/saved — unrelated); the pipeline stats bar wording; auto/manual mode behaviour. Verb/label/style/no-captions-state only.