---
phase: 04-animation-flow-control
plan: 13
type: execute
wave: 11
depends_on:
  - "04-12"
files_modified:
  - .planning/phases/04-animation-flow-control/04-RULING.md
  - .planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json
  - .planning/phases/04-animation-flow-control/04-06-PLAN.md
  - .planning/phases/04-animation-flow-control/04-07-PLAN.md
  - .planning/phases/04-animation-flow-control/uat_ack_stream.py
  - .planning/ROADMAP.md
  - .planning/REQUIREMENTS.md
  - .planning/STATE.md
autonomous: false
gap_closure: true
requirements:
  - ANIM-03
  - ANIM-04

must_haves:
  truths:
    - "The certification resolution is recorded as exactly what it is — an OPERATOR RULING (verbatim reply \"2\", 2026-07-17) accepting the accumulated cross-device directional dossier over an honestly-recorded statistical FAIL — and is never worded as, or mistakable for, a statistical pass"
    - "Every evidence citation in the ruling wording names on-disk files and measured numbers (04-UAT-SWEEP.json aggregate FAIL 5/5 valid sessions; per-device ratios 1.28–3.38x; 04-11 paired ratios 2.45x/1.77x; historical 5.25x) — nothing subjective, no evidence file modified"
    - statement: "The operator approved the complete final wording at a single blocking checkpoint BEFORE any governing document was touched — exactly one wording ask"
      verification: backstop
    - "Every governing reference to the Capsule's zone grid is corrected: 16×8 zones (128 zones, 8 rows of 16), 26 in × 13 in PHYSICAL — the old zone-grid attribution was an early-planning units mix-up (operator correction verbatim, 2026-07-17); historical records (04-SWEEP-DESIGN.md, prior summaries, sweep JSONs) are left as records"
    - statement: "The operator watched a full gated streaming round on the powered-on Capsule and gave an explicit DUAL verdict — ANIM-03 smoothness AND ANIM-04 geometry — recorded verbatim"
      verification: backstop
    - "ANIM-03 and ANIM-04 checkboxes flip ONLY in the close-out task, ONLY after an approved visual verdict; on a negative verdict they stay unchecked and the verdict routes back to the operator as gap evidence"
    - "After close-out, no gate anywhere (STATE.md sequencing gate, ROADMAP plan bullets, 04-06/04-07 supersession notices, REQUIREMENTS Traceability) contradicts the ruling — nothing still says visual ownership sits with the skipped 04-12 Task 5 or that fresh routing is pending"
    - "The Capsule is powered on for the visual round and restored to its as-found power and colour afterwards; no other device is touched"
  artifacts:
    - path: ".planning/phases/04-animation-flow-control/04-RULING.md"
      provides: "the ruling record: routing options + verbatim operator reply, supporting operator context verbatim, the full directional-evidence dossier with file citations, the dims correction verbatim, and the complete proposed final wording"
    - path: ".planning/ROADMAP.md"
      provides: "Phase 4 criteria 3+4 replaced with the approved ruling-based wording; plan list reconciled"
    - path: ".planning/REQUIREMENTS.md"
      provides: "ANIM-03/ANIM-04 replaced with the approved ruling-based wording; checkboxes flipped only at close-out"
    - path: ".planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json"
      provides: "the visual round's session context (non-gating; distinct filename — sweep evidence untouched)"
  key_links:
    - from: ".planning/ROADMAP.md"
      to: ".planning/phases/04-animation-flow-control/04-RULING.md"
      via: "criteria 3 and 4 replaced verbatim with the checkpoint-approved wording"
      pattern: "applied only after the Task 2 checkpoint approval"
    - from: ".planning/phases/04-animation-flow-control/04-RULING.md"
      to: ".planning/phases/04-animation-flow-control/04-UAT-SWEEP.json"
      via: "cites the aggregate FAIL and per-device numbers read-only — the FAIL evidence is the basis of the ruling, never edited"
      pattern: "read-only citation; git diff shows no evidence-file modification in any 04-13 commit"
    - from: ".planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json"
      to: ".planning/phases/04-animation-flow-control/uat_ack_stream.py"
      via: "written by the preserved single-device ceiling mode (the documented Task 5 visual vehicle)"
      pattern: "uat_ack_stream.py --ip <capsule-ip> --profile ceiling --rounds 1 --duration 60 --json-out"
---

<objective>
Close Phase 4 by executing the operator's ruling on the ANIM-03/ANIM-04 certification.
The 04-12 cross-device sweep ran honestly, one attempt per device, and FAILed its
aggregate statistical bar (04-UAT-SWEEP.json: 5/5 valid gate sessions FAIL, 2
INCONCLUSIVE) in the lossiest network window of the day — while the ack-gated arm beat
blind-fire DIRECTIONALLY in every one of the 8 sessions (ratios 1.28–3.38x), as it has
in every session ever measured across the phase (8 devices, 2 radio generations, ~10
sessions, ratios 1.28–5.25x). Presented with the routing options, the operator chose
option 2 (verbatim reply "2"): accept the eight-device directional dossier as
satisfying the requirement's intent, record the ruling, do the Capsule visual, close
Phase 4.

This plan records that ruling honestly (an operator acceptance over a recorded FAIL —
never dressed as a statistical pass), gets the exact final wording approved at one
blocking checkpoint, applies it verbatim, corrects the Capsule dims units mix-up in
every governing reference (16×8 zones / 128 zones / 26 in × 13 in physical — operator
correction verbatim, 2026-07-17), runs ONE visual streaming round on the Capsule for
the dual smoothness + geometry verdict, and — only on an approved verdict — flips
ANIM-03 and ANIM-04 complete and clears every gate that would otherwise contradict
the ruling.

