---
phase: 04-animation-flow-control
plan: 06
type: execute
wave: 5
depends_on:
  - "04-05"
files_modified:
  - .planning/phases/04-animation-flow-control/04-UAT-TILES.json
autonomous: false
requirements:
  - ANIM-03

must_haves:
  truths:
    - "Concurrent single-shot queries under 20 FPS ack-gated streaming on real Tiles show 0% loss over repeated rounds (blind-fire baseline: 14.6%), with delivered frames ≥ 85% per round — measured by the shipped implementation and recorded honestly"
    - "The operator has watched a streaming run and given an explicit visual-smoothness verdict — the ANIM-03 criterion the numbers cannot capture (spike: the ack-gated arm was smoothest by eye)"
    - "Outcomes are recorded exactly as measured: PASS with numbers, FAIL flagged loudly with per-round data, or ENV-ERROR — thresholds never adjusted after a run"
  artifacts:
    - path: ".planning/phases/04-animation-flow-control/04-UAT-TILES.json"
      provides: "machine-readable ANIM-03 measurement evidence (or recorded ENV-ERROR outcome)"
  key_links:
    - from: ".planning/phases/04-animation-flow-control/04-UAT-TILES.json"
      to: ".planning/phases/04-animation-flow-control/uat_ack_stream.py"
      via: "written by the fixed-contract harness from plan 04-05"
      pattern: "uat_ack_stream.py --profile tiles"
---

**AMENDMENT NOTICE (2026-07-17, applied by 04-10):** The operator routed the
ANIM-03 gap to recalibrate (04-09-SUMMARY.md, verbatim reply "1"), adopting the
amendment in 04-GAP-ANALYSIS.md "## Proposed amendment". Consequences here:

- **Task 1 is SUPERSEDED — do not re-run it from this plan.** Its 0%-loss gate
  and its "harness byte-identical to the plan-04-05 commit" acceptance criterion
  no longer apply; 04-10 owns the amended-gate headless re-run (per-round
  ≤ 9.0%, pooled ≤ 5.0% per run). The original Task 1 FAIL evidence stays in
  history unmodified (commit 5de88f6).
- **Task 2 (operator visual verdict) remains live** and is the single owner of
  the ANIM-03 visual checkpoint. It executes only after 04-10's headless run
  PASSes, using the IP 04-10's run actually used.

**AMENDMENT NOTICE (2026-07-17, applied by 04-11):** The operator routed the
twice-FAILed 04-10 amended absolute gate (pooled 11.21% then 5.74% vs ≤ 5.0%)
to a paired-relative redesign (verbatim reply "2"). Consequences here:

- **04-11 owns the paired headless run**: ambient control (prober only), then
  alternating ack-gated and same-session instrument-level blind-fire rounds,
  judged by the operator-approved paired-relative criterion in
  04-CRITERION-DESIGN.md (approval at the 04-11 checkpoint, verbatim reply
  "1" = approve as presented).
- The 04-10 notice's per-round ≤ 9.0% / pooled ≤ 5.0% percentages are
  **superseded in turn** by the paired-relative gate (Fisher one-sided
  p < 0.05 AND >= 2x lower, OR gated pooled <= 2.5%, within a 9.0% pooled
  ceiling; ambient-degraded sessions are INCONCLUSIVE).
- **Task 2 (operator visual verdict) REMAINS LIVE** as the single owner of the
  ANIM-03 visual checkpoint. It executes only after 04-11's paired headless
  run PASSes, against the IP 04-11's run actually used.

**SUPERSESSION NOTICE (2026-07-17, applied by 04-12):** After the paired-relative
gate FAILed twice on System Test Tiles I with both sessions VALID (04-11), the
operator ruled the gen3 Tiles radios notoriously flaky — "even this amount of
improvement is a win" — and retargeted ANIM-03 certification to the 04-12
cross-device paired sweep over 7 healthy-radio matrix devices
(04-SWEEP-DESIGN.md, approved at the 04-12 checkpoint). Consequences here:

- **Task 2 (operator visual verdict) is SUPERSEDED — visual ownership moves to
  04-12 Task 5**, the single consolidated smoothness + geometry checkpoint on My
  Office Ceiling Capsule. The sole-visual-owner rule moves with it: 04-12 Task 5
  is now the only ANIM-03 visual checkpoint.
- This plan is now fully superseded (Task 1 by 04-11's paired headless run, Task 2
  by 04-12 Task 5). Do not execute any task from this plan.

