The T-436 soak run came back green but produced no conpty-kill-crumbs artifact:
IsolateCrumbFile opened its append handle without creating the parent dir, and
the standalone orphan probe points it at a fresh CLIDE_LOG_DIR that nothing
else had made — so openSync failed, the crumb file was disabled, and no crumbs
were written. In the app this was masked because FileLogSink already creates
logDirectory() at boot. Create the parent ourselves (no-op when it exists).
Verified by the bundle-smoke artifact, which DID capture a real watchdog sample
(threads=31, handles=25, rssMB=175) because the release app makes the dir.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The freeze hypothesis is a wedged FFI call — a reader isolate blocked forever
in ReadFile, a waiter in WaitForSingleObject, Isolate.kill unable to interrupt
either. To NAME the wedge after a power-cycle, each backend now drops a
breadcrumb before/after every risky syscall.
- pty_log.dart (new, Flutter-free, tested): PtyLog — an injectable, no-op-by-
default breadcrumb hook for the MAIN isolate (wired to the kernel Logger,
source 'conpty'/'pty' = an eager FileLogSink source) — and IsolateCrumbFile,
which the SPAWNED reader/waiter isolates use to open their OWN append handle
and flushSync per line, so a wedged isolate's last crumb survives even a
frozen main isolate (the whole point). Bounded by a truncating size cap.
- native_pty.dart + windows_pty.dart: crumbs around posix_spawn/read and
CreatePseudoConsole/CreateProcessW/ReadFile/WaitForSingleObject; the reader/
waiter isolates carry a sendable crumb path + verbose flag. Per-syscall crumbs
only at debug/trace; lifecycle crumbs always.
- Wiring: startPtySession → PaneRegistry → buildDispatcher build the PtyLog from
the kernel Logger + a crumb file under logDirectory(); verbose follows the log
level. Default everywhere is PtyLog.none — zero behaviour change off the wire.
Tested: PtyLog/IsolateCrumbFile units (cap-truncation, append, no-op) + an
end-to-end real-PTY test asserting the reader isolate writes its own crumbs
('reader started' / 'read -> n=' / 'reader exiting'), which validates the
identical Windows structure that can't run here. Coverage gate 95.10%.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>