Purpose: ANIM-03 and ANIM-04 are the last two Phase 4 requirements; the operator has
ruled with full context; what remains is faithful recording, one wording approval, one
visual verdict, and contradiction-free close-out.
Output: 04-RULING.md, amended ROADMAP criteria 3+4 and REQUIREMENTS ANIM-03+ANIM-04
(post-approval), 04-UAT-VISUAL-CAPSULE.json, the verbatim dual visual verdict, and —
on approval — ANIM-03/ANIM-04 marked complete with the STATE.md UAT sequencing gate
cleared and Phase 4 ready for phase-level verification.
</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-12-SUMMARY.md
@.planning/phases/04-animation-flow-control/04-SWEEP-DESIGN.md
@.planning/phases/04-animation-flow-control/04-UAT-SWEEP.json
</context>

## The operator ruling this plan records

After the 04-12 sweep's aggregate FAIL, the routing options were presented and the
operator chose **option 2, verbatim reply "2"** (2026-07-17), where option 2 was:

> "Operator judgement now — accept the eight-device directional dossier as satisfying
> the intent, record the ruling, do the Capsule visual, close Phase 4."

Supporting operator context, verbatim, from earlier in the same session:

> "The Tiles are notoriously flaky devices due to suboptimal antenna design, so even
> this amount of improvement is a win. There is also a lot of other stuff happening on
> my PC at the moment and the network, so overall we're working in a noisy environment"

Additional operator correction to encode, verbatim (2026-07-17):

> "26"x13" is the physical size of the Ceiling. It has 128 zones arranged in 8 rows
> of 16"

i.e. the Capsule is 26 in × 13 in PHYSICAL with a 16-wide × 8-high zone grid (128
zones). Every zone-grid reference in the applied criterion 4 / ANIM-04 wording that
swapped physical inches for zones was an early-planning units mix-up. The sweep
already validated the framebuffer-path mechanics against the true dims: the harness
derived 3 packets/frame from the REPORTED 16×8 chain (row-aligned rule), every gated
frame matched it across all rounds, and CopyFrameBuffer ack RTTs were recorded
(median 150 ms) — the D4-04 attachment confirmed (04-UAT-SWEEP-d073d587daab.json,
04-12-SUMMARY.md).

## The directional-evidence dossier (the basis of the ruling — cite, never modify)

| Session | Device(s) | Gated vs comparator | Ratio | Fisher p | Recorded outcome | Evidence file |
|---|---|---|---|---|---|---|
| 04-08 five-arm | Tiles I | shipped 2/189 = 1.06% vs replica 9/185 = 4.86% (blind baseline 14.6%) | ~5.25 (historical pooled 2.78% vs 14.6%) | 0.0342 | threshold-free investigation | 04-GAP-INVESTIGATION.json |
| 04-11 paired run 1 | Tiles I | 4/136 = 2.94% vs 9/125 = 7.20% | 2.45 | 0.0973 | VALID FAIL | 04-UAT-TILES-paired-run1-FAIL.json |
| 04-11 paired run 2 | Tiles I | 7/127 = 5.51% vs 11/113 = 9.73% | 1.77 | 0.1601 | VALID FAIL | 04-UAT-TILES.json |
| 04-12 sweep | 8 devices, one attempt each | gated 13.7–38.9% vs blind 25.9–94.7% | 1.28–3.38 | 0.018–0.184 (Capsule 1e-12) | aggregate FAIL (5/5 valid FAIL, 2 INCONCLUSIVE) | 04-UAT-SWEEP.json + 8 per-device JSONs |

The gated arm won directionally in **every session ever measured**; the statistical
certification bar was never cleared in a single session window because the
environment's ambient loss floor swings ~10x between sessions (0/113 ambient in 04-11
vs gated losses of 13.7–38.9% in the 04-12 window).

## Non-negotiable constraints (restated from the gap directive)

- The ruling is recorded as exactly what it is — an operator acceptance over honest
  FAIL evidence, never dressed up as a statistical pass. The FAIL evidence files
  (04-UAT-SWEEP*.json, 04-UAT-TILES*.json, 04-GAP-*.json*) are NEVER modified.
- Wording approval before application; visual verdict before checkbox flips; both
  checkpoints per protocol. Exactly one wording ask; exactly one visual ask.
- Keep it SMALL — this is a close-out plan, not another measurement programme. No new
  instruments, no new thresholds, no harness behaviour changes (comment-text dims
  corrections only). The only hardware touch is the single Capsule visual round.
- Historical documents (04-SWEEP-DESIGN.md, prior SUMMARYs, evidence JSONs) retain
  their original text as records — dims corrections apply to GOVERNING/live documents
  only (ROADMAP, REQUIREMENTS, live plan bullets, harness comments).
- src/ and tests/ untouched. Signed commits (`git commit -s`, GPG automatic), uv-only,
  Australian English.

<!-- planner-discipline-allow: 13×26 -->
<!-- planner-discipline-allow: 13x26 -->
<!-- planner-discipline-allow: Capsule 13×26 -->
<!-- planner-discipline-allow: 13×26 Capsule -->
<!-- planner-discipline-allow: 13x26 Capsule -->
<!-- planner-discipline-allow: Capsule still expects 8 -->

<tasks>

