Skip to content

Record screen decisions live in App code with few tests (refactor) #552

Open

Description

@Skeyelab

Summary

Most of what the Record screen decides (which alerts show, the status chips, the record block, the Copy summary, what the watch and Live Activity show) is worked out in App code with few tests. Nothing changes on screen; the aim is to move those decisions into the Kit where they can be tested.

Why it matters

The Record screen is what an operator watches during a show, and its alert order and wording are where regressions would hurt most. Today a change there is checked only by eye.

Done when

  • Interface agreed with Eric in a grilling session first, done in small stacked PRs.
  • A Kit "Record screen state" module takes one snapshot in and gives chips, alerts, the record block, the Copy summary, the watch status and Live Activity content out, with tests through that interface.
  • RecordScreenModel keeps platform glue only, and the glue left in App/ has tests in AppTests (App test coverage isn't on Codecov yet #679).
  • Eric's device check on a TestFlight build: the alerts and chips look the same as before through a Take with no Drive and a Mixer Link loss.

Details

Rating: Strong (architecture review of 2026-10-13). Eric (from comment 2026-10-14): do this first, in small stacked PRs, plus an App unit test target for the glue that stays in App/.

Today. RecordScreenModel spans App/RecordScreen.swift:647-1840, about 1,200 lines and 60-odd functions. It runs the polling loop, device listing (availableDevices(), line 1752), route-change filtering, assembles the alerts by hand in raisedAlerts (line 968: arm, idle, record, Destination, Drive rejoin, shortfall, interruption, no signal, routing, power, Copy summary, move report, setup hint, Soundcheck), the chips (chips, line 925), Templates (startShow(from:) 1337 and applyDefaultTemplate 1370), Pro offers and the watch and Live Activity status. It has 14 #if os blocks and 6 UserDefaults sites.

Since the review.

Direction. A Kit "Record screen state" module: one snapshot of Recorder, sequencer, Drive, Mixer Link, power and License in; chips, alerts, record block, Copy summary, watch status, Live Activity content and announcements out. It would absorb several one-function deciders (RecordAvailability, Soundcheck.shouldOffer, NoSignalAlert) and #553's problem list. Don't propose the interface here; settle it in a grilling session first.

Verified on main 2026-10-21 at 2e43ca8

Activity

  1. added this to the Code health milestone on Oct 13, 2026
  2. Skeyelab commented on Oct 14, 2026

    @Skeyelab
    MemberAuthor

    Eric (2026-10-14): do this first, in small stacked PRs, and also add an App unit test target that runs on the Mac (see the new issue) for the glue that stays in App/. The App folder has no tests today, so RecordScreenModel's ~1,000 lines are untested.

    https://claude.ai/code/session_01K4ihr1i6m1uuUswAENvJKJ

  3. changed the title [-]Record screen state: move RecordScreenModel's decisions into a tested Kit module (Strong)[/-] [+]Record screen decisions live in App code with few tests (refactor)[/+] on Oct 21, 2026
  4. Skeyelab commented on Oct 27, 2026

    @Skeyelab
    MemberAuthor

    This was generated by AI during triage.

    Triage: ready-for-human

    Per "Done when", the interface is agreed with Eric in a grilling session first. After that, this splits into small stacked PRs for an agent.

  5. added
    enhancementNew feature or request
    hitlNeeds a human: hardware, a decision or a review
    on Oct 27, 2026
  6. Skeyelab commented on Oct 27, 2026

    @Skeyelab
    MemberAuthor

    This was generated by AI during triage.

    Agent brief (grilling session with Eric, 2026-10-27)

    Summary: The Record screen's chips, alerts, record block, Copy summary, watch status, Live Activity problems, header text and VoiceOver announcements come from one tested Kit value, fed a snapshot of plain facts once a second.

    Since the issue was written: the single deciders already live in the Kit (status chips, Destination, no-signal and Drive-rejoin alerts, record availability, watch status, swiped alerts, announcements). What is left in the App is the assembly: which alerts are raised and in what order, the screen's memory between seconds, and passing the same facts to each decider.

    Eric's decisions:

    1. One snapshot, one value. A snapshot of plain facts (armed, recording, starting, finishing, Copies, Drive status, Device space, Mixer Link state, power, License, arm and record errors, routing changes, interruption notice, no-signal result, sample rate, Take number, Show, Soundcheck offered, setup-hint state). No Recorder, sequencer or store objects go in; the App reads them and builds the snapshot.
    2. The screen's memory lives in the Kit value, which the App holds as one property and updates once a second with the snapshot and the time. It owns: alerts swiped away, the 10 s fade of a green banner while the app is on screen, the Drive-rejoined notice, "Check Drive found nothing", and the re-patch the operator has already seen. Swiping and dismissing a re-patch go through the value.
    3. Outputs: chips, the alert queue, the record block, the Copy summary, watch status, Live Activity problems, the header text (input summary, compact input summary, Take title, Show summary) and VoiceOver announcements.
    4. Stays in the App: no-signal detection (it runs at the meter rate; only its result goes into the snapshot), Pro offers after a Take, Templates, the paywall, alert actions that open Settings or sheets, and the polling loop itself.

    Behavior that must not change: every alert's wording, tone, priority and order; which alert shows and the count of the rest; when a swiped alert comes back; the green banner's fade; the watch and Live Activity problems; VoiceOver announcing a change once.

    Done when:

    • Tests through the value's interface cover a sequence of seconds: a problem raised, swiped, cleared and raised again; a green banner fading after 10 s on screen and not off screen; the Drive dropping and rejoining during a Take; a re-patch seen, then a second one; arm failure with the microphone denied; a Take with no Drive and a Mixer Link loss (chips, alerts, watch problems).
    • The App's Record screen model reads all those outputs from the value and keeps platform glue only.
    • Small stacked PRs: (1) the snapshot, the value and the alert queue with its memory; (2) chips, record block, Copy summary, watch status, Live Activity problems, header text and announcements; (3) AppTests for the glue that stays in the App (App test coverage isn't on Codecov yet #679's second half).
    • Eric's device check on a TestFlight build: alerts and chips look and behave as before through a Take with no Drive and a Mixer Link loss.

    Out of scope: any change on screen, new alerts, reordering, wording edits, Pro offers, Templates, no-signal detection, the polling loop, Mixer Link or Take control (already moved in #551 and #556).

    Timing (Eric): start now; no tag until #963 and these PRs have run on main. Then v1.1.18 is tagged [internal] for Eric's device Take.

  7. added
    ready-for-agentFully specified; an agent can pick this up
    and removed
    hitlNeeds a human: hardware, a decision or a review
    on Oct 27, 2026
  8. added
    hitlNeeds a human: hardware, a decision or a review
    and removed
    ready-for-agentFully specified; an agent can pick this up
    on Oct 27, 2026
  9. Skeyelab commented on Oct 27, 2026

    @Skeyelab
    MemberAuthor

    This was generated by AI during triage.

    All three PRs merged: #978, #979, #980. The only open item is Eric's device check on a TestFlight build (v1.1.18 [internal]): the alerts and chips should look and behave as before through a Take with no Drive and a Mixer Link loss. Close this when that passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthitlNeeds a human: hardware, a decision or a review

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions