Skip to content

fix: bound session worktree discovery - #481

Open
HashemKhalifa wants to merge 5 commits into
ScriptedAlchemy:masterfrom
HashemKhalifa:fix/session-git-discovery-cpu
Open

fix: bound session worktree discovery#481
HashemKhalifa wants to merge 5 commits into
ScriptedAlchemy:masterfrom
HashemKhalifa:fix/session-git-discovery-cpu

Conversation

@HashemKhalifa

@HashemKhalifa HashemKhalifa commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • resolve linked-worktree identity with one paired, two-second-bounded git rev-parse child
  • kill and reap timed-out Git children without entering the expensive repository fallback
  • preserve timeout as explicit Unknown membership so transcript pages remain retryable
  • cache transcript location metadata with a 30-second Unknown cooldown
  • retry daemon watcher identity discovery with bounded backoff instead of degrading permanently
  • use the bounded identity resolver while comparing legacy profile stores, so one stale historical root cannot block every project warmup

Root cause

Session catch-up repeatedly resolved Git identity while parsing transcript metadata. An unhealthy worktree path could leave Git blocked and then reopen the repository through gix, causing large pack-index scans, multi-core CPU use, high memory, stalled searches, and eventually an unavailable daemon.

The initial timeout fix exposed three follow-up correctness gaps:

  • Hermes could treat Unknown as false and advance a cursor
  • transcript location metadata could retry the same two-second timeout for every message
  • watcher startup could turn one timeout into a permanent no-op degraded loop

Cold runtime validation then found one remaining open-path call: legacy-store comparison still used the old gix-first common-directory resolver for every historical manifest root. This PR now uses the same bounded identity outcome there; only a resolved common directory participates in matching.

Behavior

  • Resolved identities keep the existing exact-root and shared-Git behavior.
  • NotFound remains a definitive non-match.
  • Unknown never advances the affected transcript page or enters a route cache.
  • Hermes retries from the first unpersisted page; earlier fully classified pages remain valid.
  • repeated metadata lookups for the same timed-out cwd are suppressed for 30 seconds, then retried.
  • watcher identity timeouts back off from 500 ms to 60 seconds and recover without a daemon restart.
  • exact-root manifests still bypass shared-Git probing, and existing identity-conflict preservation remains unchanged.

Validation

  • new Hermes retry regression: 1 passed
  • new metadata cooldown/retry regression: 1 passed
  • new watcher timeout disposition regression: 1 passed
  • direct Codex, Claude, matcher, and metadata-cache regressions: 4 passed
  • Hermes shared-routing integration: 1 passed
  • legacy-store timeout/non-match regression and exact-root/conflict unit coverage: 4 passed
  • storage resolver module: 42 passed
  • Git module: 11 passed serially
  • worktree module: 7 passed
  • cargo fmt --all -- --check: passed
  • git diff --check: passed
  • cargo check --release --locked: passed
  • optimized release build: passed
  • independent exact-tip audit of 88975508d1e0d4fb98802ff32952fe88541a7320: PASS

Local runtime validation

Installed the exact optimized candidate for 88975508d1e0d4fb98802ff32952fe88541a7320 and restarted the managed daemon.

  • installed binary SHA-256: 2d2b985058be37de34ced78a53e72842b5c02e1a2ac76edccb10fe3232a99f15
  • socket connectable; service running from the managed binary
  • active-project resolution completed in 4.38 seconds on the first cold request
  • status completed in 0.48 seconds
  • an already-ingested transcript search completed in 9.86 seconds
  • active stack sampling showed transcript parsing and SQLite work with no gix, pack-index, or Git child frames
  • during catch-up, observed CPU declined from about 58% to 31% of one core and RSS declined from about 426 MB to 383 MB
  • the prior multi-core Git/gix runaway did not reproduce

The candidate is ready for maintainer review.

@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8897550

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

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

@HashemKhalifa

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 520ddcfd2d

ℹ️ 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/sessions/shared.rs
Comment thread src/sessions/shared.rs Outdated
Comment thread src/daemon/git_watch.rs Outdated
@HashemKhalifa
HashemKhalifa marked this pull request as ready for review July 27, 2026 12:02
@HashemKhalifa

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 88975508d1

ℹ️ 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/sessions/shared.rs
Comment on lines +332 to +333
if matcher.identity == crate::worktree::GitRepoIdentityOutcome::Unknown {
return matcher;

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 Back off unknown project matcher identities

When a project or registered root hits the two-second Git deadline, returning the matcher without caching it makes every subsequent transcript immediately repeat the same timeout. Both CodexSource::parse_new and ClaudeSource::parse_new call get for every discovered file—even when new.lines is empty—so one stale registered root and hundreds of transcripts can still stall a sweep for many minutes while repeatedly spawning Git children. Cache an Unknown cooldown entry, without treating it as a definitive route, as the location-worktree cache already does.

Useful? React with 👍 / 👎.

Comment thread src/git.rs

fn git_command_at(repo_root: &Path, args: &[&str]) -> Command {
let mut command = Command::new(git_program());
command.arg("-C").arg(repo_root).args(args);

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 Clear repository overrides before using -C

When TraceDecay is started from a Git hook or another process that exports repository-local variables, GIT_DIR, GIT_WORK_TREE, or GIT_COMMON_DIR overrides the repository selected by -C (git rev-parse --local-env-vars lists these variables). The new CLI-first resolver can therefore report the inherited repository's common directory for every supplied root, causing unrelated transcript paths and daemon initialize roots to be treated as the same project; clear these repository-selection variables on this command while preserving the supported GIT executable override.

Useful? React with 👍 / 👎.

Comment thread src/sessions/shared.rs
Comment on lines 205 to +206
pub(crate) fn contains(&self, path: &Path) -> bool {
self.contains_status(path) == ProjectMembership::Match

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 Preserve unknown membership in boolean callers

When the bounded identity lookup times out, this converts Unknown to false, but many existing callers still use the boolean path_belongs_to_project API rather than contains_status. In particular, user-scoped Vibe, Kiro, Cline-like, and Cursor Composer ingestion interpret that false value as “not registered,” persist the project transcript into the user store, and advance its offset; a transient two-second timeout can therefore permanently misattribute project sessions. These callers need the tri-state result and must defer the affected transcript or row on Unknown instead of treating it as a non-match.

Useful? React with 👍 / 👎.

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.

1 participant