feat(db): add series, season and episode model #71

Merged
naps62-yolo merged 9 commits from issue/34-series-model into main 2026-08-22 22:04:28 +01:00
6 changed files with 322 additions and 45 deletions
+4 -4
View File
@@ -3,8 +3,8 @@ use std::time::UNIX_EPOCH;
use arr_core::policy::{evaluate, Candidate};
use arr_core::{
DolbyVisionProfile, HdrRules, Language, MovieOverrides, Policy, PolicyId, RequiredAudio,
Resolution, Rule, ScoreWeights, SizeBand, Source, Verdict,
DolbyVisionProfile, HdrRules, Language, Policy, PolicyId, RequiredAudio, Resolution, Rule,
ScoreWeights, SizeBand, Source, TitleOverrides, Verdict,
};
use arr_indexer::{ProwlarrClient, SearchRelease, SearchRequest};
use axum::extract::{Query, State};
@@ -293,7 +293,7 @@ pub async fn releases(
})?;
let overrides: OverridesJson = serde_json::from_value(movie.overrides)
.map_err(|error| ApiError::Database(error.to_string()))?;
let overrides = MovieOverrides {
let overrides = TitleOverrides {
only_4k: overrides.only_4k,
allow_english_audio: overrides.allow_english_audio,
};
@@ -460,7 +460,7 @@ fn policy_from_row(row: PolicyRow) -> Result<Policy, ApiError> {
fn classify(
release: SearchRelease,
policy: &Policy,
overrides: &MovieOverrides,
overrides: &TitleOverrides,
original_language: &Language,
) -> Result<ClassifiedRelease, ApiError> {
let parsed = arr_parse::parse(&release.name);
+42 -4
View File
@@ -21,6 +21,9 @@ macro_rules! id_type {
id_type!(RootId);
id_type!(PolicyId);
id_type!(MovieId);
id_type!(SeriesId);
id_type!(SeasonId);
id_type!(EpisodeId);
id_type!(MediaFileId);
id_type!(ReleaseId);
id_type!(GrabId);
@@ -155,13 +158,13 @@ pub struct Policy {
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct MovieOverrides {
pub struct TitleOverrides {
pub only_4k: bool,
pub allow_english_audio: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MovieState {
pub enum MediaState {
Missing,
Downloading,
Available,
@@ -176,13 +179,48 @@ pub struct Movie {
pub original_language: Language,
pub root_id: RootId,
pub wanted: bool,
pub overrides: MovieOverrides,
pub state: MovieState,
pub overrides: TitleOverrides,
pub state: MediaState,
pub blocked: bool,
pub search_attempts: u32,
pub last_searched_at: Option<SystemTime>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Series {
naps62-yolo marked this conversation as resolved Outdated
Outdated
Review

SeriesOverrides is field-for-field identical to MovieOverrides, but policy::evaluate takes &MovieOverrides (policy.rs:58, policy.rs:106). As written, a series cannot be evaluated against a policy without a conversion that does not exist yet, and the two structs will drift.

DESIGN §5.1 describes one override mechanism, not one per aggregate. Reuse a single type — rename MovieOverrides to TitleOverrides and drop this one.

`SeriesOverrides` is field-for-field identical to `MovieOverrides`, but `policy::evaluate` takes `&MovieOverrides` (`policy.rs:58`, `policy.rs:106`). As written, a series cannot be evaluated against a policy without a conversion that does not exist yet, and the two structs will drift. DESIGN §5.1 describes one override mechanism, not one per aggregate. Reuse a single type — rename `MovieOverrides` to `TitleOverrides` and drop this one. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
pub id: SeriesId,
pub tmdb_id: u64,
pub title: String,
pub year: u16,
pub original_language: Language,
pub root_id: RootId,
pub auto_track: bool,
pub overrides: TitleOverrides,
pub upstream_ended: bool,
pub blocked: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Season {
pub id: SeasonId,
pub series_id: SeriesId,
pub number: u16,
pub tracked: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Episode {
pub id: EpisodeId,
pub season_id: SeasonId,
naps62-yolo marked this conversation as resolved
Review

MovieState and EpisodeState are now the same two-line enum twice over, which is the duplication TitleOverrides just removed one instance of. Worth the same treatment, or a comment saying the two are expected to diverge.

Related: pub type MovieOverrides = TitleOverrides keeps the old name alive for five in-tree call sites (policy.rs:58, policy.rs:106, search.rs:6, search.rs:296, search.rs:463). Nothing outside this workspace consumes it, so the alias can go with a rename in the same pass.

`MovieState` and `EpisodeState` are now the same two-line enum twice over, which is the duplication `TitleOverrides` just removed one instance of. Worth the same treatment, or a comment saying the two are expected to diverge. Related: `pub type MovieOverrides = TitleOverrides` keeps the old name alive for five in-tree call sites (`policy.rs:58`, `policy.rs:106`, `search.rs:6`, `search.rs:296`, `search.rs:463`). Nothing outside this workspace consumes it, so the alias can go with a rename in the same pass. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
pub number: u16,
pub title: String,
pub air_date: Option<SystemTime>,
naps62-yolo marked this conversation as resolved
Review

EpisodeState is Missing | Downloading | Available; the column added in this migration is CHECK (state IN ('missing', 'grabbed', 'imported')).

Movies already have this split, and 0001_movies.sql says arr-core "owns the canonical enum this mirrors" — it does not mirror it. This PR makes it two mismatches instead of one. Pick one vocabulary here, or leave a comment saying which side is authoritative.

`EpisodeState` is `Missing | Downloading | Available`; the column added in this migration is `CHECK (state IN ('missing', 'grabbed', 'imported'))`. Movies already have this split, and `0001_movies.sql` says arr-core "owns the canonical enum this mirrors" — it does not mirror it. This PR makes it two mismatches instead of one. Pick one vocabulary here, or leave a comment saying which side is authoritative. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
pub wanted: bool,
pub state: MediaState,
pub search_attempts: u32,
pub last_searched_at: Option<SystemTime>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AudioTrack {
pub language: Language,
+26 -26
View File
@@ -2,8 +2,8 @@ use arr_parse::{LanguageMarker, NameClaims};
use crate::{
lang::{is_resolved_portuguese, language_of_marker},
HdrFormat, Language, MovieOverrides, Policy, ProbedMedia, RequiredAudio, Resolution, Rule,
Source, Verdict,
HdrFormat, Language, Policy, ProbedMedia, RequiredAudio, Resolution, Rule, Source,
TitleOverrides, Verdict,
};
/// The evidence available while evaluating a candidate.
@@ -55,7 +55,7 @@ pub enum EvaluationPhase {
#[derive(Clone, Copy, Debug)]
pub struct EvaluationContext<'a> {
pub policy: &'a Policy,
pub overrides: &'a MovieOverrides,
pub overrides: &'a TitleOverrides,
/// The title's original language from TMDB (`DESIGN.md` §5.2) — a
/// distinct input from any track's language.
pub original_language: &'a Language,
@@ -103,7 +103,7 @@ pub struct Evaluation {
#[must_use]
pub fn evaluate(
policy: &Policy,
overrides: &MovieOverrides,
overrides: &TitleOverrides,
original_language: &Language,
candidate: Candidate<'_>,
size_bytes: Option<u64>,
@@ -514,7 +514,7 @@ mod tests {
}
}
fn verdict(policy: &Policy, overrides: &MovieOverrides, candidate: Candidate<'_>) -> Verdict {
fn verdict(policy: &Policy, overrides: &TitleOverrides, candidate: Candidate<'_>) -> Verdict {
evaluate(policy, overrides, &en(), candidate, None).verdict
}
@@ -525,7 +525,7 @@ mod tests {
) -> Verdict {
evaluate(
policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
original_language,
candidate,
None,
@@ -539,7 +539,7 @@ mod tests {
let claims = claims(None, None);
let report = evaluate(
&policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PreGrab(&claims),
None,
@@ -562,7 +562,7 @@ mod tests {
#[test]
fn preferred_resolutions_pass_in_both_phases() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
for claimed in [ClaimedResolution::P2160, ClaimedResolution::P1080] {
let claims = claims(Some(claimed), Some(ClaimedSource::WebDl));
@@ -583,7 +583,7 @@ mod tests {
#[test]
fn resolution_outside_root_policy_hard_fails_in_both_phases() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let cases = [
(ClaimedResolution::P480, Resolution::Other(480)),
(ClaimedResolution::P576, Resolution::Other(576)),
@@ -607,9 +607,9 @@ mod tests {
#[test]
fn only_4k_tightens_the_root_policy_in_both_phases() {
let policy = policy();
let overrides = MovieOverrides {
let overrides = TitleOverrides {
only_4k: true,
..MovieOverrides::default()
..TitleOverrides::default()
};
let claims_1080 = claims(Some(ClaimedResolution::P1080), Some(ClaimedSource::WebDl));
let media = probed(Resolution::R1080p, Some(Source::WebDl));
@@ -634,9 +634,9 @@ mod tests {
fn only_4k_never_expands_the_root_policy() {
let mut policy = policy();
policy.resolution_preference = vec![Resolution::R1080p];
let overrides = MovieOverrides {
let overrides = TitleOverrides {
only_4k: true,
..MovieOverrides::default()
..TitleOverrides::default()
};
let claims = claims(Some(ClaimedResolution::P2160), Some(ClaimedSource::WebDl));
@@ -649,7 +649,7 @@ mod tests {
#[test]
fn every_unsafe_source_hard_fails_in_both_phases() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let cases = [
(ClaimedSource::Cam, Source::Cam),
(ClaimedSource::Telesync, Source::Telesync),
@@ -679,7 +679,7 @@ mod tests {
assert_eq!(
verdict(
&policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
Candidate::PreGrab(&claims),
),
Verdict::Rejected(Rule::Source(Source::Telesync))
@@ -689,7 +689,7 @@ mod tests {
#[test]
fn every_safe_source_passes_in_both_phases() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let cases = [
(ClaimedSource::Dvd, Source::Dvd),
(ClaimedSource::Telecine, Source::Telecine),
@@ -721,7 +721,7 @@ mod tests {
let media = probed(Resolution::R2160p, source);
let report = evaluate(
&policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PostDownload(&media),
None,
@@ -777,7 +777,7 @@ mod tests {
let report = evaluate(
policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PostDownload(&media),
None,
@@ -796,7 +796,7 @@ mod tests {
let claims = arr_parse::parse("Movie.2024.2160p.WEB-DL.DV-GROUP");
let report = evaluate(
&policy(),
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PreGrab(&claims),
None,
@@ -823,7 +823,7 @@ mod tests {
#[test]
fn hard_failure_wins_over_an_earlier_soft_failure() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let original = en();
let claims = claims(None, None);
let context = EvaluationContext {
@@ -851,7 +851,7 @@ mod tests {
#[test]
fn first_failure_of_the_deciding_kind_is_stable() {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let original = en();
let claims = claims(None, None);
let context = EvaluationContext {
@@ -984,9 +984,9 @@ mod tests {
#[test]
fn kids_allow_english_audio_grabs_and_imports_with_a_waiver() {
let policy = kids_policy();
let overrides = MovieOverrides {
let overrides = TitleOverrides {
allow_english_audio: true,
..MovieOverrides::default()
..TitleOverrides::default()
};
let claims = name_claims(&[LanguageMarker::English]);
assert_eq!(
@@ -1078,7 +1078,7 @@ mod tests {
// kids: could be the required pt-PT — must not reject as a dub.
let report = evaluate(
&kids_policy(),
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PostDownload(&media),
None,
@@ -1089,7 +1089,7 @@ mod tests {
// as verified either.
let report = evaluate(
&policy(),
&MovieOverrides::default(),
&TitleOverrides::default(),
&Language::PortugueseBrazil,
Candidate::PostDownload(&media),
None,
@@ -1102,7 +1102,7 @@ mod tests {
let claims = name_claims(&[LanguageMarker::Portuguese]);
let report = evaluate(
&kids_policy(),
&MovieOverrides::default(),
&TitleOverrides::default(),
&en(),
Candidate::PreGrab(&claims),
None,
+3 -3
View File
@@ -156,7 +156,7 @@ mod tests {
use super::*;
use crate::{
policy::{evaluate, EvaluationContext, PolicyRule, RuleEvaluation, SizeRule},
HdrRules, Language, MovieOverrides, PolicyId, RequiredAudio, Rule, Verdict,
HdrRules, Language, PolicyId, RequiredAudio, Rule, TitleOverrides, Verdict,
};
const GIB: u64 = 1 << 30;
@@ -221,7 +221,7 @@ mod tests {
fn size_rule(size_bytes: u64) -> RuleEvaluation {
let policy = policy();
let overrides = MovieOverrides::default();
let overrides = TitleOverrides::default();
let language = Language::Other("en".to_owned());
let claims = claims(ClaimedSource::WebDl);
SizeRule.evaluate(&EvaluationContext {
@@ -256,7 +256,7 @@ mod tests {
let claims = claims(ClaimedSource::Remux);
let evaluation = evaluate(
&policy,
&MovieOverrides::default(),
&TitleOverrides::default(),
&Language::Other("en".to_owned()),
Candidate::PreGrab(&claims),
Some(gib(60)),
+119
View File
@@ -0,0 +1,119 @@
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- TV side of the separate aggregates in DESIGN.md §4. This migration is
naps62-yolo marked this conversation as resolved Outdated
Outdated
Review

series stores nothing about upstream status, so DESIGN §4.2 ended — "series finished upstream and complete" — cannot be derived from this schema. The other four statuses come from episode rows; this one needs TMDB's series status persisted.

§4's sketch omits it, §4.2 requires it. Per CLAUDE.md the design document wins, so either add the column or record on #34 that ended is unimplementable until it exists.

`series` stores nothing about upstream status, so DESIGN §4.2 `ended` — "series finished upstream and complete" — cannot be derived from this schema. The other four statuses come from episode rows; this one needs TMDB's series status persisted. §4's sketch omits it, §4.2 requires it. Per CLAUDE.md the design document wins, so either add the column or record on #34 that `ended` is unimplementable until it exists. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- additive: the movie tables stay concrete and unchanged.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TABLE series (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
id INTEGER PRIMARY KEY,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
tmdb_id INTEGER NOT NULL UNIQUE,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
title TEXT NOT NULL,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
year INTEGER,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
original_language TEXT,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
root_id INTEGER NOT NULL REFERENCES roots (id),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
auto_track INTEGER NOT NULL DEFAULT 0 CHECK (auto_track IN (0, 1)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
overrides TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(overrides)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- §4.2 needs upstream completion to derive `ended` rather than store it.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
upstream_ended INTEGER NOT NULL DEFAULT 0 CHECK (upstream_ended IN (0, 1)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
blocked INTEGER NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
) STRICT;
naps62-yolo marked this conversation as resolved Outdated
Outdated
Review

Two things about this block:

  • movies_require_movie_root and its update twin change the movies invariant, in a migration named for series. Nothing in the file says why they arrive here (they do belong to this change — TV roots only start existing now — but a reader of 0005_series.sql has no way to know that).
  • The file carries no comments at all, while 0001–0004 cite DESIGN sections throughout. That convention is what makes the schema readable without the design document open.
Two things about this block: - `movies_require_movie_root` and its update twin change the movies invariant, in a migration named for series. Nothing in the file says why they arrive here (they do belong to this change — TV roots only start existing now — but a reader of `0005_series.sql` has no way to know that). - The file carries no comments at all, while 0001–0004 cite DESIGN sections throughout. That convention is what makes the schema readable without the design document open. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE INDEX series_root ON series (root_id);
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- Before TV roots existed, the roots foreign key was sufficient to keep a
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- movie on a movie root. Preserve that invariant now that both kinds exist.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TRIGGER movies_require_movie_root
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEFORE INSERT ON movies
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEGIN
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT RAISE(ABORT, 'movies require a movie root');
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
END;
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TRIGGER movies_require_movie_root_on_update
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEFORE UPDATE OF root_id ON movies
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEGIN
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT RAISE(ABORT, 'movies require a movie root');
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
END;
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TRIGGER series_require_tv_root
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEFORE INSERT ON series
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'tv')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEGIN
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT RAISE(ABORT, 'series require a TV root');
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
END;
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TRIGGER series_require_tv_root_on_update
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEFORE UPDATE OF root_id ON series
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'tv')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
BEGIN
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT RAISE(ABORT, 'series require a TV root');
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
END;
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TABLE seasons (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
id INTEGER PRIMARY KEY,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
series_id INTEGER NOT NULL REFERENCES series (id) ON DELETE CASCADE,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
number INTEGER NOT NULL CHECK (number >= 0),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
tracked INTEGER NOT NULL DEFAULT 0 CHECK (tracked IN (0, 1)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
UNIQUE (series_id, number)
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
) STRICT;
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE TABLE episodes (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
id INTEGER PRIMARY KEY,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
season_id INTEGER NOT NULL REFERENCES seasons (id) ON DELETE CASCADE,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
number INTEGER NOT NULL CHECK (number >= 0),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
title TEXT NOT NULL,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
air_date TEXT,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
wanted INTEGER NOT NULL DEFAULT 0 CHECK (wanted IN (0, 1)),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- Mirrors arr-core's canonical MediaState vocabulary. The older movies
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- table uses legacy names; its additive correction is tracked in #73.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
state TEXT NOT NULL DEFAULT 'missing'
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Episodes now mirror EpisodeState, but movies.state in 0001_movies.sql is still CHECK (state IN ('missing', 'grabbed', 'imported')). One database, two vocabularies for the same concept, and 0001's comment — "arr-core (#7) owns the canonical enum this mirrors" — is now false for the table it sits on.

Either bring movies.state across in this migration (it is a rewrite of the CHECK plus an UPDATE mapping grabbed→downloading, imported→available), or open an issue and say here which table is the odd one out. Leaving it silent means the next reader has to diff two migrations to find out.

Episodes now mirror `EpisodeState`, but `movies.state` in `0001_movies.sql` is still `CHECK (state IN ('missing', 'grabbed', 'imported'))`. One database, two vocabularies for the same concept, and 0001's comment — "arr-core (#7) owns the canonical enum this mirrors" — is now false for the table it sits on. Either bring `movies.state` across in this migration (it is a rewrite of the CHECK plus an UPDATE mapping grabbed→downloading, imported→available), or open an issue and say here which table is the odd one out. Leaving it silent means the next reader has to diff two migrations to find out. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
CHECK (state IN ('missing', 'downloading', 'available')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
search_attempts INTEGER NOT NULL DEFAULT 0,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
last_searched_at TEXT,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
UNIQUE (season_id, number)
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
) STRICT;
naps62-yolo marked this conversation as resolved
Review

The TV policies are seeded by copying rows matched on name = 'Movies — main' / 'Movies — kids', and the roots below are then matched on name = 'TV — main' / 'TV — kids'.

If a later migration renames or removes the 0002 seed rows, both inserts match zero rows and this migration succeeds having created no TV policies and no TV roots — a silent seed failure on any database migrating through it. Spell the policy values out as 0002 does, or guard with RAISE(ABORT, ...) when the source row is absent.

The TV policies are seeded by copying rows matched on `name = 'Movies — main'` / `'Movies — kids'`, and the roots below are then matched on `name = 'TV — main'` / `'TV — kids'`. If a later migration renames or removes the 0002 seed rows, both inserts match zero rows and this migration succeeds having created no TV policies and no TV roots — a silent seed failure on any database migrating through it. Spell the policy values out as 0002 does, or guard with `RAISE(ABORT, ...)` when the source row is absent. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE INDEX episodes_pending_search
naps62-yolo marked this conversation as resolved
Review

The TV policies inherit source_weights from the movie policies: {"Remux":4,"BluRay":3,"WEB-DL":2,...} for main, BluRay-biased.

TV releases are overwhelmingly WEB-DL, so the tiebreaker points the wrong way out of the box. 0002 set these per root deliberately (the kids policy biases WEB-DL for pt-PT dubs); copying them wholesale drops that reasoning.

The TV policies inherit `source_weights` from the movie policies: `{"Remux":4,"BluRay":3,"WEB-DL":2,...}` for main, BluRay-biased. TV releases are overwhelmingly WEB-DL, so the tiebreaker points the wrong way out of the box. 0002 set these per root deliberately (the kids policy biases WEB-DL for pt-PT dubs); copying them wholesale drops that reasoning. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
ON episodes (last_searched_at)
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
WHERE wanted = 1 AND state = 'missing';
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
CREATE INDEX episodes_state ON episodes (state);
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
INSERT INTO policies (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
name, required_audio, dub_blacklist, hdr_rules, size_bands,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
resolution_pref, source_weights, score_weights
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
) VALUES (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
'TV — main',
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"require":"original_language","allow_english_fallback":false}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('["pt-BR"]'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"dv_profile_allow":["8.1"],"dv_profile_reject":["5","7"],"allow_hdr10":true,"allow_hdr10plus":true,"allow_sdr":true}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"2160p":{"floor_gib":8,"target_gib":22,"penalty_points_per_gib_over":60},"1080p":{"floor_gib":3,"target_gib":8,"penalty_points_per_gib_over":60}}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('["2160p","1080p"]'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

TV — kids picked up the WEB-DL bias and a comment, but TV — main still carries the movie weights verbatim: Remux 4, BluRay 3, WEB-DL 2.

Same point as before — TV releases are mostly WEB-DL, so on the main root the tiebreaker still leans towards the rarer source. If that is deliberate (you want the remux when one exists), a one-line comment saying so closes it.

`TV — kids` picked up the WEB-DL bias and a comment, but `TV — main` still carries the movie weights verbatim: `Remux 4, BluRay 3, WEB-DL 2`. Same point as before — TV releases are mostly WEB-DL, so on the main root the tiebreaker still leans towards the rarer source. If that is deliberate (you want the remux when one exists), a one-line comment saying so closes it. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
-- §5.5 deliberately keeps source tier as Remux > BluRay > WEB-DL.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"Remux":4,"BluRay":3,"WEB-DL":2,"WEBRip":1,"HDTV":0}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"size_at_target":1000,"source_tier":25,"seeder_doubling":8}')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
);
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
INSERT INTO policies (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
name, required_audio, dub_blacklist, hdr_rules, size_bands,
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
resolution_pref, source_weights, score_weights
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
) VALUES (
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
'TV — kids',
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"require":"any_of","langs":["pt-PT"],"or_original_when_portuguese":true,"allow_english_fallback":false}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('["pt-BR"]'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"dv_profile_allow":["8.1"],"dv_profile_reject":["5","7"],"allow_hdr10":true,"allow_hdr10plus":true,"allow_sdr":true}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"2160p":{"floor_gib":8,"target_gib":22,"penalty_points_per_gib_over":60},"1080p":{"floor_gib":3,"target_gib":8,"penalty_points_per_gib_over":60}}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('["2160p","1080p"]'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
-- §5.2: pt-PT dubs are concentrated in streaming releases.
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"WEB-DL":4,"WEBRip":2,"BluRay":1,"Remux":1,"HDTV":0}'),
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
json('{"size_at_target":1000,"source_tier":25,"seeder_doubling":8}')
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
);
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
INSERT INTO roots (kind, audience, path, policy_id)
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT 'tv', 'main', '/mnt/media/tv/main', id FROM policies WHERE name = 'TV — main';
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
INSERT INTO roots (kind, audience, path, policy_id)
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
SELECT 'tv', 'kids', '/mnt/media/tv/kids', id FROM policies WHERE name = 'TV — kids';
Review

Kept the main TV source order because DESIGN §5.5 explicitly defines Remux > BluRay > WEB-DL as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing.

Kept the main TV source order because DESIGN §5.5 explicitly defines `Remux > BluRay > WEB-DL` as the source tiebreaker. The TV kids policy still uses the §5.2 WEB-DL bias for pt-PT dubbing. <!-- agent-meta: {"model":"gpt-5.6-terra","session":"41d2e26a"} -->
+128 -8
View File
@@ -156,13 +156,13 @@ mod tests {
let roots = db.list_roots().await.expect("list roots");
assert_eq!(roots.len(), 2, "§5.1: two movie roots, TV comes with #34");
assert!(roots.iter().all(|r| r.kind == "movie"));
assert_eq!(roots[0].audience, "kids");
assert_eq!(roots[0].path, "/mnt/media/movies/kids");
assert_eq!(roots[1].audience, "main");
assert_eq!(roots[1].path, "/mnt/media/movies/main");
assert_ne!(roots[0].policy_id, roots[1].policy_id);
let movie_roots: Vec<_> = roots.iter().filter(|root| root.kind == "movie").collect();
assert_eq!(movie_roots.len(), 2);
assert_eq!(movie_roots[0].audience, "kids");
assert_eq!(movie_roots[0].path, "/mnt/media/movies/kids");
assert_eq!(movie_roots[1].audience, "main");
assert_eq!(movie_roots[1].path, "/mnt/media/movies/main");
assert_ne!(movie_roots[0].policy_id, movie_roots[1].policy_id);
}
#[tokio::test]
@@ -200,7 +200,7 @@ mod tests {
.await
.expect("policies");
assert_eq!(rows.len(), 2);
assert_eq!(rows.len(), 4);
for row in rows {
let size_bands: String = row.get(0);
let score_weights: String = row.get(1);
@@ -247,4 +247,124 @@ mod tests {
.await
.expect_err("state is a closed set");
}
#[tokio::test]
async fn seeds_two_tv_roots_with_distinct_policies() {
let (_dir, db) = fresh().await;
let roots = db.list_roots().await.expect("list roots");
let tv_roots: Vec<_> = roots.iter().filter(|root| root.kind == "tv").collect();
assert_eq!(tv_roots.len(), 2);
assert_eq!(tv_roots[0].audience, "kids");
assert_eq!(tv_roots[0].path, "/mnt/media/tv/kids");
assert_eq!(tv_roots[1].audience, "main");
assert_eq!(tv_roots[1].path, "/mnt/media/tv/main");
assert_ne!(tv_roots[0].policy_id, tv_roots[1].policy_id);
}
#[tokio::test]
async fn episode_wanted_is_independent_of_tracking_rules() {
let (_dir, db) = fresh().await;
let root_id: i64 =
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' AND audience = 'main'")
.fetch_one(db.pool())
.await
.expect("TV root");
let series_id = sqlx::query("INSERT INTO series (tmdb_id, title, root_id, auto_track) VALUES (82728, 'Bluey', ?, 1)")
.bind(root_id)
.execute(db.pool())
.await
.expect("series")
.last_insert_rowid();
let season_id =
sqlx::query("INSERT INTO seasons (series_id, number, tracked) VALUES (?, 1, 1)")
.bind(series_id)
.execute(db.pool())
.await
.expect("season")
.last_insert_rowid();
sqlx::query("INSERT INTO episodes (season_id, number, title) VALUES (?, 2, 'Hospital')")
.bind(season_id)
.execute(db.pool())
.await
.expect("episode");
let wanted: bool = sqlx::query_scalar("SELECT wanted FROM episodes")
.fetch_one(db.pool())
.await
.expect("wanted");
assert!(
!wanted,
"auto_track and tracked are rules, not episode intent"
);
sqlx::query("INSERT INTO movies (tmdb_id, title, root_id) SELECT 693134, 'Dune Part Two', id FROM roots WHERE kind = 'movie' AND audience = 'main'")
.execute(db.pool())
.await
.expect("existing movie schema remains writable");
}
naps62-yolo marked this conversation as resolved
Review

Counting rows in an untouched movies table asserts nothing — it is 0 whether or not this migration broke the movie schema. Either drop it or assert something the migration could plausibly break, e.g. that a movie insert against a movie root still succeeds.

Counting rows in an untouched `movies` table asserts nothing — it is 0 whether or not this migration broke the movie schema. Either drop it or assert something the migration could plausibly break, e.g. that a movie insert against a movie root still succeeds. <!-- agent-meta: {"model":"claude-opus-5","session":"b9c31479"} -->
#[tokio::test]
async fn aggregates_require_roots_of_their_own_kind() {
let (_dir, db) = fresh().await;
let movie_root: i64 =
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'movie' LIMIT 1")
.fetch_one(db.pool())
.await
.expect("movie root");
let tv_root: i64 = sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' LIMIT 1")
.fetch_one(db.pool())
.await
.expect("TV root");
sqlx::query("INSERT INTO movies (tmdb_id, title, root_id) VALUES (1, 'x', ?)")
.bind(tv_root)
.execute(db.pool())
.await
.expect_err("movie cannot use TV root");
sqlx::query("INSERT INTO series (tmdb_id, title, root_id) VALUES (2, 'x', ?)")
.bind(movie_root)
.execute(db.pool())
.await
.expect_err("series cannot use movie root");
}
#[tokio::test]
async fn episode_state_and_upstream_completion_are_constrained() {
let (_dir, db) = fresh().await;
let root_id: i64 =
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' AND audience = 'main'")
.fetch_one(db.pool())
.await
.expect("TV root");
let series_id = sqlx::query(
"INSERT INTO series (tmdb_id, title, root_id, upstream_ended)
VALUES (82728, 'Bluey', ?, 1)",
)
.bind(root_id)
.execute(db.pool())
.await
.expect("ended series")
.last_insert_rowid();
let season_id = sqlx::query("INSERT INTO seasons (series_id, number) VALUES (?, 1)")
.bind(series_id)
.execute(db.pool())
.await
.expect("season")
.last_insert_rowid();
sqlx::query(
"INSERT INTO episodes (season_id, number, title, state)
VALUES (?, 1, 'Magic Xylophone', 'grabbed')",
)
.bind(season_id)
.execute(db.pool())
.await
.expect_err("database mirrors MediaState");
sqlx::query("UPDATE series SET upstream_ended = 2 WHERE id = ?")
.bind(series_id)
.execute(db.pool())
.await
.expect_err("upstream completion is boolean");
}
}