<task type="auto">
  <name>Task 1: Draft the ruling record and the complete final governing wording (04-RULING.md)</name>
  <files>.planning/phases/04-animation-flow-control/04-RULING.md</files>
  <read_first>
    - .planning/phases/04-animation-flow-control/04-12-SUMMARY.md — the sweep record: per-device table, aggregate arithmetic, ANIM-04 framebuffer evidence, honest-reporting discipline
    - .planning/phases/04-animation-flow-control/04-SWEEP-DESIGN.md — section 8 is the currently-applied wording this ruling overrides; section 6 is the visual checkpoint definition being inherited
    - .planning/phases/04-animation-flow-control/04-11-SUMMARY.md — the paired Tiles evidence (2.45x/1.77x) and the wording-checkpoint precedent
    - .planning/ROADMAP.md — Phase 4 criteria 3+4 as currently applied (the text being replaced)
    - .planning/REQUIREMENTS.md — ANIM-03/ANIM-04 as currently applied (the text being replaced)
  </read_first>
  <action>
    Write `.planning/phases/04-animation-flow-control/04-RULING.md` — the ruling
    record and the complete proposed final wording, following the 04-11/04-12
    design-brief discipline (drafted and committed BEFORE the checkpoint; nothing
    applied to any governing document until approval). Australian English. Required
    sections:

    1. **The ruling, verbatim.** The routing situation (04-12 aggregate FAIL, 5/5
       valid sessions FAIL + 2 INCONCLUSIVE in the lossiest window of the day), the
       option 2 text verbatim, the operator's verbatim reply "2", the date
       2026-07-17, and the supporting operator context verbatim (the
       Tiles/noisy-environment quote) — all exactly as given in this plan's "The
       operator ruling this plan records" section.
    2. **What the ruling is and is not.** State plainly: the statistical sweep
       criterion was designed (04-SWEEP-DESIGN.md), operator-approved, applied
       (commits ec87a76, 4e7640b), and honestly FAILed in a degraded network window
       (04-UAT-SWEEP.json, evidence commit 15e2cc0). The operator, with full context,
       accepted the cross-device directional dossier as satisfying the requirement's
       INTENT. This is an operator acceptance over a recorded FAIL — it is not, and
       must never be described as, a statistical pass. The FAIL evidence files are
       never modified; this document cites them read-only.
    3. **The dossier.** Reproduce this plan's directional-evidence dossier table with
       file citations, plus the 04-12 per-device rows (device, outcome, gated/blind
       pooled, ratio, p) from 04-12-SUMMARY.md, and the one-line summary: gated beat
       blind directionally in every session ever measured — 8 devices, 2 radio
       generations, ~10 sessions, ratios 1.28–5.25x — while the ambient loss floor
       swung ~10x between session windows.
    4. **The dims correction, verbatim.** The operator's quoted correction; the
       corrected facts (Capsule = 16×8 zone grid, 128 zones, 8 rows of 16, 26 in ×
       13 in physical); what the sweep already validated against the true dims
       (chain-dims-derived 3 packets/frame, packet_shape_ok true all rounds, CopyFB
       ack RTT median 150 ms, D4-04 confirmed — cite
       04-UAT-SWEEP-d073d587daab.json); and the scope rule: governing/live documents
       are corrected, historical records are not rewritten.
    5. **Proposed final wording (complete, ready to apply verbatim):**
       (i) ROADMAP Phase 4 success criterion 3 replacement — must state: the
       cross-device paired-sweep criterion was designed, applied and honestly FAILed
       its aggregate bar on 2026-07-17 (cite 04-UAT-SWEEP.json numbers); resolution
       is BY OPERATOR RULING (verbatim reply "2", 2026-07-17) accepting the
       directional dossier (cite the ratio range and the evidence files); the
       operator's visual smoothness verdict on the Capsule (04-13) completes ANIM-03.
       (ii) ROADMAP Phase 4 success criterion 4 replacement — must state: the
       framebuffer-path evidence captured in the sweep sessions of the three
       ceiling-class devices stands (chain-dims-derived packets/frame matched on
       every gated frame; ≥ 1 CopyFB ack RTT sample per gated round; D4-04
       confirmed — cite the per-device JSONs); the Capsule is 16×8 zones (26 in ×
       13 in physical; supersedes the units mix-up); the operator's visual geometry
       verdict on the Capsule (04-13) completes ANIM-04.
       (iii) REQUIREMENTS ANIM-03 replacement and (iv) REQUIREMENTS ANIM-04
       replacement — same substance in requirement form; both presented with
       UNCHECKED checkboxes (flips happen only in Task 5).
       (v) One final supersession note each for 04-06-PLAN.md and 04-07-PLAN.md:
       visual ownership moved from 04-12 Task 5 (skipped on aggregate FAIL) to
       04-13 Task 4 by operator ruling; plans remain never-execute.
       (vi) The replacement STATE.md "UAT sequencing gate" bullet text for the
       ruling-recorded-visual-pending state (applied in Task 3) and the cleared
       close-out text (applied in Task 5).
       (vii) The harness comment-only dims corrections (listed exactly, see Task 3
       item 4) — no behaviour change.
       Every sentence in the wording must cite files, numbers, or the dated ruling —
       nothing subjective.
    6. **What this ruling does NOT change:** the shipped src/ flow-control behaviour
       (ANIM-01/ANIM-02, already complete), the harness's measurement semantics, the
       evidence files, or the honest FAIL record.

    Commit: `docs(04-13): record operator ruling and proposed final wording`
    (`git commit -s`, GPG-signed) — strictly before the Task 2 checkpoint.
  </action>
  <verify>
    <automated>test -f .planning/phases/04-animation-flow-control/04-RULING.md && grep -c 'accept the eight-device directional dossier' .planning/phases/04-animation-flow-control/04-RULING.md | grep -qv '^0$' && grep -c 'notoriously flaky' .planning/phases/04-animation-flow-control/04-RULING.md | grep -qv '^0$' && grep -c '04-UAT-SWEEP.json' .planning/phases/04-animation-flow-control/04-RULING.md | grep -qv '^0$' && grep -c '128 zones' .planning/phases/04-animation-flow-control/04-RULING.md | grep -qv '^0$' && grep -c 'not.*a statistical pass\|never.*a statistical pass' .planning/phases/04-animation-flow-control/04-RULING.md | grep -qv '^0$'</automated>
  </verify>
  <acceptance_criteria>
    - 04-RULING.md exists with all six sections: ruling verbatim (option 2 text + reply "2" + date), what-it-is/is-not framing, dossier with file citations, dims correction verbatim, complete proposed wording (i)–(vii), does-not-change list
    - The wording explicitly frames resolution as operator ruling over a recorded FAIL; the phrase-level framing "never a statistical pass" (or equivalent) is present
    - Every number in the dossier matches the committed evidence files (spot-check ratios 2.45/1.77/1.28–3.38 and the 5/5-valid-FAIL aggregate against 04-11-SUMMARY.md, 04-12-SUMMARY.md, 04-UAT-SWEEP.json)
    - No governing document (ROADMAP, REQUIREMENTS, STATE, 04-06/04-07 plans, harness) is touched by this task; no evidence file is modified
    - Commit signed and strictly before the Task 2 checkpoint
  </acceptance_criteria>
  <done>The complete ruling record and final wording exist on disk, committed, ready to be judged by the operator from concrete text.</done>
