Files
status-indicators/docs/settings-plan-audit.md
T

56 KiB

How the settings-page plan was made and audited

Four inventory readers went through the repo first, one lens each: operator workflow (97 items), hard-coded site facts in code and scripts (132), config keys and the code that reads them (182), and what runs where (133): 544 items in all. The "what runs where" reader also verified NA Ingest live, read-only, on 2026-10-13 (LaunchAgents, data dir, Companion connections and the flash trigger, keychain item presence, interfaces, the DVS helper, sshd and the StatusPanel tasks on the three PCs). Three designers then wrote independent plans from the same inventory: simplest, operations-first and teach-first. Three judges scored each on completeness, simplicity, safety, multi-site and feasibility. Totals over the three judges: simplest 126, operations 127, teaching 117. Two judges picked operations and one picked simplest, so operations won and the ideas listed under "Judges' reasoning" were grafted onto it. One synthesis produced the plan (second full revision, 844 lines; a partial third revision of sections 10-14). Three audit rounds followed, each with four critics (completeness, simplicity, safety, feasibility), finding 47, 59 and 51 gaps. Each round's fixes were written into the next revision. The loop did not converge: every revision added machinery (apply classes with SIGHUP reload, a queued-job runner, a stop flag, live-signal triples, a full-inventory diff with ignore lists) that the next round found unsafe, unteachable or infeasible, and the gap count stayed flat. So instead of a fourth revision the plan was consolidated against a decisions list dated 2026-10-13: K1-K18 are the mechanisms kept, D1-D10 details pinned after this audit, C1-C13 the deliberate cuts, P0-P6 the phases, Q1-Q4 the questions only the user can answer. Those decisions override the source wherever they differ. The table below judges every one of the 157 findings against that list, not against the revision that first answered it. Round 1 and 2 findings were addressed by the source's own revisions; where the decisions keep that mechanism the row says ADOPTED, where they cut it the row says CUT, and where a later critic replaced the ask the row says SUPERSEDED.

Judges' reasoning

Simplest (126)

Strengths

  • The most exhaustive coverage table: every config key, check field, fix-kind field and hard-coded constant is either editable or "shown, not editable" with a reason, and the new keys pull constants out of code (hold time, grid, stale seconds, flash variable, cooldown, Dante max shared, export stamps, settings port).
  • Machines are data, not code: hosts..runs[] with a closed kind list and a probe per kind; the Ingest Mac is a host too, so its interfaces, Satellite and other agents show on the map.
  • A safe apply order: validate (schema, load_config, make_page dry run), atomic write, restart the watchdog, prove it is up, only then import pages and run nav keys; page imports refused outright during a service; rollback re-runs the page and nav steps.
  • Least invasive to the watchdog: an AST test checks the code's literal defaults against the schema instead of refactoring them out; a Phase 0 round-trip test proves today's config still builds byte-identical pages.
  • A password-free edit gate (Unlock editing, typed APPLY, an address allow-list), a "Try it on a copy" sandbox, a generated "Where things are" page, a dedicated Spotify screen, and an import wizard whose cascade drops and lists everything that depended on a dropped computer.

Weaknesses

  • No automatic rollback: if the watchdog does not come back the page only "offers Rollback", leaving the deck purple until someone clicks.
  • Page freshness by hashing Companion's live export against the built file cannot work (Companion normalises what it imports); a stored build hash is what works.
  • Fix templates fill the command box once and the string becomes a copy again, so task names, the playlist and the DVS path can drift.
  • The draft lives only in the browser tab; "who changed it" is only a browser address; History auto-prunes.
  • A text-only Overview with no live deck picture; "bridge" refused by name in shared code; 13 screens with Site, Companion and Schedules as grab-bags; NA moved into sites/na in Phase 0 before any UI exists.

Operations (127, winner)

Strengths

  • The fullest per-machine model: runs[] with kinds, watched:false, networks[] per interface, pingable:false for PCs that block ping and self:true for the Ingest Mac, seeded for all eight NA machines, with the per-OS probe commands spelled out, cached, and never waking a PC.
  • Fix builders store template + params beside the generated run with a regenerate test, which structurally fixes the duplicated Spotify URI, task names and DVS path.
  • The safest apply pipeline: server-side draft.json, History with a sidecar, atomic replace, automatic rollback on a traceback, typed APPLY during a service, layout re-import refused then, and install.sh never overwriting a page-edited config; pages and nav keys are detected by diffing the built page JSON rather than a hand-kept rule list.
  • site.live_services separate from auto_fix.windows; a "What this key will say" preview on every key; the Companion screen flags vanished labels with a "rename to" dropdown and offers "Add a key for it"; the Dante screen offers "Watch it".
  • An editor name per browser for History attribution, "Things only a person can do" as a live checklist, a walkthrough that ticks itself, a cheat sheet with contacts, phases that split editing/apply from layout and fleet setup, and the guideline fit enumerated 1-16.

Weaknesses

  • Apply imports pages before restarting the watchdog, and rollback restores only the config, so a bad config can leave new pages on the deck over the old settings.
  • No edit gate beyond a self-declared first name; anyone on the LAN can Apply.
  • test_defaults_single_source demands ripping some fifty literal defaults out of the 2,500-line watchdog in Phase 0, the most invasive refactor of the three; config.json leaves the repo root in the same phase.
  • 14 screens with two grab-bags ("Site & times", "Timing & limits" whose global thresholds have no config key); three window tables; deleting a computer is blocked until every reference is removed by hand.
  • The playlist is only a field inside the SPOTIFY key's check (no screen, no link paste, no search); no sandbox; export stamps and the settings port stay constants; no nav_locations viewer or revert; Restart and Mute not said to respect the guard.

Teaching (117)

Strengths

  • Wizards that map one-to-one onto the user's own examples: Add a MIDI link with live pickers, Add or remove a HyperDeck adjusting the REC members too, Change the playlist by pasting a link, Add a computer, Add a Windows app task, New campus; plus a demo-mode walkthrough.
  • The live Stream Deck picture on the Overview from sp_state with click-through to the key, a global search box, and a "Stream Deck" (where it sits) versus "Keys" (what it does) split a volunteer gets in one sentence.
  • The safest Apply order (restart the watchdog, prove it, then pages and nav with a dry run shown), the key guide regenerated on every Apply, History never auto-pruned, and "Undo last apply" on the Overview.
  • Makes alert_after_seconds and the fix cooldown editable with the key texts following; validates fix kinds in load_config; requires host or conn on ping checks; the settings server imports only load_config, CHECKS and make_page so a page bug cannot take the panel down.
  • Per-field help with an example and "what you will see on the key"; "done by hand" ticks for things no probe can see; the nav_locations list with a Revert button.

