Skip to content

sync: catch up fork to upstream develop v1.5.32 + harden the macOS dev loop - #5

Merged
nexartis-fleet[bot] merged 246 commits into
masterfrom
release/v1.5.32
Oct 16, 2026
Merged

nexartis-fleet[bot] merged 246 commits into
masterfrom
release/v1.5.32

Conversation

@tony-nexartis

Copy link
Copy Markdown

Summary

Catch our fork up to upstream flupkede/codesearch develop = v1.5.32 (143 commits, v1.5.3 → v1.5.32), then harden the fork's own dev loop.

Base branch is master; head is release/v1.5.32 (satisfies protect-master.yml).

Why

The fork was pinned at upstream master v1.5.2 (release/v1.5.2, PR #4). Upstream develops on develop, which is 30 versions / 143 commits ahead. The fleet installs this fork's pinned binary (nexartis-ozzydev/mcp/upstream-forks.json, pin_version: v1.5.2), so it has been running v1.5.2 while upstream shipped a security refresh and a long list of index-integrity fixes.

Ranked catch-up (per-commit SHA evidence in docs/UPSTREAM-SYNC.md)

Tier 0 — security

Tier 1 — index integrity / data-loss class

  • f149091 stop wiping index caches on every binary swap; harden the transient FTS redrive
  • 241e641 persist file metadata per index batch, atomically
  • 120169b chunk-store records stamped with the builder version, wiped on mismatch
  • 3807739 retry the transient FTS commit race; keep failed adds registered
  • 43d0cc4, 6120429 symbol pairing / noise / FTS scope / atomic save / rebuild guard
  • 85e0a4e file-meta invalidation via warn_print, not println

Tier 2 — tool correctness / status honesty / C# helper robustness — 17 fixes incl. 93090a3 (find→find_impact position round-trip), 07cdddb+abb792c (MCP session negotiation keeps resultType), b4614bd+2cb4976 (symbol-index status), the 0e87917..7ce51e5 status-while-rebuilding group, cc46d3f (Razor FindReferencesAsync), bfcae91 (backslash project paths), d2cf10a (kill helper children on shutdown), 4574c23 (VS-MSBuild restore fallback).

Tier 3 — features — 235f593 (run C# source generators before symbol collection), the #225 restore-gate group.

Tier 4 — chore/deps/CI — tree-sitter-php 0.25.1, cargo/actions bumps, 92d8876.

Not yet in develop (follow-up): upstream/perf/csharp-refs-at-index (fa10be7, 5 perf commits) — land when upstream merges it.

Hardening (this fork)

  • fix(git): scrub ambient GIT_* at every repo-discovery git call. The class was already fixed at ONE site (src/symbols/mod.rs); six others never got it. Measured impact: scripts/qc.ps1 (the pre-push gate) runs cargo test while git push has GIT_DIR/GIT_WORK_TREE set for the hook, so the git-shelling tests read the wrong repo — and the tests' own git commit landed spurious initial commits on the checkout under test. Sites fixed: db_discovery/repos.rs, watch/mod.rs, db_discovery/repos_tests.rs, tests/helpers/mod.rs, cli/mod.rs, serve/mod.rs.
  • test(db_discovery): macOS-correct register_leaves_unix_path_untouched — register() canonicalizes and macOS temp dirs live under the /var → /private/var symlink, so the test failed on every Mac and blocked every push via the QC gate.

Measured before/after: qc.ps1 FAILED (7 lib tests red under the hook env) → ALL CHECKS PASSED; cargo test --lib 1 pre-existing failure → 870 passed / 0 failed (also green under a deliberately poisoned GIT_DIR).

Conflict resolution

Two files, both touched by the fork's two local commits:

  • src/mcp/tests.rs — keep both tests.
  • helpers/csharp/RestorePolicy.cs — take upstream's evolved policy and re-apply the fork delta (packages.lock.json as a restore input).

Fork-local deltas preserved

  • 237a73c — group= hits attributed to their owning repo at creation (chunk ids are store-local and collide across repos; upstream a0c00b1 keys a u32 map, which is what this replaces). Reviewer: this is the one intentional 96-line divergence from upstream on a hot file — worth a second look.
  • 7b47a9b — structured project_unresolved refusal (NEE-P-CONSISTENT).

Review items

  1. search.rs fork delta vs upstream a0c00b1 (above).
  2. docs/UPSTREAM-SYNC.md is a fork-local file upstream lacks — intended as a living sync ledger.
  3. Follow-up: fold the seven inline GIT_* scrubs into one shared git-command helper.
  4. pin_version in nexartis-ozzydev/mcp/upstream-forks.json should be bumped (and the fork release asset republished) once this lands, so the fleet runs v1.5.32.

Verification

  • cargo check --all-targets clean; cargo fmt --check clean; cargo clippy --all-targets -- -D warnings clean.
  • cargo test --lib: 870 passed / 0 failed (13 ignored); integration + doc-tests pass.
  • scripts/qc.ps1 -Fast: ALL CHECKS PASSED.

flupkede and others added 30 commits September 22, 2026 23:52
…lupkede#135)

The incremental-refresh and watcher paths constructed a fresh
EmbeddingService per call; in serve the process-global LMDB registry
refused the second open of the persistent cache, silently degrading
every refresh to cache-less re-embedding. Thread the serve-wide pool
through and reuse pool.get(model) so the process-shared cache is hit;
CLI/standalone-MCP processes keep direct construction (no pool there).
- Chunk paths stored relative to the indexed project root so built DBs are portable across machines/containers (snapshot federation previously worked only because build path == serve path). One-time consequence: existing DBs refreshed by this binary see all files as changed and re-embed once.
- index add --local: skip serve delegation, always build in-process.
- index/index add: write a clean, unmangled build log to <project>/.codesearch.db/build.log when stdout is not a TTY (or --log-file).
- current_git_head: strip GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE/GIT_OBJECT_DIRECTORY from the child env so repo discovery keys off the given root (pre-existing test failure under push hooks).
…locks, status labels

2c13826 made chunk paths project-relative, which re-embedded every legacy
index and duplicated chunks; it also exposed three latent failure modes.
One commit carries four fixes for that fallout:

- Key migration (index/key_migration.rs, cache/file_meta.rs): re-key legacy
  absolute FileMetaStore keys to project-relative form, rewrite the chunk
  paths in both stores, and sweep chunks no tracked file references —
  unchanged files are recognised as up to date instead of re-embedded, and
  the duplicates an earlier interrupted re-embed left are removed.
- FTS writer (fts/tantivy_store.rs): prepare_commit joins and respawns the
  indexing workers and can abort half-way, leaving zero workers behind a
  live 10k-doc pipeline; that writer is now discarded and an error returned
  instead of silently dropping every later batch and blocking add_document.
- Warmup (serve/mod.rs): register Warm and release the per-alias open_lock
  before the refresh so queries are answered from existing data; track the
  refresh with begin/end_indexing so the reaper leaves the repo alone and
  FSW does not start mid-warmup; queries acquire open_lock with a deadline
  instead of queueing behind a cold open forever.
- Status (mcp/status.rs): report indexing/busy/held-in-process instead of
  mislabelling our own activity as locked-externally, and fetch live stats
  via try_read so status answers while a batch holds the store lock.
The orphan sweep ran on every refresh and could delete chunks a concurrent refresh had inserted but not yet recorded; it now runs only after a real migration, and the FSW no longer starts while warmup indexes. Path rewrites lock per batch so queries interleave, every re-keyed chunk is mirrored into FTS so a failed mirror is retried, and a killed FTS writer now returns an error instead of silently dropping documents.
BOIN-scale warmups run past MAX_INDEXING_SECS (30 min), so the lazy
stale-marker eviction dropped the marker mid-refresh and the idle reaper
could evict the repo while the refresh still held its stores (LMDB
double-open / todo flupkede#131 shape). perform_incremental_refresh_with_stores now
fires an IndexingHeartbeat at start, before each slow phase, and per batch;
warmup passes a closure that renews its marker via ServeState::renew_indexing
(upsert, so a raced eviction cannot strip protection for the rest of the run).
Other callers pass None for now.
The FSW's completion callback cleared the whole alias entry with a plain
bool, so a watcher refresh finishing mid-run erased a concurrent force
reindex's marker — the reaper could then evict a repo the reindex still
held (LMDB double-open shape). active_reindexes now maps alias -> owner ->
last heartbeat (IndexingOwner: Warmup/Watcher/Reindex/Symbol); begin/end/
renew are owner-scoped, is_indexing evicts only stale owners and still
cancels a leaked index task, and begin_indexing keeps rejecting while any
owner's marker is fresh. Adds the race, exclusivity and per-owner eviction
tests.

Review-fixes:
- [Critical] Empty-map cleanup dropped the shard guard before remove(alias), so a concurrent begin_indexing insert landing in the gap was erased (re-opened the plain-bool marker race) → all three sites (is_indexing, end_indexing, FSW callback) now use atomic remove_if(alias, |_, m| m.is_empty()) which holds the shard lock across predicate + removal.
index_status_impl's group fan-out still blocked on vector_store.read():
an indexing run holds the write lock for whole batches (build_index for
minutes), so a group status call queued behind it. It now uses try_read
like list_projects; a busy store counts as a failed stats read with a
caller-facing warning naming the busy lock — never silently skipped.
… reader

A live indexer holds vector_store's write lock for the whole batch; every
read-side caller blocked on it forever, so one slow/stuck reindex hung
every query. New CODESEARCH_STORE_LOCK_WAIT_SECS-configurable
bounded_vector_read() (default 300s) wraps every direct
vector_store.read().await in mcp/mod.rs (with_vector_store_read_for,
with_vector_store_read_multi), mcp/runtime.rs (require_prebuilt_index_ready)
and every per-store fan-out site in mcp/find.rs, mcp/get_chunk.rs,
mcp/graph.rs, mcp/literal_search.rs, mcp/search.rs, mcp/explore.rs — each
site now attributes a timeout the same way it already attributes a store
error (note_store_failure / push_store_warning, per its existing warnings
channel), so a busy store degrades one caller's answer instead of hanging
it. FTS store reads are left unbounded — short-duration ops, out of scope.
The prior commit bounded vector_store reads but deliberately left
fts_store.read().await unbounded. A live indexing batch holds both
locks for its duration, so a stuck reindex could still hang every
literal-search/find caller through the FTS side alone. New
bounded_fts_read() (src/mcp/mod.rs, same CODESEARCH_STORE_LOCK_WAIT_SECS
wait as bounded_vector_read) wraps the FTS lock at all four direct
read sites: with_fts_store_read_for's two branches, the per-store loop
in with_fts_store_read_multi (attributes the timeout into `failures`,
same as its vector-store sibling), and runtime.rs's
require_prebuilt_index_ready.
Final review of the 6-stage fallout fix found a narrow window: warmup
registered RepoState::Warm before calling begin_indexing(Warmup), so a
concurrent query landing in that gap could see is_indexing() == false
and let FSW start a Watcher-owned refresh racing warmup's still-loading
file_meta — the exact hazard the per-owner marker refactor exists to
prevent. Fix: acquire the Warmup marker before the repo becomes visible
in self.repos, so FSW's gate always sees it.
Pre-push leak scan flagged 'BOIN' (client codename) in three rationale
comments about warmup duration at scale. Reworded to 'large repos' /
'large-repo' — no behavior change.
find(kind="usages") names its target via 'symbol', not file/path —
edit-guard-post hooks now recognize it so both find_impact and find
attribute to the same key.
…ath-migration

Fix warmup-lock fallout after path migration
`Path::has_root()` only recognizes a Windows drive-letter path (`C:/...`)
as rooted on Windows. On the Linux CI runner it saw such a path as
relative, so `prefix_path_with_alias` wrongly alias-prefixed an
out-of-root Windows path instead of leaving it unprefixed — failing
mcp::tests::test_path_prefix_preserves_path_outside_root in CI.

Added `is_portable_absolute()`, a platform-independent textual check
(leading `/` or drive-letter + `:`), used instead of `has_root()`.
…rts ready

Small models misread "Closed"/indexed:false as "index unavailable" and
wrongly fall back to grep. Idle = index on disk, stores evicted by the
idle reaper, auto-reopen on next query — i.e. searchable, first query
slightly slower. Aggregated index_status now reports indexed:true +
status:"ready" when any repo has an index on disk, with an explicit
"Indexed and searchable: N" hint; "no_index" only when nothing is on
disk. Wire string becomes "idle" (TUIs keep tolerating legacy "closed").

Review-fixes:
- [Important] aggregation branches untested → extracted pure helper
  aggregate_serve_index_status (responses.rs), index_status_impl calls
  it, 3 unit tests pin all-idle→ready/indexed, nothing-on-disk→no_index,
  empty hub→no_repos
- [minor, pre-existing] all-Readonly hub still reports no_index →
  deferred, out of this change's scope (requirement enumerated
  open+warm+idle); follow-up candidate
status_cell's string match now delegates to pure status_cell_spec ->
(String, Style) so tests can assert text+style without a Cell style getter
(ratatui 0.30 Cell::style is a setter). 3 tests: idle+closed gray cell,
Idle label mapping, unknown-status fallback distinct from idle.

Review-fixes:
- [Important] status_cell/detail_status_style string arms (idle | closed) had no test — silent _ fallback could regress legacy-peer tolerance unnoticed → 3 tests in src/serve/tui_common.rs pin cell text/style and label mapping for idle, legacy closed, and the fallback arm
…status

Direct push to develop bypassed bump-develop.yml (fires on PR merges only), so the patch bump is done by hand; CHANGELOG gains the pending [1.4.11] section (finalized with a date at tag time, per the file's convention note).
…de#192)

- .cargo/config.toml: [target.x86_64-pc-windows-msvc] linker = "rust-lld"
  Bin-crate rebuild+link: 2m56s -> 1m32s (x2 faster dev cycle)
- build.ps1: -Release now throws (debug-only policy); release artifacts are
  built by CI (release.yml) only
- sccache: REJECTED — caches compilation, not linking (linking is the
  bottleneck), and disables incremental compilation; same verdict as audit flupkede#191
- tests/*.rs consolidation (53-binaries item in flupkede#191): N/A here — codesearch
  has 5 integration test binaries, consolidation not warranted
- README.md "cargo build --release" (end-user build-from-source) kept:
  end users want optimized binaries; debug-only applies to the dev loop
  (AGENTS.develop.md already mandates it, CI builds debug)
…uilds

build: debug-only policy + rust-lld linker (issue flupkede#192)
Bumps the cargo-minor-and-patch group with 4 updates: [thiserror](https://github.com/dtolnay/thiserror), [fastembed](https://github.com/Anush008/fastembed-rs), [rand](https://github.com/rust-random/rand) and [rmcp](https://github.com/modelcontextprotocol/rust-sdk).


Updates `thiserror` from 2.0.20 to 2.0.21
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](dtolnay/thiserror@2.0.20...2.0.21)

Updates `fastembed` from 7.0.1 to 7.1.0
- [Release notes](https://github.com/Anush008/fastembed-rs/releases)
- [Commits](anush008/fastembed-rs@v7.0.1...v7.1.0)

Updates `rand` from 0.10.2 to 0.10.3
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](rust-random/rand@0.10.2...0.10.3)

Updates `rmcp` from 3.4.0 to 3.4.1
- [Release notes](https://github.com/modelcontextprotocol/rust-sdk/releases)
- [Changelog](https://github.com/modelcontextprotocol/rust-sdk/blob/main/release-plz.toml)
- [Commits](modelcontextprotocol/rust-sdk@rmcp-v3.4.0...rmcp-v3.4.1)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-version: 2.0.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
- dependency-name: fastembed
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor-and-patch
- dependency-name: rand
  dependency-version: 0.10.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
- dependency-name: rmcp
  dependency-version: 3.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…ipeline, find_impact self-heal, tests

- trigger_symbol_rebuild generalized per language with a per-(alias,lang)
  in-flight claim (DashMap, stale takeover past indexing timeout); C#-only
  TUI status / last_scip bookkeeping stays gated on the language
- evaluate_symbol_rebuild: generic gate (applies_to before availability);
  C# delegates to evaluate_csharp_rebuild; non-C# rebuilds only a MISSING
  index (drift stays watcher/branch driven — no-thrash)
- run_phase_2_symbols (was run_phase_2_csharp_scip) iterates all installed
  languages; TypeScript now gets its initial build at startup
- reindex_handler: maybe_rebuild_symbols gate-driven for every language,
  symbols=true still forces (previous unconditional behaviour)
- add_repo_handler: maybe_rebuild_symbols after restart_fsw — closes the
  runtime-add gap (registered while serve runs never saw the startup pass)
- find_impact: missing definitions index (has_index false) starts a
  fire-and-forget heal_missing_symbol_index (serve mode, alias re-resolved
  from config so remote mounts are never healed locally) and the empty
  NotFound answer carries a warning marking it UNKNOWN; a successful
  resolution keeps its payload to the persisted-warnings channel
- tests: claim reject/stale-takeover, gate applicability-before-availability,
  heal no-op outside remit, force skips non-applicable, never-built index
  warns on the empty answer (fails without the fix), phase-2 composition
  wires candidate through to trigger, serve-mode self-heal runs the rebuild

Review-fixes:
- [Test] no coverage reached run_phase_2_symbols wiring or the serve-mode
  heal spawn → added run_phase_2_symbols_wires_candidate_through_to_trigger
  and find_impact_self_heal_runs_the_rebuild_in_serve_mode (dummy helper via
  env override; rebuild failure observable as CSharpIndexStatus::Error)

Validation: cargo fmt --check clean, clippy --all-targets -D warnings clean,
cargo test --lib --bins: 761 passed / 0 failed.
flupkede and others added 16 commits October 15, 2026 16:12
Pressing 'q' stopped the HTTP server but orphaned running helper
children: a one-shot rebuild (index / find-refs / batch-find-refs)
kept computing for minutes on a dead host and held the helper exe
locked — the intermittent 'file in use' failure in copy-to-common —
and a resident workspace child sat waiting on its stdin pipe forever.
Static clients are not dropped at process exit, so the existing
Drop-impl kill never ran on this path.

- LiveHelperChild RAII registry tracks one-shot helper PIDs;
  kill_live_helper_children() taskkills (tree) whatever is still live.
- WorkspacePool::kill_all() drains and kills resident clients.
- run_serve calls both after the graceful-shutdown select.
…the repo

When dotnet restore fails on SDK resolution (NETSDK1045-class: CLI SDKs
older than the repo TFM, or global.json unsatisfiable) scip-csharp now
retries the solution restore once with the newest VS MSBuild located via
vswhere - VS ships its own MSBuild + SDK targets independent of
C:\Program Files\dotnet, so it restores what the CLI cannot even
evaluate. SDK-resolution detection reads stdout AND stderr: NETSDK1045
prints to stdout on current dotnet hosts (verified live with a net99.0
probe). Bounded wait with the existing pending-marker/gated-exit
contract; on failure the per-project fallback still runs (mixed-TFM
recovery preserved).

The MSBuild instance choice for the workspace load is now
capability-aware too: when every CLI SDK instance is older than the
repo's raw-text TFM floor (max over all TFMs + global.json major), the
highest VS instance is registered instead - the SDK preference stands
everywhere else, including its net472-BuildHost-crash rationale.

Motivated by the 2026-10-15 HUSQ worktree incident (hollow 10.0.101 SDK
dir left the CLI blind to net10 while VS built fine).

Deliberate behavior change: SDK-instance pick is the HIGHEST qualifying
SDK (was first-enumerated, an undocumented order); single-SDK machines
unaffected.

Review-fixes:
- [Important] NETSDK1045/global.json detection read stderr only while the
  trigger text prints to stdout on current hosts -> classify over both
  streams; match current ('compatible .NET SDK') and legacy
  ('compatible installed .NET SDK') global.json wordings.
- [Important] multi-target floor took the FIRST TFM (net8.0;net10.0
  floored at 8) -> take the max over all TFMs.
- [Important] inline 10_000 vswhere wait -> VsWhereWaitMs const.
- [Important] healthy-machine invariant weakened on multi-SDK machines ->
  kept highest-qualifying (matches the original 'highest available
  MSBuild' intent) and recorded the change in the branch plan.
17 tests over the fallback's pure logic: SDK-resolution detection pinned
against the live host wordings (NETSDK1045 on stdout, current and legacy
global.json phrasings; NU11xx feed/auth failures must NOT trigger),
vswhere -find parsing, the capability-aware instance choice (qualifying
SDK beats newer VS, floor-miss falls to VS, no-VS keeps SDK as last
resort, highest sibling wins), and the repo TFM floor scan (multi-target
max, project/global.json composition, parent-dir walk-up, null paths).
The multi-target pin was defect-probed: first-TFM-only fails the floor
assert as predicted.

Also fixes the vswhere probe bound (round-1 review note): async drain
before the bounded WaitForExit, and a KILL on timeout — vswhere is a
local metadata probe, not a restore, so its death costs one failed
probe; the kill also closes the pipes so the stdout read cannot block.

Review-fixes:
- [Important] WaitForExit timeout result was discarded and the stdout
  GetResult still blocked until process exit, so VsWhereWaitMs never
  actually bounded a hung vswhere -> kill(entireProcessTree) on timeout
  and return null.
CHANGELOG entry under the pending 1.5.31 heading, highlights line in
AGENTS.develop.md, and the branch registered in the active-branches
table. Plan stage 3 ticked in the branch-local AGENTS.md.

Validation: dotnet build 0w/0e; dotnet test 99/99; cargo fmt --check
clean; cargo clippy --all-targets -D warnings clean; cargo test --lib
--bins all green (Rust side untouched by this branch).
The floor regex ^net(\d+)(?:\.|$) matched the extension-less .NET
Framework spellings (net48 -> 48, net472 -> 472), so a solution with one
net4x project floored at 48: no CLI SDK qualifies, and healthy SDK+VS
machines flipped to the VS MSBuild instance for no reason (exposing the
documented net472-BuildHost crash risk where the SDK path loaded those
projects fine). The minor dot is now required (^net(\d+)\.) - every
net-core TFM has one, no net4x spelling does.

Pinned by NetFrameworkSpellingsDoNotRaiseTheFloor (net48 alone -> null,
net48;net10.0 -> 10); defect-probed: the old regex fails the mixed case
with a 48 floor.

Final-review fix (round 1 finding). Round-2 re-review executed by the
worker itself (reviewer subagent hit its 5h usage limit); scope was the
fix diff only - verified: regex at HEAD, diff touches solely
MsbuildRestore.cs + tests, 100/100 green, round-1 met-rows untouched.
…uild-fallback

feat: VS-MSBuild restore fallback when the CLI SDK set cannot target the repo
… bad project= alias now returns a structured project_unresolved envelope with available_projects/groups instead of the bare "Unknown alias 'x'" string; + unit test (cargo test 812 pass, 1 pre-existing macOS /private symlink failure in db_discovery, unrelated)

Signed-off-by: Tony Rose <174962249+tony-nexartis@users.noreply.github.com>
Merges upstream develop (143 commits, v1.5.3..v1.5.32) onto release/v1.5.2.

Carries every upstream fix/feature since v1.5.2, including the quinn-proto
GHSA-53rm-773f-4q8c security refresh (e61b2cd).

Conflict resolution (2 files, both touched by our 2 fork-local commits):
- src/mcp/tests.rs: keep BOTH tests (our project_unresolved classification
  test + upstream's single_index_status rebuild test).
- helpers/csharp/RestorePolicy.cs: take upstream's evolved restore policy
  (FindWalkRoot repo-root walk, case-insensitive input enumeration,
  HasUsableAssets, ExtractRestoreErrors) and re-apply our fork delta
  (packages.lock.json as a restore input, 237a73c P2).

Fork-local commits preserved: 237a73c (group= repo attribution, auto-merged
with upstream a0c00b1) and 7b47a9b (project_unresolved NEE-P-CONSISTENT).
…2 catch-up evidence)

Ranked catch-up list with per-commit SHA evidence, the fork-local deltas
preserved through the sync, and the not-yet-in-develop perf branch as a
follow-up. Answers the ToRFO codesearch audit mission step 2 durably.
The pre-push hook (scripts/qc.ps1) blocked EVERY push from a Mac. Two causes,
both measured:

1. register_leaves_unix_path_untouched compared register()'s canonicalized
   output against the raw temp path. On macOS the temp dir lives under the
   /var -> /private/var symlink, so the two never matched. Compare against
   std::fs::canonicalize(repo) so the test pins its real intent (no
   Windows/MSYS rewrite on Unix), not the host's symlink layout.

2. `git push` exports GIT_DIR/GIT_WORK_TREE to the pre-push hook, and the QC
   gate runs `cargo test` inside it. Every git call that keys off an explicit
   path (-C dir / current_dir) was overridden by the ambient GIT_DIR and read
   the WRONG repo; worse, the tests' own `git commit` landed spurious
   'initial' commits on the checkout under test. The class was already fixed
   at ONE site (src/symbols/mod.rs current_git_head); six others never got it.
   Scrub GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE/GIT_OBJECT_DIRECTORY at all of
   them: db_discovery/repos.rs, watch/mod.rs, db_discovery/repos_tests.rs,
   tests/helpers/mod.rs, cli/mod.rs, serve/mod.rs.

Measured before/after: qc.ps1 FAILED (7 lib tests red under the hook env, and
the hook committed into the repo under test); after, 870/870 lib tests pass
under a poisoned GIT_DIR and qc.ps1 reports ALL CHECKS PASSED.

Follow-up: fold the seven inline scrubs into one shared git-command helper.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T12:06:08.830188Z 803fa32 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 803fa32472

ℹ️ 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/limits.rs Resolved
Comment thread integrations/opencode/codesearch.ts Resolved
Comment thread integrations/opencode/codesearch.ts Resolved
@tony-nexartis

Copy link
Copy Markdown
Author

Perf A/B — v1.5.2 (baseline) vs v1.5.32 (this PR)

Same corpus (29 .rs files), fresh index dir + fresh CODESEARCH_HOME per binary, release builds of both. Host was heavily loaded throughout (load avg ~46–56), so these are noisy; treat them as a no-regression check, not a precise benchmark.

metric v1.5.2 (1.5.2+1344) v1.5.32 (1.5.32+1488)
cold index (29 files) 26.94 s 26.44 s
warm incremental reindex 0.04 s 0.02 s
semantic search (3 runs) 0.09 / 0.10 / 0.10 s 0.09 / 0.08 / 0.07 s

No regression. The catch-up is at parity within noise. The cold path (~0.9 s/file) is dominated by per-file embedding + cold model init on a saturated CPU; warm reindex and query are sub-100 ms.

Caveat / follow-up: a rigorous A/B needs a quiet host. A first-class codesearch bench --json (cold/warm index + query p50/p95) would make this reproducible instead of ad-hoc — noted as a product idea in the lane report.

@tony-nexartis

Copy link
Copy Markdown
Author

CodeQL "1 new high alert" — disposition: FALSE POSITIVE on pre-existing upstream code (not introduced here)

Alert: rust/weak-sensitive-data-hashing at src/serve/mod.rs (api_key_matches) — "Sensitive data (password) is used in a hashing algorithm (SHA256) … insecure for password hashing".

Why it is a false positive: the SHA-256 there is not password storage. It builds two fixed-length 32-byte digests so the API-key comparison is constant-time and length-independent (see the function's own doc comment). The secret is a high-entropy API key, never stored as a hash for offline cracking. CodeQL's taint heuristic classified the compared strings as a password.

Not introduced by this PR: fn api_key_matches already exists on the base branch release/v1.5.2 (git show release/v1.5.2:src/serve/mod.rs | grep -c 'fn api_key_matches' = 1). It surfaced now because this PR's diff is very large, which changes CodeQL's alert attribution ("Alerts not introduced by this pull request might have been detected because the code changes were too large").

Not a merge blocker: gh api repos/Nexartis/codesearch/rules/branches/master returns [] — master carries no required status checks; CodeQL is advisory here.

Recommended hardening (deferred, not done in this lane): replace the hand-rolled SHA-256-digest compare with the idiomatic subtle::ConstantTimeEq (candidate.as_bytes().ct_eq(configured.as_bytes())) — subtle is already in Cargo.lock as a transitive dep. Trade-off: subtle's slice ct_eq short-circuits on length mismatch, so it leaks the configured key's length (negligible; the current digest approach hides it). Deliberately not changed here: a rushed edit to authentication code, late in a long session, without an independent auditor, is riskier than the documented false positive — per validate before acting / incident-class changes get a final auditor. Routed to the merger/operator to dismiss-with-rationale or take as a follow-up.

- limits: cancellation-aware memory-admission wait (abort at once, release the slot)
- opencode plugin: gate the startup /healthz probe on health.enabled; honour scope.enabled for the scope prelude
- serve: constant-time byte-fold API-key compare (no SHA-256; clears rust/weak-sensitive-data-hashing false positive)
- tests: limits cancellation test + smoke guards (health probe, scope prelude)

Signed-off-by: Test User <test@example.com>
@tony-nexartis

Copy link
Copy Markdown
Author

Sync catch-up fix-forward — head c6db0ba

Cleared the gates blocking this PR:

  • CodeQL rust/weak-sensitive-data-hashing (src/serve/mod.rs): the API-key comparison now uses a constant-time byte fold instead of SHA-256 on both sides — no hash for an equality check. Existing test_api_key_matches still passes.
  • Review threads (3, resolved): memory-admission wait is now cancellation-aware (src/limits.rs + regression test); the startup /healthz probe is gated on health.enabled; the injected scope prelude honours scope.enabled (integrations/opencode/codesearch.ts + smoke guards).
  • Rust: cargo fmt --check clean · cargo clippy -D warnings clean · cargo test --lib 871 pass / 0 fail.
  • opencode plugin: tsc --noEmit + unit + smoke green.

Supersedes #4 (this branch contains #4 head 7b47a9b as an ancestor).

@tony-nexartis

Copy link
Copy Markdown
Author

Merge routed — blocked on a stale local console release (no bypass used).

The gated verb accepts --base master and the readiness triple is green on c6db0ba (checks green, 0 unresolved threads, clean tree). It refuses merge_change_review_required — one approved D1 change review at the exact head is required.

The ceremony is blocked because this cubicle's console runs a pinned release (4ae78a0c7, 2026-10-15 11:21) that predates the operator-authorized fix 26944de60 (widen the change-review base enum to master|main for fork-mirror repos):

  • POST http://127.0.0.1:11149/api/change-reviews {base:"master"} -> invalid_request: base: Invalid option: expected one of "dev"|"prod".
  • POST https://product.nexartis.com/api/change-reviews (signed machine lane) -> machine_lane_path_not_allowed.
  • MCP route_change_review coerces base to dev/prod.

Remedy (one desk step): rebuild this cubicle's console onto HEAD, then re-run the ceremony.

  • ./bin/bootstrap.sh --per-cubicle-console --no-open --workspace <ws> is currently refused by console_release_source_dirty_preflight: a peer (feat/codesearch-wave2) holds console/src/lib/server/test-migrations.test.ts staged (the change-review base-enum migration test).
  • Once that peer lands (console-scope tree clean), re-run the bootstrap, then: route_change_review base=master -> accept -> record correctness/required_checks/merge_readiness -> approve -> ozzydev-merge-bot-ship --repo Nexartis/codesearch --pr 5 --head c6db0ba120c2133ea2535127aee24c0dfe607517 --base master.

No gh api .../merge, no --admin. #4 is closed (superseded — this branch contains its head 7b47a9b).

@tony-nexartis

Copy link
Copy Markdown
Author

Merge blocked — ROUTE with a named gate. This PR is green and merge-ready (CI green on c6db0ba, 0 unresolved threads, MERGEABLE/CLEAN; D1 change review approved at the exact head, rc-06GMCD4C71467WA11GF32Q9PBR).

The gated master-base mergebot path (ozzydev-merge-bot-ship --base master) passes the readiness triple and the D1 change review, then refuses at the mesh-effect admission:

invalid_request — control plane POST /api/mesh/execution command.base: Invalid option: expected one of "dev"|"prod"

Root cause: console/src/lib/server/mesh-execution-contract.ts types base as z.enum(['dev','prod']) at 6 sites (confirmed at origin/dev tip; no in-flight widening). The master-base gated path (SI 2026-10-16, nexartis-ozzydev#431) does not skip the mesh effect for master, so every fork-mirror (master-base) PR is unmergeable via the gated path.

Fix (owner: nexartis-ozzydev): widen the mesh-execution-contract.ts base enums to include master|main (or have the mergebot send the repo occupancy base for fork-mirror repos), land on the one-PR-to-dev, then deploy the console dev. Re-run the mergebot after — the approved D1 review at c6db0ba will be reused if the head has not moved.

Catch-up verdict: this PR carries 143 upstream commits (flupkede/codesearch develop v1.5.3 → v1.5.32). upstream/develop is now v1.5.34 (15 ahead) — next sync scope.

@tony-nexartis

Copy link
Copy Markdown
Author

Merge routed — blocked on a nexartis-ozzydev mesh-effect contract gap (no bypass, no raw merge).

The gated master-base mergebot (ozzydev-merge-bot-ship --base master) passes every gate on pinned head c6db0ba120c2133ea2535127aee24c0dfe607517:

  • readiness triple — --dry-run verdict ready, blockers: [], unresolvedThreads: 0 (checks green: Analyze csharp/rust, CodeQL, check-source-branch, no-agent-files; clean tree).
  • D1 change review — approved at the exact head, rc-06GMCD4C71467WA11GF32Q9PBR, 3/3 checks pass (correctness, required_checks, merge_readiness).
  • base pin + validate-on-tip — passed.

It then refuses at the mesh-effect admission:

invalid_request — Ozzy Dev control plane refused POST /api/mesh/execution: command.base: Invalid option: expected one of "dev"|"prod"

Root cause (measured, not inferred)

console/src/lib/server/mesh-execution-contract.ts types base as z.enum(['dev','prod']) at 6 sites (lines 24, 48, 58, 99, 279, 292; plus the repository_ref ref enum at line 189). bin/lib/mesh-effect-admission.mjs:325 sends base: input.base ?? 'dev', and the mergebot sets scope.base = v.baseRefName = 'master' (bin/ozzydev-merge-bot-ship.mjs:1057). nexartis-ozzydev#431 added the master-base mergebot path (readiness triple, no prod legs) but did not widen this server enum — so every fork-mirror (master-base) PR is unmergeable via the gated path, the exact ship_input_invalid dead-end SI 2026-10-16 replaced with a live one.

Rebuild-dist retry does not apply

git diff HEAD origin/dev -- bin/ozzydev-merge-bot-ship.mjs is empty — the local mergebot is byte-identical to origin/dev's, and the blocker is a deployed-server zod enum, not a stale local dist. mesh-execution-contract.ts is not a generated mcp/packages twin (grep: no mcp/** reference), so pnpm --filter "./mcp/packages/**" run build cannot change it.

Fix (owner: nexartis-ozzydev)

Widen the base enums in console/src/lib/server/mesh-execution-contract.ts to z.enum(['dev','prod','master','main']) (6 sites) — or have the mergebot send the repo's occupancy base for fork-mirror repos — land on the one-PR-to-dev, deploy the console dev, then re-run the mergebot. The approved D1 review at c6db0ba is reusable while the head does not move.

Catch-up verdict

This PR carries 143 upstream commits (flupkede/codesearch develop v1.5.3 → v1.5.32), per docs/UPSTREAM-SYNC.md. upstream/develop is now v1.5.35 (22 ahead of this branch) — next sync scope.

No gh api …/merge, no --admin, no bypass.

@tony-nexartis

Copy link
Copy Markdown
Author

⛔ Merge blocked — fleet control-plane contract gap (not a codesearch defect)

The gated merge verb (bin/ozzydev-merge-bot-ship.mjs --repo Nexartis/codesearch --pr 5 --head c6db0ba12… --base master) refuses, deterministically (before and after the instructed pnpm --filter "./mcp/packages/**" run build rebuild):

class:   invalid_request
message: Ozzy Dev control plane refused POST /api/mesh/execution (invalid_request):
         command.base: Invalid option: expected one of "dev"|"prod"

This PR is otherwise merge-ready: all required checks green (Analyze (csharp), Analyze (rust), CodeQL, check-source-branch, no-agent-files), 0 unresolved threads, MERGEABLE / CLEAN, mergebot dry-run ready.

The gate (named)

Repo: Nexartis/nexartis-ozzydev — not this repo.
File: console/src/lib/server/mesh-execution-contract.ts
The governed-effect contract restricts base to dev|prod (:279 check_effect, :292 authorize_effect, and the operation schema :24/:48/:58/:99), so POST /api/mesh/execution rejects any fork-mirror default-branch base. The mergebot admits every merge as a governed effect (runMeshEffect, no bypass) with command.base = 'master'.

Contrast: change-reviews.ts:379 already accepts dev|prod|master|main, and the change_review_requests_base_check D1 constraint already includes master,main — the change-review plane supports fork-mirror bases; the effect plane does not.

Origin: SI ace2943c9 added master to the mergebot GATED_BASES but did not extend mesh-execution-contract.ts (git log --all -S finds no branch that does).

Owner action

  1. Widen the effect base enum to ['dev','prod','master','main'] in mesh-execution-contract.ts (mirror change-reviews.ts:379).
  2. Check occupancies_base_check (db/schema.ts:2794, still dev,prod) — the effect-begin lease heal may need a migration if master occupancies are required (mesh_execution_effect_reservations.base has no check — fine).
  3. Deploy the console to product.nexartis.com.
  4. Re-run the gated merge at head c6db0ba120c2133ea2535127aee24c0dfe607517.

Routed by session codesearch-5 (workstream codesearch-5). Full packet: _WORKSPACE-DOCS/codesearch-5-route-2026-10-16.md.

@nexartis-fleet nexartis-fleet 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.

Fleet merge bot: exact reviewed candidate readiness re-verified at execution.

@nexartis-fleet
nexartis-fleet Bot merged commit c3364e0 into master Oct 16, 2026
5 checks passed
@nexartis-fleet
nexartis-fleet Bot deleted the release/v1.5.32 branch October 16, 2026 22:49
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.

4 participants