fix(pipeline): finish three changes that had only been half applied
Each of these was recorded as done and was done in one place out of two. AR-011 -- the TransNetV2 dedup window. The derived window (dedup_window_sec, median observed interval halved) reached scenes.json and nothing else. SceneBoundaries, the path that actually feeds is_scene_boundary to the tracker, kept the literal 0.04 s under a comment claiming it "matches the dedup scenes.json applies, so the two views agree". They did not agree. 0.04 is one frame at 25 fps and wider than a frame at 30, so two cuts on consecutive frames merged into one and the loss was invisible: the pipeline simply saw fewer boundaries. The detector now supplies the window it derived. AR-019 -- ownership. The register says ownership "comes from the registry, not a second local tally". Both existed: promotion fired on a local accepted-frame count and fell back to a local per-actor plurality when the registry had not yet claimed the track. That fallback was reachable in the live pipeline, not just in tests -- three accepted frames arrive well before a posterior crosses the ownership threshold -- so in practice the plurality usually decided, and it could not see the AR-025 correlation discounting it was meant to defer to. The tally is gone; promotion now requires the registry's verdict, with the accepted -frame count demoted to an explicit evidence floor. AR-017 -- the route. DeadTrack carried belief but no route, and the sink wrote the literal string "live", so a field the schema publishes could not distinguish anything. AR-017's own verification asks for "deferred and pooled routes distinguishable". Route is now an enum on the claim. Only `live` occurs today; `deferred` exists so AR-020's pass has somewhere to write instead of a serialisation change to make. Also: TrackGallery::forget had no callers, under a comment asserting the matcher called it "on a cut or track disappearance". The cut half was true by another route; the disappearance half was not, so a track that died quietly kept its diversity buffer until the next cut cleared everything. Replaced with prune_dead against the registry's own liveness, the same shape as the tracker's prune_boxes -- a second opinion about which tracks exist is a second thing that can be wrong. Removes dead logistic/logit helpers and fixes five TRACES tags that used a comma where a pipe separates requirement types, which the gate had been reporting as diagnostics. TRACES: AR-011, AR-017, AR-019 | IR-002 | SR-002, SR-005
This commit is contained in:
@@ -311,6 +311,16 @@ struct IdentityMatcherFunc {
|
||||
|
||||
absorb_promotions();
|
||||
|
||||
/// TRACES: AR-019 | SR-005
|
||||
// Drop buffers for tracks the registry has reaped. Without this a track
|
||||
// that simply went off screen kept its diversity buffer until the next
|
||||
// cut, so the store grew with the film rather than with what is on
|
||||
// screen — and a buffer that outlives its track is evidence about a
|
||||
// person nobody is looking at any more.
|
||||
if (registry_)
|
||||
track_gallery_.prune_dead(
|
||||
[this](int id) { return registry_->is_live(id); });
|
||||
|
||||
return {std::move(tf.source), std::move(actors)};
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ private:
|
||||
double start{0.0};
|
||||
double end{0.0};
|
||||
float belief{0.f}; ///< the posterior that justified the claim (AR-017)
|
||||
Route route{Route::live}; ///< how it was identified (AR-017)
|
||||
};
|
||||
struct ActorWindow {
|
||||
std::string name, imdb_id, tmdb_id, jellyfin_id;
|
||||
@@ -180,7 +181,7 @@ private:
|
||||
aw.jellyfin_id = it->second.jellyfin_id;
|
||||
}
|
||||
}
|
||||
aw.scenes.push_back({c.first_seen, c.last_seen, c.belief});
|
||||
aw.scenes.push_back({c.first_seen, c.last_seen, c.belief, c.route});
|
||||
}
|
||||
|
||||
std::vector<ActorWindow> result;
|
||||
@@ -204,7 +205,7 @@ private:
|
||||
windows.push_back({{"start", w.start},
|
||||
{"end", w.end},
|
||||
{"belief", w.belief},
|
||||
{"route", "live"}});
|
||||
{"route", route_name(w.route)}});
|
||||
json ja;
|
||||
ja["name"] = aw.name;
|
||||
ja["imdb_id"] = aw.imdb_id;
|
||||
|
||||
@@ -149,8 +149,15 @@ private:
|
||||
// final verdict. The face branch consults this for frames it has not
|
||||
// reached yet, and the watermark is what lets it tell "no boundary
|
||||
// here" from "not scored yet".
|
||||
if (shared_ && hi > lo)
|
||||
shared_->publish(fresh, times_[hi - 1]);
|
||||
/// TRACES: AR-011 | SR-002
|
||||
// Hand the join the same dedup window scenes.json uses, derived from the
|
||||
// observed cadence rather than assumed. Set on every window because the
|
||||
// median refines as intervals accumulate; it converges within the first
|
||||
// window and costs a double assignment thereafter.
|
||||
if (shared_) {
|
||||
shared_->set_merge_window(dedup_window_sec(intervals_));
|
||||
if (hi > lo) shared_->publish(fresh, times_[hi - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// At EOF the tail (< kWindow frames) never formed a full window. Pad it out
|
||||
@@ -183,7 +190,10 @@ private:
|
||||
// the last full window — reach the join with no verdict and are treated
|
||||
// as boundary-free without evidence, which is precisely the ambiguity
|
||||
// the watermark exists to prevent.
|
||||
if (shared_ && n > 0) shared_->publish(fresh, times_[n - 1]);
|
||||
if (shared_ && n > 0) {
|
||||
shared_->set_merge_window(dedup_window_sec(intervals_));
|
||||
shared_->publish(fresh, times_[n - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void write_output() {
|
||||
|
||||
Reference in New Issue
Block a user