Weaknesses

  • The per-machine inventory is a code catalogue (machines.py) with NA facts in it, not data: weak on R3 and R5.
  • The live-service guard is only a typed phrase and is conditioned on ALARMS being on, so muting alarms disables it; page re-imports are allowed during a service.
  • No edit gate (a PIN is only an open question); drafts live in localStorage.
  • Phase 4 is a single 1,800-line, 4-5 day big bang; tests are rebuilt from scratch rather than made config-driven; the regex-based schema sync is the weakest of the three.
  • The Site screen is a grab-bag (ATEM, grid, hold time, cooldown, thresholds, the Windows app table, SSH key, webhook); no Logs screen; no install.sh non-clobber rule; {playlist} substitution has no fallback for restart keys.

Ideas the judges asked to graft (and where they landed)

  • From teaching: restart the watchdog and prove it started before pages are rebuilt or imported, and roll pages back too (K10).
  • From teaching: the job wizards for the named asks (K4, P4) and "New campus from this one" (K17).
  • From teaching: the live deck on the Overview from sp_state with click-through (K4); the "what this key will say" preview (K5); the key guide rebuilt on every Apply (K15); History never auto-pruned (K14); Undo the last change (K14); "done by hand" ticks (K8 manual_done); alert_after_seconds and the fix cooldown editable with the key texts following (K12); a global search box (D10).
  • From simplest: the AST-based schema sync test and "defaults in code must equal the schema" instead of removing the defaults from the watchdog (K16); the Phase 0 round-trip test (K16, P0).
  • From simplest: the password-free edit gate, later reduced from allow-list plus unlock plus typed APPLY to one editing code (K2, C3).
  • From simplest: the new-campus cascade that drops and lists dependants, sites/_template, and export stamps as config with a mismatch warning (K17, K9).
  • From simplest: the dedicated Spotify screen with link paste (K9), the generated "Where things are" page (K15, C12), the "Try it on a copy" sandbox (deferred, C4).
  • From operations, kept as the base: draft.json on the server with the editor's name (K10), pages/nav detected by diffing the built JSON (K10), site.live_services (K9, K12), the runs[] model with watched:false, networks[] and pingable (K8), the Companion screen's rename-to and "Add a key for it" (K9), builders with template + params (K6), "Things only a person can do" (K15).
  • Reviewer additions: probe whether tRPC can create custom variables and import the flash trigger before declaring those steps by hand (P1 spike); an Ingest self-check that python3 is 3.9-3.12 because telnetlib disappears in 3.13 (K12); explain the Stream Deck "use last page" setting beside the nav settings (K9 Advanced); a generic never_auto flag instead of "bridge" by name (K12); split settings.html into files; a dated folder for previous ATEM files (K14).

Audit findings and what happened to them

Round 1 critics read the first full plan, round 2 the revision that answered them, round 3 the second full revision. Lenses are listed in the order completeness, simplicity, safety, feasibility. K, C, P and Q numbers refer to the decisions list (K = kept, C = cut, P = phase, Q = open question).