</task>

<task type="checkpoint:decision" gate="blocking">
  <name>Task 2: Blocking operator approval of the complete final wording (exactly one wording ask)</name>
  <decision>Approve the exact final wording that records the ANIM-03/ANIM-04 resolution by operator ruling</decision>
  <context>
    You chose route 2 after the sweep FAIL: accept the eight-device directional
    dossier as satisfying the intent, record the ruling, do the Capsule visual, close
    Phase 4. This checkpoint is the established sovereignty pattern (04-09/04-11/04-12
    precedent): you chose the route; the exact text still gets your eyes before it
    governs anything. Present INLINE at this checkpoint the complete proposed wording
    from 04-RULING.md section 5: both ROADMAP criteria replacements, both REQUIREMENTS
    entries (checkboxes unchecked), the 04-06/04-07 final supersession notes, the
    STATE gate texts (visual-pending and cleared), and the harness comment-only dims
    corrections. State explicitly in the presentation: (a) the wording records an
    operator acceptance over a recorded statistical FAIL — the FAIL evidence stays
    untouched and cited; (b) checkbox flips happen only after your visual verdict at
    Task 4; (c) the dims correction (16×8 zones / 26 in × 13 in physical) is applied
    to governing text only, historical records keep their original text.
  </context>
  <options>
    <option id="approve">
      <name>Approve the wording as presented</name>
      <pros>Task 3 applies it verbatim in a single docs commit; the plan proceeds directly to the Capsule visual round</pros>
      <cons>None — the wording can still be revisited before Task 5 flips anything, since checkboxes only move after the visual verdict</cons>
    </option>
    <option id="amend">
      <name>Approve with amendments (state them)</name>
      <pros>Your edits are applied to 04-RULING.md first, then the amended wording is applied verbatim — no second checkpoint</pros>
      <cons>Slightly longer path; amendments are recorded verbatim in the summary</cons>
    </option>
    <option id="reject">
      <name>Reject — re-route</name>
      <pros>Nothing is applied; the plan stops and returns to you for fresh routing</pros>
      <cons>ANIM-03/ANIM-04 stay unchecked; Phase 4 stays open</cons>
    </option>
  </options>
  <action>
    Present the complete final wording from 04-RULING.md section 5 INLINE at this
    checkpoint (04-09/04-11/04-12 precedent — the operator decides from concrete
    text, never from a description of text): both ROADMAP criteria, both
    REQUIREMENTS entries, the 04-06/04-07 final supersession notes, both STATE gate
    texts, and the harness comment corrections, together with the three framing
    statements from the context above. Exactly one ask. BLOCK until the operator
    replies; record the reply verbatim in the summary. On "1": proceed to Task 3
    unchanged. On "2: amendments": apply the amendments to 04-RULING.md first
    (amend-commit, signed), then Task 3 applies the amended wording — no second
    checkpoint. On "3": skip Tasks 3–5, record the rejection in the summary and
    STATE.md, and end the plan (ANIM-03/ANIM-04 stay unchecked; routing returns to
    the operator).
  </action>
  <verify>
    <human-check>Operator has seen the complete final wording inline and replied; the verbatim reply is recorded</human-check>
  </verify>
  <done>The operator's decision on the exact governing text exists verbatim — approve, amend (with amendments applied to 04-RULING.md), or reject (plan ends honestly).</done>
  <resume-signal>Reply "1" (approve as presented), "2: [your amendments]", or "3" (reject). Your reply is recorded verbatim in the summary.</resume-signal>
</task>

