`--focus-handoffs` could not pass at all. It auto-selected viewer 2 as the same-peer replacement target, but viewers 1 and 2 are the handoff participants, so the replacement tore down a participant's control session mid-exchange and the run died on "did not observe all handoffs". Replacement now targets a viewer outside the participant set, and stays off entirely when the roster has no spare viewer. Requesting an in-set target fails fast with a clear reason instead of a confusing cardinality error. Focus participants also left as soon as their handoffs landed, shrinking the roster underneath any concurrent replacement cycle. They now hold the session for the full frame budget, exactly like a spectator. The replacement cycle asserted an exact roster cardinality on every cycle, but the harness only controls that on cycle 1, which runs immediately after every viewer attaches and long before any exhausts its frame budget. Later cycles race the primaries' own exit, so they now assert what same-peer replacement actually governs: the roster never grows past the configured bound, and the replacement joined a generation that still has a survivor. This is what made the three-viewer proof flake under load. The spectator path reported the roster from a single sample taken at hello time; it now tracks the maximum across the snapshot stream, as the focus participant already did. With the gate working, `verify-demo-build.sh` now runs it, so slot-0 focus handoff and neutral-before-successor are covered by the release preflight rather than by hand.
- commit
- b2e61ed63c2f58e5dd0cd541f95c443c8d46dc9a
- parent
- 9c040f61fa919e1b68f26efa87db8fba09b05b9d
- author
- Dami <dami.etoile@gmail.com>
| M | scripts/ | 34 | +++++++++++++++++++++++++++--- |
| M | scripts/ | 3 | +++ |
@@ -261,7 +261,20 @@ if [[ "$stress" == true ]]; then
261261 fi262262 if [[ "$control_v2" == true && "$viewers" -gt 1 && "$reconnect_cycles" -gt 0 \263263 && "$replace_viewer" -eq 0 ]]; then264- replace_viewer=2264+ if [[ "$focus_handoffs" -gt 0 ]]; then265+ # Viewers 1 and 2 are the focus-handoff participants. Replacing one tears266+ # down its control session mid-exchange, so it can never observe the267+ # required handoffs. Replace a viewer outside the participant set, and268+ # leave replacement off entirely when the roster has no spare viewer.269+ if [[ "$viewers" -gt 2 ]]; then270+ replace_viewer=3271+ fi272+ else273+ replace_viewer=2274+ fi275+fi276+if [[ "$focus_handoffs" -gt 0 && "$replace_viewer" -gt 0 && "$replace_viewer" -le 2 ]]; then277+ die "--replace-viewer must target a viewer above 2 when --focus-handoffs is requested"265278 fi266279 [[ "$primary_frames" -ge 4 ]] \267280 || die "--primary-frames must be at least 4 for keyframe recovery"@@ -735,8 +748,23 @@ if [[ "$control_v2" == true ]]; then
735748 grep -Eq '^roster_viewers=(7|8)$' "$replacement_log" \736749 || die "replacement cycle $cycle exceeded the bounded stress roster"737750 else738- grep -Fxq "roster_viewers=${viewers}" "$replacement_log" \739- || die "replacement cycle $cycle did not preserve bounded roster cardinality"751+ observed_roster="$(sed -n 's/^roster_viewers=\([0-9][0-9]*\)$/\1/p' \752+ "$replacement_log" | head -1)"753+ [[ -n "$observed_roster" ]] \754+ || die "replacement cycle $cycle did not report a roster cardinality"755+ # Cycle 1 runs immediately after every viewer attached and long756+ # before any of them exhausts its frame budget, so the full roster is757+ # deterministic there. Later cycles race the primaries' own exit, so758+ # assert only what same-peer replacement actually governs: the roster759+ # never grows past the configured bound, and the replacement joined a760+ # generation that still has at least one survivor.761+ if [[ "$cycle" -eq 1 ]]; then762+ [[ "$observed_roster" -eq "$viewers" ]] \763+ || die "replacement cycle 1 observed roster $observed_roster instead of $viewers"764+ else765+ [[ "$observed_roster" -ge 2 && "$observed_roster" -le "$viewers" ]] \766+ || die "replacement cycle $cycle observed unbounded roster $observed_roster"767+ fi740768 fi741769 grep -Fxq 'media_progress=monotonic' "$replacement_log" \742770 || die "replacement cycle $cycle did not report monotonic progress"@@ -124,6 +124,9 @@ fi
124124 ./scripts/loopback-proof.sh125125 ./scripts/loopback-proof.sh --profile debug --control-v2 --viewers 3 \126126 --primary-frames 600 --reconnect-cycles 3127+./scripts/loopback-proof.sh --profile debug --control-v2 --viewers 3 \128+ --focus-handoffs 3 --assert-neutral-before-successor \129+ --primary-frames 600 --reconnect-cycles 3127130 ./scripts/loopback-proof.sh --profile release --legacy-exclusive --viewers 2 \128131 --expect-second-rejected129132