**FINAL SUPERSESSION NOTICE (2026-07-17, applied by 04-13):** The 04-12
aggregate sweep FAILed (5/5 valid sessions FAIL), so its Task 5 consolidated
visual checkpoint never executed. By operator ruling (verbatim reply "2",
2026-07-17, `04-RULING.md`), ANIM-03/ANIM-04 certification is resolved by
operator acceptance of the cross-device directional dossier, and visual
ownership moves to **04-13 Task 4** — the same Capsule smoothness+geometry
checkpoint 04-12 Task 5 would have run. This plan remains fully superseded;
do not execute any task from it.

<objective>
Execute the ANIM-03 hardware UAT: run the plan-04-05 harness against the standard
Tiles (192.168.19.243 primary, 192.168.18.62 fallback) — 3 rounds of 20 FPS streaming
with a concurrent single-shot query prober — then gate on the operator's visual
smoothness verdict. This is the mandatory hardware evidence for ANIM-03 (per
04-CONTEXT.md the emulator cannot model real ack RTT under load, and repeated rounds
are mandatory for loss claims).

Purpose: prove on real hardware that the shipped gate eliminates the 14.6%
concurrent-query starvation (target 0%) while the stream remains visually smooth
(spike 003: the ack-gated arm was both loss-free AND the smoothest to watch).
Output: 04-UAT-TILES.json evidence + recorded operator verdict.
</objective>

<execution_context>
@$HOME/.claude/gsd-core/workflows/execute-plan.md
@$HOME/.claude/gsd-core/templates/summary.md
</execution_context>

