NEXO 7.4.1 — guardian-claude-desktop lifecycle closure: ledger/authority split made explicit, Desktop v0.24.1 covers the last 3 routes
Published 2026-04-22. Patch release over v7.4.0.
v7.4.1 is a same-day follow-up to v7.4.0. Its job is twofold: close the remaining gap in the guardian-claude-desktop-plan.md pipeline, and correct the over-promise that the v7.4.0 release notes made. Both motions happen in lock-step with Desktop v0.24.1.
The honest correction
The v7.4.0 CHANGELOG and blog used phrases like "every conversation lifecycle transition" and "full pipeline end-to-end". Francisco called that out, rightly, after installing the release. Two facts the old entry blurred:
nexo_lifecycle_eventis a ledger + reconciliation authority, NOT the canonical executor ofdiary+stop. The handler records the transition with strict idempotency byevent_id, but the actual diary-write and session-stop injection still happens on the Desktop side, insidecloseConversationGraceful(the only place that writes to the live Claude process's stdin). Moving that to Brain needs a Brain↔Claude-session bridge that does not exist today. That refactor is scheduled for v7.5 under followupNF-V75-CANONICAL-DIARY-AUTHORITY.- v7.4.0 wired only
close,deleteandarchivethrough the durable service.switch,window-closeandapp-exitstayed on the legacy flow until today's partner release Desktop v0.24.1.
Both v7.4.0's CHANGELOG and blog now carry an in-line "honest correction" block pointing to v7.4.1. No silent history rewrite.
What Desktop v0.24.1 ships (no Brain code change needed)
Every action the v7.4.0 ledger already accepted (switch, window-close, app-exit) now has a real producer on the Desktop side:
switch: the renderer'sswitchTo()records aswitchevent withtarget_conversation_idinpayload_snapshotbefore reassigningactiveId, but only when the previous conversation had a livesessionIdand the user actively changed target. Fire-and-forget so the UI never blocks.window-close: when the user minimises the main window to the tray (closeAction === 'hide'), main.js enqueues awindow-closeevent per live sessionId-backed conversation withtrigger: 'minimize_to_tray'in the payload. Observational — no canonical side effect, but the telemetry trail and boot reconciler see it.app-exit: insidecloseAndArchiveActiveConversations, every live conversation gets anapp-exitevent persisted BEFOREcloseConversationGracefulis allowed to run. A crash between "user quit" and the diary+stop injection can no longer eat the transition; the next boot's reconciler picks it up. Ordering invariant pinned by a dedicated integration test.
Two hardenings Francisco explicitly asked for
- NDJSON telemetry by
event_id:<NEXO_HOME>/runtime/logs/lifecycle-telemetry.ndjson. Every transition emits one row withkind∈ enqueued, sent, acked, retry, reconciled, failed. Telemetry write failures never crash the lifecycle flow (safe-append). - Real exponential backoff in the reconciler: 2s, 8s, 30s, 2m, 10m, 30m, 1h, 2h, 4h, 8h. Events inside their scheduled window are deferred without contacting Brain, so a reconnect flood after a crash cannot hammer the ledger endpoint.
Tests added
Desktop side: +24 cases across lifecycle-switch-integration, lifecycle-window-close-integration, lifecycle-app-exit-integration, lifecycle-shutdown-e2e, lifecycle-telemetry and lifecycle-backoff. Total: 658 pass, 1 skipped, 0 fail. Brain side: no code change, contract tests from v7.4.0 still pass.
What stays in the backlog
Canonical diary+stop execution inside nexo_lifecycle_event is deferred to v7.5. That is the piece that would let Brain drive the session-end policy directly, without Desktop being the only process that can reach the Claude stdin pipe. Today the session-end policy still fires end-to-end through the existing Desktop pipeline — v7.4.1 just stops the release notes from pretending otherwise.
Related: full v7.4.1 changelog · v7.4.0 release notes · source on GitHub