JR-004, JR-005, JR-006: scene-scoped read path

Presence was decided by a LINQ predicate inline in the controller, so the
semantics SR-002 sets were nowhere stated in code -- the read path complied by
accident rather than by requirement. PresenceLookup is now the unit that
decides, tagged, with the reasoning next to it.

JR-004: windows are served exactly as given. UT-021 pins that [0,10] and
[10,20] are not merged despite looking mergeable -- two windows mean a genuine
departure and return, and collapsing them answers a different question from the
one the truth file asked. UT-022 pins a byte-identical round trip.

JR-005: bounds inclusive at both ends, zero-length windows are real sightings
rather than degenerate ones to discard, overlaps resolve.

The wording was the larger half of JR-005. The overlay rendered a bare list: it
asserted nothing, but told the viewer nothing either, and the default reading of
a paused frame is "these people are on screen" -- exactly what SR-002 forbids.
It now carries an "In this scene" heading. ActorAtTime became ActorInScene, and
README no longer contains "on screen" anywhere; it stated the forbidden reading
outright in seven places, including the opening sentence.

JR-006: measured rather than assumed. UT-023 builds 50 actors x 1000 windows and
asserts the response is bounded by actor count, never window count. The lookup
is a full scan on purpose -- an early exit on `start > t` would exploit the
sortedness the format requires, but would silently under-report the moment one
producer emitted windows out of order. UT-020 pins that unsorted input still
resolves; WindowsAreSorted is a diagnostic, not a correctness dependency.

Third mutation check: making the end bound exclusive fails UT-016 and UT-018 and
nothing else. One character turns an inclusive window into a half-open one,
dropping an actor at exactly the moment a scene ends.

TRACES: UT-016, UT-017, UT-018, UT-019, UT-020, UT-021, UT-022, UT-023
TRACES: JR-004, JR-005, JR-006 | SR-002

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 11:29:46 +02:00
co-authored by Claude Opus 5
parent 305b898b15
commit c04d5a3dcc
11 changed files with 378 additions and 47 deletions
+16
View File
@@ -218,6 +218,22 @@
overlayEl.style.gap = '12px';
overlayEl.style.pointerEvents = 'none';
// "In this scene", not "on screen now". Presence is scene-scoped, so
// this list includes people the camera is not currently pointing at —
// without the heading a viewer reads a paused frame and concludes the
// overlay is wrong whenever someone is off-camera mid-conversation.
var heading = document.createElement('div');
heading.className = 'jrayOverlayHeading';
heading.textContent = 'In this scene';
heading.style.width = '100%';
heading.style.color = '#fff';
heading.style.opacity = '0.75';
heading.style.fontSize = '13px';
heading.style.textTransform = 'uppercase';
heading.style.letterSpacing = '0.08em';
heading.style.textShadow = '0 1px 3px rgba(0,0,0,0.9)';
overlayEl.appendChild(heading);
actors.forEach(function (actor) {
var card = document.createElement('div');
card.className = 'jrayActorCard';