Skip to content

release: v1.4.3 - #276

Merged
flupkede merged 569 commits into
masterfrom
release/v1.4.3
Sep 17, 2026
Merged

flupkede merged 569 commits into
masterfrom
release/v1.4.3

Conversation

@flupkede

Copy link
Copy Markdown
Owner

Release v1.4.3 — squashed delta from develop (v1.3.14 → v1.4.3).

Highlights (full details in CHANGELOG.md, section [1.4.3]):

  • Fixes: federated chunk fetch (URL residue + project-scope routing, todo 🔒️ fix: pin actions/checkout SHA in codeql.yml (supply-chain hardening) #153); LMDB storage-format corruption auto-recovery (sequential wipe + rebuild, one repo at a time); graceful tantivy FTS reset
  • Security: rmcp 1.8→3.3.0 (5 Aikido advisories cleared), cargo update wave (h2/rustls/quinn-proto/zerovec-derive/moka), ort rc.13 + fastembed 5.17.4/6.1, arroy 0.8 + heed 0.22, actions refresh
  • New: per-index embedding models end-to-end, serve --model default, dependabot targeting develop

Behavior-identical MCP protocol (legacy lifecycle preserved).

Test User and others added 30 commits August 5, 2026 18:13
Per-feature work log covering the whole cycle: the disproven original
hypothesis, the Azure Log Analytics ground truth, the two defects, the three
stages with their commit SHAs and review outcomes, and the open follow-ups.

Records why this behaviour took three attempts across PR #181, #184 and this
branch, so the rejected reasoning is not re-litigated a fourth time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ci.yml`'s push trigger is a prefix allowlist that never included `fix/**`,
and there is no `pull_request` trigger. Every `fix/...` branch therefore
merged into develop having never run fmt, clippy or a single test in CI.

This is the repo's own documented naming convention — the comment directly
above the branch list even says "feature/fix -> develop". Only the prefix
was missing.

It was invisible because CodeQL is a separate, pull_request-triggered
workflow, so the PR still showed a green check. On PR #192,
`gh pr checks` listed CodeQL and nothing else; the local pre-push QC gate
was the only thing validating the branch.

Adds `fix/**` and documents the footgun plus how to verify coverage
(`gh pr checks <n>` should list the CI jobs, not just CodeQL).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🐛 fix: stop waking the scale-to-zero federated peer (and stop it self-warming afterwards)
… duplicates

All hooks now live in .githooks/ and are enabled with a single
`git config core.hooksPath .githooks`. Removes the copy-into-.git/hooks step
that had let the tracked template and the installed hook drift apart.

Why this was broken:
- scripts/pre-push documented its own install as `cp scripts/pre-push
  .git/hooks/pre-push`, but the tracked copy had no customer-reference leak
  scan. Following the documented install silently removed that guard.
- .githooks/post-checkout documented `git config core.hooksPath .githooks`,
  which would have made git look only in .githooks/ — a directory that held
  neither pre-commit nor pre-push. Enabling one hook would have disabled the
  two that matter. core.hooksPath was in fact unset, so post-checkout had
  never run at all.

Changes:
- .githooks/{pre-commit,pre-push,post-checkout} + README.md; scripts/pre-commit
  and scripts/pre-push deleted (scripts/qc.ps1 stays).
- Customer patterns move to untracked .githooks/customer-patterns.local. The
  list is itself the thing being hidden, so it must never be tracked. When it
  is missing, pre-push prints a loud "SKIPPED — nothing was checked" warning
  instead of passing silently: a guard that quietly stops running is worse
  than no guard, because it is still trusted.
- pre-push skips the Rust QC gate when the branch changes no .rs/Cargo.* files
  vs origin/develop. Compared against origin/develop rather than the pushed
  ref range because stdin is already consumed by the master guard, and this
  avoids the all-zeros remote_sha case for a new branch. Every uncertain case
  runs the gate rather than skipping it.
- Leak scan uses `git grep -lIE` instead of `git ls-files | xargs grep`:
  space-safe paths, no argument-length limit, no stderr to suppress.
- ci.yml: add chore/** to the push branch allowlist — same prefix-allowlist
  footgun that left fix/** with no CI at all until PR #192.
- .gitattributes: drop the now-dead scripts/pre-commit eol=lf rule.

Master-push guard is unchanged.

Verified by running the hook directly: master push blocked (exit 1); normal
push passes with QC correctly skipped (exit 0); missing pattern file warns
loudly and exits 0; a planted customer reference in a tracked file blocks
(exit 1); the Rust-path filter matches src/foo.rs and Cargo.toml and not
docs/x.md, proving the skip can actually un-skip.

Review-fixes:
- [Important] `|| true` on the leak scan turned a git grep *failure* into a
  clean pass, and the comment-stripper could manufacture that failure: an
  inline `#` in a valid pattern like `Acme(NV#|BV)` was truncated to `Acme(NV`,
  producing an invalid ERE that exits 128. One bad line poisoned the whole
  alternation, so a real leak sailed through with "No customer references
  detected." Now: only whole-line comments are stripped (a mid-line `#` stays
  part of the pattern), and the git grep exit status is branched on — 0 blocks,
  1 is clean, anything else prints the loud SKIPPED banner. One rule overall:
  configuration problems warn, actual leaks block.
- [Important] RELEASING.md still documented `cp scripts/pre-commit
  .git/hooks/pre-commit` — a copy of a file this commit deletes, into a
  directory core.hooksPath makes git ignore. Replaced with the single
  `git config core.hooksPath .githooks` install.
- [Important] The QC-skip regex missed `rust-toolchain.toml` and `.cargo/`,
  both tracked here: a toolchain bump changes no .rs file yet flips fmt,
  clippy and tests, so the gate was skipped for exactly the change most likely
  to need it. Widened to cover rust-toolchain, .cargo/, scripts/qc.*, and
  rustfmt/clippy.toml (the latter two not tracked today, listed so adding one
  later cannot silently reopen the gap).
- [doc] README.md said `hooks git install` writes to `.git/hooks/`. It resolves
  the target with `git rev-parse --git-path hooks`, so it honours
  core.hooksPath and chains into an existing hook as a marker-delimited block.
  Corrected; no code change needed.

Re-verified after the fixes: master push blocked (exit 1); normal push passes
with QC skipped (exit 0); an invalid regex in the pattern file now warns
loudly and exits 0 instead of reporting clean; a mid-line `#` survives
parsing; rust-toolchain.toml, .cargo/config.toml, scripts/qc.ps1, src/a.rs and
Cargo.lock all trigger the gate while docs-only paths do not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🔧 chore: unify git hooks in .githooks/ — one location, no hand-copied duplicates
The worklog still said "Not pushed. Three commits sit locally" while the work
was merged (PR #192) and running in production. A change record that is wrong
is worse than none, so this brings it to ground truth.

- Header: status is now shipped + verified, with the merge SHA and the
  deployed revision; test line records CI green, not just the local run.
- Stage 4 added: the branch had no CI at all until `fix/**` was added to
  ci.yml's prefix allowlist, with the self-test that proved it.
- Stage 5 added: deployment of both sides (local via copy-to-common, cloud as
  revision --0000024), config verified intact across the image update, and the
  scale-to-zero verification — replicas held at 0 across six consecutive
  checks with no traffic. Recorded as positive evidence: under the old binary
  the keep-warm fallback made reaching 0 at ten minutes impossible.
- Build note: two az acr build runs failed at the identical step with
  "layer does not exist" on a byte-identical Dockerfile; a local docker build
  succeeded. Root cause is the ACR Tasks agent, not this repo.
- Follow-ups: dropped the stale "not pushed" item; the production check is now
  a 24h Log Analytics sample rather than "unverified"; added the ACR Tasks
  failure as a blocker for any automated cloud deploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 docs: worklog — record merge, deploy and production verification
…e forever

A repo whose database failed to open (typically a transient write lock - an
indexing run holding the DB when a query arrived) was cached as
RepoState::Conflicted, and the fast path in get_or_open_stores replayed that
error on every later call without ever retrying the open.

The state's only documented exit was idle eviction, and it was unreachable:
evict_idle_repos iterates last_access, but both paths that mark a repo
Conflicted (warmup_repo and the get_or_open_stores slow path) propagate the
failure with `?` before reaching their touch_access call. A repo that
conflicts on first open therefore never gets a last_access entry and is never
considered for eviction, however long it sits idle. Querying it did not help
either: the fast path replayed the cached error while calling touch_access on
the way, so the only queries that would have registered it for eviction were
also the ones resetting its idle timer.

Net effect: a momentary lock was indistinguishable from permanent corruption
and could only be cleared by restarting serve - while the error text promised
"the next query will retry automatically".

A cached conflict is now dropped on next access and the open genuinely
retried. Retrying is cheap when it still fails (a refused file lock), and this
mirrors the missing-DB path, which already refuses to cache Conflicted for the
same reason (missing_db_not_cached_as_conflicted).

Regression test asserts recovery without a restart and without an idle wait.
It carries two preconditions so it cannot pass vacuously (the first open must
genuinely fail, and that failure must actually be cached as Conflicted), and
was confirmed to FAIL with the fix neutralised - reproducing the exact
user-visible error string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Review-fixes:
- [Important] Non-atomic get()+remove() on the Conflicted cache entry could race with a concurrent insert (e.g. add_repo_handler or force-reindex installing a fresh RepoState::Write) and delete that live entry instead, dropping its cancel_token without cancelling it. Fixed by using DashMap::remove_if with a matches!(v, RepoState::Conflicted) predicate — same atomic check-and-remove primitive already used by is_indexing() in this file — so removal can only ever affect an entry still Conflicted at removal time.
…-retries

🐛 fix: retry a conflicted repo instead of replaying the cached failure forever
When an agent (or any non-MSYS caller, e.g. an MCP client) sent codesearch
a POSIX-style path like `/c/Users/foo`, Rust on Windows resolved the
leading `/` as "rooted on the current drive" — i.e. `<drive>:\c\Users\foo`,
silently creating junk directories like `C:\c\Users\...` and indexing the
wrong project. This is the path-pollution defect behind the orphan
`<repo>-propagate-tmp` indexes (diagnosed end-to-end: an agent-created
staging folder was indexed under a polluted `C:\c\...` mirror that nothing
ever cleaned up).

Fix: add `translate_msys_path()` to `cache/file_meta.rs`. It detects a
leading `/` + single ASCII letter (+ `/` or EOL) and rewrites it to
`<UPPER>:/...`. Idempotent on every other input; no-op on non-Windows.

Apply it at every user-supplied path boundary so existing AND not-yet-existing
paths are both caught:
  - `safe_canonicalize` calls it before canonicalising (existing-path case)
  - `ReposConfig::register` / `register_with_alias` fallbacks use it on the
    raw path (non-existing-path case)
  - `resolve_database_with_message` fallback uses it on the raw path too

Added tests:
  - `translate_msys_path_converts_single_letter_drive` (table-driven, Windows)
  - `translate_msys_path_leaves_non_drive_paths_untouched` (Windows)
  - `translate_msys_path_leaves_existing_windows_paths_untouched` (Windows)
  - `translate_msys_path_is_noop_on_unix` (non-Windows)
  - `register_translates_msys_posix_path` — integration regression for the
    exact defect (Windows)
  - `register_leaves_unix_path_untouched` — guards against the fix leaking
    to Unix where `/c/...` is legitimate

Validation: cargo check + cargo clippy -D warnings clean; cache:: + db_discovery::
tests = 101 passed, 7 ignored, 0 failed.

Review-fixes (round 1 → 2):
- [Critical] translate_msys_path missed a #[cfg(windows)] guard; the body
  ran on Unix too, rewriting legitimate `/c/...` paths to `C:/...` and
  breaking test-linux CI. Split into a Windows real impl + a Unix no-op.
- [Important] register_translates_msys_posix_path only pinned the
  existing-path branch (safe_canonicalize success path); the actual defect
  site — the non-existing-path fallback — was unpinned. Added a dedicated
  test `register_translates_msys_posix_path_when_dir_does_not_exist` that
  fails if the fallback is reverted to `strip_unc_prefix`.
- [Important] unregister_path and alias_for_path had the same fallback
  pattern as register() but weren't updated → register stored `C:\Users\foo`
  while unregister compared against the raw `/c/Users/foo`, leaving entries
  stuck. Added register/unregister symmetry test. Fixed by introducing a
  shared `normalize_user_path(path)` helper (translate_msys_path +
  strip_unc_prefix) and routing all 5 fallback sites through it, per the
  repo's "structural fix" rule for the warnings-channel class of defect.
…e_user_path

Stage 1 introduced `normalize_user_path` and applied it to the 6 db_discovery
fallback sites. Round-2 review flagged 5 more sites with the same pattern
(`safe_canonicalize(...).unwrap_or_else(|_| <raw path>)`) elsewhere in the
codebase that still leaked caller-supplied MSYS POSIX paths (`/c/...`) to
Windows file APIs when canonicalize failed. Same defect class, same fix.

Sites updated:
- `try_delegate_reindex_to_serve` (src/index/mod.rs ~2064)
- `try_delegate_add_to_serve`    (src/index/mod.rs ~2296)
- `try_delegate_rm_to_serve`     (src/index/mod.rs ~2394) + its inner
  `normalize_for_cmp` closure (~2397)
- `run_serve` `--register` loop  (src/serve/mod.rs ~4745)

All 5 now use `crate::cache::normalize_user_path(...)` on the fallback, so
the `validate_path_within_allowed_roots` check and the eventual
`config.register(...)` call both see the translated `C:/...` form, not the
raw `/c/...`.

Added tests for `normalize_user_path` itself (the helper shared across all
fallback sites):
- `normalize_user_path_translates_msys_and_strips_unc` (Windows)
- `normalize_user_path_only_strips_unc_on_unix` (non-Windows)

The 5 sites here are CLI-arg paths feeding serve-delegation flows; the
register/unregister symmetry tests from stage 1 cover the integration
guarantee indirectly (the delegation flows terminate in `register()`).

Validation: cargo check + cargo clippy -D warnings clean;
cache:: + db_discovery:: tests = 104 passed, 7 ignored, 0 failed.
…e_reindex_to_serve

Stage 2 swept 5 safe_canonicalize fallback sites for the MSYS path-pollution
defect, but missed the twin of the closure it fixed in try_delegate_rm_to_serve.
Stage-2 review round-1 flagged this site (src/index/mod.rs:2076) as the
identical pattern in the identical closure name in the sibling delegation
function. Same defect class, same one-line fix: route the fallback through
`crate::cache::normalize_user_path(p)`.

Post-fix detector sweep confirms every production `safe_canonicalize(...).
unwrap_or_else(_)` site now goes through `normalize_user_path`:
  - db_discovery/repos.rs: register, register_with_alias, unregister_path,
    alias_for_path, scan_for_remote (5 sites)
  - index/mod.rs: try_delegate_reindex_to_serve outer + closure,
    try_delegate_add_to_serve, try_delegate_rm_to_serve outer + closure (5 sites)
  - serve/mod.rs: run_serve --register loop (1 site)
The only remaining hits are a test helper (repos_tests.rs:12) and the
vectordb mapsize-pin key (vectordb/store.rs:67, input is already canonical
by design — flagged by stage-2 review as out-of-scope).

Validation: cargo clippy -D warnings clean. No test changes needed — the
closure is private and the register/unregister symmetry tests from stage 1
cover the integration guarantee.
fix: translate MSYS POSIX paths at the path boundary (path-pollution defect)
…nners

PR #196's two existing-path MSYS tests failed on Windows CI:

  - db_discovery::repos::tests::register_translates_msys_posix_path
  - db_discovery::repos::tests::unregister_path_matches_msys_posix_form

Root cause: Windows CI runners use a temp root under `C:\Users\RUNNER~1\...`
(8.3 short name for `runneradmin`). The tests built the expected path from
the raw `tmp.path()` (short-name form) but `safe_canonicalize` inside
`register()` resolves 8.3 names to their long form, so the stored path had
`runneradmin` while the expected had `RUNNER~1` → string mismatch.

For `unregister_path_matches_msys_posix_form` the same asymmetry hits the
fallback branch: after the dir is deleted, `safe_canonicalize` fails and
`normalize_user_path` returns the input verbatim (no short-name resolution),
so unregister's comparison ran short-form input against long-form stored
entry and returned false.

Fix: pre-canonicalize the fixture with `safe_canonicalize(&win_repo)` BEFORE
building both the MSYS input and the expected value. Both sides then use the
long form regardless of which branch (success or fallback) executes.

The non-existing-path sibling test passed on CI by luck (both sides bypass
canonicalize), but its assertion is correct and untouched.

Local: all 3 MSYS register/unregister tests green.
fix(tests): pre-canonicalize MSYS test fixtures for 8.3 short-name runners
Investigated a reproducible bug where a `search` chunk_id for the
federated cloud/custom-kb project does not reliably resolve to the
same content via `get_chunk`. Root-cause hypothesis and fix options
are documented in AGENTS.md under Open TODOs; no code changes yet.

Review-fixes:
- [Important] Root cause section stated an unlabelled conclusion
  ("two id spaces/generations exist") that the code contradicts;
  literal-mode search results have no real chunk_id (Option<u32>,
  None for literal hits per src/federation/mod.rs) and get rendered
  as a fabricated chunk_id: 0 via .unwrap_or(0) in src/mcp/mod.rs.
  That alone fully explains the reproduced symptom. -> Rewrote the
  section: primary hypothesis is now the fabricated chunk_id: 0 for
  literal hits (with a concrete fix: stop defaulting to 0, keep it
  Option/omitted), and the cross-generation id-drift theory is
  demoted to an explicitly unconfirmed secondary hypothesis with its
  own "reproduce before fixing" TODO.
…son unregister (todo #48)

`remove_from_index` unregistered the repo from repos.json (and saved it)
before deleting `.codesearch.db`. When the delete failed — typically LMDB
files still held by a serve instance the delegation probe could not see —
the command returned Err with the config entry already gone: the registry
claimed the repo did not exist while its still-locked database sat on
disk, and re-running the command did nothing because there was no entry
left to remove.

The database directory is now deleted first and repos.json is only
mutated once that succeeds, so a failed delete leaves the config
untouched and the identical command finishes the job after the lock is
cleared. A save failure after a successful delete now returns Err instead
of printing a warning and reporting Ok. Also folds the pre-existing
double-unregister in the global-only path and drops the "(Global index
remains)" line, which printed after the global entry had already been
removed.

Review-fixes (round 1):
- [Important] CHANGELOG.md had two `(unreleased)` sections and no
  [1.2.10] heading — renamed to `## [1.2.10] - 2026-08-12` and restored
  the missing MSYS path-translation entry; develop's [1.2.10] section is
  byte-identical to master's.
- [Important] `--keep-config` printed "Global index removed!" after
  "Config entry preserved." in the global-only case — guarded with
  `&& !keep_config`.
- [Coverage] Added keep_config_global_only_preserves_entry for the one
  untested quadrant.

Review-fixes (round 2 — test hermeticity + CI correctness):
- [Important] Tests issued live HTTP against a real serve: the delegation
  probe now points at an in-test reset-server (accepted-then-closed TCP
  listener => instant RST => ServeProbe::Down on first attempt). No test
  can fire a live DELETE at a developer's running serve, and the Windows
  loopback hang (3 retries x 3s per delegating test) is gone too.
- [Important] Env-var race with doctor tests: remove_order_tests +
  doctor's repos.json readers are now #[serial] (serial_test) with
  panic-safe restore via new crate::testing::EnvRestore. Rule recorded in
  AGENTS.md.
- [Important] changelog-check.yml diffed two-dot against the raw base
  SHA — a rebase or develop-merge into the branch false-passed the check
  with develop's own changelog commits. Now diffs from the merge base.
- [Added] Global-config canary in remove_order_tests: snapshot of the
  developer's real ~/.codesearch/repos.json asserted byte-identical at
  every test end — a mis-ordered seed save (which once overwrote the
  real registry during development of this very test) fails the test
  instead of destroying data. Seed now sets the env override BEFORE
  saving.

Adds .github/workflows/changelog-check.yml: visible-not-blocking PR
check that CHANGELOG.md was touched, bypassable via `no-changelog`
label; AGENTS.md documents it in the PR workflow section.

Positive control: restoring the old operation order makes
failed_delete_leaves_repos_json_untouched fail with "repos.json must be
untouched after a failed delete, got: []".
Two follow-up hardenings from the live incident during this session's test
work (a mis-ordered test seed briefly overwrote the developer's real
~/.codesearch/repos.json — 17 repos, groups, remotes, all replaced by a
one-entry fixture, recovered from serve startup logs).

repos.rs:
- `ReposConfig::save()` now refuses, under cargo test, to write the real
  global repos.json unless CODESEARCH_REPOS_CONFIG is set to an override
  (or CODESEARCH_TEST_ALLOW_GLOBAL_SAVE is explicitly set). This is the
  guard that would have turned today's incident into an instant panic
  instead of silent data loss.
- `ReposConfig::save_to()` was a bare `fs::write` — neither atomic nor
  recoverable. Now: (1) keeps one generation of the previous file as
  `<path>.bak`, best-effort; (2) writes to a sibling `.new` temp file and
  renames it into place, with a bounded retry (5 attempts / 20ms) on
  transient Windows rename errors (AV / search-indexer handle races),
  mirroring `vectordb::store::atomic_write_json`'s classification.

logger/mod.rs (unrelated discovery while investigating the incident):
- `cleanup_old_logs` was only ever invoked from standalone-MCP-mode
  (src/mcp/mod.rs). Neither the CLI logger init nor the serve logger init
  called it, so `~/.codesearch/logs/` grew forever — found at 299MB / 183
  files spanning back to April, despite `DEFAULT_LOG_MAX_FILES=5` /
  `DEFAULT_LOG_RETENTION_DAYS=5`.
- `parse_log_date` only recognized the CLI prefix (`codesearch.log.`), so
  even a cleanup call over the shared global log dir would silently skip
  every `serve.log.*` file. Now recognizes both prefixes.
- Both `init_logger` (CLI) and `init_serve_logger` (serve) now call
  `cleanup_old_logs` on their log dir before wiring the appender.

Validation: cargo fmt --check clean; targeted suites green (repos: 55,
logger: 9, remove_order_tests: 5, doctor: 13).
fix: codesearch index rm reorders file-delete before repos.json unregister (todo #48)
SearchResultItem.chunk_id is now Option<u32>, omitted from JSON when
absent. Literal-mode hits (local serve-delegation parse and federated
convert_remote_item) carry no chunk id; the old .unwrap_or(0) rendered
a real-looking chunk_id: 0 that callers could combine into a bogus
"<peer>/<alias>:0" chunk_ref, which get_chunk then silently resolved
to an unrelated chunk — a confident wrong answer (reproduced against
cloud/custom-kb, see AGENTS.md Open TODOs).

Semantic/similar paths wrap their real store ids in Some. Both fixed
sites carry a regression pin asserting a literal hit has no chunk_id
AND the key is absent from the JSON (not null); each pin verified red
under a temporarily reintroduced unwrap_or(0).

Review-fixes:
- [Important] parse_search_items_from_call_result had zero test
  coverage, so its fix had no pin (the class-not-a-site reship mode
  AGENTS.md warns about) → added table-driven tests: literal payload
  without chunk_id (None + absent key + snippet->content mapping),
  explicit chunk_id preserved as Some, semantic keeps real ids,
  unparseable payload still yields empty list (pinned as-is).
…te TODOs/changelog

Store-level repro of the secondary hypothesis from AGENTS.md Open
TODOs (todo #51): next_id = max_key + 1 is recomputed on every open,
so deleting the chunks holding the HIGHEST ids lowers max_key and the
next reopen reassigns those ids to unrelated content — get_chunk
returns the wrong file with no error. Boundary control proves a
low-range delete is safe (surviving ids stable, next insert beyond
the range). Mechanism confirmed locally; production two-cold-start
comparison still owed before any content-keyed-id/generation-stamp
fix is spent on (per the TODO's reproduce-first gate).

AGENTS.md: primary fix marked DONE (branch reference, red-verified
pins, live before/after repro still owed); secondary hypothesis
upgraded from unconfirmed to mechanism-confirmed-at-store-level with
test names. CHANGELOG entry added under 1.2.11 (unreleased).

Review-fixes:
- [Important] drift_chunk helper had two no-op 'as usize' casts ->
  clippy -D warnings failed at HEAD (missed because the post-recovery
  re-apply of these tests was only cargo-tested, not re-linted) ->
  casts removed; clippy clean, 1172 tests pass.
…sites

A broken vector store rendered as an ordinary empty/short result with
zero signal to the caller — the "search errors must not become empty
results" class. Sites fixed (all in src/mcp/mod.rs):

- single-store literal search: .ok()?? in filter_map -> Err propagates
  via ? to the existing "Error resolving search results" exit
- hybrid semantic+literal fusion: closure mapped Err -> Ok(None) and the
  caller did .ok() on top -> Err noted into single_warnings, loop stops
- find (definitions) + find (usages), multi-store branches: bind the
  lookup, note Err via note_store_failure into the existing
  find_warnings channel (same pattern as resolve_chunk_from_stores)
- find (definitions) + find (usages), single-store branches: pre-resolve
  with ? so Err reaches the "Error opening database" exit

Pinned by tests/store_err_swallow_detector.rs: source-scanning test in
the caller_facing_literals.rs style — fails the build on a direct
get_chunk(..).ok() or an if-let scrutinee on store.get_chunk(..)
anywhere under src/mcp/. Red/green proof: reintroducing the if-let form
on two sites made it fail with exactly those two violations; restored,
it passes.

Validation: cargo check --all-targets, cargo clippy --all-targets
-D warnings, cargo test --lib --bins (1160 passed, 42 ignored),
cargo test --test store_err_swallow_detector (1 passed).

Ref: todo #57 full-review findings.
donbowman and others added 28 commits September 15, 2026 16:24
`serve --model X` was used as the query fallback for a repo whose
`metadata.json` records no model. That overrode a legacy index the moment the
flag was set: a 384-dim index queried with a 768-dim model failed every search
with "Query embedding dimension mismatch: expected 384, got 768", and a
same-dimension model compared incomparable vector spaces without erroring.

Resolve such an index to the built-in default instead: that is both the
historical behaviour and the value every other reader assumes for metadata
without a `model_short_name`. `serve --model` keeps doing what its name says,
selecting the model for newly created indexes.

Surface the assumption to the caller as a search warning naming the repo, the
assumed model and the re-index command, and log it once per repo so a busy hub
does not repeat the same line. The scope-free status summary still reports the
serve default.
fix: resolve each index's own embedding model (serve queries, CLI index/stats/status)
… + rebuild

Symbol-rebuild failures of the MDB_BAD_VALSIZE class (data written by an
older storage-major, observed on all C# repos after the arroy 0.8/heed
0.22 upgrades) now queue the repo for automatic recovery: evict stores
(remove_repo's sequence, minus unregister), wipe the DB dir with the
bounded lock-retry, and force-reindex through the TUI machinery whose
store-open path recreates fresh formats.

Recoveries are processed strictly one repo at a time (single worker,
flag+queue under one lock so no lost wake-up); read-only repos are
skipped with a pointer to the owning writer. Detection helper is
unit-tested; queue dedup + worker start pinned by test.
Serve: auto-recover LMDB format corruption (sequential wipe + rebuild)
…ting (todo #153)

Two independent defects in remote chunk fetch:
1. The peer URL replaced '{id' without the closing brace, producing
   /chunk/2058%7D — axum's {id} param swallowed the stray '}' into the
   captured value, so mock-based tests passed while real peers answered
   400. Replace the full '{id}' placeholder; pinned by a test whose route
   echoes the exact path it was hit on.
2. get_chunk(project=<peer>/<alias>, chunk_id) died with 'Unknown alias':
   mounted remote projects now route through the same federated fetch
   search uses (local aliases win name clashes). Hermetic routing test
   (temp config file — the in-memory config is reloaded from disk, which
   previously leaked the developer's real peers into the test).
Fix federated chunk fetch: URL residue + project-scope routing (todo #153)
dependabot: target develop (rebases were resetting the base to master)
Bumps [criterion](https://github.com/criterion-rs/criterion.rs) from 0.5.1 to 0.8.2.
- [Release notes](https://github.com/criterion-rs/criterion.rs/releases)
- [Changelog](https://github.com/criterion-rs/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](criterion-rs/criterion.rs@0.5.1...criterion-v0.8.2)

---
updated-dependencies:
- dependency-name: criterion
  dependency-version: 0.8.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.5.0 to 4.0.1.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](palfrey/serial_test@v3.5.0...v4.0.1)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-version: 4.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [indicatif](https://github.com/console-rs/indicatif) from 0.17.11 to 0.18.6.
- [Release notes](https://github.com/console-rs/indicatif/releases)
- [Commits](console-rs/indicatif@0.17.11...0.18.6)

---
updated-dependencies:
- dependency-name: indicatif
  dependency-version: 0.18.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [colored](https://github.com/mackwic/colored) from 2.2.0 to 3.1.1.
- [Release notes](https://github.com/mackwic/colored/releases)
- [Changelog](https://github.com/colored-rs/colored/blob/master/CHANGELOG.md)
- [Commits](colored-rs/colored@v2.2.0...v3.1.1)

---
updated-dependencies:
- dependency-name: colored
  dependency-version: 3.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@ea165f8...043fb46)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.6.2 to 3.0.3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@3bb1273...efb3536)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 8.0.1.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@d3f86a1...3e5f45b)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.3.1 to 6.0.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](actions/setup-dotnet@67a3573...a98b568)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/cache](https://github.com/actions/cache) from 4.3.0 to 6.1.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](actions/cache@0057852...55cc834)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Dependency batch 2: cargo majors (criterion/serial_test/indicatif/colored) + CI actions refresh
@flupkede
flupkede merged commit d0ec304 into master Sep 17, 2026
2 checks passed
@flupkede
flupkede deleted the release/v1.4.3 branch September 17, 2026 12:07
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.

3 participants