Skip to content

feat: Rust rewrite foundation — ADR-0003, Phase 0 scaffold, plan#34

Merged
amondnet merged 32 commits into
mainfrom
tracks/rust-rewrite-20260618
Jun 19, 2026
Merged

feat: Rust rewrite foundation — ADR-0003, Phase 0 scaffold, plan#34
amondnet merged 32 commits into
mainfrom
tracks/rust-rewrite-20260618

Conversation

@amondnet

@amondnet amondnet commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Rust rewrite of @pleaseai/csp (ADR-0003)

Incremental, leaf-first port of the TypeScript/Bun implementation to a Rust Cargo workspace (crates/csp library + crates/csp-cli csp binary), verified against the TS test suite reused as golden fixtures. Closes the bulk of track rust-rewrite-20260618 (T001–T024).

Status — 263 tests green (255 lib + 8 CLI), fmt + clippy -D warnings clean

Phase Tasks State
P1 pure core (tokens, ranking, BM25 math) T001–T008 ✅ done
P2 chunking (line-fallback at TS-stub parity) T009–T010 ✅ done
P3 indexing (file walking, cache, dense-stub, create) T011–T016 ✅ done
P4 search pipeline + CspIndex core API T017–T018 ✅ done
P5 clap CLI + savings telemetry T019–T020 ✅ done
P6 MCP tool core (cache + safety + handlers) T021 ◐ core done; rmcp stdio transport deferred
P7 distribution (release workflow, npm wrapper, Homebrew) T022–T024 ◐ authored; CI/publish-gated cutover

Honest caveats (why the last two phases are partial)

  • Behavioral oracle is the TS test suite. Parity is verified against golden fixtures captured from the TS tests. Two upstream behaviors are deterministic stubs in the TS source itself — the Model2Vec dense embedding (dense.ts) and the inline search-ranking shims (search.ts TODO(integration)). The Rust port reproduces those stubs bit-for-bit. Real model2vec + full ranking wiring is future work the TS side hasn't done either.
  • T021 MCP: the verifiable core (csp::mcpIndexCache LRU/evict/routing, get_index URL-safety, search/find_related handlers) is ported and tested against server.test.ts. The rmcp stdio transport is intentionally not wired — its on-the-wire schema can't be verified without an MCP client (plan STOP).
  • T022–T024: release workflow + npm wrapper + Homebrew are authored as reviewable, non-disruptive scaffolding (release-rust.yml is workflow_dispatch-only; the live npm package still ships the TS build). Cutover is a maintainer decision gated on runtime parity — see the plan Progress log for the checklist.

Full task-by-task log: .please/docs/tracks/active/rust-rewrite-20260618/plan.md.

Verification Checklist

Each item reflects evidence actually observed this session (per the evidence gate), not the diff alone:

  • Per-phase equivalence (T001) — cargo test --workspace green: 255 lib + 8 CLI tests pass against the golden fixtures.
  • CI gate (SC-005) — cargo fmt --all --check OK, cargo clippy --all-targets --all-features -- -D warnings clean, cargo test --workspace green (all run locally this session).
  • Parity on fixtures (SC-001, SC-004) — Rust reproduces the TS behavior encoded in the reused test fixtures. ⚠️ Caveat: the oracle is the TS test suite; TS's dense embedding + ranking are deterministic stubs in the TS source, which Rust reproduces bit-for-bit. Real model2vec/full ranking is future work the TS side hasn't done either.
  • Surface — bunx @pleaseai/csp (SC-002) — the npm wrapper launcher was verified end-to-end locally (resolves the platform package, execs the binary, forwards argv, exits 1 cleanly when the platform package is absent). ⚠️ The published npm package still ships the TS build; cutover to the Rust binary is a maintainer decision (checklist in plan Progress log).
  • Distribution — single binary, no Node/Bun (SC-003) — built the release binary (x86_64-apple-darwin, 3.9M stripped+lto) and cross-compiled aarch64-apple-darwin; ran ./target/release/csp --version + a real search directly as a native executable (no Node/Bun runtime).
  • Publish side-effects — GitHub Releases upload, npm publish --provenance, homebrew-tap push: NOT done (require secrets + a real release tag; CI-only, not part of this PR).

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a Rust-based csp CLI with search, indexing, related-code discovery, agent initialization, and cache/savings clearing.
    • Added an MCP stdio server exposing search and related-code tools.
    • Implemented hybrid retrieval with improved chunking, language detection, ranking, and scoring.
  • Infrastructure
    • Added Rust CI (format/lint/test) and a manually-triggered cross-platform release workflow that builds artifacts and uploads checksums/assets.
    • Added an npm wrapper that selects the correct prebuilt binary per platform.
  • Documentation
    • Expanded “csp-search” agent guidance templates and added migration/rewrite planning docs.

amondnet added 4 commits June 18, 2026 17:38
Record the decision to rewrite @pleaseai/csp from TypeScript/Bun to Rust.
Captures the four motivations (single-binary distribution, indexing/embedding
performance, ecosystem fit, maintainer preference), verified Rust crate mapping
(model2vec-rs, rmcp, tree-sitter, ignore, clap), the Biome-style multi-channel
distribution strategy that preserves the bunx entrypoint, and the decision to
defer the JS library API behind a future napi-rs seam.

Status: Proposed.
Set up the Rust rewrite skeleton alongside the TypeScript tree:

- Cargo workspace with crates/csp (library seam) and crates/csp-cli (the
  `csp` binary), pinned to Rust 1.94.1 via rust-toolchain.toml.
- workspace.dependencies pre-declares the ADR-0003 crate menu (model2vec-rs,
  tree-sitter, ignore, rmcp, clap, serde) for phase-by-phase wiring.
- clap CLI stubs all README subcommands (search/index/find-related/mcp/init/
  savings/clear); each bails with an ADR-0003 reference until its phase lands.
- Rust CI workflow (fmt/clippy -D warnings/test), reusing the SHA-pinned
  actions/checkout and the runner's rustup honoring rust-toolchain.toml.
- release profile tuned for single-binary distribution (lto/strip).

The TypeScript src/ remains source of truth until Rust reaches parity.
Track for ADR-0003 Phases 1-7 (Rust rewrite). spec.md captures scope, the
behavioral-equivalence success criteria (SC-001..005), and the JS library API
deferral; plan.md breaks the work into 24 tasks across 7 phase sections with
golden-fixture verification and STOP conditions on the high-risk ports
(model2vec-rs embeddings, rmcp MCP parity, cache format).

Refs #33
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9caa1d2c-7a32-45b8-be4d-2db68ea46c9b

📥 Commits

Reviewing files that changed from the base of the PR and between d19d727 and 2bfb2d5.

📒 Files selected for processing (4)
  • eslint.config.ts
  • npm/csp/bin/csp.js
  • npm/csp/package.json
  • npm/scripts/generate-platform-packages.mjs
🚧 Files skipped from review as they are similar to previous changes (4)
  • eslint.config.ts
  • npm/csp/package.json
  • npm/scripts/generate-platform-packages.mjs
  • npm/csp/bin/csp.js

📝 Walkthrough

Walkthrough

This PR implements a full Rust rewrite of @pleaseai/csp as a two-crate Cargo workspace (crates/csp core library and crates/csp-cli binary), porting TypeScript/Bun logic for chunking, BM25+dense hybrid indexing, search, ranking, MCP server, telemetry, and CLI. It adds CI workflows, an npm platform-selector wrapper, per-agent markdown guides, and ADR/track documentation.

Changes

Rust CSP Rewrite

