DishTTY 1.0.2 -> 1.0.4: CPU fix, endpoint discovery, CloudKit inheritance
Host rdmsm4x · agent claude@rdmsm4x/121ebdd7 (headless) · tasks ISSUE-20260928-11, FEAT-20260928-04 · project apps/DishTTY · 2026-09-28 19:55–20:55 EDT
Started 2026-09-28 19:55:28 EDT · finished 2026-09-28 ~20:55
EDT (America/New_York). Step log: PROGRESS.md.
Headline
- The CPU fix is on every DishTTY Mac. Idle CPU dropped from 55 % to 2.2–2.5 %, measured over 300 s windows with the dashboard open and a live dish.
- Latest shipped build is 1.0.4 (5): Developer ID, notarized, stapled, universal2, on all 5 DishTTY hosts.
- Endpoint autodiscovery and the "Connect to your Starlink" window shipped (Task B).
- CloudKit works in the Development environment. A second process read back a terminal profile that a first process wrote. Production needs one click from you (schema deploy, below).
What shipped
| Version | Commit | Notarization | What |
|---|---|---|---|
| 1.0.2 (3) | 10699c3 |
83644e15-870b-480b-990a-e67ac4dea8cc |
CPU fix (bd2a1d0), dish-switch race fix, deflaked test |
| 1.0.3 (4) | 3d4cf25, a3df618,
b48448f |
a97f0ff7-8028-4668-af91-4003cf17944a |
Endpoint autodiscovery and the Connect window; fixes a transport crash |
| 1.0.4 (5) | 1c5d605, 16724f5 |
953fa70c-ff4e-4e0f-821f-54cccceba929 |
CloudKit settings inheritance over ECSCloudKit. It is compiled in but has no entitlement until the schema is deployed. |
- Docs are in
ffc516eandad373e7. - Every commit is pushed to
fleet,backupandorigin. - Release staging is in
release-1.0.{2,3,4}-*beside this file. Each directory holds the zip,SHA256SUMSandnotarize.log. The 1.0.4 zip sha256 is3786fa4b….
CPU
before and after (ps -o time=, 300 s, dashboard open, live
dish via relay)
| Host | 1.0.1 (before) | 1.0.2 | 1.0.3 | 1.0.4 |
|---|---|---|---|---|
| rdmsm4x | 112 CPU-s / 202 s = 55 % (handoff) | 11.28 CPU-s = 3.8 % | 7.39 = 2.5 % | 6.82 = 2.3 % |
| rdmbair15m5 | 30–50 % (your report) | 7.09 = 2.4 % | 6.93 = 2.3 % | 6.46 = 2.2 % |
The poller was live on both hosts: the newest SQLite sample was 5–7 s old at each check.
Fleet
install table (1.0.4 (5), lipo = x86_64 arm64,
spctl = Notarized Developer ID)
| Host | State now | Notes |
|---|---|---|
| rdmsm4x | running (pid 77377) | no Connect window: the active relay profile answered |
| rdmbair15m5 | running (pid 43714) | same. claude@rdmbair15m5/085ed326 had quit my first
canary, thinking you wanted it off; I explained on the bus and ticket
and relaunched it |
| rdmbair13m5 | installed, stopped (as it was) | launch-checked for 20 s. It is on a Starlink LAN:
192.168.100.1:9200 answered as a dish and live samples were
recorded. It went unreachable over SSH from about 20:17 to 20:36, so it
skipped 1.0.3 (1.0.2 → 1.0.4) |
| rdmpw3265m, rdmpw3275m | installed, not launched (prompt rule) | codesign ok, x86_64 slice minos 15.0,
macOS 26.7.1 |
| jdmbair13m5 | not added | never had DishTTY |
Old copies were moved (not deleted) to
~/Archive/app-retired-20260928/DishTTY-<ver>-<build>-devid-20260928-<host>/
on each host.
Tests
swift test rc 0 at 16724f5: Core
355 (1 skipped: an env-gated live probe) · UI
135 · E2E 154 · swift-testing
20. It was 315 / 125 / 154 / 20 at bd2a1d0.
- Two timing-flaky tests failed once each in loaded full runs and passed alone. I fixed them and three siblings to poll to a deadline instead of sleeping for a fixed time.
- One real crash. A full run crashed with
CONTINUATION MISUSEin the NWConnection connect wait (a double resume during rapid dish switches). It is fixed with a resume-once gate. I couldn't reproduce it in 6 isolated runs, so the fix is by construction; there have been 5 full runs since with no recurrence. - XCPE has the same code. That fix is ticketed as ISSUE-20260928-17.
Task B: endpoint autodiscovery (FEAT-20260928-04, DEC-20260928-DT2), shipped in 1.0.3
Discovery order.
DishEndpointDiscoveryprobes, concurrently: the active profile, then the other saved profiles, then192.168.100.1:9200. It picks the earliest one in that order that is a dish.What counts as a dish. Only an endpoint where
get_statusdecodes with a device id. The three outcomes are dish, "something answered but it is not a Starlink dish", and unreachable (with the reason).Live probe results:
Endpoint Result 100.103.136.116:9200dish ut01000000-00000000-001b340b(hp1_proto0), 0.08 sstarlink18a.dataroo.net:9200same dish, 0.19 s 192.168.100.1from rdmsm4xrefused a bad DNS name unreachable The Connect window opens at most once per launch, and only when nothing answers. It offers Test connection, Use this dish (hostnames are stored as typed), Retry automatic discovery and Use demo data. Afterwards it can be reopened from the popover's Offline banner, the app menu, Settings and the sidebar.
Not observed live: every fleet host reaches a dish, so I never saw the Connect window on a real Mac. Unit tests cover the prompt path.
Your profiles were not rewritten. rdmsm4x and rdmbair15m5 still use
100.103.136.116. You can switch them tostarlink18a.dataroo.netin Settings → Edit; the name is kept as typed and tracks the relay if its address changes.
Task C: CloudKit (DEC-20260928-DT3)
- Agent-side provisioning is done:
- App ID
com.eastcoastscience.DishTTYis registered (5B9R22SVHV, UNIVERSAL) with iCloud. - Xcode automatic provisioning (Aqua session) created the
container
iCloud.com.eastcoastscience.DishTTYwithout asking for an Apple ID. - Development profile
223aeed9…(expires 2027-09-28). - Developer ID profile "DishTTY Direct Developer ID"
27c2d13d…: Production only; expires 2027-02-01 together with the Developer ID certificate.
- App ID
- The old sync code could not have worked. Its query
failed on the real container with "Field 'recordName' is not marked
queryable", and it swallowed upload errors. Profiles now sync through
~/dev/lib/ECSCloudKit, as the baseline requires: custom zone, change tokens, compare-and-swap, newest edit wins. - Proven in Development:
- Process A (pid 16016) pushed
starlink18a.dataroo.net. - A fresh process B (pid 16282, empty store) pulled it at 2026-09-29 00:33Z.
- Deleting it reached process B as well.
- Evidence:
taskC/evidence/.
- Process A (pid 16016) pushed
- Production:
- A Developer ID build with the embedded profile passes AMFI, then
fails with
CKError 12: Cannot create new type DishTerminal in production schema. - I seeded all 17 fields in the Development schema so one deploy is complete.
- 1.0.4 therefore ships without the entitlement. To
enable it, run
DISHTTY_CLOUDKIT_SIGNING=1 ./build.sh --sign-notarize.
- A Developer ID build with the embedded profile passes AMFI, then
fails with
- Inheritance on a new Mac: discovery waits up to 8 s for the first iCloud pull and tries inherited terminals before the first-launch placeholder.
- Telemetry upload to iCloud is now opt-in. With the entitlement it would have written about 100 records a minute per Mac.
- Migrate now or later: I migrated now, so the schema you deploy is the final one.
Blocked on you (one line each)
- CloudKit Console →
iCloud.com.eastcoastscience.DishTTY→ Deploy Schema Changes to Production (DEC-20260928-09; I recommend deploying now). After that an agent flipsDISHTTY_CLOUDKIT_SIGNING=1and proves a round trip between two Macs. - The status page is behind Cloudflare Access like the rest of dev.ecs0.net. Open it while logged in; no agent token can fetch it through the public URL.
Documentation and links
- Status page, dev.ecs0.net first (SA-28): https://dev.ecs0.net/products/dishtty/dishtty-status-v1.0.4-20260928-2047.html
- The origin gateway serves it with HTTP 200 and
meta version 1.0.4 (5), byte-identical to the committed copy. - The public URL needs a Cloudflare Access login.
- The committed copy is
docs/dishtty-status-v1.0.4-20260928-2047.html. dishTTY.htmlis now generated byscripts/gen_status_html.py.
- The origin gateway serves it with HTTP 200 and
- Updated: SESSION-STATE (new RESUME HERE), STATUS, ISSUES (ISS-20260912-05 resolved; new ISS-20260928-01 to -04), PRD (R2a/R2b), CLAUDE.md and AGENTS.md (counts plus 6 traps), RELEASE_NOTES (1.0.2–1.0.4), DECISIONS.md (new: DT2, DT3).
Tickets
| Ticket | State |
|---|---|
| ISSUE-20260928-11 | resolved |
| ISSUE-20260927-48 | resolved |
| FEAT-20260928-04 | resolved |
| DEC-20260928-09 | needs-decision (you) |
| ISSUE-20260928-17 | open (XCPE transport fix) |
| FEAT-20260928-05 | open (preferences record) |
Other things noticed
- rdmbair15m5, about 20:37: Raycast Backend at 97 % CPU, tyrelld at 22 %. The fleet-health run owns that host check.
- CloudKit Development environment: the probe's test records were deleted. The seeded "Primary Dish (Local)" record remains; it is the same placeholder every Mac has.