Record the loose end the taskNote outage exposed, and the last of its checks

A session whose transcript will not parse is skipped with only a log line, so
from the phone it is indistinguishable from an idle unresponsive one. That is
why the outage needed a report from Bryan rather than showing itself. The
cause is fixed; the class is not, and it is the "design the unknown state
first" rule rather than a bug in one code path.

Also rustfmt on the parse path, which the fix landed unformatted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-06 22:43:01 -04:00
1 parent fd71d876e1
commit 0862b47f76
2 files changed
+20 -3

No files matched your search

+18
View File
@@ -33,3 +33,21 @@ one in place when it turns out to need a decision.
that would work today, for Claude sessions, and it is the option that was that would work today, for Claude sessions, and it is the option that was
not chosen. not chosen.
## A session the server could not load
- [ ] **A session whose transcript will not parse is skipped with nothing but a
log line, and from the phone it looks exactly like an idle unresponsive
one.** `SessionManager::new` catches a failing `launch` and logs
"couldn't relaunch session <id>", so the session has no pump and no
driver: no status, no history, nothing sendable. That is what the
`taskNote` incident (fd71d87) looked like from Bryan's phone, and why it
needed a report from him rather than being visible in the app.
`Event::Unreadable` removes the cause that time, but not the class — an
unreadable `process.json`, a provider edited away and an unreachable host
all reach the same place.
This is the "design the unknown state first" rule: a session the server
could not load is not a session with nothing to say, and only the phone
can show the difference. It needs a status the wire can carry for it —
the failure with its reason, reported on the session itself — rather than
the reader having to tell it apart from silence.
+2 -3
View File
@@ -353,9 +353,8 @@ impl<'a> Indexed<'a> {
#[serde(rename = "type")] #[serde(rename = "type")]
kind: Option<String>, kind: Option<String>,
} }
let place: JustPlace = serde_json::from_str(line).with_context(|| { let place: JustPlace = serde_json::from_str(line)
format!("bad transcript line in {}", self.path.display()) .with_context(|| format!("bad transcript line in {}", self.path.display()))?;
})?;
// Debug rather than a warning: a transcript written against a // Debug rather than a warning: a transcript written against a
// newer build has one of these per line it wrote, and the row // newer build has one of these per line it wrote, and the row
// on the phone is where this is actually reported. // on the phone is where this is actually reported.