Ship the SR-003 schema bump: jmanifest_version 2
Moves the exchange envelope to version 2 in lockstep with the truth file's schema_version, per SR-003's requirement that breaking changes be batched and ship together rather than piecemeal. The plugin had already moved to schema_version 2; the server declaring 1 while accepting the new fields defeated the point of having a version at all. Flag day, not dual-accept (JR-003): version 1 is now rejected outright. All three components are pre-release, and a v1 read path would be the one nobody exercises, so it is the one that would rot while being dragged through every later change to the reader. A pipeline still emitting v1 is incompatible until updated — stated plainly rather than papered over with a shim nobody tests. scenes become objects carrying belief and route (extraction AR-017) instead of float pairs. Belief is bounded to [0, 1] rather than merely stored: §5a's Threat 1 argument rests on every accepted value being bounded, and an unbounded float is a 64-bit channel however harmless it looks. route is a closed enum, so an invented value cannot be stored. UR-018 is the requirement with the trap in it, and the reason content_id.rs is untouched by this commit: belief is a producer-side estimate that may legitimately differ between pipeline versions for identical timings, so including it in the canonical form would give two servers different ids for the same content — the exact failure mode §9a quantises centiseconds to avoid, reintroduced one field along. It replicates as an attribute, exactly as audio_signature does. The golden vector still passes unchanged, which is the evidence rather than the claim. 191 tests. UR-015..018 move from Planned to Done; coverage 24/32 (75%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: UR-014, UR-015, UR-016, UR-017, UR-018 | SR-003
This commit is contained in:
+38
-11
@@ -374,7 +374,7 @@ pub fn insert_actor_scenes(
|
||||
tx: &Transaction<'_>,
|
||||
manifest_id: &str,
|
||||
tmdb_person_id: u64,
|
||||
scenes_cs: &[(i64, i64)],
|
||||
scenes_cs: &[crate::validate::SceneCs],
|
||||
) -> anyhow::Result<()> {
|
||||
tx.execute(
|
||||
"INSERT INTO manifest_actors (manifest_id, tmdb_person_id) VALUES (?1, ?2)
|
||||
@@ -382,11 +382,18 @@ pub fn insert_actor_scenes(
|
||||
params![manifest_id, tmdb_person_id as i64],
|
||||
)?;
|
||||
let mut stmt = tx.prepare_cached(
|
||||
"INSERT INTO scenes (manifest_id, tmdb_person_id, start_cs, end_cs)
|
||||
VALUES (?1, ?2, ?3, ?4)",
|
||||
"INSERT INTO scenes (manifest_id, tmdb_person_id, start_cs, end_cs, belief, route)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||
)?;
|
||||
for (start, end) in scenes_cs {
|
||||
stmt.execute(params![manifest_id, tmdb_person_id as i64, start, end])?;
|
||||
for scene in scenes_cs {
|
||||
stmt.execute(params![
|
||||
manifest_id,
|
||||
tmdb_person_id as i64,
|
||||
scene.start_cs,
|
||||
scene.end_cs,
|
||||
scene.belief,
|
||||
scene.route.map(|r| r.as_str()),
|
||||
])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -399,7 +406,7 @@ pub fn insert_actor_scenes(
|
||||
pub struct StoredActor {
|
||||
pub tmdb_person_id: u64,
|
||||
pub name: Option<String>,
|
||||
pub scenes_cs: Vec<(i64, i64)>,
|
||||
pub scenes_cs: Vec<crate::validate::SceneCs>,
|
||||
}
|
||||
|
||||
/// TRACES: UR-010 | DR-002 | SR-001
|
||||
@@ -421,7 +428,7 @@ pub fn actors_for_manifest(
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
|
||||
let mut scene_stmt = conn.prepare_cached(
|
||||
"SELECT start_cs, end_cs FROM scenes
|
||||
"SELECT start_cs, end_cs, belief, route FROM scenes
|
||||
WHERE manifest_id = ?1 AND tmdb_person_id = ?2
|
||||
ORDER BY start_cs ASC",
|
||||
)?;
|
||||
@@ -429,7 +436,18 @@ pub fn actors_for_manifest(
|
||||
let mut out = Vec::with_capacity(people.len());
|
||||
for (id, name) in people {
|
||||
let scenes_cs = scene_stmt
|
||||
.query_map(params![manifest_id, id as i64], |r| Ok((r.get(0)?, r.get(1)?)))?
|
||||
.query_map(params![manifest_id, id as i64], |r| {
|
||||
Ok(crate::validate::SceneCs {
|
||||
start_cs: r.get(0)?,
|
||||
end_cs: r.get(1)?,
|
||||
belief: r.get(2)?,
|
||||
// An unrecognised stored route means a row from a schema
|
||||
// this build does not know; report absent rather than guess.
|
||||
route: r
|
||||
.get::<_, Option<String>>(3)?
|
||||
.and_then(|v| crate::model::Route::from_stored(&v)),
|
||||
})
|
||||
})?
|
||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||
out.push(StoredActor { tmdb_person_id: id, name, scenes_cs });
|
||||
}
|
||||
@@ -742,6 +760,7 @@ pub fn release_all_leases(tx: &Transaction<'_>) -> anyhow::Result<usize> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::db::Db;
|
||||
use crate::validate::SceneCs;
|
||||
|
||||
async fn db() -> Db {
|
||||
Db::open(":memory:").unwrap()
|
||||
@@ -837,7 +856,12 @@ mod tests {
|
||||
},
|
||||
)?;
|
||||
upsert_person(tx, 884, "Steve Buscemi", false, NOW)?;
|
||||
insert_actor_scenes(tx, &id, 884, &[(19160, 20920), (43820, 46560)])?;
|
||||
insert_actor_scenes(
|
||||
tx,
|
||||
&id,
|
||||
884,
|
||||
&[SceneCs::plain(19160, 20920), SceneCs::plain(43820, 46560)],
|
||||
)?;
|
||||
Ok(id)
|
||||
})
|
||||
.await
|
||||
@@ -857,7 +881,10 @@ mod tests {
|
||||
assert_eq!(actors[0].tmdb_person_id, 884);
|
||||
// §7: names come from `people`, populated from TMDB, never from upload.
|
||||
assert_eq!(actors[0].name.as_deref(), Some("Steve Buscemi"));
|
||||
assert_eq!(actors[0].scenes_cs, vec![(19160, 20920), (43820, 46560)]);
|
||||
assert_eq!(
|
||||
actors[0].scenes_cs,
|
||||
vec![SceneCs::plain(19160, 20920), SceneCs::plain(43820, 46560)]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1097,7 +1124,7 @@ mod tests {
|
||||
},
|
||||
)?;
|
||||
upsert_person(tx, 1, "A", false, NOW)?;
|
||||
insert_actor_scenes(tx, "m", 1, &[(0, 100)])?;
|
||||
insert_actor_scenes(tx, "m", 1, &[SceneCs::plain(0, 100)])?;
|
||||
delete_manifest(tx, "m")?;
|
||||
let scenes: i64 = tx.query_row("SELECT COUNT(*) FROM scenes", [], |r| r.get(0))?;
|
||||
let actors: i64 =
|
||||
|
||||
+7
-1
@@ -70,7 +70,13 @@ CREATE TABLE IF NOT EXISTS scenes (
|
||||
manifest_id TEXT NOT NULL REFERENCES manifests(id) ON DELETE CASCADE,
|
||||
tmdb_person_id INTEGER NOT NULL,
|
||||
start_cs INTEGER NOT NULL,
|
||||
end_cs INTEGER NOT NULL
|
||||
end_cs INTEGER NOT NULL,
|
||||
-- Per-window provenance (SR-003, extraction AR-017). Deliberately NOT part
|
||||
-- of `content_id`: belief is a producer-side estimate that may differ
|
||||
-- between pipeline versions for identical timings, so hashing it would give
|
||||
-- two servers different ids for the same content (§9a).
|
||||
belief REAL,
|
||||
route TEXT -- live | deferred | pooled
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reports (
|
||||
|
||||
Reference in New Issue
Block a user