Fixed
- Headless runs (
claude -p) emitted nothing at all. The hook was registered only onStopwithasync: true, and Claude Code registers an async Stop hook but exits before spawning it in headless mode — verified with a probe hook that never executed once, while the same hook registered synchronously ran and received its full payload. Since headless is how another harness drives Claude Code, every non-interactive session was invisible. The installer now also registers a synchronousSessionEndhook, which does fire there.Stopstaysasync, so interactive turns are unaffected.SessionEndadditionally fires on interactive quit and on Ctrl-C, so it catches a final turn whose asyncStophook died with the process; onlySIGKILLescapes both. The two never double-count — emission is incremental behind a byte offset and a state lock. Both hand their work to a detached worker (detached: true, sosetsidmoves it out of the session's process group), which keeps the synchronousSessionEndfrom delaying session teardown: 0.03s rather than the 0.32s annpx-resolved inline run costs.LATITUDE_CLAUDE_CODE_DETACH=0forces the inline path. TheSessionEndentry carries an explicittimeout, because Claude Code kills those hooks on a shared ~1.5s budget that a coldnpxresolve can exceed on its own. Existing installs needinstallre-run to pick up theSessionEndentry.
Added
- Cross-harness trace correlation. When another harness launches Claude Code — a Hermes tool call, a CI job, a subprocess agent — it can hand over its active span through the standard
TRACEPARENTvariable. The hook then joins that trace instead of deriving one fromsessionId:turnNumber, and parents its turns on the supplied span, so a Claude Code session appears nested under the tool call that launched it rather than beside it.LATITUDE_SESSION_IDjoins the parent's Latitude session andLATITUDE_PROJECTkeeps both halves in one project (ingest is project-scoped, so a mismatch would split the trace with no error).LATITUDE_TRACEPARENTtakes precedence for setups whereTRACEPARENTalready belongs to something else, andLATITUDE_CLAUDE_CODE_INHERIT_CONTEXT=0opts out. A session launched on its own is unaffected: with no valid header the hook generates ids exactly as before, and a malformed header is ignored rather than failing the turn. Claude's own session id stays available asclaude_code.session.id. Joining is capped per session so one long-lived session cannot grow a trace it does not own without bound. Contract:dev-docs/trace-correlation.md.