Layer / File(s) Summary
Workspace, toolchain, CI, and project config
Cargo.toml, rust-toolchain.toml, rustfmt.toml, .gitignore, .github/workflows/rust.yml, .github/workflows/release-rust.yml, .claude/settings.json, .codacy.yaml, eslint.config.ts
Establishes the Cargo workspace with two member crates and shared dependency versions, pins the toolchain to 1.94.1 with rustfmt/clippy, configures CI for format/lint/test on relevant path changes, adds a manual cross-platform release workflow building macOS/Linux/Windows binaries and uploading to GitHub Releases, and extends .gitignore for /target/ and npm/dist/.
Core domain types, tokenizer, utilities, and crate root
crates/csp/src/lib.rs, crates/csp/src/types.rs, crates/csp/src/tokens.rs, crates/csp/src/utils.rs
Defines CallType, ContentType, Chunk, ChunkDict, SearchResultDict, IndexStats, and their Serde serialization/validation; implements camelCase/snake_case identifier-aware BM25 tokenization; adds JSON wire-format helpers, scp-style git URL detection, and resolve_chunk; declares the public crate module surface.
AST and line-based chunking
crates/csp/src/chunking/mod.rs, crates/csp/src/chunking/core.rs, crates/csp/src/chunking/source.rs
Implements ChunkBoundary, AstNode trait, language_for grammar resolver, recursive merge_node, chunk_lines (CRLF-preserving), and real tree-sitter chunk implementation; adds chunk_source dispatching to AST or line-based chunking and computing 1-indexed line numbers.
File walking, language detection, BM25, and dense embedding
crates/csp/Cargo.toml, crates/csp/src/indexing/mod.rs, crates/csp/src/indexing/file_walker.rs, crates/csp/src/indexing/files.rs, crates/csp/src/indexing/sparse.rs, crates/csp/src/indexing/dense.rs
Implements gitignore-aware recursive file walking with .cspignore and negation-bypass support; provides EXTENSION_TO_LANGUAGE mapping and detect_language/get_extensions lookups; implements enrich_for_bm25 and Bm25Index with TS float-accumulation parity; implements deterministic stub embedding model and SelectableBasicBackend with top-k cosine query.
Index cache, creation orchestration, and CspIndex API
crates/csp/src/indexing/cache.rs, crates/csp/src/indexing/create.rs, crates/csp/src/indexing/index.rs
Implements SHA-256 cache-key computation with source/content/ref invariance, Unix 0700 permissions, and clear_index_cache with symlink-escape prevention; adds create_index_from_path orchestrating walk→chunk→tokenize→embed→build; implements CspIndex with from_path/from_git, search/find_related, persistence, and load_or_build_index cache orchestration.
Ranking pipeline and hybrid search
crates/csp/src/ranking/mod.rs, crates/csp/src/ranking/weighting.rs, crates/csp/src/ranking/boosting.rs, crates/csp/src/ranking/penalties.rs, crates/csp/src/search.rs
Defines Scores alias and ranking submodules; adds resolve_alpha for semantic/BM25 blending; implements symbol-query detection, fancy-regex definition boosting, keyword/stem matching, and boost_multi_chunk_files; adds path penalties and rerank_top_k with saturation decay; implements semantic+BM25→RRF→alpha-combine→rerank pipeline.
MCP core, telemetry, CLI, and MCP server
crates/csp/src/mcp.rs, crates/csp/src/stats.rs, crates/csp-cli/src/main.rs, crates/csp-cli/src/mcp_server.rs, crates/csp-cli/Cargo.toml
Implements LRU IndexCache, get_index scheme validation, search_tool/find_related_tool handlers; adds JSONL telemetry with UTF-16 accounting, UTC bucketing, and colored ASCII reports; wires all CLI subcommands via clap; implements rmcp stdio MCP server with async tool handlers and schema-backed parameters.
npm distribution wrapper, agent guides, and project documentation
npm/README.md, npm/csp/bin/csp.js, npm/csp/package.json, npm/scripts/generate-platform-packages.mjs, crates/csp-cli/agents/*, .please/docs/, CLAUDE.md
Adds Node.js platform-selector launcher with musl detection, @pleaseai/csp manifest with per-platform optional dependencies, and generate-platform-packages.mjs release script; adds ADR 0003, track plan/spec/metadata, product spec entries, tech-debt tracker updates, CLAUDE.md Rust guidance, and ten per-agent markdown guides.

Sequence Diagram(s)

sequenceDiagram
  participant Client as MCP Client
  participant CspMcpServer as CspMcpServer (stdio)
  participant IndexCache as IndexCache (LRU, Mutex)
  participant CspIndex as CspIndex
  participant OnDisk as On-disk Cache
  
  Client->>CspMcpServer: tool call search(query, repo, top_k)
  CspMcpServer->>IndexCache: lock + get(source, git_ref)
  alt cache miss
    IndexCache->>OnDisk: load_from_disk (check contentHash)
    OnDisk-->>IndexCache: miss or stale
    IndexCache->>CspIndex: from_path / from_git (build)
    CspIndex-->>IndexCache: built CspIndex
    IndexCache->>OnDisk: save(dir, content_hash)
  else cache hit
    OnDisk-->>IndexCache: load success
  end
  IndexCache-->>CspMcpServer: Arc~CspIndex~
  CspMcpServer->>CspIndex: search(query, QueryOptions)
  CspIndex-->>CspMcpServer: Vec~SearchResult~
  CspMcpServer-->>Client: CallToolResult (JSON text)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #33: The PR directly implements the seven-phase Rust rewrite scope tracked in this issue. The .please/docs/tracks.jsonl explicitly links rust-rewrite-20260618 to issue #33, and the Cargo workspace, core modules (chunking, indexing, search, ranking), CLI, and MCP server fulfill all enumerated phase goals (types, tokenization, BM25, dense embeddings, search orchestration, CLI subcommands, MCP tools, and npm distribution).

Poem

🦀 Hop hop, little rabbit, the TypeScript days are done,
Now Rust takes the stage with BM25 fun!
The chunks align, the vectors glow,
Through RRF pipelines the search results flow.
From csp search to MCP—all wired up tight,
A rewrite complete by the end of the night! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing the Rust rewrite foundation (ADR-0003) with Phase 0 scaffold and planning documentation. It is concise, specific, and clearly communicates the primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tracks/rust-rewrite-20260618

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request scaffolds Phase 0 of rewriting the @pleaseai/csp tool from TypeScript to Rust, establishing a Cargo workspace with a core library (csp) and a CLI binary (csp-cli). It also includes the architectural decision record (ADR 0003), a detailed migration plan, and toolchain configurations. The review feedback recommends fixing a typo ('невали드' to 'invalid') in the plan document and deriving standard traits (Debug, PartialEq, Eq) on the ContentFilter enum in the CLI crate for better idiomatic Rust design.

Comment thread .please/docs/tracks/completed/rust-rewrite-20260618/plan.md Outdated
Comment thread crates/csp-cli/src/main.rs Outdated
@codacy-production

codacy-production Bot commented Jun 18, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 937 complexity · 34 duplication

Metric Results
Complexity 937
Duplication 34

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

amondnet added 20 commits June 18, 2026 19:00
Phase 1 leaf modules ported into crates/csp with behavioral equivalence
verified against the TypeScript test vectors (32 tests):

- types: ContentType/CallType enums, Chunk, chunk_location, chunk_to_dict,
  chunk_from_dict (untrusted-JSON validation replacing the TS TypeError guards).
- tokens: identifier-aware tokenizer; the camelCase splitter is reimplemented
  as a state machine because the Rust regex crate lacks the upstream CAMEL_RE
  lookahead — it reproduces the regex match sequence exactly.
- utils: is_git_url (scheme + hand-rolled scp lookahead) and resolve_chunk.

cargo fmt / clippy -D warnings / test all green.

Refs #33
…007, T006 partial)

Add the crates/csp ranking module:

- weighting: resolve_alpha (adaptive alpha 0.3 symbol / 0.5 NL), honoring an
  explicit Some(0.0) vs None like the TS null/undefined distinction.
- penalties: file_path_penalty (test/barrel/.d.ts/compat/examples patterns) and
  rerank_top_k with file-saturation decay and early-exit, keyed by chunk index.
- boosting::is_symbol_query (SYMBOL_QUERY_RE) — consumed by weighting; the rest
  of boosting (apply_query_boost, definition detection) follows in T006.

Score maps use IndexMap<usize, f64> (insertion-ordered, chunk-index keys) as
the Rust analogue of the TS Map<Chunk, number>. Penalty regexes stay
Unicode-aware (negated classes can't disable Unicode in a string Regex).
58 equivalence tests pass; fmt / clippy -D warnings / test green.

Refs #33
Add crates/csp/src/indexing/sparse.rs:

- enrich_for_bm25: content + stem×2 + last 3 dir parts, POSIX-normalized.
- selector_to_mask: index selector → 0/1 mask, out-of-bounds dropped.
- Bm25Index::{build, get_scores}: minimal Okapi BM25+ (k1=1.5, b=0.75) with
  Lucene/Robertson IDF and optional weight mask.

Two parity details reproduced exactly: per-addition f32 rounding (matching the
TS Float32Array accumulation) and first-appearance unique-term ordering — both
affect the order-sensitive f32 scores. save/load (filesystem) is deferred to
the Phase 3 cache task (T014); the state already derives serde.

73 equivalence tests pass; fmt / clippy -D warnings / test green.

Refs #33
Complete the boosting module:

- apply_query_boost: symbol-definition boosts (with non-candidate stem scan),
  embedded-symbol half-strength boosts, and NL stem-match boosts.
- boost_multi_chunk_files: per-file coherence boost of the top chunk.
- chunk_defines_symbol / definition_pattern: language definition detection via
  fancy-regex, since the upstream (?<=\s) lookbehind is unsupported by the
  regex crate; patterns transcribed verbatim and cached per symbol name.
- supporting helpers (extract_symbol_name, stem_matches, count_keyword_matches,
  path stem/parent helpers) all matched to the TS semantics.

This completes Phase 1 (pure core). 88 equivalence tests pass across tokens,
types, utils, ranking (weighting/boosting/penalties), and BM25; fmt /
clippy -D warnings / test green.

Refs #33
…mplete

Add the crates/csp/src/chunking module:

- core: the merge algorithm (merge_node_inner/merge_node/merge_adjacent_chunks)
  generic over an AstNode trait (unit-tested with mock nodes, later driven by
  tree_sitter::Node), chunk_lines (CRLF-aware, character offsets), constants
  (RECURSION_DEPTH=500, MIN_CHUNK_SIZE=50), and the chunk()/is_supported_language
  contract.
- source: chunk_source with 1-indexed line numbering (only \n counts) and the
  AST-vs-line-fallback selection.

At parity with the current TS, is_supported_language is a false stub and real
tree-sitter grammar parsing activates with the language map (T012); until then
callers use the line fallback. 115 equivalence tests pass; fmt / clippy
-D warnings / test green.

Refs #33
Add crates/csp/src/indexing/files.rs:

- EXTENSION_TO_LANGUAGE: the full ~330-entry extension→language map,
  transcribed verbatim (.txt→vimdoc intentionally omitted as upstream).
- DOC/CONFIG/DATA language sets and the derived CODE set (ALL − DOC − CONFIG −
  DATA).
- detect_language: case-insensitive final-suffix lookup, dotfile-aware
  (.gitignore → None), POSIX + Windows separators.
- get_extensions: sorted, de-duplicated union of extensions by content type
  with user extensions appended.

This populates ALL_LANGUAGES (the future driver for tree-sitter activation in
chunking, currently a separate stub at TS parity). 129 equivalence tests pass;
fmt / clippy -D warnings / test green.

Refs #33
Add crates/csp/src/indexing/file_walker.rs using the `ignore` crate:

- DEFAULT_IGNORED_DIRS (.csp/ replacing semble's .semble/).
- load_ignore_for_dir: merge .gitignore + .cspignore (gitignore first),
  skipping blanks/comments, preserving source order.
- is_ignored: maps Gitignore::matched -> Match::{None,Ignore,Whitelist} onto the
  upstream npm `ignore` {ignored,unignored} contract. Reproduces the
  negation-with-extension bypass (found) via per-pattern matchers, gated on a
  precomputed has_negated_ext_pattern fast-path. Multi-spec, outer-state-
  preserving (later matches override earlier).
- walk / walk_files: recursive, symlink-skipping, name-sorted, with the
  extension allowlist (case-insensitive) and the found bypass.

17 filesystem integration tests (tempfile dev-dep) mirror the TS suite; 146
tests total pass. fmt / clippy -D warnings / test green.

Refs #33
Add Bm25Index::{save,load} persisting to dir/bm25.json. The on-disk shape
matches the TS serialization exactly — camelCase keys (version, numDocs,
avgDocLength, docLengths) and [[term, postings]] / [[term, df]] entry arrays —
via a dedicated Bm25Serialized struct, so a Rust-written index is loadable by
the TS implementation and vice versa.

Tests: score round-trip after save/load, TS-compatible JSON key assertions, and
missing-file error. 149 tests total pass; fmt / clippy -D warnings / test green.

Refs #33
Add crates/csp/src/indexing/cache.rs with the pure cache pieces:

- resolve_cache_dir: sha256 cache key over a field-ordered {sourceId, content,
  ref} JSON payload (byte-parity with the TS JSON.stringify), keyed dir
  <home>/index/<key-32>. Adds ContentType::as_str for the lowercase form.
- resolve_index_root: <home>/index, parent of every cache leaf.
- compute_content_hash: order-independent sha256, folding "<utf16-len>:<path>"
  prefixes + raw content bytes (string/bytes hash identically).
- ensure_cache_dir: create the ~/.csp -> index -> leaf chain at 0700, tightening
  pre-existing dirs (Unix; no-op elsewhere).
- clear_index_cache: canonicalize the target and require it be the direct
  `index` child of the resolved home, rejecting symlink escapes (AC-015).

load_or_build_index orchestration is deferred to T016 (it composes CspIndex,
which needs the dense index, T013). Adds the sha2 dependency. 168 tests total
pass; fmt / clippy -D warnings / test green.

Refs #33
The TS dense.ts ships a STUB Model2Vec (deterministic hash-seeded vectors),
not the real model — its TODO(dense) is still open. Since the behavioral oracle
is the TS test suite, reproduce the stub bit-for-bit:

- fnv1a over UTF-16 code units (u32 wrapping + Math.imul semantics).
- mulberry32 PRNG (exact u32 ops; JS `t ^= t + imul` = wrapping-add then xor).
- stub_embed: Box-Muller with the precise f64<->f32 narrowing (store g as f32,
  accumulate norm from the pre-narrow f64 g, final scale reads f32 back).
- Verified against golden vectors captured from the TS functions
  (fnv1a("hello")=1335831723; stub("hello",8) / stub("foo",4) exact f32).

This resolves the plan's embedding-parity STOP: both implementations use the
stub, so no model weights are needed for parity. Real model2vec-rs integration
is a separate future task (out of scope for oracle parity).

Also ports SelectableBasicBackend: cosine query with optional selector pool,
dim validation, and vectors.bin/args.json save/load. 187 tests total pass;
fmt / clippy -D warnings / test green.

Refs #33
Phase 3 complete. create_index_from_path walks the resolved extensions, chunks
each file via chunk_source, embeds the chunks, builds the BM25 index over
tokenize(enrich_for_bm25(chunk)), and wraps embeddings in a
SelectableBasicBackend. Honors MAX_FILE_BYTES (1 MB skip), stores chunk paths
relative to display_root when set, and errors when no chunks are produced.

5 filesystem tests mirror create.test.ts (small TS file, no-supported-files
error, extensions override, MAX_FILE_BYTES skip, subdir descent). 192 tests
total pass; fmt / clippy -D warnings / test green.

The cache.ts load_or_build_index orchestration folds into T018 (it needs
CspIndex.save / loadFromDisk).

Refs #33
Add crates/csp/src/search.rs: semantic + BM25 -> per-list RRF (k=60) ->
alpha-weighted combine -> optional rerank. Trait-based over EmbeddingModel /
VectorBackend / SparseBackend (implemented for the real dense/sparse types,
mockable in tests).

Parity: search.ts still uses inline ranking stubs (apply_query_boost = identity;
rerank_top_k = file-saturation decay only, ignoring penalisePaths) with a
TODO(integration) to wire ranking/*. To match the TS oracle, search.rs
reproduces those stubs exactly — the fuller ranking::{apply_query_boost,
rerank_top_k} (T006/T007) stay ported but unwired, mirroring TS.
boost_multi_chunk_files is the shared ranking implementation.

Verified against search.test.ts: sort_top_k, rrf_scores (1/61 first rank),
distance->similarity, BM25 zero-exclusion + selector mask, alpha=1/alpha=0
ordering, startLine-stable tie-break, multi-chunk file boost. 209 tests total
pass; fmt / clippy -D warnings / test green.

Refs #33
Phase 4 complete. crates/csp/src/indexing/index.rs:

- CspIndex::{from_path, from_git, search, find_related, stats, save,
  load_from_disk, new}. from_git shallow-clones into a 0700 tempdir
  (std::process::Command, GIT_TERMINAL_PROMPT=0), rejects dash-prefixed refs
  (flag injection, CWE-88), re-roots at the URL, and auto-cleans on drop.
- search applies language/path filters as a selector, short-circuiting to [] on
  blank query / top_k<=0 / empty index / empty selector.
- save writes chunks.json + bm25.json + vectors.bin + args.json + manifest.json;
  load_from_disk validates artifacts, schema version, and the manifest
  (parse_manifest trust-boundary checks), then dim-aligns the query model.
- load_or_build_index: the T015-deferred cache.ts orchestration —
  resolve_cache_dir -> ensure_cache_dir -> content-hash reuse-or-rebuild, keyed
  by URL+ref for git sources. Miss/hit/invalidate verified.

Adds the IndexStats type; promotes tempfile to a normal dependency. 229 tests
total pass (incl. a real  round-trip); fmt / clippy -D
warnings / test green.

Refs #33
Add crates/csp/src/stats.rs:

- BucketStats::add (saved clamped to >= 0 via saturating_sub).
- save_search_stats: best-effort JSONL append ({ts, call, results,
  snippet_chars, file_chars}); snippet_chars uses UTF-16 length (JS .length
  parity); file_chars deduplicated per unique path.
- clear_savings: deletes (not truncates) the file.
- build_savings_summary: Today / Last 7 days / All time buckets via UTC
  YYYY-MM-DD (Hinnant civil-from-days, no date dependency), lexicographic
  date compare, NaN-ts and malformed-line skipping. now injected for testable
  bucketing.
- format_savings_report: ANSI headline + By Period (+ By Call Type when
  verbose); header reads 'Csp Token Savings'; 'No stats yet' when absent.

14 tests cover bucket math, record format, dedup, time buckets, NaN skip,
clear, report header, and known UTC dates. 243 tests total pass; fmt /
clippy -D warnings / test green. CLI wiring of `csp savings` lands in T019.

Refs #33
Phase 5 complete. Port src/cli.ts onto the clap CLI:

- search / find-related: load via the on-disk auto-cache (load_or_build_index)
  or an explicit --index, then emit { query, results } JSON. Adds
  csp::utils::format_results producing the snake_case wire dict (content,
  file_path, start_line, end_line, language, location) — distinct from the
  camelCase ChunkDict used for persistence.
- index --out: build (from_path/from_git) and save a pre-built index.
- savings --verbose, clear all|index|savings: drive the telemetry + caches.
- init --agent/--force: write .{agent}/agents/csp-search.md (.github for
  copilot) from one of 10 agent templates embedded via include_str!
  (crates/csp-cli/agents/, copied from src/agents).
- mcp: stubbed pending T021.

Pure handlers (search_output, find_related_output, run_init, resolve_content,
agent_path) are unit-tested. 243 lib + 8 CLI tests pass; fmt / clippy -D
warnings / test green.

Refs #33
Port the verifiable core of src/mcp/server.ts into csp::mcp:

- IndexCache: session cache keyed by git-URL@ref or absolutized path, LRU-bounded
  to 10, evict, build-failure-not-cached, git-vs-path routing through an
  injectable LoadOrBuild seam (default: the on-disk load_or_build_index).
- get_index: source-safety layer — rejects ssh://, git://, file:// (only
  https://, http://, or local paths), and the no-source case, with descriptive
  errors.
- search / find_related tool handlers: same format_results JSON + error strings
  as the CLI, transport-agnostic.

14 tests mirror server.test.ts (reuse/evict/LRU/routing/failure, URL-safety,
handler JSON). 255 lib + 8 CLI tests pass; fmt / clippy -D warnings / test green.

The rmcp stdio transport is intentionally NOT wired: its on-the-wire schema and
stdio behavior can't be verified without an MCP client, and the plan's STOP
requires that verification before claiming protocol parity. `csp mcp` reports
the core is ready and the transport is pending verification.

Refs #33
Phase 7 distribution infrastructure, authored without disrupting the live
TypeScript release pipeline (release-please.yml):

- T022: .github/workflows/release-rust.yml — cargo cross-compile matrix for
  darwin arm64/x64 (native), linux x64/arm64-gnu + x64-musl, windows-x64.
  SHA-pinned actions; emits csp-<target> + .sha256 matching the TS pipeline's
  asset names. workflow_dispatch only, so it does not override the live release.
- T023: npm/ wrapper (Biome model) preserving bunx @pleaseai/csp — npm/csp with
  a Node launcher (bin/csp.js: platform + libc resolution -> exec the binary)
  and optionalDependencies on per-platform packages; npm/scripts/generate-
  platform-packages.mjs materializes those packages (os/cpu/libc constraints)
  from release assets for npm publish --provenance.
- T024: the existing Homebrew formula generator already consumes the
  csp-<target> names release-rust.yml produces (works unchanged post-cutover);
  user-facing READMEs left accurate to the current TS distribution.

These are CI/publish-gated and cannot be verified locally. Cutover to the Rust
binaries is a maintainer decision gated on full runtime parity (see plan
Progress log for the checklist). Rust workspace remains green (255 lib + 8 CLI
tests); JS + YAML syntax-checked.

Refs #33
Phase 6 complete. Add crates/csp-cli/src/mcp_server.rs — an rmcp 1.7 stdio
server that exposes the search / find_related tools over the live MCP protocol,
delegating to the transport-agnostic csp::mcp handlers:

- #[tool_router] + two #[tool]s (typed Parameters<T> with schemars schemas) +
  #[tool_handler(router = self.tool_router)]; ServerInfo advertises
  SERVER_INSTRUCTIONS and the tools capability.
- run_mcp builds a tokio runtime and serves over stdio(); csp mcp invokes it.
- IndexCache switched from Rc to Arc<CspIndex> so it is Send and shareable
  across the async server's tasks (CspIndex is already Send + Sync).
- Add rmcp (server, macros, transport-io), tokio, schemars, serde deps.

Verified on the wire by driving JSON-RPC into the built binary:
- initialize -> protocol version, tools capability, our instructions;
- tools/list -> search + find_related with correct input schemas
  (required query / file_path+line, optional repo / top_k);
- tools/call search -> indexed a temp dir on demand and returned the
  snake_case {query, results:[{chunk, score}]} wire JSON as a CallToolResult
  text payload (isError:false) — matching the TS MCP contract.

This resolves the last open STOP on T021. 255 lib + 8 CLI tests pass;
fmt / clippy -D warnings / test green.

Refs #33
…la (T022-T024)

Convert the Phase 7 scaffolding from 'authored' to 'executed & verified' for
everything that does not require a publish credential:

- T022: built the release binary for the host (x86_64-apple-darwin, stripped+lto)
  and cross-compiled aarch64-apple-darwin; both smoke-tested (--version + a real
  search). Linux musl/gnu + Windows need native-runner cross-linkers (the matrix
  uses native runners for exactly this reason).
- T023: made generate-platform-packages.mjs partial-matrix-tolerant (skip a
  missing asset with a warning, pin only generated targets in the wrapper's
  optionalDependencies). Ran it against the built binaries and verified the
  launcher end-to-end in a sandbox node_modules — resolve + exec, argv
  forwarding, and a clean exit-1 when the platform package is absent (the exact
  bunx/npx path). npm/dist/ is gitignored (release artifact).
- T024: validated the generated Homebrew formula's Ruby syntax (ruby -c, with
  the workflow's sed placeholders filled).

The only steps left are publish side-effects that require secrets + a real
release tag (GitHub Releases upload, npm publish --provenance, homebrew-tap
push) — not implementation, and not runnable locally.

Refs #33
@amondnet amondnet marked this pull request as ready for review June 18, 2026 12:37
@amondnet amondnet mentioned this pull request Jun 18, 2026
5 tasks
- Move track active/ → completed/ (status: review, pr #34)
- Add Outcomes & Retrospective + Surprises & Discoveries to plan.md
- Record TD-001..004 in tech-debt-tracker.md
- Sync spec → product-specs/rewrite-csp-in-rust (created)
- CLAUDE.md: add Rust rewrite (ADR-0003) subsection (gate, parity oracle,
  snake_case wire dict, rmcp tool_handler gotcha)

Refs #33

@cubic-dev-ai cubic-dev-ai 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.

9 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/csp/src/indexing/dense.rs">

<violation number="1" location="crates/csp/src/indexing/dense.rs:258">
P2: `query` can panic on NaN distances due to `partial_cmp(...).expect(...)`. Use a non-panicking comparator (or explicit finite checks) so invalid data does not crash search.</violation>
</file>

<file name="crates/csp/src/indexing/cache.rs">

<violation number="1" location="crates/csp/src/indexing/cache.rs:214">
P2: `ensure_cache_dir` ignores directory creation/permission errors, so cache setup can fail silently and violate the intended 0700 guarantee. Return and propagate IO errors from this helper.</violation>
</file>

<file name="crates/csp-cli/src/main.rs">

<violation number="1" location="crates/csp-cli/src/main.rs:289">
P2: `clear index`/`clear all` exits 0 even if index clearing fails. This breaks automation that relies on non-zero exit status for failed maintenance commands.</violation>

<violation number="2" location="crates/csp-cli/src/main.rs:409">
P2: `mcp --ref` is accepted but never used. MCP tool calls will always run against default ref, causing wrong-revision indexing for git sources.</violation>
</file>

<file name=".github/workflows/rust.yml">

<violation number="1" location=".github/workflows/rust.yml:47">
P2: `cargo test` is missing `--locked`, so CI won't detect when `Cargo.lock` is out of sync with workspace manifests. The lockfile is tracked in git, and the paths filter already includes `Cargo.lock` — adding `--locked` ensures lockfile consistency.</violation>
</file>

<file name="crates/csp/src/chunking/core.rs">

<violation number="1" location="crates/csp/src/chunking/core.rs:50">
P2: merge_adjacent_chunks can emit chunks larger than desired_length because gap bytes/chars between boundaries are ignored in the fit check.</violation>

<violation number="2" location="crates/csp/src/chunking/core.rs:113">
P2: merge_node_inner uses child-size sums instead of span length, so grouped AST chunks can violate desired_length when children are non-contiguous.</violation>
</file>

<file name="crates/csp/src/indexing/create.rs">

<violation number="1" location="crates/csp/src/indexing/create.rs:62">
P2: Using `read_to_string` drops files with invalid UTF-8 instead of indexing lossy-decoded text. This causes functional divergence from TS behavior and can silently miss search results.</violation>
</file>

<file name="npm/csp/package.json">

<violation number="1" location="npm/csp/package.json:32">
P3: `engines.node` is `>=18.0.0` but the project requires Node.js >=22.0.0. This will mislead package managers into allowing unsupported runtimes.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/csp/src/indexing/dense.rs Outdated
Comment thread crates/csp/src/indexing/cache.rs Outdated
Comment thread crates/csp-cli/src/main.rs Outdated
Comment thread crates/csp-cli/src/main.rs Outdated
Comment thread .github/workflows/rust.yml Outdated
Comment thread crates/csp/src/chunking/core.rs
Comment thread crates/csp/src/chunking/core.rs
Comment thread crates/csp/src/indexing/create.rs Outdated
Comment thread npm/csp/package.json Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (6)
.github/workflows/release-rust.yml-62-64 (1)

62-64: ⚠️ Potential issue | 🟡 Minor

Add persist-credentials: false to the checkout step to reduce credential exposure.

This workflow does not perform any git operations after checkout, so the persisted credentials are not needed. Disabling persistence removes unnecessary token exposure.

Suggested change
       - name: Checkout code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
+        with:
+          persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-rust.yml around lines 62 - 64, The checkout step
using actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 is persisting
credentials unnecessarily since no git operations occur after checkout. Add the
parameter persist-credentials: false to the checkout action to reduce credential
exposure and improve security by preventing the GitHub token from being written
to the git config.

Source: Linters/SAST tools

crates/csp/Cargo.toml-1-20 (1)

1-20: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix TOML lint violations blocking CI.

The current manifest formatting is failing lint in CI (padding/array-spacing/newline rules), so this PR stays red until the TOML style is normalized.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/Cargo.toml` around lines 1 - 20, The Cargo.toml manifest has TOML
lint violations related to formatting rules for padding, array spacing, and
newlines that are blocking CI. Review the dependencies section and the overall
manifest formatting to ensure proper spacing around brackets, consistent padding
in arrays, and correct newline placement according to TOML lint standards. Use
an auto-formatter or manually adjust the whitespace to normalize the TOML style
and pass the linter validation.

Source: Pipeline failures

crates/csp/src/mcp.rs-148-149 (1)

148-149: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix empty-repo fallback behavior in source resolution.

Line 148 uses repo.or(default_source) before empty-string filtering, so repo="" suppresses a valid default_source and incorrectly returns the missing-source error.

💡 Proposed fix
-    let source = repo.or(default_source).filter(|s| !s.is_empty());
+    let source = repo
+        .filter(|s| !s.is_empty())
+        .or_else(|| default_source.filter(|s| !s.is_empty()));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/mcp.rs` around lines 148 - 149, The source resolution logic on
line 148 has incorrect fallback behavior because
repo.or(default_source).filter() applies the filter after the or() operator,
meaning an empty string in repo will prevent default_source from being used. Fix
this by filtering out empty strings from repo before the or() operator so that
an empty repo properly falls back to default_source, then apply a final filter
to check the result is not empty. This ensures empty repo values are treated as
missing and default_source can be used as a valid fallback.
crates/csp/src/search.rs-230-230 (1)

230-230: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use saturating multiplication for candidate over-fetch sizing.

Line 230 can overflow on large top_k values. Use saturating_mul(5) to keep behavior safe and deterministic.

Suggested fix
-    let candidate_count = top_k * 5;
+    let candidate_count = top_k.saturating_mul(5);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/search.rs` at line 230, The candidate_count variable
assignment uses regular multiplication which can overflow when top_k is a large
value. Replace the multiplication operator with the saturating_mul method to
ensure overflow-safe behavior. Change the expression from `top_k * 5` to
`top_k.saturating_mul(5)` to keep the calculation deterministic and safe.
crates/csp/src/ranking/penalties.rs-80-85 (1)

80-85: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize separator handling for re-export basename checks.

Line 80 uses only / to compute basename, so src\__init__.py won’t match REEXPORT_FILENAMES even though other checks already normalize backslashes.

Suggested fix
-    let basename = match file_path.rfind('/') {
-        Some(i) => &file_path[i + 1..],
-        None => file_path,
-    };
+    let basename = normalised.rsplit('/').next().unwrap_or(&normalised);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/ranking/penalties.rs` around lines 80 - 85, The basename
extraction in the file path handling uses only the forward slash character to
split the path, which fails to properly extract the basename from Windows-style
paths containing backslashes. Modify the rfind call in the basename extraction
logic to handle both forward slashes and backslashes as path separators. You can
do this by replacing the single character search with a closure that matches
either '/' or '\' character to ensure consistent behavior across different path
formats when checking against REEXPORT_FILENAMES.
crates/csp/src/ranking/boosting.rs-229-233 (1)

229-233: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the rightmost separator across all delimiter types in symbol extraction.

Line 229 currently returns on the first separator type that matches, not the last separator position. Mixed namespace styles like A::B.C produce B.C instead of C, which mis-targets definition boosts.

Suggested fix
 pub fn extract_symbol_name(query: &str) -> String {
-    for separator in ["::", "\\", "->", "."] {
-        if let Some(idx) = query.rfind(separator) {
-            return query[idx + separator.len()..].to_string();
-        }
-    }
-    query.trim().to_string()
+    let trimmed = query.trim();
+    let mut best: Option<(usize, usize)> = None; // (separator_start, separator_len)
+    for separator in ["::", "\\", "->", "."] {
+        if let Some(idx) = trimmed.rfind(separator) {
+            if best.map_or(true, |(best_idx, _)| idx > best_idx) {
+                best = Some((idx, separator.len()));
+            }
+        }
+    }
+    match best {
+        Some((idx, len)) => trimmed[idx + len..].to_string(),
+        None => trimmed.to_string(),
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/ranking/boosting.rs` around lines 229 - 233, The code in the
loop at line 229-233 returns on the first separator type that matches in the
iteration order, rather than finding the rightmost separator across all types.
For mixed namespace styles like `A::B.C`, it matches `::` first and returns
`B.C`, but should instead find `.` which appears rightmost and return `C`.
Refactor this logic to find the rightmost position across all separators by
checking all of them and comparing their positions, then return the substring
after whichever separator appears latest in the query string.
🧹 Nitpick comments (3)
crates/csp/src/indexing/dense.rs (1)

294-318: 💤 Low value

Minor: loaded vectors are re-normalized unnecessarily.

load calls Self::new(vectors, ...) which normalizes vectors that were already normalized before saving. This is harmless but slightly inefficient. Consider a from_normalized constructor if performance becomes a concern, though the current approach is defensively correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/indexing/dense.rs` around lines 294 - 318, The load method is
unnecessarily re-normalizing vectors that were already normalized before being
saved to disk, as they are passed through Self::new which performs normalization
again. To fix this, create a new constructor method (such as from_normalized)
that accepts pre-normalized vectors and skips the normalization step, then
update the load method to call this new constructor instead of Self::new to
avoid the redundant normalization.
crates/csp/src/indexing/index.rs (1)

217-221: 💤 Low value

Silent error handling in find_related query.

The .unwrap_or_default() on line 221 silently swallows potential query errors (e.g., dimension mismatches). While this prevents panics, legitimate errors are hidden, returning an empty result that's indistinguishable from "no related chunks found."

Consider propagating the error or at minimum logging it for debugging purposes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/csp/src/indexing/index.rs` around lines 217 - 221, In the find_related
method, the call to self.semantic_index.query() uses unwrap_or_default() which
silently swallows potential errors like dimension mismatches. Replace the
unwrap_or_default() with proper error handling: either propagate the error up
using the ? operator if the method returns a Result, or use a match/if let
statement to explicitly handle the error case by logging it before falling back
to a default empty result. This ensures legitimate query failures are visible
for debugging rather than silently returning empty results.
npm/README.md (1)

23-31: ⚡ Quick win

Add a language hint to the fenced block.

This fence is missing a language tag (MD040), which can trigger markdownlint warnings.

Suggested patch
-```
+```text
 `@pleaseai/csp`                     (wrapper — bin/csp.js launcher)
 ├── `@pleaseai/csp-darwin-arm64`    (optionalDependency, os=darwin cpu=arm64)
 ├── `@pleaseai/csp-darwin-x64`
 ├── `@pleaseai/csp-linux-x64`
 ├── `@pleaseai/csp-linux-arm64`
 ├── `@pleaseai/csp-linux-x64-musl`
 └── `@pleaseai/csp-win32-x64`       (csp.exe)
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @npm/README.md around lines 23 - 31, The fenced code block displaying the
package dependency tree is missing a language identifier. Add a language tag
after the opening triple backticks in the code fence that starts with the
@pleaseai/csp package structure. Use "text" as the language identifier to
satisfy the markdownlint MD040 rule and eliminate the warning.


</details>

<!-- cr-comment:v1:43f4439cc18c64b434429d7e -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release-rust.yml:

  • Line 45: The macos-13 runner label is retired and will cause the job to fail
    to schedule on GitHub Actions. Replace the os value of macos-13 with
    macos-15-intel to provide Intel (x86_64) architecture support using the current
    supported runner label.
  • Around line 118-121: The Upload to release step directly interpolates
    inputs.tag into the run command, creating a shell injection vulnerability. To
    fix this, add inputs.tag as an environment variable (TAG) in the env section
    alongside GH_TOKEN, then add validation logic in the run command to check that
    the tag matches a valid format (typically a semantic version pattern) before
    passing it to the gh release upload command. Replace the direct interpolation of
    inputs.tag in the gh command with the environment variable reference.

In @.github/workflows/rust.yml:

  • Around line 9-21: The path filter strings in both the push and pull_request
    sections use quoted scalars (single quotes around strings like 'Cargo.toml',
    'Cargo.lock', etc.) which violate the repository's YAML linter rule
    yaml/plain-scalar. Remove the single quotes from all the path strings in both
    the push: paths and pull_request: paths sections to convert them to plain
    scalars, as these filenames and paths do not contain special characters
    requiring escaping.
  • Around line 34-36: The Checkout code step in the rust.yml workflow is using
    the actions/checkout action but is not disabling persisted credentials. Add a
    with configuration section to the actions/checkout step that sets
    persist-credentials to false. This prevents Git credentials from being persisted
    in the runner since all subsequent workflow steps (format check, clippy, test)
    are read-only operations that do not require Git credentials, thereby
    implementing the principle of least privilege.

In @Cargo.toml:

  • Around line 8-36: The workspace configuration in Cargo.toml has formatting
    violations preventing CI from passing. In the members array on line 8 and
    throughout the workspace.dependencies section (lines 21-36), reformat to comply
    with the repo's TOML linter rules: ensure array formatting follows the linter's
    array style rules, add proper padding lines between dependency pairs in the
    workspace.dependencies block, and remove excessive spaces in front of inline
    comments to follow the no-multi-spaces rule. Adjust all spacing and line breaks
    in these sections to match the enforced TOML style configuration.

In @crates/csp-cli/Cargo.toml:

  • Around line 4-25: The Cargo.toml file in the csp-cli crate has TOML formatting
    violations that are blocking CI. Fix the manifest by adding appropriate blank
    lines between the workspace metadata fields and the [[bin]] section, ensuring
    proper padding-line-between-pairs formatting between logical sections. Verify
    that all array brackets in the [[bin]] and dependency sections follow proper
    array-bracket-spacing conventions, and ensure array elements in the dependencies
    sections follow consistent formatting without multi-space padding around the
    equals signs and braces. Review the entire manifest structure from the workspace
    metadata through the dev-dependencies to ensure it adheres to TOML linting
    standards.

In @crates/csp-cli/src/main.rs:

  • Around line 94-96: The git_ref field defined with the --ref argument in the
    argument struct is accepted by the CLI but is never used or forwarded to the
    server/index loading logic, creating a misleading CLI contract. Remove the
    git_ref field definition (including the #[arg(long = "ref")] attribute) from the
    argument struct to eliminate the non-functional flag. Verify that no other code
    paths reference this field, particularly in the server/index loading section
    around line 409 onward, to ensure no compilation errors occur after removal.
  • Around line 232-239: The code converts line_num from i64 to u32 without
    validating that line_num does not exceed u32::MAX, which allows invalid inputs
    to silently truncate and cause incorrect chunk lookup. After parsing the line as
    i64 and before calling resolve_chunk, add validation to check if line_num
    exceeds u32::MAX and return an appropriate error message if it does (similar to
    the existing validation for negative numbers). Only proceed with the u32
    conversion when line_num is within the valid range.

In @crates/csp/src/chunking/core.rs:

  • Around line 49-62: The fit check in the chunk merging logic uses the sum of
    individual segment lengths (current_length + length) rather than the actual span
    that would result from merging boundaries. This ignores gaps between current_end
    and group.start, which can cause the merged chunk's real span to exceed
    desired_length. Replace the fit check condition to calculate the actual span
    from current_start to group.end instead of summing the individual lengths, so
    that the desired_length constraint accounts for all gaps within the merged
    range.
  • Around line 110-118: The threshold check in the while loop of merge_node_inner
    does not account for spacing between sibling nodes when determining if adding
    the next child would exceed the desired length. Calculate the gap between the
    current end position and the next child's start_byte, then include this gap in
    the length calculation when evaluating whether run_length plus child_length plus
    the gap exceeds desired_length. This ensures that the span size check accurately
    reflects the total byte range needed including all inter-sibling spacing.

In @crates/csp/src/mcp.rs:

  • Around line 199-203: The code checks if line is negative before converting to
    u32, but lacks an upper-bound check for values exceeding u32::MAX. Add an
    additional condition in the if statement to ensure line does not exceed u32::MAX
    before calling the conversion in resolve_chunk. If line exceeds u32::MAX, it
    should return None, similar to the existing negative check.

In @crates/csp/src/search.rs:

  • Around line 72-74: The SelectableBasicBackend::query call uses .expect() which
    causes a panic on backend errors instead of allowing graceful error handling
    during search operations. Remove the .expect() call and instead return the
    Result type directly so that callers can handle query failures appropriately
    rather than crashing the process. This allows the search function to propagate
    errors up the call stack where they can be handled more gracefully, particularly
    important for MCP server usage where process crashes are undesirable.
  • Around line 90-91: Replace all instances of sort_by with
    partial_cmp(...).expect(...) pattern with a NaN-safe ordering implementation
    that gracefully handles NaN values without panicking. The current approach
    assumes all scores are finite and will crash if NaN is encountered. Implement a
    custom comparator (or use a built-in NaN-safe method) that defines a consistent
    ordering for NaN values, such as placing them at the end or beginning of the
    sorted sequence. Apply this fix to all occurrences mentioned in the comment: the
    main instance around the ranked.sort_by call and also at the other locations
    noted in the diff (lines 101-102, 177-178, 206-207, 277-278).

In @crates/csp/src/types.rs:

  • Around line 133-137: The chunk_from_dict function does not validate line-range
    invariants during deserialization of untrusted chunk data. After extracting
    start_line and end_line from the dictionary, add validation checks to ensure
    that start_line is at least 1 (not 0) and that end_line is greater than or equal
    to start_line. Return ChunkFromDictError with descriptive messages if either
    validation fails. Apply the same validation pattern to any other deserialization
    locations mentioned at lines 147-153 where line ranges are extracted.

In @npm/csp/bin/csp.js:

  • Around line 14-94: The code is violating the ESLint rule
    node/prefer-global/process by using the global process object directly without
    an explicit import. To fix this, add an explicit import statement for the
    process module at the top of the file before the resolvePlatformPackage
    function. This will allow all the existing usages throughout the file
    (process.platform, process.arch, process.report, process.stderr, process.argv,
    and process.exit) to reference the explicitly imported process binding instead
    of relying on it being a global.
  • Around line 20-38: The code has brace-style formatting inconsistencies in the
    platform detection logic. Ensure that all else if statements are properly
    formatted according to the project's brace-style rule by placing the else
    keyword on the same line as the closing brace of the preceding block, rather
    than on a separate line. Review the conditional blocks for darwin, linux, and
    their nested architecture checks, and adjust the brace positioning to maintain
    consistent formatting throughout the function.

In @npm/csp/package.json:

  • Around line 2-41: The package.json file fails the jsonc/sort-keys linting rule
    because the keys are not in alphabetical order. Reorder all top-level keys in
    the package.json (name, version, description, homepage, repository, bugs,
    license, keywords, bin, files, engines, optionalDependencies) alphabetically.
    Additionally, ensure the optionalDependencies object entries (the
    @pleaseai/csp-* packages) are also sorted alphabetically by their key names.
    This will satisfy the linting rule and pass CI checks.

In @npm/scripts/generate-platform-packages.mjs:

  • Around line 31-93: The script uses the global process object (in process.argv,
    process.stderr, process.exit, process.stdout) without explicitly importing it,
    which violates the ESLint node/prefer-global/process rule. Add an explicit
    import statement at the top of the generate-platform-packages.mjs file to import
    process from the node:process module, then the linter will recognize the process
    object as properly defined.

In @rust-toolchain.toml:

  • Line 3: The components array assignment in rust-toolchain.toml does not follow
    the enforced TOML array formatting style. Reformat the components array to use
    multi-line formatting with proper newline and bracket spacing as required by the
    TOML linter. This typically involves moving array elements to separate lines
    with proper indentation while maintaining the opening bracket on the same line
    as the assignment.

Minor comments:
In @.github/workflows/release-rust.yml:

  • Around line 62-64: The checkout step using
    actions/checkout@34e1148 is persisting
    credentials unnecessarily since no git operations occur after checkout. Add the
    parameter persist-credentials: false to the checkout action to reduce credential
    exposure and improve security by preventing the GitHub token from being written
    to the git config.

In @crates/csp/Cargo.toml:

  • Around line 1-20: The Cargo.toml manifest has TOML lint violations related to
    formatting rules for padding, array spacing, and newlines that are blocking CI.
    Review the dependencies section and the overall manifest formatting to ensure
    proper spacing around brackets, consistent padding in arrays, and correct
    newline placement according to TOML lint standards. Use an auto-formatter or
    manually adjust the whitespace to normalize the TOML style and pass the linter
    validation.

In @crates/csp/src/mcp.rs:

  • Around line 148-149: The source resolution logic on line 148 has incorrect
    fallback behavior because repo.or(default_source).filter() applies the filter
    after the or() operator, meaning an empty string in repo will prevent
    default_source from being used. Fix this by filtering out empty strings from
    repo before the or() operator so that an empty repo properly falls back to
    default_source, then apply a final filter to check the result is not empty. This
    ensures empty repo values are treated as missing and default_source can be used
    as a valid fallback.

In @crates/csp/src/ranking/boosting.rs:

  • Around line 229-233: The code in the loop at line 229-233 returns on the first
    separator type that matches in the iteration order, rather than finding the
    rightmost separator across all types. For mixed namespace styles like A::B.C,
    it matches :: first and returns B.C, but should instead find . which
    appears rightmost and return C. Refactor this logic to find the rightmost
    position across all separators by checking all of them and comparing their
    positions, then return the substring after whichever separator appears latest in
    the query string.

In @crates/csp/src/ranking/penalties.rs:

  • Around line 80-85: The basename extraction in the file path handling uses only
    the forward slash character to split the path, which fails to properly extract
    the basename from Windows-style paths containing backslashes. Modify the rfind
    call in the basename extraction logic to handle both forward slashes and
    backslashes as path separators. You can do this by replacing the single
    character search with a closure that matches either '/' or '' character to
    ensure consistent behavior across different path formats when checking against
    REEXPORT_FILENAMES.

In @crates/csp/src/search.rs:

  • Line 230: The candidate_count variable assignment uses regular multiplication
    which can overflow when top_k is a large value. Replace the multiplication
    operator with the saturating_mul method to ensure overflow-safe behavior. Change
    the expression from top_k * 5 to top_k.saturating_mul(5) to keep the
    calculation deterministic and safe.

Nitpick comments:
In @crates/csp/src/indexing/dense.rs:

  • Around line 294-318: The load method is unnecessarily re-normalizing vectors
    that were already normalized before being saved to disk, as they are passed
    through Self::new which performs normalization again. To fix this, create a new
    constructor method (such as from_normalized) that accepts pre-normalized vectors
    and skips the normalization step, then update the load method to call this new
    constructor instead of Self::new to avoid the redundant normalization.

In @crates/csp/src/indexing/index.rs:

  • Around line 217-221: In the find_related method, the call to
    self.semantic_index.query() uses unwrap_or_default() which silently swallows
    potential errors like dimension mismatches. Replace the unwrap_or_default() with
    proper error handling: either propagate the error up using the ? operator if the
    method returns a Result, or use a match/if let statement to explicitly handle
    the error case by logging it before falling back to a default empty result. This
    ensures legitimate query failures are visible for debugging rather than silently
    returning empty results.

In @npm/README.md:

  • Around line 23-31: The fenced code block displaying the package dependency
    tree is missing a language identifier. Add a language tag after the opening
    triple backticks in the code fence that starts with the @pleaseai/csp package
    structure. Use "text" as the language identifier to satisfy the markdownlint
    MD040 rule and eliminate the warning.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `e5d19a0e-de58-45d4-a108-1736d3626d2d`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 900de35705a8011e51d10947e0a44764b60afea9 and feeed8cdca48058f7368fa9aaacc629375e7ae71.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `Cargo.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (52)</summary>

* `.github/workflows/release-rust.yml`
* `.github/workflows/rust.yml`
* `.gitignore`
* `.please/docs/decisions/0003-rewrite-in-rust.md`
* `.please/docs/decisions/index.md`
* `.please/docs/tracks.jsonl`
* `.please/docs/tracks/active/rust-rewrite-20260618/metadata.json`
* `.please/docs/tracks/active/rust-rewrite-20260618/plan.md`
* `.please/docs/tracks/active/rust-rewrite-20260618/spec.md`
* `Cargo.toml`
* `crates/csp-cli/Cargo.toml`
* `crates/csp-cli/agents/antigravity.md`
* `crates/csp-cli/agents/claude.md`
* `crates/csp-cli/agents/commandcode.md`
* `crates/csp-cli/agents/copilot.md`
* `crates/csp-cli/agents/cursor.md`
* `crates/csp-cli/agents/gemini.md`
* `crates/csp-cli/agents/kiro.md`
* `crates/csp-cli/agents/opencode.md`
* `crates/csp-cli/agents/pi.md`
* `crates/csp-cli/agents/reasonix.md`
* `crates/csp-cli/src/main.rs`
* `crates/csp-cli/src/mcp_server.rs`
* `crates/csp/Cargo.toml`
* `crates/csp/src/chunking/core.rs`
* `crates/csp/src/chunking/mod.rs`
* `crates/csp/src/chunking/source.rs`
* `crates/csp/src/indexing/cache.rs`
* `crates/csp/src/indexing/create.rs`
* `crates/csp/src/indexing/dense.rs`
* `crates/csp/src/indexing/file_walker.rs`
* `crates/csp/src/indexing/files.rs`
* `crates/csp/src/indexing/index.rs`
* `crates/csp/src/indexing/mod.rs`
* `crates/csp/src/indexing/sparse.rs`
* `crates/csp/src/lib.rs`
* `crates/csp/src/mcp.rs`
* `crates/csp/src/ranking/boosting.rs`
* `crates/csp/src/ranking/mod.rs`
* `crates/csp/src/ranking/penalties.rs`
* `crates/csp/src/ranking/weighting.rs`
* `crates/csp/src/search.rs`
* `crates/csp/src/stats.rs`
* `crates/csp/src/tokens.rs`
* `crates/csp/src/types.rs`
* `crates/csp/src/utils.rs`
* `npm/README.md`
* `npm/csp/bin/csp.js`
* `npm/csp/package.json`
* `npm/scripts/generate-platform-packages.mjs`
* `rust-toolchain.toml`
* `rustfmt.toml`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread .github/workflows/release-rust.yml Outdated
Comment thread .github/workflows/release-rust.yml Outdated
Comment thread .github/workflows/rust.yml Outdated
Comment thread .github/workflows/rust.yml
Comment thread Cargo.toml
Comment thread npm/csp/bin/csp.js
Comment thread npm/csp/bin/csp.js Outdated
Comment thread npm/csp/package.json
Comment thread npm/scripts/generate-platform-packages.mjs
Comment thread rust-toolchain.toml
amondnet added 2 commits June 18, 2026 21:58
Replace the chunking stub (chunk() returned None, is_supported_language()
returned false) with real tree-sitter parsing, based on the semble Python source
(chunking/core.py):

- Statically link a curated grammar set (rust, python, javascript, typescript,
  tsx, go, java, c, cpp, ruby, json, bash, html, css) — all compatible with
  tree-sitter 0.26 via the LanguageFn → Language conversion. Static linking keeps
  the single-binary goal (ADR-0003); the dynamic tree-sitter-language-pack was
  rejected for that reason.
- language_for(name) maps semble language names to grammars; is_supported_language
  consults it. Unsupported languages still fall back to line chunking (semble
  behavior when no parser is available).
- TsNode bridges tree_sitter::Node to the existing generic AstNode merge algorithm
  (children collected via a transient cursor). chunk() parses, runs merge_node on
  byte offsets, then converts boundaries to char offsets (semble's
  len(as_bytes[:n].decode())).

Tests: replaced the stub-era assertions (typescript/python now supported) and
added real-parse tests (rust splits into covering boundaries; multibyte byte→char
conversion stays in range). 257 lib + 8 CLI tests pass; fmt/clippy green.

Note: DESIRED_CHUNK_LENGTH_CHARS stays 1500 (current behavior); semble@main uses
750 — flagged separately, not changed here.

Refs #33
Replace the deterministic embedding stub with the real Model2Vec model via
model2vec-rs (official MinishLab Rust port), based on semble's index/dense.py
(StaticModel.from_pretrained + encode):

- Model is now an enum: Static { Arc<StaticModel>, dim } (real) or Stub { dim }
  (deterministic fallback). load_model loads the real model
  (StaticModel::from_pretrained, HF id or local dir; dim probed via encode_single),
  caching it; on any load error it falls back to the stub WITH a stderr warning so
  indexing still degrades gracefully offline.
- The former TS stub (fnv1a/mulberry32/Box-Muller) is kept bit-for-bit as Model::Stub
  for offline unit tests and the fallback path; its golden test still passes.
- load_model_with(loader) seam keeps unit tests fully offline; a #[ignore]d
  network test exercises the real potion-code-16M download + encode.
- SelectableBasicBackend (cosine) unchanged. index.rs uses model.dim().

Verified: the network test loads potion-code-16M and embeds distinct vectors;
end-to-end "csp search" over a temp repo ranks the semantically-relevant chunk
first using real embeddings (no stub-fallback warning), with tree-sitter chunking
active for rust/python. 258 lib + 8 CLI tests pass (1 ignored network);
fmt/clippy -D warnings green.

Refs #33
@amondnet amondnet force-pushed the tracks/rust-rewrite-20260618 branch from 55773d5 to 8b05745 Compare June 18, 2026 13:06
amondnet added 2 commits June 18, 2026 22:06
The CI test job Lint step (eslint with TOML/YAML plugins) failed on Rust-side
artifacts. Fixes:
- eslint.config.ts ignores: Cargo.toml, Cargo.lock, crates/**, rust-toolchain.toml,
  rustfmt.toml, target (Rust manifests/artifacts are governed by cargo fmt, not
  eslint JS-project TOML rules), and npm/ (the CommonJS distribution launcher +
  release script, not linted TS app source).
- rust.yml: unquote path scalars (Cargo.toml, Cargo.lock, rust-toolchain.toml,
  rustfmt.toml, .github/workflows/rust.yml) to satisfy yaml/plain-scalar; the
  crates/** glob stays quoted.

Verified locally: bun run lint, typecheck, and test (408 pass) all green.

Refs #33
@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Codacy flagged 5 warnings in npm/scripts/generate-platform-packages.mjs (dynamic
node:fs path args + a false-positive "HTML in stderr.write"). That generator is
release-time tooling over a controlled in-repo target list, not untrusted input.
Add .codacy.yaml exclude_paths: npm/** — mirroring the eslint ignore for the same
distribution wrapper. The flagged code is unchanged; only analysis scope.

Refs #33
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 9 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread crates/csp/src/indexing/dense.rs
Robustness / correctness:
- dense.rs, search.rs: NaN-safe ordering — replace partial_cmp().expect()
  sorts with total_cmp (a stray NaN can no longer panic the search path).
- search.rs: vector backend query error degrades to no semantic hits + a
  stderr warning instead of panicking the long-running MCP server.
- create.rs: lossy UTF-8 decode (read + from_utf8_lossy) to match the TS
  oracle's readFileSync(path,'utf8') — previously non-UTF-8 files were dropped.
- cache.rs: ensure_cache_dir now propagates create/permission IO errors
  (Result<(),String>) so the 0700 cache guarantee can't fail silently.
- main.rs: `clear index`/`clear all` returns a non-zero exit on failure.
- main.rs find_related + mcp.rs find_related_tool: guard the full u32 range
  before `as u32` (a line above u32::MAX would wrap to the wrong chunk).
- main.rs: wire `mcp --ref` through run_mcp → CspMcpServer → get_index so the
  default git source is pinned to the requested revision (was accepted/ignored).
- main.rs: derive Debug on ContentFilter/Agent enums (Rust C-derive).

CI / packaging:
- rust.yml: persist-credentials: false on checkout; `cargo test --locked`.
- release-rust.yml: macos-13 (retired Dec 2025) → macos-15-intel; validate the
  workflow_dispatch tag via env var before `gh release upload` (injection guard).
- npm/csp/package.json: engines.node >=18 → >=22 (matches the project minimum).

Docs:
- plan.md: fix a stray Cyrillic "невалид" → "invalid".

Refs #33

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@eslint.config.ts`:
- Around line 20-22: In the eslint.config.ts file, the `npm` directory is being
entirely excluded from linting coverage, which prevents handwritten wrapper and
runtime JS/MJS files from being linted. Instead of excluding the entire `npm`
directory, replace this broad exclusion with more specific patterns that target
only generated files within that directory (such as paths like `npm/dist/` or
`npm/generated/`). This will maintain linting coverage for handwritten
JavaScript files while still allowing generated files to be excluded as needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4cc180bb-143e-4181-9ad4-f233d1d0e89d

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd746f and d19d727.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .claude/settings.json
  • .codacy.yaml
  • .github/workflows/release-rust.yml
  • .github/workflows/rust.yml
  • .please/docs/tracks/completed/rust-rewrite-20260618/plan.md
  • .please/docs/tracks/tech-debt-tracker.md
  • crates/csp-cli/src/main.rs
  • crates/csp-cli/src/mcp_server.rs
  • crates/csp/Cargo.toml
  • crates/csp/src/indexing/cache.rs
  • crates/csp/src/indexing/create.rs
  • crates/csp/src/indexing/dense.rs
  • crates/csp/src/indexing/index.rs
  • crates/csp/src/mcp.rs
  • crates/csp/src/search.rs
  • eslint.config.ts
  • npm/csp/package.json
✅ Files skipped from review due to trivial changes (5)
  • .codacy.yaml
  • npm/csp/package.json
  • .please/docs/tracks/tech-debt-tracker.md
  • .claude/settings.json
  • .please/docs/tracks/completed/rust-rewrite-20260618/plan.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • .github/workflows/rust.yml
  • .github/workflows/release-rust.yml
  • crates/csp/Cargo.toml
  • crates/csp-cli/src/mcp_server.rs
  • crates/csp-cli/src/main.rs
  • crates/csp/src/mcp.rs
  • crates/csp/src/search.rs
  • crates/csp/src/indexing/cache.rs
  • crates/csp/src/indexing/create.rs

Comment thread eslint.config.ts Outdated
CodeRabbit flagged that ignoring the whole npm/ tree dropped the maintained
launcher + generator from eslint coverage (the repo guideline requires all
**/*.{ts,tsx,js,jsx} to follow eslint rules). Narrow the ignore to the generated
npm/dist output and fix the now-surfaced lint in the hand-written files:

- eslint.config.ts: ignore `npm/dist` only (was `npm`).
- csp.js: import `process` via require('node:process'); brace-style → stroustrup.
- generate-platform-packages.mjs: `import process from 'node:process'`.
- npm/csp/package.json: jsonc/sort-keys ordering (license, optionalDependencies).

.codacy.yaml keeps excluding npm/** — Codacy's security heuristics (dynamic fs
paths, spawn-with-variable) misfire on a distribution launcher/generator; that
exclusion is on different grounds than the eslint coverage contract.

Refs #33
@amondnet amondnet merged commit 2f2baa2 into main Jun 19, 2026
6 checks passed
@amondnet amondnet deleted the tracks/rust-rewrite-20260618 branch June 19, 2026 07: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.

1 participant