test: two tests still describe the registry as it was before 1477c53
Both failed the first time CI was able to run this suite, and neither is
a new break. They assert semantics that two deliberate changes replaced,
and nothing noticed because nothing had ever executed them.
1477c53 (2026-08-09) made a dormant track associable only if it had been
identified: an unowned dormant track has no actor to re-attach to, so it
only enlarges the matcher's comparison set and invites a new face landing
on an anonymous stub. Its message records the trade -- "a small recall
cost (~1-3pp on some films, e.g. Lord of War -2.5) for a cleaner, bounded
pool. Kept deliberately." Both tests build tracks that are never
identified, so since that commit they are dropped from the pool one line
before the extinction horizon they were written to exercise. The tests
date from 2026-07-31 and 2026-08-08 -- they are older than the gate.
Fixed by owning the track while it is still on screen, which is what a
real run does and what makes the horizon reachable at all.
The second test needed more than that, and the rename says why. It was
built on association and reaping answering on DIFFERENT clocks -- the
tracker's for "may this associate?", the watermark for "is this
finished?" -- which is exactly the arrangement track_registry.hpp:184-203
now rules out: one clock, one threshold, "nothing is offered past its
reap horizon, nothing is reaped while still offerable". So there is no
longer an interval in which a track is retired from association but still
alive, and a test named for that interval cannot pass. Rewritten to
assert what the unified clock actually promises: a tracker racing 50 s
past the horizon retires nothing while the matcher lags, the votes in
flight still land, and the watermark passing the horizon retires, reaps
and emits in one step.
Kept: the AR-008 tag, the late-vote check and dropped_votes() == 0, which
were the point of the test and still hold.
Full suite now 151/151 in sae-builder-cpu.
TRACES: AR-008, AR-013 | SR-002
This commit is contained in:
@@ -192,6 +192,13 @@ TEST_CASE("a track within the extinction window is still a candidate",
|
||||
|
||||
Embedding person = at_sim(0, 1, 0.99f);
|
||||
int id_pre = r.track_of(frame(0.0, 10, 10, person));
|
||||
|
||||
// Identify it while it is still on screen. Since 1477c53 a DORMANT track is
|
||||
// only associable if it was owned -- an anonymous one has no actor to
|
||||
// re-attach to, so it is dropped from the pool a line before the horizon is
|
||||
// ever consulted. Without this the test cannot reach what it is about.
|
||||
r.reg->observe(id_pre, /*actor=*/0, /*posterior=*/0.99f, person);
|
||||
|
||||
r.track_of(frame(1.0, 300, 300, axis(7), /*is_cut=*/true));
|
||||
|
||||
// Back inside the window: the same person continues the same track, so the
|
||||
|
||||
@@ -415,15 +415,21 @@ TEST_CASE("a track is not reaped until the evidence clock passes it",
|
||||
}
|
||||
|
||||
/// TRACES: UT-001 | AR-008, AR-013 | SR-002
|
||||
TEST_CASE("a track retired from association is still open to evidence",
|
||||
TEST_CASE("association and reaping retire a track on the same clock",
|
||||
"[registry][AR-008]") {
|
||||
// The two clocks answer different questions and must not share an answer.
|
||||
// Association asks "may this detection link to that track?" on the tracker's
|
||||
// clock; reaping asks "is that track finished?" and cannot answer until the
|
||||
// votes are in. Deferring both to the evidence clock was the second half of
|
||||
// this bug: retired tracks lingered in the candidate pool for as long as the
|
||||
// matcher lagged, so a new face re-associated onto a long-dead track and two
|
||||
// people merged into one window.
|
||||
// Renamed and rewritten for the unified clock. This test used to assert the
|
||||
// opposite arrangement -- association answered on the tracker's clock while
|
||||
// reaping answered on the evidence watermark -- and it was written when that
|
||||
// was true. It is not true now: candidates() and reap_locked() take the SAME
|
||||
// clock and the SAME threshold, so "retired from association" and "reaped"
|
||||
// are one event, and the interval the old name described does not exist.
|
||||
//
|
||||
// Both halves of the AR-013 bug are visible here. Offering on a LOOSER
|
||||
// horizon than the reap left retired tracks in the candidate pool for as
|
||||
// long as the matcher lagged, so a new face re-associated onto a long-dead
|
||||
// track and two people merged into one window. Reaping on a looser horizon
|
||||
// than the offer killed tracks whose votes were still in flight. One clock
|
||||
// makes both unrepresentable.
|
||||
TrackRegistry reg(cfg(/*extinction=*/5.0), disc());
|
||||
Sink sink; sink.attach(reg);
|
||||
reg.expect_evidence();
|
||||
@@ -432,21 +438,37 @@ TEST_CASE("a track retired from association is still open to evidence",
|
||||
{
|
||||
auto s = reg.begin_frame(0.0);
|
||||
id = s.create(0.0, axis(1));
|
||||
}
|
||||
// Owned before it goes dormant, for the reason above: an unidentified
|
||||
// dormant track is not a candidate at any horizon (1477c53).
|
||||
reg.observe(id, 7, 0.99f, axis(1));
|
||||
{
|
||||
auto s = reg.begin_frame(0.0);
|
||||
s.mark_lost(id, 0.0);
|
||||
}
|
||||
|
||||
{
|
||||
auto s = reg.begin_frame(3.0); // inside the window
|
||||
auto s = reg.begin_frame(3.0); // watermark inside the window
|
||||
CHECK(s.candidates().size() == 1); // still associable
|
||||
}
|
||||
// The tracker races far past the horizon while the matcher lags. This
|
||||
// retires nothing, and that is the point of the watermark: the clock that
|
||||
// decides has not moved, so the track is still offered.
|
||||
{
|
||||
auto s = reg.begin_frame(50.0); // far outside it
|
||||
CHECK(s.candidates().empty()); // retired from association...
|
||||
auto s = reg.begin_frame(50.0);
|
||||
CHECK(s.candidates().size() == 1);
|
||||
}
|
||||
// ...but not gone, and still able to receive the votes in flight for it.
|
||||
// Which is what keeps it able to receive the votes still in flight for it.
|
||||
reg.observe(id, 7, 0.99f, axis(1));
|
||||
CHECK(reg.dropped_votes() == 0);
|
||||
|
||||
// Only the watermark passing last_seen + extinction retires it -- and the
|
||||
// same call reaps and emits it, so there is no interval in between.
|
||||
reg.advance_evidence(50.0);
|
||||
{
|
||||
auto s = reg.begin_frame(50.0);
|
||||
CHECK(s.candidates().empty());
|
||||
}
|
||||
REQUIRE(sink.claims.size() == 1);
|
||||
CHECK(sink.claims[0].actor_idx == 7);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user