Skip to content

fix(daemon): stable first-index on empty profiles (discovery deferral, real init reconcile, accept/SUN_LEN hardening) - #575

Merged
ScriptedAlchemy merged 5 commits into
codex/tracedecay-total-redesign-planfrom
cursor/init-discovery-deadline-fix-6ce4
Aug 20, 2026
Merged

fix(daemon): stable first-index on empty profiles (discovery deferral, real init reconcile, accept/SUN_LEN hardening)#575
ScriptedAlchemy merged 5 commits into
codex/tracedecay-total-redesign-planfrom
cursor/init-discovery-deadline-fix-6ce4

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Problem

On 0.1.0-beta.10/11, a fresh isolated profile (TRACEDECAY_DATA_DIR) on a Mac showed a broken first-index journey:

  • tracedecay init / first status failed in ~2.1s with initialize route repository discovery deferred for <root>: DeadlineExceeded, and the daemon ended up shut down / flapped to a new PID.
  • A second init returned "daemon code-index reconciliation requested" — but no code-index work followed; tracedecay tool context stayed on exact_scope_generation_not_ready ("the verified code graph is not ready for the exact project root").
  • Separately (Aug 19, thread 01a01bf2): a profile path deep enough to overflow sockaddr_un (SUN_LEN, 104 bytes on macOS) kills the daemon at socket bind, which launchd turns into a restart loop.

