Commit 333a99e
p2: the developer/reliability screen, the session notice, and the null-list crash
Three things, in the order they had to happen.
1. The app survives a live session.
`detail::parseJson<T>` in the generated C-ABI wrapper guards a NULL char* but
not the four-byte document "null" — which is exactly what Go marshals for a
non-nil list whose backing slice is nil. `json::parse("null").get<vector<T>>()`
throws type_error.302 out of an ordinary getter, and the ReadStats path is
reached from a DISPATCHED window Activated, so it is outside ShowWindow's
try/catch: unhandled, the process dies the moment a device_ exists. P2 is the
first phase that reliably produces one.
app/third_party is a build artifact with exactly one tracked file (a README) and
Go is not installed here, so the generator cannot be fixed from this repo.
Guarded instead at every list-shaped getter through one helper, ReadList, whose
`logged` argument is a per-call-site latch — each of these sites is a listener
callback or a poll, and an unlatched log would fill the file at the listener's
rate. Nine sites; the two that already had try/catch + LogWarn were left alone
and the helper matches them.
The one-line upstream fix (cgo/gen/hpp.go:260-267, make a null document yield an
empty container — struct from_json is already null-tolerant, only the top-level
vector unwrap is not) is written up in
docs/superpowers/reports/2026-08-07-sdk-null-list-unwrap.md, along with why this
is the FOURTH defect Windows has found in that wrapper: it is the only compiling
consumer of the header, so nothing else in the fleet can find them.
2. The session notice has a consumer.
P1 shipped SetModeNoticeHandler with nothing bound to it, so an rpc-only session
showed no banner and a failed resume was log-only — the app could fail to reach
the service and say nothing on screen. DeveloperPage binds it and drives
ModeNoticeBar, a window-level InfoBar (row 2 of the root grid, appended so no
existing element's Grid.Row moves) that is visible from every destination,
renders `message` verbatim with no title, and has no dismiss control. The
handler does one thing — TryEnqueue and return — because it is invoked with
SdkHost's mutex_ held.
3. The screen itself.
iOS DeveloperView + ReliabilityStore parity: measurements, all 34
ReliabilitySettings across Detection / Placement / Recovery / Probing /
Observability, and the actions. Desktop goes past the phones with real tables
for the exits and, new here, the destination exits — the readout that shows a
site split across exits, which iOS never renders.
The traps, kept:
- an edit is a read-modify-write of the WHOLE struct from a FRESH read, inside
SdkHost under its lock so two edits cannot revert each other;
- a NIL settings read is "nothing in force", NOT "everything off" — the write is
a no-op and says so, because a zeroed struct disables the whole reliability
stack and sync() latches it;
- the 5s poll runs only while the destination is selected AND the window is
presenting, always on a worker: every getter is a synchronous rpc;
- MigrateExit/ProbeAllExits drop their int32 counts over the C ABI, so the
feedback is "Requested:" and no count is invented.
dropExit, stallExit, shuffleExits and the probe-suite getters are DeviceLocal-
only with no DeviceRemote equivalent and are deliberately absent.
MainWindow's share of this is a nav item, a host ScrollViewer, the notice bar,
and one line in the navigation relay — the whole ~50-control surface is built in
code, because four phases share that file.
Strings: the localization store has NO key for this surface (`reliability` is the
earnings word, `reliability_settings` is "Contribute bandwidth"), and iOS is in
the same position — its DeveloperView passes bare LocalizedStringKey literals
with no catalog entries. So each of the 84 strings carries the `dev_*` key id it
should have plus the iOS literal, and Dev() prefers the store the moment the key
lands, using the same missing-key test Plural() already uses. Adding the keys to
urnetwork/localizations is separate work and this screen needs no code change
when they arrive.
Verified by running, not reading: --preview-ui=developer now renders both the
empty state and (via a synthetic snapshot, the same pattern as the two preview
snackbars) the populated one, and the log shows the poll starting and stopping
with window activation. What could NOT be verified is stated in the phase
report: no account is signed in on this machine, so no live DeviceRemote was
ever created and neither the null-list guard firing nor a real
RpcOnly/SessionFailed notice has been observed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PT7KcWCPKfFwQUc7SM3oZY1 parent 2bb3918 commit 333a99e
11 files changed
Lines changed: 1772 additions & 10 deletions
File tree
- app/src/App
- docs/superpowers/reports
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
| 231 | + | |
231 | 232 | | |
232 | 233 | | |
233 | 234 | | |
| |||
256 | 257 | | |
257 | 258 | | |
258 | 259 | | |
| 260 | + | |
259 | 261 | | |
260 | 262 | | |
261 | 263 | | |
| |||
0 commit comments