Carry the accepted query deadline through local dispatch and router forwarding, and include spool queue time. Stop joining blocked request workers after the drain window and reject retained queued jobs.
Validated with 593 default and 617 CUDA-enabled tests, including expired dispatch/router deadlines and blocked shutdown regressions.
Co-authored-by: Codex <[email protected]>
Concurrent daemons, retry policy, stale sym: fallback, warming reads,
and the shared tokenizer (lane B, ee503ec). Reconciliations:
- src/cli/emission.rs: lane C's `help` word arm stays first; the
execution arm now runs through retry::run_with_retry.
- src/source/acquisition.rs + acquisition/symbol.rs: lane D's symbol
split wins; AcquiredSource keeps D's SymbolSelection and adds B's
`freshness`; acquire_symbol's read_first closure calls B's
stale_symbol::acquire_symbol_entry (the republish wait is skipped for
parent views, so lane A's parent_show read_symbol path is unchanged).
parent_show.rs's two AcquiredSource literals gain `freshness: None`;
the stale-symbol test uses D's acquire(.., InvocationDirectory).
- src/source.rs: `freshness` renders before D's definitions/imports block.
- Lane A tests that open WorkspaceResults (home_index/tests.rs,
result_cache/tests.rs) create the store first, as B's create/open
split requires; production callers (coordinator start, local runs)
already create it.
- docs/index-contract.md: A's worktree read-path text kept; B's stale
sym: and warming-read rules condensed after it (file stays <=200 lines).
Worktree warming in workspace mode still answers from the parent index
(A's resolve_home_index, which never goes through a root daemon);
B's index_warming applies to a root's own unpublished index.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review follow-ups for concurrent serving:
- Pool workers catch job panics; a panicking request answers
`internal_error: MESSAGE` (sockets and spool) and the worker keeps
serving. A panic in reconcile or idle work fails the server, which
releases its socket and lock so the next client starts a fresh daemon.
- A `daemon_busy` refusal reads the request (200 ms) before replying, so
a client still writing never sees EPIPE; retry.rs also treats EPIPE and
ECONNRESET on read verbs as busy.
- Socket timeouts are retried only when they struck within 5 s; a full
30 s (or relayed 28 s) wait is final, so a retry never doubles it.
- A router `daemon_unavailable` reply falls back to direct execution;
other router errors remain the answer.
- daemon::running probes with a socket connect instead of the startup
flock, which could make a starting daemon exit; ensure_member uses it.
A connection closed before any request gets no reply.
- Nested waits are deadline-bound: run_direct takes a reply wait (the
coordinator passes its query deadline's remainder), and the router's
spawn wait plus forwarded read share one 28 s budget (request_by).
- A spool client whose claiming router stops beating fails at once with
`daemon_unavailable`; stop lets accepted requests finish (up to 28 s)
after releasing the socket.
- src/daemon/server/tests.rs is split; failure tests live in
server/tests/failures.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A stale sym: read whose current bytes are binary or cannot be extracted
(reextract_definition's source_excluded / extraction_unavailable) now
answers stale_source with that reason appended instead of replacing it.
Docs state which reads need a publication and which reconcile in the
request. The owner-status test bound rises to 20 s: under a loaded test
machine it took 13 s, still well short of the 28 s re-entry deadlock it
guards against.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every daemon tier (router, coordinator, root) now accepts on one thread
and answers on a bounded RequestPool (src/daemon/pool.rs: 16/8/4
workers, 64 queued); a full queue replies `daemon_busy: retry`.
Reconciliation, watching, root-gone detection, spool draining, and idle
work move to one maintenance thread (src/daemon/reconciler.rs), which
lowers its own priority only after the initial reconcile. Handlers
implement DaemonHandler (src/daemon/server.rs); each request's query
deadline starts at accept. The root daemon (src/cli/root_daemon.rs)
binds before any database work and answers `index_warming` for reads
until the first publication (explicit path reads still serve current
files); status reports `state: warming` without writing. Spooled
requests are claimed by rename to `<id>.claimed`.
Re-entry: workspace owner verbs and member semantic commands run via
cli::run_direct, never through the router still proxying the request
(the bare-status deadlock), and a router error is now the answer
instead of silently re-running the command directly. The router proxies
with a 28 s read timeout; clients wait 30 s (CLIENT_REPLY_WAIT, shared
with the spool) and retry read verbs once on EAGAIN/TimedOut,
daemon_busy, database is locked, or (after 2 s) index_warming
(src/cli/retry.rs).
Coordinators build a SemanticSession per request, create
workspace.sqlite3 once (WorkspaceResults::create/open), and never run
workspace index/init; `ws status` opens members query-only. Spawned
daemons are reaped by their own waiter thread (BackgroundChild) instead
of a racing waitpid(-1), and a losing startup race keeps polling the
winner. DiagnosticQueue is Sync so concurrent requests share it.
A `sym:` read whose file changed waits up to 3 s for a serving daemon to
republish, then re-extracts the definition from current bytes and
answers `verified: false, freshness: unpublished`; handle reads keep
`stale_source`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges 6260bfa (reconciles off the query deadline). --no-daemon search
now indexes each member under deadline.pause_during before
resolve_home_index opens it; HomeIndexPolicy::IndexInline only reads.
Phase 0 review fixes for lane A:
- Reads never open the writer: navigation's warming fallback and
MemberSnapshot::open's generation-0 fallback to Store::open are
gone. A show with no published own or parent index answers
index_warming; --no-daemon show indexes the member first, paused
off the deadline.
- The parent view requires linked_root_of_member (shared Git common
directory) before Store::reading_from.
- search_prepared gets the answering index's cache
(store.index_cache()), so semantic vectors and live-regex cache
exclusion match the parent index in a fallback.
- Worktree caches record a cache-root marker; the sweep evicts a
cache holding only the marker (e.g. results.sqlite3 from parent
reads) once its root is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A linked worktree whose own index is unpublished now answers at once
from its member's published index, opened query-only and reading the
worktree's current bytes (src/workspace/home_index.rs,
resolve_home_index -> Own | Parent | Warming | Unavailable). Search,
refs, map, and both show paths use it; --no-daemon still indexes
inline, and a home with neither index waits up to 8 s as before.
- Divergence (home_index/worktree_divergence.rs): worktree diff
against the parent's HEAD, worktree untracked files, and the
parent's own uncommitted and untracked files, each Git probe bounded
to 2 s (history::git::run_bounded) and reused from
divergence.json for 5 s. Hits in those files carry `differs`
(JSON) and a trailing `differs` field (lines).
- Changed files: show re-extracts a definition in a changed file,
sym: with no hit re-extracts up to 64 changed files, and map FILE
re-extracts a changed file (served_from: "MEMBER index;
re-extracted in worktree"). A stale parent-view handle re-reads
the same-name/kind definition from current bytes.
- Handles: MemberSnapshot.parent_index records the parent index, so
show HANDLE reopens the same view after the worktree publishes.
- Coverage: state parent_fallback with home_state, served_from,
differs, differing_hits; footer "M@WT warming -> served from M
index (N files differ)". Warming/unavailable homes keep a reason
and never widen: scope "home (warming; ws:all searches N members)"
on an empty page. Partial coverage names its kind ("partial (20
unsearched: fact_limit)", issues.unsearched_paths); live regex
counts binary and oversized files as excluded, not unsearched.
- Seeding is silent: outcomes go to seed-outcome.json, shown by
ws status.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add personal skill installation for Codex and Claude Code, with
harness-specific session attribution and Claude SessionStart exports.
Preserve existing Claude hooks and configure narrow runtime access.
Share disk-backed spool and audit settings, correct audit parsing and
ordering, and stop router-owned child daemons during service upgrades.
Update the search workflow and navigation replay for compact results,
verified source reads, explicit relationship limits, and fallbacks.
Validation: 21 integration tests and 60 evaluation tests pass. Verify
four navigation replay scenarios, native skill discovery and Claude
hook execution, and search-to-source reads through the installed wrapper.
Automatic model selection and Claude-sandbox execution remain untested;
small exact-symbol comparisons do not establish token savings.
Co-authored-by: Codex <[email protected]>