feat(runner): bound auto-summarize to a recency window

In automatic mode the scheduler now only summarizes videos published within
TAPIR_AUTO_SUMMARIZE_WINDOW (default ~7d). Older videos are still discovered
and listed — they keep the manual "Summarize" affordance — but are not
auto-processed, so a large back-catalogue (the maintainer's ~256-deep queue)
stops self-inflicting 429s against the per-IP caption gate each cycle (UX
review B1, recency design).

- runner.WithAutoWindow + Stats.SkippedTooOld; tooOld() treats a zero window
  as disabled and an undated video as never-aged-out (processed, not stranded).
- An explicit manual request bypasses the bound even in auto mode (requested
  videos are loaded in auto mode when a window is active).
- Wired through cmdRun, the scheduler's per-user runner, sumStats, and pass
  logging. config: TAPIR_AUTO_SUMMARIZE_WINDOW (default 168h), .env.example.
- Account copy (A7) updated to match: "Automatic summarizes new videos from
  about the last week; older videos stay browsable — summarize on demand."

The rate gate is untouched; the manual path still serialises through it. This
bounds auto LOAD, it does not fetch harder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 13:48:04 +02:00
co-authored by Claude Opus 4.8
parent 4a0a56e152
commit 2384c47b81
9 changed files with 199 additions and 42 deletions
+15
View File
@@ -71,6 +71,14 @@ type Config struct {
// the web click-path serialise through it. Zero = unlimited (dev/tests).
FetchRate time.Duration
// AutoSummarizeWindow bounds auto-summarization to recent videos: in automatic
// mode the scheduler only summarizes videos published within this window of now.
// Older videos are still discovered and listed, but wait for an explicit manual
// "Summarize" — so a large back-catalogue does not self-inflict 429s against the
// caption rate gate. Zero disables the bound (summarize every unseen video, the
// pre-recency behaviour). Default ~7 days.
AutoSummarizeWindow time.Duration
// DiscoveryInterval, when > 0, makes `serve` run in-process scheduled discovery
// for ALL users on that cadence (ADR-018). Zero/unset = disabled, so dev and
// tests never auto-fetch. Single-replica assumption — see cmdServe.
@@ -110,6 +118,7 @@ const (
defaultFetchBackoff = time.Hour
defaultFetchRate = 2 * time.Second
defaultPublicURL = "https://tapir.d-ma.be"
defaultAutoSummarizeWindow = 7 * 24 * time.Hour
)
// Load reads the environment into a Config, applying defaults. It does not
@@ -168,6 +177,12 @@ func Load() (Config, error) {
}
c.DiscoveryInterval = discovery
autoWindow, err := durationOr("TAPIR_AUTO_SUMMARIZE_WINDOW", defaultAutoSummarizeWindow)
if err != nil {
return Config{}, err
}
c.AutoSummarizeWindow = autoWindow
return c, nil
}