Skip to content

[Part 2b] feat(commands): tell the user when a stop, start or calibration does not reach the transmitter - #10

Open
ETolboom wants to merge 4 commits into
trioneer-dev:mainfrom
ETolboom:feat/command-delivery-notices
Open

ETolboom wants to merge 4 commits into
trioneer-dev:mainfrom
ETolboom:feat/command-delivery-notices

Conversation

@ETolboom

@ETolboom ETolboom commented Oct 3, 2026

Copy link
Copy Markdown

Depends on #2 (Part 2: CommandQueue). Like the rest of the stack this targets main, so until Parts 0–2 merge the diff also shows their commits; this PR's own change is the top commit.

Why

A tester pressed End Sensor on a transmitter with a near-dead battery. The stop sat in the queue while every connection failed, the settings screen didn't change, so they pressed it again (three times in the log). The session was never stopped, and nothing told them.

What

  • enqueue(_:notifyIfUndelivered:): commands can be flagged when the user is relying on them. Stop, start and calibration are flagged. The flag and the queue time are persisted with the queued command; entries saved by older builds read back as unflagged.
  • Transmitter not responding (time-sensitive alert): a flagged command still queued after 15 minutes (three missed cycles). It is still sent if the transmitter connects; the alert is retracted once it goes out.
  • Request not sent (time-sensitive alert): a flagged command that failed to send, or expired unsent (a calibration older than the 5-minute stale window). Wording is per command: the session was not stopped / was not started; the calibration was not applied.
  • No automatic retries. A failed or expired command is dropped and the user decides whether to ask again. Calibrations are never re-sent.
  • The failed notice clears when the user asks again, when the session state makes it moot (a stop when the session has ended, a start once one runs), or, for calibrations, once the alert is acknowledged. Acknowledging stops the alert from being raised again.
  • Settings: a queued stop replaces the button with a disabled "Stopping Sensor Session…" row plus a queued note; after 15 minutes the note becomes an orange warning; after a failed stop the button returns with a warning. A failed calibration gets a warning in the Sensor section.
  • Alerts are also evaluated from fetchNewDataIfNeeded, so time-based alerts fire without a connection.
  • Every enqueue is logged (previously only ones that replaced a queued command, which is why the first press was missing from the tester's log).
  • Simulator: a relaunch resumes a running session instead of restarting warm-up.

Follow-ups in other PRs

Screenshots

Stop queued Stop delayed (15+ min) Stop failed

Testing

  • Core tests: 88 pass, including new CommandQueue tests for flag persistence, the 15-minute threshold, a dequeued command never coming back, and a stale flagged calibration being reported rather than sent.
  • G6SensorKitUI and G6SensorKitPlugin build on Part 2.
  • Not covered by automated tests: the manager-level path (failure → notice → alert → acknowledgement), which needs LoopKit.

🤖 Generated with Claude Code

OSLog.log handed the [CVarArg] array to os_log for 6+ arguments, which
reads garbage pointers for each %@ and segfaults in
_os_log_fmt_flatten_object_impl. The seven-argument "Backfill
acknowledged" line hit this on every backfill, crash-looping the app.

The fallback now asserts in debug and logs the pre-rendered line as a
single argument in release. The backfill ack line is collapsed to one
argument.
…ensor work in the simulator

Extract the pending-command logic into a pure CommandQueue value type
(supersede-on-enqueue, 5-minute stale-calibration drop, raw-value
persistence round-trip) with unit tests, and mirror didComplete into
enqueue on the simulator, where no link will ever drain the queue.

Also: stop simulated readings once no session is active, and mark
Locked.mutate @discardableResult (both copies).
…not reach the transmitter

A tester pressed End Sensor on a transmitter with a near-dead battery. The
stop sat in the queue while every connection failed, the screen did not
change, and the session was never stopped.

- Commands can be queued with notifyIfUndelivered. Stop, start and
  calibration are flagged.
- A flagged command still queued after 15 minutes raises "Transmitter not
  responding"; one that fails to send, or expires unsent (a calibration
  older than 5 minutes), raises "Request not sent" with command-specific
  wording. Nothing is ever retried: a failed command is dropped and the
  user decides whether to ask again. Calibrations in particular are never
  re-sent.
- Settings shows a pending stop ("Stopping Sensor Session…" and a queued
  note), turns it into a warning once delayed, and shows a warning with the
  button restored after a failed stop or calibration.
- Alerts are also evaluated from fetchNewDataIfNeeded, so time-based ones
  fire without a connection.
- Every enqueue is logged, not only ones that replace a queued command.
- Simulator: a relaunch resumes a running session instead of restarting
  warm-up.
Copilot AI balanced review requested due to automatic review settings October 3, 2026 23:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants