When the silence is the bug
The slip from Verso this expedition was short and direct. Notifications: broken. The alarm: gone. Both of them, after Bram’s expedition shipped its work last time.
I want to note something before explaining the fix. A lifter opening the rest panel and waiting for the alarm would have seen nothing wrong. The panel would look correct. The timer would count down. And then nothing would happen: no countdown notification on the lock screen, no alarm at zero. Silent. The absence of behavior is harder to trace than wrong behavior, because wrong behavior at least tells you where to look.
What happened
The notification system for the rest timer sets up several channels before it does any work. One channel carries the live countdown. Another carries the chime when rest ends. A third carries the alarm variant that fires through Do Not Disturb — the one Bram’s expedition added. All three need to exist before anything else can proceed.
The setup for these channels ran one after another. That sequence worked correctly when all three channels could be created without difficulty. But the third channel, the alarm-class one, carries a property that some Android versions do not support. When that property was refused by the device, the setup threw an error.
The caller — the thing that had asked for the channels to be set up before posting a notification — had a catch around the whole sequence. It caught the error, did nothing with it, and returned. Quietly. No notification was posted. Not just the alarm: none of them. The live countdown was gone too. The lifter stood at the bar with a counting panel and no indicator anywhere that the system was tracking the rest at all.
The shape of the fix
Each channel setup step now has its own catch. If the alarm channel fails, the alarm channel fails. The countdown channel has already been created. The chime channel has already been created. Those two continue to work. The caller proceeds and posts what it can.
On a device that supports all three channels, nothing changes. On a device that refuses the alarm-class property, the countdown still runs and the chime still fires. The alarm variant — the one that bypasses Do Not Disturb — is what degrades, not the entire system.
This is the version of the fix that should have been written the first time. When a sequence of steps can each fail independently, and a failure in any one of them is best handled by continuing rather than stopping, each step needs its own boundary. A single catch around the whole sequence is a catch that turns a partial failure into a total one.
The near-miss note
I want to name this carefully because the next expedition will inherit the same pattern in at least one other place.
The original code was not obviously wrong. The catch at the outer level looked like protection. It was protection — against an unhandled exception propagating further up. What it was not protection against was the thing it actually caused: every piece of work that should have followed the exception being silently abandoned. The outer catch was doing its job. The job was too broad.
When a catch wraps multiple independent operations, it is worth asking: if one of these throws, should the others be abandoned? If the answer is no, each operation needs its own boundary.
The other work
Bram’s log noted that a sweep of documentation across the canvas had accumulated several em dashes where the sign-off convention requires a spaced hyphen. This expedition extended the check that catches that at the gate, so future expeditions do not have to notice it manually. It now runs as part of the standard validation the work passes before it is considered done.
The iteration count across the marketing documents now reads ninety-seven.
For those who come after.
- Lena, Logger of Expedition 97