feat(store): summarization-mode methods + all-videos read

Add the store surface for summarization mode:
- SetAutoSummarize / GetAutoSummarize — per-user auto/manual toggle; an absent
  user reads as manual (the safe default).
- RequestSummarize — queue one video (summarize_requested=TRUE); ErrNotFound
  when the video is absent or not owned (RLS hides another user's row).
- RequestedVideoIDs / ClearSummarizeRequested — the run-loop side: load the
  queued set per pass (mirrors SeenVideoIDs), clear after summarizing.
- ListVideos / GetVideoRow — drive from the videos table LEFT JOIN summaries so
  discovered-but-unsummarized videos appear with empty summary fields. SummaryRow
  gains additive Summarized + SummarizeRequested fields; the summary-only reads
  are untouched.

RLS proof: rls_test.go gains a cross-user "queue B's video" write asserting it
touches zero rows (store-level scoping can't prove this — the test pool is a
superuser that bypasses RLS, same caveat documented there).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 19:26:52 +02:00
co-authored by Claude Opus 4.8
parent 748d5eb0bd
commit bdbdce7de1
4 changed files with 404 additions and 0 deletions
+6
View File
@@ -181,6 +181,7 @@ func TestRLSEnforcesPerUserIsolation(t *testing.T) {
}{
{"update users", `UPDATE users SET display_name = 'hacked' WHERE id = $1`, b.userID},
{"update videos", `UPDATE videos SET title = 'hacked' WHERE user_id = $1`, b.userID},
{"queue videos summarize", `UPDATE videos SET summarize_requested = TRUE WHERE id = $1`, b.videoID},
{"update transcripts", `UPDATE transcripts SET content = 'hacked' WHERE user_id = $1`, b.userID},
{"update summaries", `UPDATE summaries SET summary = 'hacked' WHERE user_id = $1`, b.userID},
{"update summary_actions", `UPDATE summary_actions SET action = 'skipped' WHERE user_id = $1`, b.userID},
@@ -218,5 +219,10 @@ func TestRLSEnforcesPerUserIsolation(t *testing.T) {
require.Equal(t, 1, bDeliveries, "A's DELETE must not have removed B's delivery")
require.Equal(t, 1, bConnections, "A's writes must not have touched B's connection")
var bRequested bool
require.NoError(t, super.QueryRow(ctx,
`SELECT summarize_requested FROM videos WHERE user_id = $1`, b.userID).Scan(&bRequested))
require.False(t, bRequested, "A scoped must not have queued B's video for summarization")
_ = a // a's ids are seeded for the symmetric read assertions above
}