<task type="auto">
  <name>Task 3: Apply the approved wording verbatim to every governing document (single docs commit)</name>
  <files>.planning/ROADMAP.md, .planning/REQUIREMENTS.md, .planning/STATE.md, .planning/phases/04-animation-flow-control/04-06-PLAN.md, .planning/phases/04-animation-flow-control/04-07-PLAN.md, .planning/phases/04-animation-flow-control/uat_ack_stream.py</files>
  <read_first>
    - .planning/phases/04-animation-flow-control/04-RULING.md — the approved (or operator-amended) wording to apply verbatim
    - .planning/phases/04-animation-flow-control/uat_ack_stream.py lines 25–45, 185–195, 395–410 — the three comment/docstring locations carrying the erroneous Capsule zone-grid attribution
  </read_first>
  <action>
    Apply the Task-2-approved wording EXACTLY (single-line reflow of ROADMAP criteria
    being the only permitted deviation — 04-10/04-11 precedent; if the operator
    amended, apply the amended text and record the amendments verbatim in the
    summary). If Task 2's reply was "3" (reject), skip this task and all later tasks,
    record the rejection in the summary and STATE.md, and end the plan.

    1. **ROADMAP.md:** replace Phase 4 success criteria 3 and 4 with the approved
       replacements. In the Phase 4 plan list: append to the 04-12 bullet the note
       that Task 5 was skipped on aggregate FAIL and visual ownership moved to 04-13
       Task 4 by operator ruling; append the dims correction in brackets to the 04-07
       bullet's Capsule grid mention (its old text described the Capsule's zone grid
       with the units mix-up); leave the 04-01 bullet untouched (its geometry
       describes the test arithmetic, which is accurate as written); the 04-13 bullet
       itself already exists from planning — leave it unchecked here (Task 5 ticks
       it).
    2. **REQUIREMENTS.md:** replace the ANIM-03 and ANIM-04 entries with the approved
       wording. Both checkboxes REMAIN `- [ ]` (unchecked); both Traceability rows
       REMAIN "Pending". Flips are Task 5's exclusive job, per D-ruling and the
       04-11 precedent (completion is owned by the visual owner).
    3. **04-06-PLAN.md and 04-07-PLAN.md:** append exactly ONE final supersession
       note each (the approved (v) text): visual ownership moved from 04-12 Task 5
       (skipped on aggregate FAIL) to 04-13 Task 4 by operator ruling; do not execute
       any task from these plans. Every other line of those files byte-identical.
    4. **uat_ack_stream.py — comment text ONLY, zero behaviour change:** correct the
       three erroneous attributions of the old zone grid to the Capsule: (a) the
       module docstring line "the 13x26 Capsule still expects 8, a 16x8 ceiling
       expects 3" — rephrase so 13x26 is a hypothetical chain geometry and the
       Capsule is identified as 16×8 zones (26 in × 13 in physical) expecting 3;
       (b) the EXPECTED_CEILING_PACKETS_PER_FRAME comment block — the constant stays
       8 (a superseded-assumption reference value surfaced in the thresholds JSON;
       changing it would alter recorded output) but the comment must say the value
       reflects the superseded roster assumption and that the Capsule's REPORTED
       chain is 16×8 → 3 (measured 2026-07-17, 04-UAT-SWEEP-d073d587daab.json);
       (c) the expected_packets_per_frame() docstring worked example "13x26 Capsule"
       — relabel the 13x26 row as a plain chain-geometry example and add that the
       Capsule itself reports 16×8 → 3. The function body, all constants, and all
       behaviour are untouched.
    5. **STATE.md:** replace the "UAT sequencing gate" bullet with the approved
       visual-pending text (ruling recorded and applied; Capsule visual verdict at
       04-13 Task 4 is the only remaining ANIM-03/ANIM-04 item), and delete the
       now-stale separate "fresh operator routing required" blocker bullet (the
       routing happened — reply "2" is the routing).
    6. Style gates: `uv run ruff format --check`, `uv run ruff check`,
       `uv run pyright` clean on the harness; no evidence file modified; src/ and
       tests/ untouched.

    Commit everything as ONE signed docs commit:
    `docs(04-13): apply operator ruling wording to governing documents`.
  </action>
  <verify>
    <automated>uv run ruff format --check .planning/phases/04-animation-flow-control/uat_ack_stream.py && uv run ruff check .planning/phases/04-animation-flow-control/uat_ack_stream.py && uv run pyright .planning/phases/04-animation-flow-control/uat_ack_stream.py && uv run python -c "import sys; sys.path.insert(0, '.planning/phases/04-animation-flow-control'); import uat_ack_stream as u; assert u.expected_packets_per_frame(1, 13, 26) == 8; assert u.expected_packets_per_frame(1, 16, 8) == 3; assert u.expected_packets_per_frame(5, 8, 8) == 5; assert u.EXPECTED_CEILING_PACKETS_PER_FRAME == 8" && grep -v '^#' .planning/REQUIREMENTS.md | grep -c '13×26 Capsule' | grep -q '^0$' && grep -v '^#' .planning/ROADMAP.md | grep -c 'Capsule 13×26' | grep -q '^0$' && grep -c '13x26 Capsule' .planning/phases/04-animation-flow-control/uat_ack_stream.py | grep -q '^0$' && grep -c 'Capsule still expects 8' .planning/phases/04-animation-flow-control/uat_ack_stream.py | grep -q '^0$' && grep -c '16×8' .planning/REQUIREMENTS.md | grep -qv '^0$' && grep -c 'operator ruling' .planning/REQUIREMENTS.md | grep -qv '^0$' && grep '04-07-PLAN.md' .planning/ROADMAP.md | grep -c '16×8' | grep -qv '^0$' && grep -c '13x26 large-tile Ceiling' .planning/phases/04-animation-flow-control/uat_ack_stream.py | grep -q '^0$' && git diff --stat HEAD -- src/ tests/ | wc -l | grep -q '^0$'</automated>
  </verify>
  <acceptance_criteria>
    - Approved wording applied EXACTLY (reflow-only deviation) to ROADMAP criteria 3+4 and REQUIREMENTS ANIM-03/ANIM-04; both checkboxes still unchecked; Traceability rows still "Pending"
    - No governing document still attributes the old zone grid to the Capsule; the corrected dims (16×8 zones, 128 zones, 26 in × 13 in physical) appear in the applied ANIM-04/criterion-4 wording
    - Exactly one new supersession note in each of 04-06-PLAN.md and 04-07-PLAN.md pointing visual ownership at 04-13 Task 4; every other line byte-identical
    - Harness diff is comment/docstring-only: expected_packets_per_frame worked examples still pass, EXPECTED_CEILING_PACKETS_PER_FRAME still 8, ruff/pyright clean
    - STATE.md gate bullet reflects ruling-recorded-visual-pending; the stale routing blocker bullet is gone; no text anywhere still says visual ownership sits with 04-12 Task 5
    - Single signed docs commit; git diff shows no src/, tests/, or evidence-file (04-UAT-*.json, 04-GAP-*) modifications
  </acceptance_criteria>
  <done>The ruling governs ROADMAP, REQUIREMENTS, STATE and the plan supersession chain; the dims are correct everywhere they govern; nothing contradicts the ruling except the still-unchecked boxes awaiting the visual verdict.</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <name>Task 4: Capsule visual round — dual verdict: ANIM-03 smoothness + ANIM-04 geometry</name>
  <what-built>
    The operator ruling is recorded and applied; the accumulated dossier covers every
    headless criterion. This is the ONE remaining human-only item, inherited from the
    skipped 04-12 Task 5: numbers count packets, only eyes judge smoothness (spike
    003: the ack-gated arm was the smoothest by eye) and geometry (the row-aligned
    chunking fix rendering coherently on the Capsule's real 16×8 zone grid). One
    visual streaming round on My Office Ceiling Capsule (serial d073d587daab), using
    the preserved single-device ceiling mode of uat_ack_stream.py — the documented
    visual vehicle. The harness powers the Capsule on automatically (1 s settle +
    confirmation) and restores its as-found power and colour in a finally block when
    the session ends. The session's headless outcome and exit code are recorded as
    context but gate NOTHING — certification is resolved by the ruling; this round
    exists solely to put gated streaming on the panel for your verdict.
  </what-built>
  <how-to-verify>
    1. Executor pre-step (before presenting this checkpoint): confirm the Capsule's
       current IP serial-authoritatively — run
       `uv run python -c "import asyncio; from lifx.devices.matrix import MatrixLight
       async def m():
           d = await MatrixLight.from_ip('192.168.19.231')
           async with d: print(d.serial)
       asyncio.run(m())"`
       (as a proper script string; expect d073d587daab). On mismatch or timeout,
       re-resolve with `from lifx.api import find_by_serial` /
       `find_by_serial('d073d587daab', timeout=10.0)` and use the returned `.ip`.
       Substitute the confirmed IP into the command below before presenting.
    2. Be at your desk with eyes on the Capsule, then run (or reply "run it" and the
       executor runs it while you watch):
       `uv run python .planning/phases/04-animation-flow-control/uat_ack_stream.py --ip <capsule-ip> --profile ceiling --rounds 1 --duration 60 --json-out .planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json`
    3. Timeline (~3.5 min total): power-on + 60 s ambient block (prober only — the
       panel just sits at its powered-on colour), 10 s rest, then the console prints
       "round 0 arm=gated" — the 60 s GATED round is THE verdict window: a
       continuously moving hue sweep at ~20 FPS across the whole panel. After
       another 10 s rest a 60 s blind-fire round runs (contrast only — NOT judged),
       then the Capsule is restored to its as-found power and colour.
    4. Judge BOTH criteria during the gated round only:
       a. Smoothness (ANIM-03): steady ~20 FPS colour cycle — occasional single
          skipped frames are normal (latest-frame-wins), but NO multi-second
          freezes, stutter clusters, or persistent crawling.
       b. Geometry (ANIM-04): the cycle sweeps coherently across the full 16×8 zone
          grid (26 in × 13 in panel) — no striping, no garbled or stale regions, no
          misordered rows or offset bands.
  </how-to-verify>
  <action>
    Present the how-to-verify steps with the Capsule's confirmed IP substituted, then
    BLOCK until the operator gives the dual verdict. Record the verbatim verdict in
    the summary. Exit-code handling: exits 0/1/3 are context only (record the
    headless outcome in the summary; it gates nothing under the ruling); ONLY exit 2
    (ENV-ERROR — nothing streamed, nothing to watch) invalidates the round — on
    exit 2, re-resolve the IP and re-run the round once, and if it repeats, surface
    it at this checkpoint as a blocker instead of a verdict. The visual JSON goes to
    the NEW file 04-UAT-VISUAL-CAPSULE.json — sweep evidence files are never
    rewritten. On a described issue: change NOTHING — classify it (a geometry failure
    implicates the row-aligned chunking; a pacing failure implicates the D4-04 CopyFB
    probe attachment with its documented one-line probe_template_index fallback
    seam), capture the operator's words verbatim as gap evidence, and route per
    Task 5's negative branch.
  </action>
  <verify>
    <human-check>Operator watched the full 60 s gated round on the powered-on Capsule and gave explicit verdicts on BOTH smoothness (ANIM-03) and geometry (ANIM-04); the Capsule was restored as-found afterwards (restoration block in 04-UAT-VISUAL-CAPSULE.json)</human-check>
  </verify>
  <done>A verbatim dual operator verdict exists — either both criteria approved, or the observed issue captured verbatim and classified — and the Capsule is back in its as-found state.</done>
  <resume-signal>Type "approved" if the Capsule rendered smoothly AND coherently (both criteria met), or describe exactly what you saw — striping/misplaced bands vs freezes/crawling route to different fixes</resume-signal>
</task>

<task type="auto">
  <name>Task 5: Close out — flip ANIM-03/ANIM-04, clear every gate, ready Phase 4 for verification (conditional on the approved visual verdict)</name>
  <files>.planning/REQUIREMENTS.md, .planning/ROADMAP.md, .planning/STATE.md, .planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json</files>
  <read_first>
    - .planning/phases/04-animation-flow-control/04-RULING.md — the approved cleared-gate STATE text (section 5 vi)
    - .planning/STATE.md — the gate bullet and progress blocks being closed out
  </read_first>
  <action>
    Branch on the Task 4 verdict.

    **On "approved" (both visual criteria met):**
    1. Commit the visual round evidence first:
       `test(04-13): record Capsule visual round context evidence` (signed) —
       04-UAT-VISUAL-CAPSULE.json only. Verify this commit touches no other file.
    2. REQUIREMENTS.md: flip ANIM-03 and ANIM-04 to `- [x]`; set both Traceability
       rows to "Complete". These are the ONLY checkbox flips in the phase and they
       happen here, after the verdict, never earlier.
    3. STATE.md: replace the visual-pending gate bullet with the approved cleared
       text (ruling recorded, wording applied, visual dual verdict approved — quote
       the operator's verbatim verdict — ANIM-03/ANIM-04 complete); update Current
       Position / progress counters consistently (this plan complete; 04-06/04-07
       permanently superseded, never executed); add to Operator Next Steps: Phase 4
       plans are complete — run phase-level verification (/gsd-verify-work) then
       proceed to Phase 5 (Reliability Documentation), which depends on Phase 4.
    4. ROADMAP.md: tick the 04-13 plan bullet with a one-line outcome; update the
       Phase 4 "Plans:" line to "11/13 plans executed (04-06, 04-07 superseded —
       never execute)"; update the Progress table's Phase 4 row (11/13, status
       noting plans complete / ready for phase verification — phase completion
       itself is the verifier's call, not this task's).
    5. Commit: `docs(04-13): close out ANIM-03/ANIM-04 by operator ruling + visual
       verdict` (signed). Verify no evidence file (04-UAT-SWEEP*, 04-UAT-TILES*,
       04-GAP-*) and no src/ or tests/ path appears in either commit's diff.

    **On a described issue (negative verdict):**
    1. Still commit 04-UAT-VISUAL-CAPSULE.json (test(04-13), signed) — evidence is
       evidence.
    2. Do NOT flip any checkbox; Traceability stays "Pending".
    3. Record the verbatim verdict and its classification (geometry → row-aligned
       chunking implicated; pacing → D4-04 CopyFB attachment / probe_template_index
       fallback seam implicated) in STATE.md's gate bullet as new gap evidence, with
       routing: fresh operator decision via /gsd-plan-phase 4 --gaps citing
       04-UAT-VISUAL-CAPSULE.json and the verbatim verdict.
    4. Commit the STATE/summary routing note (docs(04-13), signed) and end the plan —
       a negative visual under the ruling is a new operator decision, never an
       executor fix.
  </action>
  <verify>
    <automated>uv run python -c "