Root causes and fixes

  1. Deferred repository discovery was terminal (src/daemon/core_proxy.rs, src/daemon/connection_serving.rs). A first Git probe exceeding its 2s bound produced a plain config error: project opens failed outright (no retry — the message carried no warming hint), and on the MCP initialize route the error propagated out of the connection handler, dropping the connection without any response. Now the deferral carries the project-warming retry hint, so the CLI (120s init budget) and the serve proxy retry it, and initialize-route resolution failures answer as typed JSON-RPC errors instead of tearing down the connection. The 2s probe bound is unchanged — deferral, not a raised timeout. (Builds on ca8f06cbc, which stopped blocking-pool starvation from manufacturing these deadline misses in the first place.)

  2. Init never requested the reconciliation it reported (src/commands/index.rs). brokered_init ran an admission-only status call and then printed "daemon code-index reconciliation requested" unconditionally. Indexing only started if the background full-server upgrade survived long enough to reach its activation call — a daemon restart in that window silently discarded it, leaving an enrolled project unindexed forever (the observed no-op flap). Init now issues tracedecay_admin_sync after admission; the reconcile sink's pre-mount fallback (overflow → demand-driven activation → mount → index) makes that request start the first index immediately.

  3. One failed accept(2) ended the whole daemon (src/daemon/bootstrap.rs). A client resetting before accept (ECONNABORTED on macOS/BSD — e.g. the CLI's daemon_reachable() connect-and-drop probe at the start of every init) or transient fd pressure exited the accept loop, which a supervisor turns into a flap. Accept errors are now logged (daemon_accept) and served past, with a bounded 250ms backoff for non-connection failures.

  4. SUN_LEN overflow killed the daemon at bind (src/daemon/service.rs, src/daemon/transport.rs). default_socket_path() now re-derives an over-long profile-scoped endpoint to a short deterministic per-profile path (/tmp/tracedecay-<sha256(profile-root)[..8]>/daemon.sock) that daemon and clients converge on independently — a fixed /tmp base because $TMPDIR differs between launchd services and login shells on macOS. Squatting fails closed: group/world-accessible parents are already refused before bind, and an attacker-owned 0700 directory refuses the bind at the kernel. An explicitly configured over-long TRACEDECAY_DAEMON_SOCKET is refused with a typed remedy instead of the kernel's opaque bind error.

Verification (cloud VM, isolated TRACEDECAY_DATA_DIR, small fixture repo — never a developer machine)

End-to-end journey on an empty tmpfs profile with a 2-file git fixture:

  • daemon up → tracedecay init . completed in 1.21s, daemon PID unchanged, no daemon_shutdown/daemon_accept errors.
  • verified-graph tracedecay tool context '{"query":"alpha"}' answered with real symbols 2.13s after init start (never exact_scope_generation_not_ready); tracedecay status shows the sealed fresh generation (3 symbols / 1 edge); tool search returns ranked results through the core query authority.

Targeted tests (all falsifiable against the prior behavior):

  • deferred_repository_discovery_is_project_open_retryable — both retry classifiers accept the deferral.
  • reconcile_request_before_mount_activates_indexing — a pre-mount reconcile request mounts the scheduler and flushes the overflow request.
  • brokered_init_requests_a_real_code_index_reconciliation — wire-level fixture daemon observes tracedecay_status (admission-only) then tracedecay_admin_sync.
  • over_long_profile_socket_path_falls_back_to_a_short_deterministic_path, unix_listener_refuses_over_long_socket_path_with_a_typed_remedy — SUN_LEN behavior.

Plus clippy on the touched crate. No GitHub CI retriggers.

Open in Web Open in Cursor 

cursoragent and others added 4 commits August 20, 2026 23:07
A first Git probe that exceeds its 2s bound surfaced as a terminal
config error: tools/call project opens failed outright and the
initialize route dropped the client connection without a response.
Deferred discovery now carries the project-warming retry hint so every
existing client/proxy retry classifier retries it, and initialize-route
resolution failures answer as typed JSON-RPC errors instead of tearing
down the connection.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
tracedecay init printed 'daemon code-index reconciliation requested'
after an admission-only status call that requested nothing. Indexing
only started if the background full-server upgrade survived long enough
to call the activation, which a daemon restart silently discarded. Init
now issues tracedecay_admin_sync after admission, and the reconcile
sink's pre-mount fallback (overflow -> demand-driven activation) is
covered by a falsifiable test.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
One failed accept(2) ended the whole daemon: a client resetting before
accept (ECONNABORTED on macOS/BSD, e.g. the CLI's reachability probe)
or transient fd pressure became a supervisor flap loop. Accept errors
are now logged and served past, with a bounded backoff for
non-connection failures.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
A profile rooted deep enough to overflow sockaddr_un (104 bytes on
macOS) made the daemon socket bind fail at startup, which a service
supervisor turned into a restart loop. The default endpoint now
re-derives to a short deterministic per-profile path under /tmp that
daemon and clients converge on independently, and an explicitly
configured over-long path is refused with a typed remedy naming
TRACEDECAY_DAEMON_SOCKET instead of the kernel's opaque bind error.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a4e47ba

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a64e7d6a14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/daemon/service.rs Outdated
Comment on lines +576 to +577
if !super::transport::unix_socket_path_within_limit(&profile_scoped) {
return Ok(short_profile_socket_path(&profile_root));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Migrate legacy generated sockets during refresh

When upgrading an existing managed service whose deep profile produced the old generated <profile>/daemon.sock, refresh_installed_service_with_state overwrites this newly shortened default with the socket parsed from the old unit. The restarted binary therefore receives the overlong path as an explicit --socket value, BrokerListener::bind refuses it, and the service remains in the restart loop this fallback is intended to fix. Preserve genuine custom overrides, but migrate the prior generated default to the newly derived endpoint.

AGENTS.md reference: AGENTS.md:L90-L92

Useful? React with 👍 / 👎.

Comment thread src/commands/index.rs
// actually happened.
tracedecay::daemon::call_default_tool_awaiting_project_open(
handshake,
"tracedecay_admin_sync",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep init successful for non-Git projects

When tracedecay init targets a supported non-Git project, this unconditional tracedecay_admin_sync fails after the preceding admission call has already enrolled it. Non-Git routes intentionally have no IndexingIdentityV1, so CodeIndexActivationV1::accepts_root rejects the reconcile fallback and handle_admin_sync returns code_index_scheduler_unavailable, converting successful enrollment into a failed CLI journey. Treat the intentionally code-index-disabled state as successful initialization instead.

AGENTS.md reference: AGENTS.md:L147-L149

Useful? React with 👍 / 👎.

Comment thread src/daemon/core_proxy.rs Outdated
TraceDecayError::Config {
message: format!(
"initialize route repository discovery deferred for {}: {reason:?}",
"repository discovery for '{}' is deferred ({reason:?}); the project route {PROJECT_WARMING_RETRY_HINT}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry only transient repository-discovery failures

When Git cannot be spawned or a repository probe consistently fails, this message still includes PROJECT_WARMING_RETRY_HINT because it is applied to every GitDiscoveryUnknown, including SpawnFailed and ProbeFailed. Both retry classifiers key on that hint, so the proxy repeatedly launches the doomed probe for its roughly 15-second grace, while init can consume its full 120-second budget, instead of promptly returning the terminal error. Restrict the warming hint to genuinely transient outcomes such as DeadlineExceeded.

AGENTS.md reference: AGENTS.md:L147-L149

Useful? React with 👍 / 👎.

@ScriptedAlchemy
ScriptedAlchemy merged commit 5259f5b into codex/tracedecay-total-redesign-plan Aug 20, 2026
5 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants