Repository navigation
caude hook trace propagation - #330
Open
davidabram wants to merge 16 commits into
Open
davidabram wants to merge 16 commits into
davidabram wants to merge 16 commits into
Conversation
Capture the staged implementation plan for connecting Claude Code hook execution to managed OTLP tracing, including propagation probing, hook span admission, child spans, privacy and parity constraints, and live verification. Plan: claude-hook-trace-propagation Tasks: T01, T02, T03, T04, T05 Co-authored-by: SCE <sce@crocoder.dev>
Capture Claude Code 2.1.301 probe results for PreToolUse and PostToolUse hooks, including the interaction-span parent context, tool_use_id correlation, caller environment markers, and telemetry gating behavior. Update the context map and complete T01 with the qualified parent_context_available verdict, while documenting that future SCE hook spans must be siblings of Claude tool spans under claude_code.interaction. Plan: claude-hook-trace-propagation (T01) Co-authored-by: SCE <sce@crocoder.dev>
Enable managed OTLP telemetry for Claude-invoked hook processes while keeping non-Claude hooks and the telemetry-header helper on Noop. Classify admitted invocations before credential lookup, emit one privacy-filtered `sce.hook` span with Claude correlation IDs, and preserve fail-open hook behavior under credential or exporter failures. Add admission, payload-redaction, and parity coverage while updating the observability contract and decision record. Plan: claude-hook-trace-propagation (T02) Co-authored-by: SCE <sce@crocoder.dev>
Join Claude hook telemetry to Claude Code's W3C trace when a valid parent is available, while preserving independent roots for missing or invalid context. Extract TRACEPARENT/TRACESTATE only for Claude hooks, attach the parent to the root span, and verify child-span inheritance across async polls and worker migration. Plan: claude-hook-trace-propagation (T03) Co-authored-by: SCE <sce@crocoder.dev>
Expose the mutation-scope, Git snapshot, and persistence phases of admitted Claude hooks as low-cardinality OTLP child spans so hook traces show which operations actually ran without changing command behavior. Add reusable span instrumentation at the Claude adapter, mutation-trace coordinator, and scope runtime boundaries, and filter child spans out of ordinary command exports. Ref: context/plans/claude-hook-trace-propagation.md (T04) Co-authored-by: SCE <sce@crocoder.dev>
Complete T05 for the claude-hook-trace-propagation plan by recording Dash0 evidence for `sce doctor` and a Claude Code session, documenting the verified and unverified span hierarchy, and adding an opt-in live smoke procedure. Sync the current admission, session-boundary behavior, credential, and context-map documentation. The live evidence confirms standalone and session-boundary exports while leaving parented tool-hook delivery unverified; local validation and smoke results are recorded without claiming collector delivery. Plan: claude-hook-trace-propagation, task T05 Co-authored-by: SCE <sce@crocoder.dev>
davidabram
force-pushed
the
claude-hook-trace-propagation
branch
from
October 17, 2026 17:34
b3d9d9f to
1f10987
Compare
Prevent concurrent Claude hook processes from silently losing managed telemetry when encrypted auth.db opens contend. Enable multiprocess WAL and an auth DB busy timeout, avoid migration writes on current schemas, serialize and retry pending migrations, and skip database creation for missing-credential reads. Classify and log Noop fallbacks, identify hook entry points with `sce.hook.command`, and add multiprocess coverage for readers, migrations, and correlated hook spans. Document rollout risks and validation evidence in the `claude-hook-trace-propagation` plan. Ref: #330 Co-authored-by: SCE <sce@crocoder.dev>
Prevent mixed-version auth DB writers from overlapping by holding a shared compatibility lock, and make telemetry credential lookups open existing databases without creating files or encryption keys. Record each Claude hook command before reading stdin so malformed or unreadable payloads still identify the hook span. Update regression coverage and the related auth DB and observability documentation. Co-authored-by: SCE <sce@crocoder.dev>
Claude hooks, managed telemetry startup, and `sce setup` could open or create the encrypted auth DB and trigger OS credential-store prompts (such as the macOS Keychain dialog) in unauthenticated or non-interactive contexts. Credential access is now explicit: `CredentialAccess::Interactive` is limited to `sce auth login`, `logout`, and `whoami`, while hooks, telemetry startup, `sce auth telemetry-headers`, and `sce sync` use `NonInteractive`, which only reads existing keys and, on macOS, disables Keychain user interaction. Inaccessible keys surface as a typed `CredentialAccessUnavailable` error. Credential-store access is serialized per process, and only a missing keyring entry may generate a new key. Add a non-secret telemetry readiness hint (`telemetry-ready.json`) that `sce auth login` writes after persisting a telemetry credential and that logout clears, so managed telemetry returns Noop (`not_configured`) without opening `auth.db` when no hint exists. `sce setup` now only creates the auth DB parent directory, no longer enrolls repositories, and is excluded from managed telemetry. Non-login paths open only an existing auth DB, and token storage caches only successful opens. Update regression coverage and the auth DB, telemetry credential, and observability documentation. Co-authored-by: SCE <sce@crocoder.dev>
On macOS the shared compatibility lock used `fcntl(F_SETLK, F_RDLCK)`, a process-scoped record lock that POSIX releases whenever the process closes any descriptor for `auth.db`. The guard could silently drop while the `EncryptedTursoDb` was still open, letting a legacy writer through. macOS now takes `flock(LOCK_SH | LOCK_NB)` on the SCE-owned descriptor. The lock belongs to the open file description, so it lasts until the `EncryptedTursoDb` is dropped or the process exits, and it still conflicts with the legacy whole-file `F_WRLCK` in both directions. Linux and other Unix locking is unchanged; `EWOULDBLOCK` is now also classified as a lock conflict. Add four macOS-only multiprocess tests covering mutual exclusion with the legacy lock, survival across an unrelated descriptor close, and concurrent new holders. Record macOS validation evidence and residual risks in the auth DB documentation and the claude-hook-trace-propagation plan. Co-authored-by: SCE <sce@crocoder.dev>
Prevent compatibility regressions from masking insert failures when Turso WAL connection startup and teardown overlap concurrent writes. Keep all writers open through coordinated inserts, report row-specific errors, and document the lifecycle-race isolation while preserving the legacy reopen checks. Co-authored-by: SCE <sce@crocoder.dev>
Expose read-only managed telemetry health in doctor text and JSON output, using readiness hints and the bounded non-interactive credential lookup. Keep unconfigured installs warning-free and report disabled telemetry as manual-only warnings without changing credentials or blocking hooks. Provide platform-specific auth storage recovery guidance and separate login failures to avoid circular instructions while preserving error identifiers and exit codes. Cover health classification, output, auth side effects, and real database states; correct the macOS multiprocess sandbox state path. Plan: claude-hook-trace-propagation (auth storage guidance and telemetry health follow-up). Co-authored-by: SCE <sce@crocoder.dev>
Auth storage failures from auth commands and `sce sync` always rendered the macOS Keychain guidance, even when the real cause was a locked or unreadable `auth.db`. The cause was lost because every downstream mapper collapsed into one variant and matched on rendered text. Introduce a typed `AuthStorageFailure` cause (`CredentialAccess`, `DatabaseLocked`, `Database`) and thread it from the sources to `UserError::AuthStorageUnavailable`/`LoginAuthStorageUnavailable`. Add typed markers so classification never depends on diagnostic text: `CredentialAccessDenied` for keyring authorization failures (tagged by `NoStorageAccess` or the macOS `OSStatus` codes), `db::DatabaseLocked` for the last encrypted-open attempt failing on the compatibility-lock conflict or a Turso `Busy`/`BusySnapshot`, and `open_lock::SharedLockConflict` with unchanged text. `ControlPlaneError::Storage` now carries the cause, and `is_storage_failure()` became `storage_failure() -> Option<AuthStorageFailure>` across the control-plane, stream, and sync error types. Guidance now matches the cause: DB causes say the DB is locked or unreadable and never mention the Keychain, "Always Allow", or `sce auth login`; the macOS command guidance gained "or run `sce doctor` to verify telemetry access". Managed telemetry still reports `database_error` for a locked DB. The key `auth.storage_unavailable`, the runtime class, exit code 4, and the rendered compatibility-lock rejection text are unchanged. Plan: context/plans/claude-hook-trace-propagation.md Co-authored-by: SCE <sce@crocoder.dev>
The Linux and Windows login guidance wraps "Running login again does not help until" across lines, so the exact-substring assertion in `assert_login_storage_guidance` failed on Linux CI. Collapse whitespace before asserting, and also assert that credential-store login guidance never tells the user to run `sce auth login` again. The messages, the `auth.storage_unavailable` key, and exit code 4 are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: SCE <sce@crocoder.dev>
A legacy-mode SCE process writes auth.db-wal without updating the multiprocess authority in auth.db-tshm. The next new-mode cold opens then take Turso 0.8.1's exclusive rebuild branch in `repair_or_reseed_authority_from_local_disk_scan`. That branch is guarded only by "no writer active" and a process-local publish mutex. When several processes open concurrently, they rebuild the shared frame index at the same time. The result is a `shared WAL frame index length changed` panic, which is the Linux CI failure, or a successful open that reads a stale snapshot, such as a missing credential or an outdated schema. Take an exclusive `File::lock` on a sidecar `auth.db-open.lock` around `Builder::build()`/`connect()` only, after the compatibility lock succeeds. Queries, writes, and migrations are not serialized, and the `F_OFD_SETLK` and macOS `flock` compatibility locks are unchanged. Add a regression that runs 8 concurrent cold opens after each of 20 legacy writes. It failed 20 of 20 runs without the lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: SCE <sce@crocoder.dev>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Validation evidence: Linux CI fixes (a4ee2a3, 126221b)
Linux Nix CI on
93808955failed 3 of 666 tests (run 39295526499). The macOS-specific issues are handled separately.1. Auth guidance tests (
a4ee2a36)login_device_flow_storage_failure_uses_noncircular_guidanceandnew_session_invalidates_previous_telemetry_credential_before_savingfailed because the Linux and Windows login guidance wrapsRunning login again does/not help untilacross a line break. The assertion was brittle; the message was correct.assert_login_storage_guidancenow collapses whitespace before asserting. It also asserts that credential-store login guidance never saysRun \sce auth login` in an interactive terminal., which keeps the noncircular check. The messages, theauth.storage_unavailablekey, and exit code 4 are unchanged.services::auth_command`: 17/17 pass on Linux.2. Turso multiprocess WAL panic (
126221bd)concurrent_legacy_and_new_writers_never_lose_committed_rowsfailed with anew-writerchild panic duringturso::Builder::build:shared WAL frame index length changed while publishing an entry.Root cause: an upstream Turso 0.8.1 defect, triggered by a legacy-to-new handoff.
auth.db-walwithout updating theauth.db-tshmauthority.repair_or_reseed_authority_from_local_disk_scan's rebuild branch (wal.rs:1680, fromseed_or_sync_authority), which discards and republishes the shared frame index.!writer_or_checkpoint_lock_active(), and the publish mutex is process-local. Concurrent cold openers therefore rebuild together.The backtrace confirms this path.
failed to run auth DB migrations).Fix: an exclusive
std::fs::File::lockon the sidecarauth.db-open.lock, held only aroundBuilder::build()andconnect(). It is taken after the compatibility lock, so a legacy holder is still rejected first. With the compatibility lock in place, the authority can only be stale while no new process holds the DB, so the first serialized opener rebuilds alone.F_OFD_SETLKand macOSflocklocks are unchanged.New regression:
concurrent_cold_opens_after_legacy_write_all_see_committed_stateruns 20 rounds of one legacy write followed by 8 concurrent new readers. Every reader must resolve the credential, and every legacy row must persist withintegrity_checkok.Evidence (local Linux x86_64, isolated sandboxes)
services::auth_db::multiprocess_testsmodule with the fixnix flake check -L126221bd(run 39313063523)Remaining risks
auth.dbonly. The Agent Trace DB (TursoDb) does not take the cold-open lock.Builder::build()delays other cold opens. Hook lookups still give up at their deadline, and the kernel releases the lock when that process exits.context/sce/auth-db.md).🤖 Generated with Claude Code