import re, pathlib
req = pathlib.Path('.planning/REQUIREMENTS.md').read_text()
state = pathlib.Path('.planning/STATE.md').read_text()
approved = '- [x] **ANIM-03**' in req and '- [x] **ANIM-04**' in req
negative = 'gap evidence' in state and '- [ ] **ANIM-03**' in req
assert approved or negative, 'close-out reflects neither an approved flip nor a recorded negative-verdict routing'
if approved:
    assert re.search(r'^\|\s*ANIM-03\s*\|\s*Phase 4\s*\|\s*Complete\s*\|', req, re.M), 'Traceability ANIM-03 row not Complete'
    assert re.search(r'^\|\s*ANIM-04\s*\|\s*Phase 4\s*\|\s*Complete\s*\|', req, re.M), 'Traceability ANIM-04 row not Complete'
    assert 'fresh operator routing required' not in state, 'stale routing gate still present in STATE.md'
print('close-out consistent')"</automated>
  </verify>
  <acceptance_criteria>
    - Approved branch: ANIM-03/ANIM-04 checked with Traceability "Complete"; verbatim dual verdict quoted in STATE.md and the summary; ROADMAP plan list/counts reconciled (11/13, 04-06/04-07 never-execute); STATE gate cleared with no "routing required"/"visual pending" residue; Operator Next Steps points at phase-level verification then Phase 5
    - Negative branch: no checkbox flipped; verbatim verdict + classification recorded as gap evidence with the --gaps routing; plan ends honestly
    - Both branches: 04-UAT-VISUAL-CAPSULE.json committed as its own signed test commit; no evidence file, src/ or tests/ path modified in any 04-13 commit
    - No document anywhere still contradicts the ruling (grep STATE/ROADMAP/REQUIREMENTS for stale 04-12-Task-5 visual ownership or pending-routing text)
  </acceptance_criteria>
  <done>Either ANIM-03 and ANIM-04 are complete — backed by the recorded ruling, the cited dossier, and an explicit dual visual verdict — with every gate cleared and Phase 4 ready for phase-level verification; or a negative verdict is captured verbatim with honest routing back to the operator.</done>