Round Lens Severity Finding Disposition How the plan handles it now
1 Completeness major Machine cards only echo what was typed into runs[]; a background task nobody listed stays invisible, so the drift R5 is about is reproduced one level up. SUPERSEDED Built as a full-inventory diff with Add/Ignore in revision 2; round 2 Simplicity showed it is vendor noise and it was cut (C5). K8 keeps a collapsed raw "Everything found on this computer" list on the card, so drift is visible on inspection but never counted on Overview.
1 Completeness major Checks have no builders: remote, process and pref checks still carry hand-typed app names and helper paths that fixes no longer do. ADOPTED K5 "Add a check" asks a question and picks the type; checks.*.{template,params} (K12) regenerate the command; the K16 regenerate test covers checks as well as fixes.
1 Completeness major The new-campus walkthrough omits the bootstrap (Command Line Tools, copying the folder), Companion pages existing, the trigger export, the quiet Tascam package, nav templates, surfaces. SIMPLIFIED K17 companion-objects/ (trigger export, quiet Tascam module, nav_templates per Companion version) is captured in P1 and deployed by install.sh (K13); K9 Companion lights cover pages exist, our pages and the quiet module; surfaces are not shown (C11). Copying the staged folder to a new Mac stays scp by hand (K13).
1 Completeness minor Changing the page number or name is refused by push_page's name rule, or leaves the old page live; no path forward. SUPERSEDED Replaced by round 3 Safety's marker-only rule: K10 accepts a page when every non-nav key carries our marker, whatever its name. Old-page cleanup waits on the P1 resetControl spike; until then Review lists the keys to delete by hand.
1 Completeness minor "Test this fix now" is offered for every fix, including PoE power-cycle, ATEM restore, dante-auto and pkill fixes. ADOPTED K7: Run this fix now only for start/reconnect kinds, never during a live service, always through the watchdog.
1 Completeness minor "HOLD 2s" and "WAIT 1 MIN" are hard-coded in the watchdog; the "config loaded" log line Apply waits for does not exist. ADOPTED K12 reads page_hold_ms and timing.fix_cooldown_seconds with defaults; started.json with the config sha (K10, K12) replaces the log line.
1 Completeness minor site.live_services is new and has no stated default, so the guard is empty on day one. ADOPTED K12: it defaults to auto_fix.windows and the P0 migration writes it into NA's config.
1 Completeness minor No builders for rtpmidi_start, ableton / ableton_restart or translate_restart; they fall to Custom with free-text names. ADOPTED K6 adds Start a Windows service (+task), Reopen Ableton with its last set, and Restart a Mac program with "also stop everything it started"; all 27 fixes map to builders.
1 Completeness minor solo-mode-agent.swift is "deployed unchanged" yet its paths are said to come from site.solo_dir. ADOPTED P5 generates the swift agent from a template with the Pro Tools parameters; K13 deploys remote-access/*.template.
1 Completeness minor Phase 0 removes hyperdeck_probe but TestHyperDeckProbe and the fix-kinds assertion are untouched, so Phase 0 cannot be green. ADOPTED K16 asserts CHECKS/FIX_KINDS equality against the exported FIX_KINDS (K12); D8 removes hyperdeck_probe and TestHyperDeckProbe/FakeDeck in P0.
1 Completeness minor The settings page and Companion's tablet view cannot link "to each other"; only one direction is possible. ADOPTED P0 writes the page address into the ALL key's note; the K15 cheat sheet carries the address.
1 Completeness minor The wireframe example "x32 not in Companion, rename to m32" contradicts the inventory. NOT A GAP A wireframe illustration only; K9 draws the connection list live from /api/connections and no canned example survives into the plan.
1 Simplicity major Engineering vocabulary (ids, hold_always, sp_state, --once, file names, raw shell) sits on nearly every primary surface. ADOPTED K15 wording rule and test over ui/strings.json, schema help and the Help pages: no underscored names, flags, extensions, Companion internals or launchd labels; raw strings only under Advanced.
1 Simplicity major "What runs where" is not the default view; Computers opens on dense cards. ADOPTED K8 Computers opens on the Map: one row per computer with a status word, what it does for the panel and the keys that turn red; click a row for the card.
1 Simplicity major Five gating concepts stack (per-tab unlock, editors list, name, typed APPLY, shared draft) plus a sandbox switch on two screens. SIMPLIFIED K2 one editing code plus a first name remembered by the browser; K3 one Editing toggle; a typed word only for Emergency and Stop (C3); one shared draft whose 409 names the person (K10); the training copy is deferred (C4).
1 Simplicity major Sixteen menu entries in five groups; the wizards for the user's own asks are buried last. ADOPTED K3 eight always-visible entries, Site setup and Fixes under a "Set up once" divider; K4 puts the job buttons on Overview.
1 Simplicity major Review & Apply exposes the pipeline (check names, a seven-step list, --once, raw diff, typed word). ADOPTED K10 Review is a plain-words diff with command edits in red and "what Apply will do"; K14 History rows are "Go back to this".
1 Simplicity major The key editor shows every field for every check type at full depth. ADOPTED K5 progressive fields with the two previews at the top; per-type raw fields and the 19 types under Advanced.
1 Simplicity minor Overview packs twelve blocks and puts Undo last apply on the home screen. ADOPTED K4 Overview: problems first, the deck, computers, jobs, status lines; Undo lives in History (K14).
1 Simplicity minor Four unexplained status marks on the checklists. ADOPTED K8 Setup lights are words: OK / Not done / Can't see from here / Not needed / Done by hand.
1 Simplicity minor The Companion screen is a page of internals (export stamps, grid, purple-after, trigger names). ADOPTED K9 Companion shows lights and live connections; address, variable names, nav, grid, purple-after and export stamps under Advanced.
1 Simplicity minor Three words for the same thing (key/tile/button, machine/computer/host, fix/builder/task); "Alias" is the first field on a card. ADOPTED K15 four words only (key, computer, fix, connection), enforced by the wording test.
1 Simplicity minor Help has no "start here"; the glossary is alphabetical. ADOPTED K15 Help opens on four cards; the glossary is grouped by action (hold fixes it / check the device / ask Chris).
1 Simplicity minor The read-aloud test is scheduled only in Phase 5. ADOPTED P1 exit criterion: a newcomer explains Overview, Computers and Help back in 20 minutes; D9 adds the same for Review & Apply, the Spotify section and one key editor at the end of P2.
1 Simplicity minor Routine code updates still need MacBook, scp, ssh install.sh; no "Update program" path on the page. SIMPLIFIED K13 "Install the staged folder" from the page as a one-shot launchd job; the staged folder still arrives by scp (no git credential on Ingest); pull_site.py is replaced by Export site bundle (K17).
1 Simplicity minor Schedules offers an editable "Alarms are armed" window; the Mute button does not say what it does. ADOPTED K9 one schedule and "alarms are always on" as one sentence everywhere; K4 button "Silence alarms until midnight".
1 Safety blocker The settings server is unauthenticated remote code execution on every private range with no CSRF defence; a draft's remote.cmd runs before Apply via --once and the probes. SIMPLIFIED K2 one editing code (a setting in the repo's config, lockout, origin check on writes); K6 command edits are red in Review and need the code again; K8 probes run a fixed set with quoted parameters; there is no --once preview. The address allow-list was cut (C3). Residual: one code over plain HTTP on the LAN.
1 Safety major Every Apply kickstarts the watchdog with no SIGTERM handler: a fix in flight is killed (HyperDeck left disabled), cooldowns, acks and tries lost, UI-only edits restart too. ADOPTED K12 SIGTERM handler lets the fix finish, fixes-running.json, state.json persists acks/tries/cooldowns, the HyperDeck re-enable sits in a finally; K10 waits for no fix and page-only changes skip the restart. The SIGHUP alternative was cut (C1).
1 Safety major Apply success and rollback hinge on sp_hb, which needs Companion: spurious rollbacks and a loop with no terminal state. ADOPTED K10 success = started.json newer than T with our sha, no traceback, alive after 10 s; rollback once; red notice until the next good Apply.
1 Safety major "Test this fix now" runs outside the watchdog, bypassing the recording guard, cooldown and logging; it can PoE-cycle. ADOPTED K7 test verb through the watchdog with the recording guard, cooldown and TEST logging; start/reconnect kinds only.
1 Safety major One draft.json, last PUT wins, no base version, no Apply lock. ADOPTED K10 one shared draft with base_sha and a 409 naming the person and the field; D1 adds a server-side apply.lock so a second Apply is refused outright.
1 Safety major Page rules incomplete: name-only safe_target, a renumbered page leaves an orphan, a missing variable is not a hard error. SIMPLIFIED K10 marker-only rule replaces the name rule; Companion-dependent checks are soft warnings so Apply still works with Companion down; old-page cleanup waits on the P1 resetControl spike (by hand until then).
1 Safety major The live-service guard is clock-only; add recording, stream and Resi signals. SUPERSEDED Round 2 Safety and Feasibility showed the stream and Resi signals are always true; K11 keeps the one real signal (a HyperDeck that follows the recording is recording) and C7 defers OBS to the spike.
1 Safety major An ATEM file upload replaces the switcher reference during a service; only "XML parses" is checked, no size cap, no entity guard. ADOPTED K9 upload or pick-newest with a diff and a tick; K11 refuses Apply while live; K10 writes config and xml together; K14 keeps dated copies; D3 caps uploads at 20 MB and refuses XML with a DOCTYPE or entities.
1 Safety minor Builders interpolate names and paths into shell strings without quoting. ADOPTED K6 shlex-quoted Mac parameters, the Windows regex, zsh -n / sh -n, byte-identical regenerate test; K16 metacharacter case.
1 Safety minor Secrets leak: the webhook in config, draft, history and diffs; module passwords from db.sqlite; fetch_page without includeSecrets=false. ADOPTED K2 webhook masked on every readable surface; K14 history bodies masked; K10 re-fetches with includeSecrets=false; C11 module id from /api/connections only, no DB rows.
1 Safety minor Page rollback re-runs the failing push; build hashes recorded before verification; "pages out of sync" is not persisted. ADOPTED K10 records a build only after a verified import; the K4 "Needs Chris" strip shows pages out of sync and failed-apply restored; red notice until the next good Apply.
1 Safety minor The typed word normalises a watchdog restart during a service; kickstart fails if the agent is not loaded. SUPERSEDED Its "queue the restart" fix was found unsafe by round 2 and 3 Safety and cut (C2); K11 refuses while live with an Emergency override that logs who and why; K10 bootstraps the plist if it is not loaded.
1 Feasibility major Apply success waits for a log line that does not exist and passes on the old process's heartbeat. ADOPTED K10 and K12 started.json {pid, started_at, config_sha} newer than T, no traceback, alive 10 s, polled 25 s (refined by round 3 Feasibility's log-rotation point).
1 Feasibility major "The whole test suite per site" ignores some sixty NA-specific assertions. ADOPTED P0 freezes tests/fixture-config.json; only test_sites_validate runs over every site (K16, K17).
1 Feasibility major Running run_once in the server process blocks 10-30 s with CoreMIDI and ssh and must scrape stdout. SUPERSEDED The --once preview was dropped after round 2 Safety (TCC prompt) and round 3 Feasibility (no-op flags); K10 validates with load_config on a temp file and make_page in memory only.
1 Feasibility minor The schema-sync test misses chained receivers and expression defaults; the glossary scan collects log strings. ADOPTED K16 receiver-aware AST walk, defaults compared where literal, the glossary test over fault words only.
1 Feasibility minor A restart loses acks, debounce, auto-fix tries and cooldowns; Review does not say so. ADOPTED K12 persists acks, tries and cooldowns in state.json keyed by check identity.
1 Feasibility minor build-hashes.json has no seed; a page-number change leaves the old page populated. SUPERSEDED P3 seeds the hashes from the live exports (round 3 Safety); the old-page reset depends on the P1 resetControl spike (round 2 Feasibility).
1 Feasibility minor Phase 0 flips install.sh to non-clobber before the UI exists, so an old-style config deploy silently does nothing. ADOPTED K13 and P2: install.sh stops overwriting site-owned files only once the page exists, with a diff notice.
1 Feasibility minor The page tools load config from their own folder, settings/ needs sys.path, the module version is not in /api/connections. SIMPLIFIED K10 make_page writes into the data dir and K13 deploys the tools there; C11 shows the version only if the spike finds it trivially; pull_site.py is replaced by Export site bundle (K17).
1 Feasibility minor Stop watchdog has no Start; the shared Spotify Automation permission is an assumption. SIMPLIFIED K11 allows Start while the watchdog is down and K10 bootstraps a missing agent; K9 shows one permission light; the P1 spike checks whether one Automation grant covers both agents.
2 Completeness major The live-now signals are hard-coded NA facts (the stream connection, the Resi encoder); add site.live_signals[]. CUT C7: the conn/var/value signal list was cut after round 3 Simplicity; K11 keeps the built-in "a HyperDeck is recording", derived from each site's own checks; OBS only if the spike finds the variable.
2 Completeness major The builder mapping is wrong for translate (open-only), captions (two agents) and dvs_ingest (chained ProdCom restart). ADOPTED K6 splits Start from Restart a Mac program, Restart helpers takes a list, dvs_ingest renders the ProdCom step inline; all 27 regenerate byte-identical.
2 Completeness minor "Sunday may run it by itself" bakes NA's day into strings.json. ADOPTED K15 wording test forbids day and site names in typed text; K9 builds the one schedule sentence from the windows.
2 Completeness minor No key can alarm on "a program is running on a Windows PC". ADOPTED K12 Windows remote checks batched per PC; K5 "Is a program running?" picks the type from the computer chosen.
2 Completeness minor spotify.name has a field but nothing reads it; labels keep the old playlist name. ADOPTED K12 reads spotify.{playlist,name,open_wait,play_wait}; K9 one link field plus name; the K6 Play builder derives the label.
2 Completeness minor Add or remove a HyperDeck leaves the kept decks' optional flag untouched. ADOPTED P4: the job asks per kept deck "may it have no disk?".
2 Completeness minor hosts.ssh is classed R, but the MIDI streams and pollers keep the old address after SIGHUP. CUT C1: no partial reload; every Apply restarts the watchdog.
2 Completeness minor The edit key written to the login keychain by install.sh over ssh may fail silently. SUPERSEDED The user chose (2026-10-13) to keep the code in config.json in the private repo (K2); nothing is written to the keychain.
2 Completeness minor The surfaces light has no data source. CUT C11: surfaces are not shown.
2 Completeness minor A changed SSH host key needs a Terminal step. ADOPTED K8 Setup tab: Forget the old login key.
2 Completeness minor The nav walk stops at the first 404; a new page never prompts a re-run. SIMPLIFIED C6 drops periodic detection; K9 shows "coloured on N of M pages" when the section opens, with Colour them now and Undo; the walk rule is a P1 spike item.
2 Completeness minor The ATEM file's origin folder is NA-specific screen text with no key. ADOPTED K12 atem.source_folder; K9 "pick newest from folder".
2 Completeness minor solo-mode-agent.swift's menu titles and bundle id are literals. ADOPTED P5 templates the swift agent with the Pro Tools parameters.
2 Completeness minor Add a MIDI link only picks an existing peer; no new-peer entry or "also watch" options. SIMPLIFIED P4 adds a typed fallback for session and peer; the extra Bonjour and Companion-connection tick boxes are not in the decisions.
2 Simplicity major History (and Undo) is reachable only through the unsaved-changes badge. ADOPTED K3 History is always in the menu; K14 Go back and Undo live there.
2 Simplicity major Editing needs an IP allow-list, the key and a name; a device on DHCP is refused without explanation. ADOPTED K2 code plus name only; no address allow-list (C3).
2 Simplicity major Four schedule notions (live, auto-fix's own windows, alarms armed, mute) plus timing on one screen. ADOPTED K9 one schedule, alarms always on, timing numbers under Advanced; K4 Silence alarms until midnight.
2 Simplicity major Two fix pickers per key; the preview answers only what the key says, not what holding does. ADOPTED K5 "HOLDING 2 s WILL" preview, one "When held" section with the builder inline, "also when the key is green".
2 Simplicity major Two edit homes each for Dante devices, the playlist, page numbers and HyperDecks. ADOPTED K9 the DEVICE LIST is the only home of the DANTE key's devices and Spotify has one link field; K5 the page number lives in the Keys tab header.
2 Simplicity major Half the keys point at gear that is not a computer and has no home. ADOPTED K8 Gear filter (address, kind, where, watched by, light); K15 "Which computer or device is that" card; C13.
2 Simplicity major The full-inventory probe shows vendor noise and counts it on Overview. CUT C5: the diff, ignore lists and Add/Ignore were cut; a collapsed raw list on the card (K8); no counts on Overview.
2 Simplicity major A change queued during a service is invisible; nothing says it is pending. CUT C2: no queue; K11 refuses while live, the draft stays saved and Review says when the service ends.
2 Simplicity major "Edit key", "SSH key", "nav keys" and "connection" collide with the three-word rule. ADOPTED K15: "editing code", "login key", "page-number buttons"; connection becomes the fourth defined word.
2 Simplicity major The settings page needs its own Spotify permission, a Test button and an extra Allow step. ADOPTED K9 one permission light; K7 Run now goes through the watchdog so the page never drives Spotify; the P1 spike checks the grant.
2 Simplicity minor Map cells are walls of text. ADOPTED K4 and K8: one status word per row, what it does, the keys that turn red.
2 Simplicity minor Six different "test" buttons with unclear effects. ADOPTED K9 read-only buttons say "Check ..." ; K7 "Run this fix now" is the one that acts.
2 Simplicity minor Update the program and the campus export sit on the normal Site screen; pull_site.py is a terminal step. SIMPLIFIED C12 Install the staged folder and Export site bundle sit under Site setup (set up once); the K17 export replaces pull_site.py; scp remains (K13).
2 Simplicity minor Per-editor drafts with take-over and discard add a multi-user merge concept. ADOPTED K10 one shared draft; the 409 names who changed what.
2 Simplicity minor The Keys grid draws three overlays; "+ New key" offers ALL, ALARMS and NET. ADOPTED K5 draws the 8x4 deck; fixed chips for ALL / ALARMS / NET.
2 Simplicity minor Overview carries admin wizards, gear, a log tail and duplicate lights. ADOPTED K4 problems, the deck, computers, three job buttons plus More jobs, status lines (round 3 Simplicity set the three).
2 Safety major kickstart SIGKILLs after launchd's 5 s; the 30 s join exceeds it; check-then-act race on fixes-running.json. ADOPTED K12 ExitTimeOut derived at install, join bounded, new fixes refused once SIGTERM arrives (RESTARTING SOON), HyperDeck re-enable in a finally; K10 waits first.
2 Safety major The stream and Resi signals are always true; a stale live-now.json is undefined. ADOPTED K11 HyperDeck recording only, live-now.json timestamped, older than 30 s = schedule only and said in Review; OBS deferred (C7).
2 Safety major Writing config during a service and queuing the restart: KeepAlive can relaunch on the unverified config mid-service. CUT C2: no queue; K11 refuses while live so disk and process never diverge.
2 Safety major The R class includes keys read only at start (pollers, ServiceMode, run()). CUT C1.
2 Safety major Code deploys validate only JSON; no code rollback; the page-driven install kills its own agent. ADOPTED K13 validates with the new code before bootout, keeps prev/ with --rollback, restarts the watchdog first and settings last, as a one-shot launchd job.
2 Safety major Reads (logs, the fleet inventory, history bodies, generated scripts) are open to every private address including Wi-Fi. SIMPLIFIED K2 deliberately keeps reads open so teaching needs no code (round 3 Simplicity); K1 narrows the bind to hosts.ingest.networks; the webhook is masked. Residual: the fleet map is visible to anyone on the LAN.
2 Safety major test <fix> carries no key or check context and could toggle the wrong deck or peer. ADOPTED K7 test <key> <fix> <identity> <time>, resolved exactly as a hold.
2 Safety minor atem-reference.xml is outside the transaction, replaced in place and not restored on rollback. ADOPTED K10 config and xml written together (.tmp + os.replace), hash in History, rollback restores both; K14 Go back names the file.
2 Safety minor Remote command strings are not syntax-checked; Windows ! and ^ are allowed. ADOPTED K6 sh -n / zsh -n; the Windows regex excludes exclamation, caret, ampersand, pipe, percent and quotes; Custom under Advanced in red.
2 Safety minor Two writers for dante-map.json; sp_cmd read-modify-write from the page can clobber a hold. SIMPLIFIED K12 resurvey verb through the watchdog; C10 keeps page requests on the command variable like holds. Residual: a request can race a deck hold on sp_cmd.
2 Safety minor Hard validation needs Companion up; resetControl to blank a cell is untested. ADOPTED K10 soft live warnings; P1 spike "resetControl blanks a cell?" with a by-hand fallback.
2 Safety minor Stop, Revert nav, Prune, Re-import and Install have no confirmation; Mute sits under the guard. ADOPTED C3 one dialog style stating the consequence, a typed word for Stop and Emergency; K11 Silence alarms is never guarded.
2 Safety minor The --once preview triggers a TCC prompt; the ATEM snapshot opens a second client with no guard. SIMPLIFIED There is no --once preview in K10; "Check the switcher now" is in K11's guarded list. The ATEM client limit is not noted beside the button.
2 Safety minor wmic is gone on Windows 11 24H2; the edit key is made over ssh; Pull needs a stored git credential. ADOPTED K2 the code is a config setting in the repo; K13 no git credential and no Pull button; D4 Windows probes use Get-CimInstance and schtasks /Query /FO CSV, never wmic.
2 Safety minor The nav walk stops at a gap; hand-moved pages are not detected. SUPERSEDED Round 3 Feasibility put the walk rule in the P1 spike; periodic detection was cut (C6) in favour of the K9 count plus Colour them now.
2 Feasibility major The R apply class cannot work: start_pollers and Engine capture cfg once. CUT C1.
2 Feasibility major The 30 s graceful stop exceeds launchd's 5 s exit timeout; fix threads are anonymous daemon threads. ADOPTED K12 ExitTimeOut derived at install, a thread registry behind fixes-running.json, join at most ExitTimeOut-5; K16 SIGTERM mid-fix test.
2 Feasibility major The live signals are mostly always-true; use the OBS streaming variable. SIMPLIFIED K11 HyperDeck recording only; C7 adds OBS as a built-in kind only if the P1 spike finds the variable.
2 Feasibility major The test verb with an empty chk raises KeyError for HyperDeck and MIDI fixes. ADOPTED K7 verb carries key and identity; K12 identity per check type.
2 Feasibility major Phase 2 allows hold-ability edits a phase before pages can be re-imported. ADOPTED P2 refuses layout changes until P3 ("available after the layout step").
2 Feasibility major Phase 0 "byte-identical, no re-import" contradicts its note changes; split into 0a/0b. SIMPLIFIED K16 roundtrip strips notes and P0 is "deck identical apart from key notes"; P0 stays one phase of about 1.5 days (no 0a/0b split).
2 Feasibility minor The marker rule misses the two static jump keys. ADOPTED K10 every non-nav key must carry our marker; P0 adds the markers in make_page.
2 Feasibility minor /api/program/install kills the agent that is running it. ADOPTED K13 one-shot launchd job, settings agent last, AbandonProcessGroup.
2 Feasibility minor "Pull from repo" does not fit the real setup (no clone, the alias points the other way). ADOPTED K13 drops Pull; scp staging plus Install the staged folder.
2 Feasibility minor install.sh cannot write the keychain over ssh. ADOPTED K2.
2 Feasibility minor A receiver-less AST walk will never pass against this code. ADOPTED K16 receiver-aware walk with a UI-only allowlist.
2 Feasibility minor moduleVersionId, resetControl and tRPC variable creation are unverified Companion facts. ADOPTED P1 spike with recorded answers and fallbacks; C11 version only if trivial.
2 Feasibility minor Acks persisted by check index re-attach to the wrong check after an edit. SUPERSEDED Round 3 Feasibility showed type-plus-sub keys collide too; K12 keys state by a per-type identity and drops it for keys whose checks changed.
2 Feasibility minor Two Automation grants are assumed; one very likely covers both agents. ADOPTED P1 spike; K9 one light.
3 Completeness major The server binds only hosts.ingest.networks; the template has no ingest record; an empty list is undefined. ADOPTED K1 always 127.0.0.1 plus every private-address interface when the list is empty (with a grey notice); K17 the template has an ingest self record with networks [].
3 Completeness major install.sh's deploy list omits the remote-access templates and companion-objects. ADOPTED K13 deploys both; K17 nav_templates/.json.
3 Completeness major Windows batching with %ERRORLEVEL% expands before the command runs, so every check reports OK. ADOPTED K12 generates per-command conditional echo pairs (SPn=0 on success, SPn=1 otherwise), never a bare %ERRORLEVEL%, verified once on the Lighting PC.
3 Completeness minor No expected module per check type; a HyperDeck check on a generic TCP connection is silently wrong. ADOPTED K12 EXPECTED_MODULE filters the pickers and warns on mismatch; it lives in the K16 registry.
3 Completeness minor restart_page is optional but the Keys screen always has the tab and chips. ADOPTED K9 "this campus has no Restart page" switch (built in P3).
3 Completeness minor Windows cannot cross midnight. ADOPTED K18.
3 Completeness minor Add a MIDI link has no typed fallback when the Mac is off. ADOPTED P4.
3 Completeness minor The Ingest self record can be named by ssh fixes, remote checks and midi_peer (a runtime KeyError). ADOPTED K12 host validated (exists, has ssh, not self); builders switch to local for the self record.
3 Completeness minor The OBS streaming variable is seeded without verification. ADOPTED P1 spike reads it on and off air; C7 adds the kind only then.
3 Simplicity major About 25 destinations; Help doubles as the admin console. SIMPLIFIED K3 eight entries, the two set-up-once screens under a divider and readable by anyone (one rule: the code changes things); C12 admin actions sit there, Help keeps the recipes. Residual: a volunteer can open Site setup and see it.
3 Simplicity major Schedules asks for a Companion connection, variable and value to define a live signal. CUT C7.
3 Simplicity major "+ Add a check" lists 19 implementation types. ADOPTED K5 asks a question first; the 19 types sit under Advanced.
3 Simplicity major The walkthrough needs the editing code (Logs, History, the training copy). ADOPTED K2 reads are open; K15 the walkthrough needs no code; C4 defers the training copy.
3 Simplicity major The Overview problem line mixes room problems with admin notices. ADOPTED K4 room problems versus a grey "Needs Chris" strip.
3 Simplicity minor Three status vocabularies across Overview, Setup and Gear. ADOPTED D7 one legend for the whole page (OK / Problem + reason / Can't see from here / Not needed / Done by hand), printed once under the top bar's "?".
3 Simplicity minor The plan's own wireframes contain forbidden tokens; extend the wording test to the rendered DOM. SIMPLIFIED C8 keeps the test over strings, help and Help pages; K5 shows "Page 19" and chips. Residual: live Companion labels can appear raw in table cells.
3 Simplicity minor "Alarms are always on" next to a Pause button. ADOPTED K9 one sentence everywhere; K4 "Silence alarms until midnight".
3 Simplicity minor The page is "Status Panel Settings" and one menu entry is "Settings". ADOPTED K3 "Site setup".
3 Simplicity minor The Fixes catalogue is a second mental model beside "When held". ADOPTED K5 the builder sits inline under "When held"; K3 the Fixes catalogue sits under the "Set up once" divider.
3 Simplicity minor The Map lacks "keys that turn red"; Gear is a separate tab. ADOPTED K8 Map rows carry the keys that turn red; Gear is a filter (C13).
3 Simplicity minor ssh-keygen for the panel's own key is still a Terminal step. ADOPTED K8 Setup tab: Make the login key.
3 Simplicity minor Tuning numbers on the main screens. ADOPTED K9 every number under Advanced with its default beside it.
3 Simplicity minor Add a MIDI link is hidden under More jobs. ADOPTED K4 three job buttons.
3 Simplicity minor The "Not on the deck" tray is unexplained. ADOPTED K5 tray labelled "Shown inside the RF group (no key of their own)".
3 Simplicity minor Watchdog controls in two places, two magic words, second confirmations. ADOPTED C3 one confirmation style; D6 Start / Stop / Restart the watchdog live on Overview only, Stop behind the typed word.
3 Safety blocker No loopback listener, so /edit-key is unreachable and nobody can ever switch Editing on. SUPERSEDED There is no /edit-key page: the code is a config setting in the repo (K2, the user's choice 2026-10-13); K1 still binds 127.0.0.1 always.
3 Safety blocker setsid does not exist on macOS; the installer dies with the agent's process group half-way. ADOPTED K13 one-shot launchd job, AbandonProcessGroup on the settings plist, the watchdog bootstrapped first.
3 Safety major safe_target's name shortcut silently wipes hand-added keys. ADOPTED K10 marker-only rule; Review lists foreign keys by position; Apply refuses.
3 Safety major Raw commands behind one plaintext reusable code; no lockout; the allow-list off by default. SIMPLIFIED K2 lockout (5 tries, 10 min) and origin check; K6 Custom commands are red and re-ask the code. Admin code, RUN word and allow-list were cut (C3). Residual: one code on a LAN is a remote-command surface.
3 Safety major Probes interpolate names and paths from config into ssh lines. ADOPTED K8 quoted and regex-checked parameters; K16 metacharacter test proving the ssh line is inert.
3 Safety major A stale stop flag disables every fix forever. CUT C10: no stop flag; the SIGTERM handler plus the fixes-running.json wait cover it.
3 Safety major SIGHUP sent to old code kills the watchdog. CUT C1; K12 keeps the "(code X)" log line for install and rollback tracking.
3 Safety major The queued job has no hysteresis and no base_sha recheck; Start should be exempt from Emergency. CUT C2; K11 allows Start while the watchdog is down.
3 Safety minor A 409 merges silently. ADOPTED K10 " changed this a moment ago - your change to was not kept", with the field re-offered.
3 Safety minor history, draft and pending copies hold the unmasked webhook. ADOPTED D2 every file the settings agent writes in the data dir (draft, history, cache, log, code fallback) is 0600; K14 serves bodies masked; pending.json is gone (C2).
3 Safety minor Seeded build hashes are "assumed current". ADOPTED P3 seeds them from the live exports.
3 Safety minor A PC whose DefaultShell is not cmd returns garbage silently. ADOPTED K12 light plus refusal of cmd-batched checks for that PC (setting DefaultShell stays a by-hand step).
3 Safety minor Holds queued during the restart gap fire on the new process. ADOPTED K12 sp_cmd cleared once at start and logged.
3 Feasibility major cmd.exe %ERRORLEVEL% batching always reports OK. ADOPTED K12 (the same fix as round 3 Completeness).
3 Feasibility major A (key, type, sub) identity collides on the MIDI key; the test verb cannot pick a peer. ADOPTED K12 per-type identity (midi_peer = host, session, peer; remote = host, cmd; ...); K7 the verb carries it.
3 Feasibility major test_apply_class contradicts the R rows. CUT C1.
3 Feasibility major The LAN-only bind leaves /edit-key unreachable; interface names cannot be bound, only addresses. ADOPTED K1 and D5: bind by address (127.0.0.1 always, plus each LAN interface's IPv4), re-checked every minute and rebound on change.
3 Feasibility major install.sh copies atem-reference.xml (and nav_locations, built pages) over the data dir. ADOPTED K13 never overwrites site-owned files without --config, --site or --force-site-files, with a diff notice.
3 Feasibility major Phase 0b and Phase 2 bundle too much to test as single steps. SIMPLIFIED C1, C2, C4, C7 and C10 remove most of the load; P0 and P2 stay single phases (about 1.5 and 4 days).
3 Feasibility minor --skip-spotify is a no-op; the Preview shows unjudged keys green. SIMPLIFIED There is no preview run in K10, so there are no flags to get wrong.
3 Feasibility minor A then chain breaks byte-identical regeneration for dvs_ingest; restart_ok is misstated. ADOPTED C9 no chain executor; K6 renders the second step inline; byte-identical for all 27.
3 Feasibility minor ServiceMode._save clobbers persisted acks; live-now.json written every tick. ADOPTED K12 one atomic state.json writer that ServiceMode merges into; K11 live-now on change or every 5 s.
3 Feasibility minor ExitTimeOut 40 s is shorter than fixes legitimately run. ADOPTED K12 derived at install (max fix timeout + verify + 10, floor 60); re-enable in a finally.
3 Feasibility minor The page_number_N_name walk is unproven. ADOPTED P1 spike item; C6.
3 Feasibility minor The self record is referenceable by ssh fixes. ADOPTED K12 (the same fix as round 3 Completeness).
3 Feasibility minor Tailing a rotating log can miss the lines and trigger a false rollback. ADOPTED K10 and K12 started.json; the log is read only for tracebacks.

Disposition totals over the 157 findings: ADOPTED 113, SIMPLIFIED 20, CUT 13, SUPERSEDED 10, NOT A GAP 1.

Deliberate cuts and why

C1, partial reload. The watchdog will not re-read parts of its settings on a signal; every Apply restarts it, so the deck goes purple for about five seconds, outside services. What was lost is the comfort of changing a timing number without a restart. It is acceptable because the reload could never rebuild the pollers and the service mode (round 2 Feasibility), a signal sent to old code kills the process, and a whole family of apply-class tests disappears.

C2, queued apply. A change made during a live service is refused rather than saved for later; the draft stays and a person comes back to press Apply. What was lost is "it will go live by itself at 13:30". It is acceptable because the queue meant the file on disk disagreeing with the running process for hours, a job restarting the watchdog unattended in the gap between two services, and a pending state nobody could see; the Emergency override covers a real need.

C3, extra gates. There is no admin code, no address allow-list, no RUN re-typing and no second confirmations; one code, one dialog style, and a typed word only for Emergency during a service and for stopping the watchdog. What was lost is defence in depth around command edits. It is acceptable because an IP list is invisible to the person it blocks and one code is what volunteers can actually hold; raw command edits still turn red in Review and ask for the code again.

C4, training copy. The sandbox where a trainee could press Apply against a copy is deferred. What was lost is a risk-free place to practise editing. It is acceptable because reads are open to the LAN, so the whole walkthrough works with Editing off, and the copy can be added in a day once the page is driven by the config.

C5, full-inventory diff. Computers are not scanned for every agent, login item, port and service and diffed against the list, and there are no vendor ignore lists or Add/Ignore buttons. What was lost is an automatic "something new appeared on this computer". It is acceptable because on a real Mac or PC the list is Google, Adobe and Microsoft noise; the raw "Everything found on this computer" list on the card still shows it all when somebody looks.

C6, page-button probe. Nothing polls Companion to notice pages that were moved or added by hand. What was lost is an Overview line that appears by itself. It is acceptable because the count "coloured on N of M pages" when the Companion section opens, with Colour them now and Undo, covers the step the user keeps forgetting, and the background walk rule it would need is unproven.

C7, live-signal triples. The Schedules screen does not let anyone define "live" as a Companion connection, variable and value. What was lost is OBS or Resi counting as live. It is acceptable because only "a HyperDeck is recording" was ever a real signal, the triple was Companion jargon on a volunteer screen, and OBS can be added as a built-in kind once the spike finds its variable.

C8, DOM wording test. The wording test runs over the typed strings, schema help and Help pages, not over a rendered page. What was lost is catching jargon in table cells built from live data. It is acceptable because typed text is where the words live; a Companion label is a name, not a word; and a headless render test was heavy for the gain.

C9, chain executor. The watchdog gets no "then also run this fix" step. What was lost is a generic second-step mechanism. It is acceptable because only dvs_ingest needs it, the builder renders the ProdCom step inline, regeneration stays byte-identical and no new code path exists in the watchdog.

C10, stop flag and command folder. There is no stop.json and no commands/ folder for the watchdog to poll. What was lost is a file-based command channel from the page. It is acceptable because a stale flag could silently disable every fix; the SIGTERM handler and the fixes-running wait cover restarts, and page requests travel on the same command variable holds already use.

C11, surfaces and module versions. The page shows no surfaces table, no Satellite labels and no module version read from Companion's database. What was lost is "which Stream Decks are plugged in" and a version column. It is acceptable because the module id from /api/connections is what the expected-module check needs, the database read would expose module passwords, and a version appears only if the spike finds it trivially.

C12, admin pages. "Where things are" stays as one generated page, and "Things only Chris does" is folded into Site setup under the "Set up once" divider rather than living in Help. What was lost is a separate admin section in Help. It is acceptable because Help stays read-only and the admin actions sit in one labelled place that only the editing code can act on, which is the volunteer/admin split round 3 asked for.

C13, gear as a filter. Gear has no data model of its own beyond location and notes; it is a filter of the Computers list derived from the checks. What was lost is a separate gear register. It is acceptable because every device is already described by the check that watches it, and one list is one thing to teach.

C14, preview run. Review has no "Preview the deck with these settings" button that runs the watchdog once against the draft. What was lost is a dry run of the checks before Apply. It is acceptable because such a run could not judge the ATEM, Dante-map, Spotify and remote checks and would have shown those keys green (round 3 Feasibility), it would have run CoreMIDI and ssh from the settings process, and the key editor's "WHAT THIS KEY WILL SAY" line plus the in-memory page build at validate already answer what the key will show.

Residual risks

  • Every Apply restarts the watchdog (C1): about five seconds of purple deck, and holds pressed in that gap are dropped. Mitigation: Apply is refused during live services (K11), page-only changes skip the restart (K10), Review says it will happen, the SIGTERM handler lets a running fix finish (K12) and sp_cmd is cleared and logged at start.
  • No queue (C2): a change wanted during a service waits for a person to come back and press Apply. Mitigation: the draft is saved, Review says when the service ends, the Emergency override (typed LIVE plus a reason, logged) exists, and Q2 pins the windows.
  • Raw Custom commands (K6) are a remote-command surface on seven computers, guarded by one code over plain HTTP on a LAN. Mitigation: LAN-only binding narrowed by hosts.ingest.networks (K1), origin check and lockout (K2), the code asked again and the command shown red in Review (K6), zsh -n / sh -n, History naming the editor (K14), and the private repo's access list deciding who holds the code.
  • Probes are parameterised, not fixed (K8): names and paths from the config reach ssh lines. Mitigation: shlex-quoted and regex-checked parameters, a fixed command set, the metacharacter test (K16), never run during a fix, 6 s timeout, never wake a PC.
  • Windows DefaultShell (K12): the batched checks assume cmd.exe. Mitigation: a light on the PC's card and refusal of cmd-batched checks when the shell is not cmd; verified once on the Lighting PC; setting DefaultShell stays a by-hand system change for the user.
  • Python 3.13 removes telnetlib, which netgear.py uses (K12 self-check for 3.9-3.12). Mitigation: the self-check light; a newer macOS at another campus would lose the Dante map and PoE cycling until telnetlib is replaced, which is not in this plan.
  • Companion tRPC spike unknowns (P1): custom-variable creation, trigger import, resetControl blanking a cell, the OBS variable, the page walk, whether one Automation grant covers both agents. Mitigation: recorded answers before any Apply step depends on them, with fallbacks (by hand, "unknown", schedule-only).
  • Reads are open on the LAN (K2): logs, history bodies and the fleet map are visible to anyone who joins the network. Mitigation: the webhook is masked everywhere, no secret is in History, the bind is narrowed to the named LAN interfaces (K1).
  • The only live signal is a recording HyperDeck (K11, C7), and a stale live-now.json falls back to the schedule. Mitigation: Review says "schedule only" when stale, Q2 fills the windows, OBS follows the spike.
  • Page requests share sp_cmd with deck holds (C10): a test or resurvey request can race a hold. Mitigation: it is the same read-then-clear path holds use today, Run now is rare and never live (K7), and the watchdog logs what it drops.
  • The settings server binds addresses, not interface names (K1, D5): a DHCP change on the LAN port is picked up within a minute by the rebind check. Mitigation: the Ingest addresses are static today and a grey notice shows when the LAN list is empty.

Still open (needs the user)

  • Q1 Answered 2026-10-13: the editing code lives in config.json in the private repo; everyone with the repo has it.
  • Q2 Still open: the page refuses to apply changes while a service is on (Sunday 06:30-13:30 today, plus any time a deck is recording). Are there other times, such as a midweek service or a rehearsal, when it should refuse too?
  • Q3 Answered 2026-10-13: Apollo Beach, Woonsocket and Southcoast, each with a Mac Studio already running Companion.
  • Q4 Answered 2026-10-13: the unwatched connections stay unwatched.