Second adversarial review of the consolidation pass, six areas, and the
fixes for what it turned up.
Providers: a degraded OpenCode capture read no longer publishes the file
cursor. The read marks the capture scope incomplete, the flag rides back
across the worker hop, the writer takes the not-applied path so the store
marks the candidate stale and the next scan re-parses; no red badge for a
retryable read. The parse deadline no longer counts time queued behind
unrelated index writes (separate worker and consumer bounds). Codex metadata
refresh returns the same reference whenever nothing changed. The OpenCode
worker host is generalised to LazyWorkerThreadHost and the port scanner
adopts it, dropping its duplicate lifecycle.
Renderer: the poll latch is gone. Main emits aiVault:searchIndexingChanged
after every apply, and the store does one read, subscribes to the push, and
polls only while a phase genuinely advances; a failed read is an unconfirmed
reading that keeps polling with a 4/8/16/32 s backoff, cleared by success or
push. The web client, which has no push channel, keeps its visibility-gated
interval. Coverage is observed only from a current result and never
overwrites a newer run. idle is a resting phase. Unverified sources surface
in the panel status line.
Service and store: a store that is transiently null during clear no longer
answers "search is off"; coverage falls back to consent. A superseded
transient write failure rebuilds the progress batch so a stale failure count
does not linger. Consent is persisted before it is applied. The WAL sampling
comment names the real second connection. messages_batch is a partial index
(schema 11). The benchmarks drive the real store. Database removal uses the
shared Windows retry loop.
Query: operator-only pages go through the ranking owner, so forks collapse
the same way for `repo:app` and `needle repo:app`. Typo repair falls through
to the best visible candidate instead of abandoning the prefix. The grouped
CTE's measured cost is recorded next to it. The outbound projection validates
against strict enums while the received schema keeps its fallbacks, and the
result type is pinned by equality rather than mutual extension.
Remote and CLI: search operations share one relay lane again, so a status or
configure issued during a query waits unsent and survives a sidecar fault. A
query gets the scan budget rather than the title budget. A rejected backfill
releases the owner lease. The configure path no longer refuses on an
in-flight `applied`. The search boolean vocabulary has one owner in the spec.
The method record moves into the contract module and gains searchCoverage.
The CLI status formatter treats applied:false as a caveat, not as unavailable.
An aggregate is not partial merely because a host reported unverifiable
sources.
Every fix carries a test that fails when it is reverted.
Second pass over the session-search delta. Keeps the staged-write, streaming
capture, consent-gate and SQL-side filtering designs; removes the layers that
had accumulated around them.
Store and schema (schema 10): row visibility is two SQL views instead of four
hand-written predicates; publish nulls messages.batch_id and drops the batch row,
so the messages view is `batch_id IS NULL` and a recycled batch id can no longer
hide published rows; `published` column and its index removed; WAL checkpoint
guard taken off every read path and sampled on the write loop only;
maintenance class split into three plain functions; cwd_key indexed and the
scope filter switched to a range seek with an EXPLAIN plan assertion.
Service and capture: one write shape (streamingCapture flag and the dead
array branch deleted); configure applies policy to the store once; one
shutdown path (dispose removed) with an ownership guard so a late close
cannot unregister a replacement service's sink; unverifiable sources are
returned with a caveat instead of filtered like deletions; a transient write
failure no longer pins the indexing badge at error; refresh lane reuses
stableInFlightKey; message channel handles concurrent checkpoints.
Query: relevance sort now groups by session before the candidate limit (the
must-fix was only applied to newest); one operator parser shared by panel and
backend with the apostrophe bug fixed; OR within a key, AND across keys; one
documented case rule; received enums tolerate unknown values; result type
derived from the schema; projection applied on the desktop IPC path; tokenizer
contract pinned against fts5vocab.
Remote and CLI: SSH and local ids rejected at the RPC boundary for all four
methods; method-name regex sniffing removed from both transports; CommandSpec
gained booleanFlags/repeatableFlags so args.ts carries no command vocabulary;
settings update no longer blocks or fails on scanner reconfiguration; relay
owner keeps its lease through caller cancellation and answers index-status on
an unreadable policy; one SESSION_SEARCH_METHODS record feeds every caller.
Renderer: coverage polling stops once the index settles and re-arms on focus;
search results feed the coverage store instead of re-fetching; the `updating`
state and header line are deleted in favour of the list's own loading state;
local-only notice only when a remote host is in scope; status-bar segment is
read-only and opens settings; ownerKey folded into the args key.
Providers: OpenCode capture resets (not clears) the parse deadline; capture
and preview reads degrade to a scan issue instead of dropping the session;
a consumer write failure is not counted as a worker death; response union
discriminated on `kind`; worker host split out of the client.
Every behavioural fix carries a test that fails when the fix is reverted.