<context>
@.planning/STATE.md
@.planning/phases/04-animation-flow-control/04-CONTEXT.md
@.planning/phases/04-animation-flow-control/uat_ack_stream.py
@.planning/phases/03-retry-schedule-reshape/03-03-PLAN.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Run the headless ANIM-03 measurement (3 rounds, Tiles)</name>
  <files>.planning/phases/04-animation-flow-control/04-UAT-TILES.json</files>
  <read_first>
    - .planning/phases/04-animation-flow-control/uat_ack_stream.py — the harness and its fixed exit-code contract as landed in 04-05
    - .planning/phases/04-animation-flow-control/04-CONTEXT.md — Measured baselines table (ANIM-03 targets: 0% loss, delivered ≥ 85%, Tiles at 192.168.19.243 or 192.168.18.62)
    - .planning/phases/02-discovery-rebroadcast/02-02-SUMMARY.md — the honest-reporting precedent (actual numbers recorded, anomalies explained, thresholds never altered after seeing results)
  </read_first>
  <action>
    Run the measurement headless and record whatever it actually says:

    1. `uv run python .planning/phases/04-animation-flow-control/uat_ack_stream.py
       --ip 192.168.19.243 --profile tiles --rounds 3
       --json-out .planning/phases/04-animation-flow-control/04-UAT-TILES.json`
       (expected wall time ≈ 3 × 30 s of streaming plus setup).
    2. If exit 2 (ENV-ERROR) on the primary IP, retry ONCE with the fallback Tiles at
       192.168.18.62. If both are unreachable, record the ENV-ERROR outcome in the
       JSON and summary and STOP — ANIM-03's hardware gate is mandatory (CONTEXT: "UAT
       is mandatory"), so this plan cannot self-approve; surface the blocker to the
       operator rather than fabricating anything.
    3. Interpret strictly by exit code. 0 (PASS): record per-round loss (expect 0%),
       delivered ratios, query latency medians and ack RTT medians against the spike
       baselines (blind 14.6% loss / gated 0.0% / RTT ~98 ms). 1 (FAIL): a genuine
       signal — do NOT re-run repeatedly hoping for a pass and never touch the
       thresholds; re-run ONCE to rule out a transient network event (Phase 2/3
       precedent), and if it fails again flag it prominently with the per-round data.
    4. Never edit the harness's thresholds, rounds, or pass logic in this plan for any
       reason — the harness file must be byte-identical to its 04-05 commit.
  </action>
  <verify>
    <automated>test -f .planning/phases/04-animation-flow-control/04-UAT-TILES.json</automated>
  </verify>
  <acceptance_criteria>
    - 04-UAT-TILES.json exists with actual per-round measured data (or the recorded ENV-ERROR outcome) — never placeholder numbers
    - The exit code, its meaning, and the measured loss/delivery/RTT figures are recorded in the summary against the 14.6% blind-fire baseline
    - The harness file is byte-identical to its plan-04-05 commit
  </acceptance_criteria>
  <done>The headless half of ANIM-03 is measured and recorded honestly: 0% concurrent-query loss and ≥85% delivery over 3 rounds, or a loudly flagged failure/environment outcome.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 2: Operator visual verdict — Tiles streaming smoothness (ANIM-03)</name>
  <what-built>
    Ack-gated flow control now paces the shipped Animator's 20 FPS streaming to the
    Tiles (plans 04-01..04-04), and Task 1 measured 0% concurrent-query loss headlessly.
    ANIM-03's remaining criterion is human-only: the stream must LOOK smooth (spike 003
    finding: the ack-gated arm was the smoothest by eye; the ack-every-frame arm was
    visibly the choppiest — numbers alone cannot distinguish this).
  </what-built>
  <how-to-verify>
    1. Position yourself where you can see the Tiles (the device used in Task 1 —
       192.168.19.243, or the .18.62 fallback if Task 1 fell back).
    2. Run a single visual round:
       `uv run python .planning/phases/04-animation-flow-control/uat_ack_stream.py --ip <task-1-ip> --profile tiles --rounds 1 --json-out /tmp/04-uat-tiles-visual.json`
    3. Watch the full ~30 s round. Expected: a continuously moving colour cycle at a
       steady ~20 FPS cadence — occasional single skipped frames are normal
       (latest-frame-wins gating; the spike's winning arm delivered 530/600), but
       there should be NO multi-second freezes, stutter clusters, or visible tearing.
    4. Compare against your memory of the spike 003 arms if useful: this should match
       the "photons" arm's smoothness, not the choppy ack-every-frame arm.
  </how-to-verify>
  <action>
    Present the how-to-verify steps above to the operator (including the exact visual-run
    command with the IP Task 1 actually used), then BLOCK until the operator responds.
    Record the verbatim verdict in the summary. On "approved" the ANIM-03 visual
    criterion is met; on a described issue, do not edit anything — capture the
    description as gap evidence for `/gsd-plan-phase 4 --gaps`.
  </action>
  <verify>
    <human-check>Operator has watched a full ~30 s streaming round on the Tiles and given an explicit smoothness verdict</human-check>
  </verify>
  <done>An explicit operator verdict on Tiles streaming smoothness is recorded verbatim in the summary — approved, or issues captured for gap-closure.</done>
  <resume-signal>Type "approved" if the stream looks smooth (ANIM-03 visual criterion met), or describe what you saw (freezes, stutter clusters, tearing) — issues route to gap-closure planning, not ad-hoc edits</resume-signal>
</task>

</tasks>

## Artifacts this plan produces

| Artifact | Kind | Location |
|----------|------|----------|
| `04-UAT-TILES.json` — 3-round loss/delivery/RTT evidence vs the 14.6% baseline | Evidence artefact | `.planning/phases/04-animation-flow-control/04-UAT-TILES.json` |
| Operator visual-smoothness verdict | Human evidence (recorded in summary) | `.planning/phases/04-animation-flow-control/04-06-SUMMARY.md` |

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Harness → LAN Tiles | Outbound streaming + query load against a user-owned quiesced test device |

## STRIDE Threat Register

| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-04-10 | DoS (outbound, self-inflicted) | 20 FPS stream ×3 rounds | low | mitigate | Fixed rounds/duration; the shipped gate throttles under congestion; ENV-ERROR guard prevents hammering an absent device |
| T-04-11 | Repudiation | UAT evidence | low | mitigate | Fixed pre-declared thresholds; JSON on disk; harness byte-identical rule |

No packages are installed by this plan (zero-dependency project) — no supply-chain gate applies.
</threat_model>

<verification>
- 04-UAT-TILES.json on disk with real measured data or a recorded ENV-ERROR
- Human checkpoint answered explicitly (approved or described issues)
- Harness untouched by the run
</verification>

<success_criteria>
- ANIM-03 closed with dual evidence: 0% concurrent-query loss + ≥85% delivery over repeated rounds (headless) AND an explicit operator smoothness verdict (human) — or an honestly recorded blocker
</success_criteria>

<output>
Create `.planning/phases/04-animation-flow-control/04-06-SUMMARY.md` when done, recording:
the exit code, per-round loss/delivery/RTT vs baselines, the operator's verbatim visual verdict, and commit hashes.
</output>
