Skip to content

fix(daemon): bloom-aware warm gate (warm_plan RPC) + compact-cache delta-poisoning fix - #614

Merged
githubrobbi merged 2 commits into
mainfrom
fix/mcp-warm-gate-bloom-aware
Aug 24, 2026
Merged

fix(daemon): bloom-aware warm gate (warm_plan RPC) + compact-cache delta-poisoning fix#614
githubrobbi merged 2 commits into
mainfrom
fix/mcp-warm-gate-bloom-aware

Conversation

@githubrobbi

Copy link
Copy Markdown
Collaborator

Two field bugs from the winbox logs (2026-08-24), both root-caused to source and test-pinned. Ships as v0.6.38.

1. MCP warm gate contradicted the daemon's bloom (drive E: report)

The gate inferred readiness from the tier marker: any Parked/Cold drive in scope → ⏳ index warming — retry + a detached warm trigger that force-promoted the body into RAM. But dispatch bloom-skips a Parked drive whose bloom proves it irrelevant to the query's ext filter (Phase-4 zero-RAM-touch) — that drive answers instantly with zero rows and is ready by definition.

Fix: new warm_plan RPC — the daemon returns the exact promote set dispatch would execute (same canonicalisation, same filter resolution via the newly factored build_search_filters, same bloom pre-check, all through the shared promote_plan dispatch itself now calls — one computation, so gate and dispatch can never disagree). The MCP gate consumes the plan and warms only daemon-named drives; the tier check survives solely as the conservative fallback for pre-v0.6.38 daemons.

2. Compact-cache poisoning — the recurring bloom k_hashes out of range quarantines

Field evidence (docenta-relayed winbox logs): M/C/D/S caches written by v0.6.37 itself between two restarts quarantined on the next start; also retro-explains the 2026-08 "drive C failed for ten days" incident. AES-GCM proves the bytes were written misaligned, not disk-corrupted.

  • Reader gap: the v6+ writer always emits the full trigram CSR (an empty index still writes its [0] offsets entry + zero values count), but both loaders' tkc > 0 fast-path consumed only the key count in the empty case — 8 unconsumed bytes shifting ext-names/bloom/trie/frs. Both readers fixed; empty on-disk trigram is rebuilt from records.
  • Live writer bug (the producer): the surgical-patch path appends created records while children/trigram/ext stay the Arc-shared bases (edges in the delta overlay). serialize wrote a children CSR sized rc_base+1 under a header saying rc_new — the header-driven reader mis-walks everything after; small shifts even load silently corrupt. Fires whenever a drive had ≥1 create since the last 50k-threshold fold at save time — exactly the M/C/D/S vs E/F/G split. Fix: save-bound patches fold the delta inside the patch task (fold_delta_for_save); save_compact_cache_background refuses any delta-carrying index outright (boundary enforcement).

Tests: the exact field error reproduced pre-fix (bloom k_hashes out of range from an empty-trigram save); both faces of the poison round-trip (quarantine + silent); fold repair with the created record's child edge present; boundary refusal; save-vs-apply fold contract; warm-plan parity at three levels; MCP fallback selection.

Field note: until this lands on winbox, v0.6.37 keeps writing delta-poisoned caches for drives with creates — each restart quarantines + full-rebuilds them (loud, correct, minutes of warm cost). Historic silently-misaligned loads on affected generations are unknowable retroactively; noted for release notes.

Verified: full just go green, xwin clippy -D warnings green, 913 uffs-core + full daemon/mcp/client suites green.

…mpty-trigram reader gap

Two writer/reader mismatches produced the recurring 'bloom k_hashes
out of range' quarantines (2026-08 drive-C ten-day incident; winbox
M/C/D/S on 2026-08-24, caches provably written by the then-current
daemon between two restarts):

1. READER: the v6+ writer always emits the full trigram CSR - an
   empty index still writes its [0] offsets entry plus a zero values
   count - but both loaders' tkc>0 fast-path consumed only the key
   count in the empty case, shifting every later section by 8 bytes.
   Both readers now walk the real shape; an empty on-disk trigram is
   rebuilt from records rather than adopted.

2. WRITER (the live producer): the surgical-patch path appends
   created records while children/trigram/ext stay the Arc-shared
   bases (edges in the delta overlay). serialize then wrote a children
   CSR sized rc_base+1 under a header saying rc_new - the reader,
   sizing from the header, mis-walks everything after; small shifts
   even LOAD silently corrupt. Fires whenever a drive had >=1 create
   since the last 50k-threshold fold at save time. Save-bound patches
   now fold the delta inside the patch task (fold_delta_for_save), and
   save_compact_cache_background refuses any delta-carrying index
   outright so no future path can reintroduce the poison.

Regression tests pin the exact field error, both faces of the poison
round-trip, the fold repair (created record's child edge present),
the boundary refusal, and the save-vs-apply fold contract.
…nal gates must use

The MCP warm gate inferred readiness from the tier marker alone: any
Parked/Cold drive in a query's scope produced 'index warming - poll
uffs_status, retry' plus a detached warm trigger that force-promoted
the drive's body into RAM. But the daemon's own dispatch bloom-skips
a Parked drive whose bloom proves it holds nothing matching the
query's extension filter (Phase-4 zero-RAM-touch): such a drive
answers instantly with zero rows and is ready by definition. The gate
and dispatch disagreed - field 2026-08-24, winbox drive E: (docenta
via uffsmcp).

The daemon now exposes warm_plan: given the SAME SearchParams a
search would carry, it returns the promote set dispatch would
actually execute - same canonicalisation, same filter resolution
(build_search_filters, factored out of run_search_over so the two
can never drift), same bloom pre-check, all funneling into the shared
promote_plan the dispatch itself now calls. The MCP gate consumes the
plan and warms only daemon-named drives; the tier-based check remains
solely as the conservative fallback for pre-v0.6.38 daemons
(method-not-found detection).

Parity pinned at three levels: promote_plan against the tight-bloom
fixture (miss => empty plan, hit/no-filter => planned, scope
respected), warm_plan from wire params through the real filter
pipeline, and the MCP fallback selection on exact error shape.
@githubrobbi
githubrobbi enabled auto-merge August 24, 2026 10:26
@githubrobbi
githubrobbi added this pull request to the merge queue Aug 24, 2026
Merged via the queue into main with commit 75f6424 Aug 24, 2026
21 checks passed
@githubrobbi
githubrobbi deleted the fix/mcp-warm-gate-bloom-aware branch August 24, 2026 10:43
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