</task>

</tasks>

## Artifacts this plan produces

| Artifact | Kind | Location |
|----------|------|----------|
| `04-RULING.md` — ruling verbatim (option 2 + reply "2" + date), dossier with citations, dims correction verbatim, complete proposed final wording | Ruling record + wording source | `.planning/phases/04-animation-flow-control/04-RULING.md` |
| Operator decision on the final wording | Decision record (verbatim in summary) | `.planning/phases/04-animation-flow-control/04-13-SUMMARY.md` |
| Amended ROADMAP criteria 3+4, REQUIREMENTS ANIM-03+ANIM-04 (ruling-based), 04-06/04-07 final supersession notes, harness comment-only dims corrections | Governing documents | `.planning/ROADMAP.md`, `.planning/REQUIREMENTS.md`, phase plan files, `uat_ack_stream.py` |
| `04-UAT-VISUAL-CAPSULE.json` — visual round session context (non-gating; new file, sweep evidence untouched) | Evidence artefact | `.planning/phases/04-animation-flow-control/04-UAT-VISUAL-CAPSULE.json` |
| Verbatim dual visual verdict (smoothness ANIM-03 + geometry ANIM-04) | Human evidence (verbatim in summary + STATE) | `.planning/phases/04-animation-flow-control/04-13-SUMMARY.md` |
| ANIM-03/ANIM-04 completion (approved branch only) + cleared STATE sequencing gate + reconciled ROADMAP | Close-out | `.planning/REQUIREMENTS.md`, `.planning/STATE.md`, `.planning/ROADMAP.md` |

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Plan → governing documents | The ruling wording could misrepresent FAIL evidence as a pass (integrity of the project's own record) |
| Harness → Capsule | One bounded streaming session against a single production household device in the operator's own office, operator present |

## STRIDE Threat Register (ASVS L1 — minimal; one visual round on an already-quiesced device)

| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-04-13-01 | Repudiation | 04-RULING.md / ROADMAP / REQUIREMENTS wording | high | mitigate | Wording must state "resolved by operator ruling over a recorded FAIL — never a statistical pass"; verbatim quotes + dated reply; blocking approval before application; Task 1/3 acceptance criteria enforce the framing phrases |
| T-04-13-02 | Tampering | Existing FAIL evidence files (04-UAT-SWEEP*, 04-UAT-TILES*, 04-GAP-*) | high | mitigate | Read-only citation only; every task's acceptance criteria require git diffs free of evidence-file modifications; visual JSON goes to a NEW filename |
| T-04-13-03 | Spoofing | Roster IP drift → streaming at the wrong device | medium | mitigate | Serial-authoritative pre-step before the visual round (MatrixLight.from_ip serial match, find_by_serial fallback — the harness's own resolution pattern) |
| T-04-13-04 | Denial of Service | Capsule (production household device) | low | accept | One bounded ~3.5 min session (60 s ambient + 60 s gated + 60 s blind), operator watching, as-found power+colour restore in a finally block, device already quiesced from Home Assistant |
| T-04-13-SC | Tampering | npm/pip/cargo installs | low | accept | No package installs in this plan (uv-only rule stands; zero new dependencies permitted by the gap directive) |
</threat_model>

<verification>
- `git log --oneline` shows the commit order: Task 1 ruling commit → Task 3 single docs commit → Task 5 test commit → Task 5 docs commit; all signed; the Task 2 approval and Task 4 verdict recorded verbatim in the summary between them
- `git diff <task1-commit>^..HEAD --name-only` contains no src/, tests/, 04-UAT-SWEEP*, 04-UAT-TILES*, or 04-GAP-* paths
- REQUIREMENTS.md, ROADMAP.md, STATE.md, 04-06/04-07 notices, and the harness comments tell one consistent story: statistical bar honestly FAILed → operator ruling (verbatim, dated) → dossier cited → visual dual verdict → completion (or honest negative-verdict routing)
- The Capsule's governing dims read 16×8 zones / 128 zones / 26 in × 13 in physical everywhere they govern; historical records are unmodified
</verification>

<success_criteria>
- 04-RULING.md records the ruling verbatim with the full cited dossier and the dims correction; committed before the wording checkpoint
- Exactly one wording checkpoint (Task 2) and exactly one visual checkpoint (Task 4), both blocking, both verdicts recorded verbatim
- Approved wording applied verbatim in a single docs commit; checkboxes untouched until Task 5
- One visual streaming round on the Capsule (single-device ceiling mode, --rounds 1 --duration 60, power-on + as-found restore) with a dual smoothness + geometry verdict
- On approval: ANIM-03/ANIM-04 complete, Traceability Complete, STATE sequencing gate cleared, ROADMAP reconciled, Phase 4 flagged ready for phase-level verification; on a negative verdict: verbatim gap evidence + honest routing, no flips
- FAIL evidence files byte-identical throughout; src/ and tests/ untouched; no new instruments or thresholds; the only hardware touch is the single Capsule round
</success_criteria>

<output>
Create `.planning/phases/04-animation-flow-control/04-13-SUMMARY.md` when done, recording verbatim: the Task 2 reply, any amendments, the Task 4 dual verdict, and the branch taken at Task 5.
</output>
