Files
tapir/docs/specs/video-card-states.md
T
mathiasandClaude Opus 4.8 27fd33c99c
CI / Lint / Test / Vet (push) Successful in 11s
CI / Build & Import (push) Successful in 10s
docs: reconcile requirements + architecture with ADR-020
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>
2026-06-08 20:21:14 +02:00

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.md ADR-020 and views.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.

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):

  1. Summarized — preview + provider chip + fallback badge + actions. No button. (unchanged)
  2. 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.
  3. Queued / requested (r.SummarizeRequested) — "Queued · waiting for the next run". No button. (unchanged)
  4. Rate-limited (r.TranscriptStatus == "rate_limited") — quiet status (keep the "fetching soon" sense) + a quiet "Summarize now" button POSTing to retryNowURL (clears backoff then processes). Same quiet style as state 5.
  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-retry pattern (small, pill, outline, transparent bg) — NOT .btn-secondary/.btn (heavier). Rename the CSS class to something state-neutral (e.g. .btn-quiet or .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 (retryNowURL for rate-limited, summarizeURL for 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, no summarize/retry-now URL present), queued (no button), rate-limited ("Summarize now" → retry-now URL), pending ("Summarize now" → summarize URL). The key new assertion: a none-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.