NEXO 7.8.2 — hook_runs sid observability closed
Published 2026-04-23. Patch release over v7.8.1.
v7.8.0 shipped compaction continuity (PostCompact as a real registered hook, exact SID via CLAUDE_SESSION_ID, per-conv sidecar, fail-closed on mismatch). v7.8.1 patched the Layer-2 emergency auto-diary and Layer-3 record_auto_flush to use the same exact target SID instead of "latest active session". Francisco audited the resulting telemetry and found one remaining gap: hook_runs.session_id was empty for 7 out of 8 recent compaction rows, and when populated it stored the raw Claude Code token instead of the NEXO sid. Per-session queries over hook_runs for compact events therefore could not be joined back to the NEXO session that actually compacted.
What v7.8.2 changes
- New
src/hooks/compact_session_resolver.pywithresolve_nexo_sid(claude_session_id). It walks the same rails the shell uses:sessions.claude_session_idmatch, thensession_claude_aliases.claude_session_id(most recentlast_seenwins), then the per-conversation sidecar underruntime/data/compacting/<safe-claude-id>.txt, then the legacy global sidecar for single-conversation setups. Returns(nexo_sid, source). src/hooks/pre_compact.pyandsrc/hooks/post_compact.pynow call the resolver and store the real NEXO sid inhook_runs.session_id. Both wrappers also stash{claude_session_id, sid_source}inhook_runs.metadata, so "why is this row still empty?" becomes a one-query answer.
Tests — the empty-state rail is the one we actually care about
Nine new tests in tests/test_hook_runs_compact_sid_resolution.py:
- Five resolver rails: sessions match, alias match (newest wins), per-conv sidecar fallback, legacy global sidecar, and the
noneoutcome when nothing matches. - Malformed sidecar rejection: non-NEXO-shaped content must not poison
hook_runs.session_id— the resolver returns("", "none")instead. - Pre-compact and post-compact wrappers end-to-end via a stubbed
hook_observability: assert bothsession_idandmetadataare captured with the correct values on a sessions hit and an alias hit respectively. - Empty-state wrapper path: when nothing resolves, the row is still written with
session_id=''andsid_source='none'so the audit trail stays diffable instead of being silently skipped.
No Desktop bump
Entirely Brain-side. Desktop v0.27.0 continues to ship.
Live smoke
First compaction after the update emitted a hook_runs row carrying the resolved NEXO sid (nexo-NNNNNN-N) plus the raw Claude token in metadata.claude_session_id and metadata.sid_source='alias'. Per-session SELECT * FROM hook_runs WHERE session_id = ? joins work again end-to-end.
Related: full v7.8.2 changelog · v7.8.1 release notes · source on GitHub