# Tensor Grep Build And Env

> Use when setting up the tensor-grep dev environment from a fresh clone, rebuilding the Rust/PyO3 extension or standalone `tg` binary after touching `rust_core/`, or debugging a build/toolchain problem — uv install, `maturin develop`, `cargo build`, the pinned 1.96.0 Rust toolchain, Python >=3.11 floor, a "hanging" cargo build, cargo/rustc missing from PATH, ruff CRLF false-alarms, a dependency upper-cap silently downgrading tensor-grep on a newer Python, a flaky/un-retried rustup pinned-toolchain fetch, the CPU-SAFE scope of local Rust compiles on the shared desktop, the A60 WSL-venv destruction trap, the uv.lock hand-splice discipline, or a broken-WSL-stdlib verify-before-trust probe. Gives exact copy-paste setup commands and the traps that have each cost a real cycle.

- Skill: `oimiragieo/tensor-grep-build-and-env` (Agent Skill)
- Install (CLI): `npx skillmds@latest add oimiragieo/tensor-grep-build-and-env`
- Raw SKILL.md: https://api.skillmd.com/api/skills/oimiragieo/tensor-grep-build-and-env/raw
- Safety review: WARNING
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: oimiragieo (https://skillmd.com/u/oimiragieo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/oimiragieo/tensor-grep-build-and-env

---


# tensor-grep: Build & Environment Runbook

Recreate a working tensor-grep dev environment from nothing, and rebuild it correctly after
touching Rust. Every command below is verified against `pyproject.toml`, `rust_core/Cargo.toml`,
`rust_core/rust-toolchain.toml`, `CONTRIBUTING.md`, `AGENTS.md`, and `.github/workflows/ci.yml` as
of **2026-07-08, v1.49.3** (toolchain pins + test counts re-verified **2026-07-16, v1.78.1**, again
**2026-07-22, v1.93.2** — the LTO `Cargo.toml` cite had drifted `234-235` → `463-464` and test
counts moved to 263/16/16 — and again **2026-07-24, v1.95.0** — every `pyproject.toml`/`AGENTS.md`/
`ci.yml` line citation below had drifted from the language-expansion campaign's growth
(`pyproject.toml` +~230 lines of new dev-dependency/tree-sitter-grammar entries, `AGENTS.md` +~300
lines), all re-pointed below; test counts de-stamped in favour of the `ls tests/<dir>/*.py | wc -l` commands; a new Trap 11 added for an un-retried
rustup toolchain fetch; all other version pins verified still current). Re-verify anything
version-shaped before trusting it long-term — see "Provenance and maintenance" at the bottom.

## When to use this skill

- Fresh clone → getting `tg` runnable locally.
- You touched `rust_core/src/**` and need to rebuild the PyO3 extension and/or the standalone binary.
- A build "hangs", `cargo`/`rustc` isn't found, `ruff format --check` flags files you didn't edit,
  or a fresh install resolved to a suspiciously old version.
- You're about to run the required local validation gate before a push/PR.

## When NOT to use this skill (go to the sibling instead)

| If you're actually trying to... | Use |
|---|---|
| Add a new `tg` command or search flag (registration sites) | `tensor-grep-architecture-contract` (why) + `tensor-grep-change-control` (the gates) |
| Interpret a full pytest/mypy/ruff QA run or CI failure in depth | `tensor-grep-validation-and-qa` |
| Debug wrong runtime *behavior* in code that already builds | `tensor-grep-debugging-playbook` |
| Understand `tg doctor` / `tg dogfood` / launcher routing | `tensor-grep-diagnostics-and-tooling` |
| Cut or diagnose a release, semantic-release, push-race | `tensor-grep-release-and-positioning` |
| Look up an env var / feature flag's default and meaning | `tensor-grep-config-and-flags` |
| Read about a settled historical incident before re-litigating it | `tensor-grep-failure-archaeology` |
| Run or interpret a benchmark | `tensor-grep-benchmark-and-proof-toolkit` |

## Jargon glossary (defined once, used throughout)

- **uv** — fast Python package/venv manager (Astral). Replaces `pip` + `venv` + `pip-tools` in one binary.
- **maturin** — a PEP 517 build backend *and* CLI that compiles a Rust crate into a Python extension
  module and can install it into the active venv in one step (`maturin develop`).
- **PyO3** — the Rust crate that generates CPython C-API glue so Rust functions are callable from Python.
- **abi3 / stable ABI** — a subset of the CPython C-API guaranteed stable across minor versions; a wheel
  built with `abi3-py311` loads unmodified on any CPython >=3.11 without a per-version rebuild.
- **cargo / rustc** — Rust's build tool and compiler, respectively.
- **rustup** — the Rust toolchain version manager; reads `rust-toolchain.toml` and auto-selects the
  pinned channel for any `cargo`/`rustc` invocation inside the crate.
- **LTO (link-time optimization)** — a release-build pass that optimizes across crate boundaries. Slow
  to compile, faster at runtime. The reason `--release` Rust builds here take minutes.
- **PEP 517** — the standard that lets `pip`/`uv` delegate "how do I build this package" to a
  build-backend package (here, `maturin`) instead of assuming plain `setuptools`.

## Repo layout: two build systems, one package

- **Python package**: `src/tensor_grep/` — driven by `pyproject.toml`. Entry point:
  `tg = "tensor_grep.cli.bootstrap:main_entry"` (`grep -n "main_entry" pyproject.toml` — was `:648`, now `:668`; found during this pass's audit, not previously measured).
- **Rust workspace**: `rust_core/` — driven by `rust_core/Cargo.toml` (crate `tensor_grep_rs`). It
  builds **two separate targets** from the same source:
  1. a `cdylib` PyO3 extension module, importable as `tensor_grep.rust_core`
     (`module-name = "tensor_grep.rust_core"`, `pyproject.toml:8`) — this is what the Python CLI calls
     into for accelerated search.
  2. two standalone binaries declared as `[[bin]]` targets (`grep -n "^\[\[bin\]\]" rust_core/Cargo.toml` — was `:53-59`, now `:58-60` (`tg`) and `:62-64` (`tg-search-fast`)): `tg` and
     `tg-search-fast` — the "native front door" shipped as a release asset and picked up by launcher
     resolution ahead of the Python path.

Editing `rust_core/src/**` requires an explicit rebuild of whichever target you're testing — an
editable Python install does **not** watch and recompile Rust for you. See the rebuild table below.

## Prerequisites

| Tool | Version pin | Pinned where | Why it matters |
|---|---|---|---|
| Python | `>=3.11` | `pyproject.toml:558` | floor for the PyO3 `abi3-py311` stable ABI |
| uv | `0.11.25` | every `pip install uv==...` step in `.github/workflows/ci.yml` | exact CI parity |
| maturin | `>=1.5,<2.0` | `pyproject.toml:2` `[build-system].requires` | PEP 517 backend that compiles `rust_core/` |
| Rust toolchain | `1.96.0` | `rust_core/rust-toolchain.toml` | reproducible, supply-chain-safe builds (audit MEDIUM finding) |
| rustfmt, clippy | bundled with 1.96.0 | `rust_core/rust-toolchain.toml` `components` | CI's "Check Rust Formatting" + clippy jobs need them; a channel-only pin on a minimal-profile runner would drop them |
| ruff | `==0.15.20` | `pyproject.toml` `[project.optional-dependencies].dev` | lint + format gate |
| mypy | `==1.19.1` | same | typecheck gate, `strict = true` (`pyproject.toml:117`) |

## Zero-to-running setup (copy-paste)

### 1. Clone and install uv

```bash
git clone https://github.com/oimiragieo/tensor-grep.git
cd tensor-grep
python -m pip install uv==0.11.25   # exact version CI pins — do this, not "latest"
```

If you'd rather install `uv` itself via the official Astral installer script (`curl ... | sh`),
that invocation is a remote-script-exec pattern — read `supply-chain-hardening` first and prefer a
package manager (`pipx install uv`, Homebrew, winget) or the pinned `pip install uv==0.11.25` above.
This repo's own installers were hardened against exactly this pattern (#312, checksum-gating the
Unix uv bootstrap in the release `build_command`).

### 2. Install/select the Rust toolchain

```bash
rustup default 1.96.0
rustup component add rustfmt clippy
```

If `rustup` isn't installed yet, this is the exact bootstrap the release pipeline itself uses
(`pyproject.toml:138`, semantic-release `build_command`):

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
. "$HOME/.cargo/env"
rustup default 1.96.0
```

Once `rust_core/rust-toolchain.toml` is on disk (it already is, checked into the repo), any `cargo`/
`rustc` invocation from inside `rust_core/` auto-selects channel `1.96.0` — you rarely need to think
about the pin again after this step.

### 3. Create the venv and install the package (this also builds the Rust extension)

```bash
uv venv --python 3.12          # any >=3.11 works; CI's test matrix is 3.11 and 3.12
uv pip install -e ".[dev,ast]" # dev = pytest/ruff/mypy/hypothesis/...; ast = tree-sitter grammars
```

This single command invokes `maturin` as the PEP 517 build backend and compiles `rust_core/` into
the `tensor_grep.rust_core` extension automatically as part of the editable install — you do **not**
need a separate `maturin develop` call for the first install.

### 4. Verify

```bash
uv run tg --version
uv run python -c "import tensor_grep.rust_core; print('rust_core OK')"
uv run pytest tests/unit/test_rust_core.py -q
```

`tests/unit/test_rust_core.py` asserts the extension actually imports (`find_spec`) and that
`RustCoreBackend().search(...)` returns real results — a stronger check than "the install didn't
error," which can pass even when the extension silently failed to build (see Trap 8).

### `uv run tg` is NOT your working tree — assert `__file__` before any behavioural claim

`uv run --no-sync tg <args>` resolves `tg` from `.venv/Lib/site-packages/tensor_grep`, **not** from
`src/`. On a box with a PATH-installed release as well, that is **three disagreeing `tg`s**, and
nothing in the output says which one answered.

Cost, measured 2026-07-28: two confidently-stated wrong conclusions in one session — a fix
"verified" against a copy that predated it, and a phantom Python-vs-Rust divergence that was
actually the same stale copy on both arms. The tell was there and skipped: *both arms read
identical*, which should trigger "is my instrument loading my code?" before "I found a divergence."

```bash
# ALWAYS, before believing any measured tg behaviour:
PYTHONPATH=src uv run --no-sync python -c "import tensor_grep.cli.main as m; print(m.__file__)"
# must print ...\src\tensor_grep\cli\main.py -- if it says site-packages, every number is stale

PYTHONPATH=src uv run --no-sync python -m tensor_grep.cli.bootstrap <args>   # not `uv run tg`
```

Use `bootstrap` rather than the console script: it is the real front door (`CliRunner` bypasses it),
and it honours `PYTHONPATH`.

### Editing `pyproject.toml` can destroy the venv mid-command

`uv run --no-sync` re-syncs anyway when `pyproject.toml` changes. On Windows that sync deletes the
venv and can then fail on `.venv/lib64` (a symlink) with `Access is denied`, leaving only
`pyvenv.cfg` and a dangling link — every subsequent command dies with "No module named pytest".

```bash
# recover WITHOUT paying for a maturin rebuild (the Rust extension is the expensive part):
powershell -c "(Get-Item .venv\lib64 -Force).Delete(); Remove-Item .venv -Recurse -Force"
uv sync --no-install-project     # deps only, no cargo build
uv pip install pytest            # dev extras are not covered by --no-install-project
```

`--no-install-project` leaves the compiled `rust_core` **absent**, so tests needing the native
extension will fail. That is a degraded environment, not a regression: baseline any new failure by
reverting your change and re-running before blaming it (`tests/unit/test_cli_modes.py::
test_refs_json_deduplicates_parser_call_references` fails this way). Run a full `uv sync` when you
need the extension back.

## Rebuilding after a Rust-side change

> **CPU-SAFE scope (read before running ANY row below).** This desktop is a SHARED machine, and
> the table below is a CI-parity REFERENCE, not a local to-do list: on the shared desktop, do NOT
> run local `cargo` compiles — `cargo build` / `cargo test` / `cargo clippy` / `cargo check` (and
> repeated `maturin` rebuilds) all invoke `rustc` and can starve the other agents/services on this
> box. Route compiles to CI (PR/main runs) or cloud subagents; the user can explicitly approve a
> heavy local build when it is genuinely needed. The ONE local exception is `rustfmt --check`: it
> is not a compiler (no codegen; parses + formats in milliseconds), so format checks stay local.
> Sources in `AGENTS.md` (grep, don't trust line numbers): Operating Rule 3 ("keep them scoped on
> this desktop"), "A12 -- CPU-safe shared-server discipline", "CPU-SAFE forbids compiling", and the
> rustfmt exception under Local Dev Gotchas ("rustfmt is not a compiler").

| You changed... | Rebuild with | Typical time |
|---|---|---|
| `rust_core/src/**` for the extension consumed by the Python CLI | `uv run maturin develop` (installs the `maturin` CLI transparently via `uv run`/`uvx`; or `pip install "maturin>=1.5,<2.0"` once, matching the build-system pin) | **~15 s** (dev/debug profile) |
| Same, but you need the release-optimized extension | `uv run maturin develop --release` | minutes (LTO — see Trap 2) |
| the standalone `tg` / `tg-search-fast` binaries | `cargo build --manifest-path rust_core/Cargo.toml` (debug) or add `--release` | debug: seconds; release: minutes (LTO) |
| just want fast Rust-only feedback (no Python glue) | `cargo test`, `cargo fmt -- --check`, `cargo clippy -- -D warnings` — all run from `rust_core/` | seconds |

Re-run the two verify commands from step 4 after **any** Rust rebuild. Do not assume the change took
effect — see Trap 6 (stale in-tree binaries) and Trap 8 (mocked tests hiding a dead bridge).

## Local validation (run before every push)

Exact commands from `CONTRIBUTING.md` "Local Validation" + `AGENTS.md` "Required Local Validation":

```bash
uv run ruff check .
uv run ruff format --check --preview .
uv run mypy src/tensor_grep
uv run pytest -q
```

Rust equivalents of CI's `static-analysis` job (`.github/workflows/ci.yml:277-342`) — that job runs
`cargo fmt`/`cargo clippy` only, **not** `cargo test`:

```bash
cd rust_core
cargo fmt -- --check
cargo clippy -- -D warnings
cd ..
```

Rust equivalent of CI's separate `test-rust-core` job (a 3 OS × stable/nightly matrix — not covered by
the block above; `grep -n "^  test-rust-core:" .github/workflows/ci.yml` — was `:415-481`, now header
`:448`; found during this pass's audit, not previously measured):

```bash
cd rust_core
cargo test --verbose --no-default-features
cd ..
```

Registration-completeness gate (blocking since v1.17.1 — catches "added a command/flag, missed a
front-door site"; string/comment-aware, so a `#`-commented entry won't false-pass):

```bash
PYTHONPATH=src python -m tensor_grep.core.registration_check .tg-registration.toml
```

Fast pre-push agent-surface gate (complements, does not replace, the full gate above):

```bash
python scripts/agent_readiness.py --output artifacts/agent_readiness.json
tg dogfood --output artifacts/dogfood_readiness.json
```

**Test corpus size** -- **do NOT stamp a number here.** This figure has now been wrong in FOUR
consecutive passes (266 -> a mid-flight 282 -> 291 -> the real 327), because it changes with every
PR that adds a test file while nothing fails when the doc lags. Run it:
`git ls-files 'tests/unit/*.py' | wc -l` / same for `tests/e2e/*.py` / `tests/integration/*.py`
(327 / 22 / 16 on 2026-08-02, recorded only so a reader can tell whether this line is stale --
not as a value to cite).

Use `git ls-files`, NOT `ls tests/unit/*.py`: a bare glob counts untracked scratch files, and a
count contaminated by gitignored artifacts is how a census in this repo has been wrong before.
Both methods agreed here (327/22/16), which is the only reason this number is trusted.

## Known traps (each one has cost a real cycle — read before debugging blind)

### 1. `cargo`/`rustc` "missing" from PATH

**Symptom:** `cargo: command not found` / `rustc: command not found` despite Rust being installed.
**Cause:** rustup's install directory (`~/.cargo/bin`) isn't on this shell's `PATH`.
**Fix:** prepend `~/.cargo/bin` to `PATH`, or call the binaries by full path. (This project's own dev
box hits this concretely at `C:/Users/oimir/.cargo/bin/cargo.exe` — that exact path is a
machine-specific example, not a portable claim; the general fix is "put *your* `~/.cargo/bin` on
PATH.") Source: `AGENTS.md:900`.

### 2. A "hanging" Rust build is not hung — it's LTO

**Symptom:** `cargo build --release` or `maturin develop --release` appears to sit for minutes with
no output.
**Cause:** `grep -n "profile.release" -A2 rust_core/Cargo.toml` (was `:463-464`, now `:467-468`) sets `[profile.release] lto = true` — link-time optimization
is slow to run but does complete.
**Fix:** don't kill it; let it finish. Use plain `maturin develop` (no `--release`, ~15s) for the fast
inner dev loop, and reserve `--release` builds for when you actually need release-profile
performance or are reproducing a release artifact. Source: `AGENTS.md:900`.

### 3. Windows CRLF makes `ruff format --check` false-alarm

**Symptom:** a bare local `ruff format --check .` flags files you never touched.
**Cause:** `.gitattributes` pins `*.py`/`*.rs` to `eol=lf`; a Windows working tree can smudge lines to
CRLF even though the committed blob is LF, and CI's Linux runner enforces LF.
**Fix:** run `ruff format --preview .` (which normalizes line endings per `pyproject.toml:92`
`line-ending = "lf"`) before committing, not just `--check`. Audit actual on-disk endings with
`git ls-files --eol` — `git show`/`git cat-file -p` smudge output and can report false CR.
Source: `CONTRIBUTING.md:24`, `AGENTS.md:906`.

### 4. `ruff format` WITHOUT `--preview` is an active revert

CI runs an asymmetric split: `ruff format --check --preview .` for the format gate but bare
`ruff check .` (no `--preview`) for lint. Running a bare `ruff format` (no `--preview`) locally
**rewrites preview-style lines back to non-preview style on disk** — the next CI
`ruff format --check --preview` then fails on lines you didn't intend to touch, even though your
local lint was clean.
**Rule:** always pass `--preview` to `ruff format`; never pass `--preview` to `ruff check` (preview
lint rules like RUF056 produce false failures that don't match CI). Source: `CONTRIBUTING.md:22`,
`AGENTS.md:604`.

### 5. A dependency upper-cap can silently downgrade the whole install

**Symptom:** a fresh install on a newer Python (e.g. 3.14) resolves to a stale `tensor-grep` version
with no error message at all.
**Cause:** an upper-bound pin (historically `typer<0.25`) had no release compatible with the newer
Python; `pip`/`uv` then silently resolve the **entire package** down to the newest version whose full
dependency graph is satisfiable on that interpreter. `requires-python>=3.11` has no upper bound, so it
can't catch this.
**Fix:** when a fresh Python yields a suspiciously old `tg --version`, suspect a transitive
dependency cap (`typer`/`click`/`pydantic` today — `pyproject.toml:567` currently pins
`typer>=0.12,<0.26`), not `requires-python`. Fixed for the `typer` case in #310; see the
`tensor-grep-dep-cap-silent-downgrade-2026-06-30` memory note for the full incident.

### 6. Stale in-tree native binaries shadow your rebuild

**Symptom:** you rebuilt `rust_core/`, but `tg`'s behavior didn't change.
**Cause:** a leftover `rust_core/target/debug/tg.exe` or `rust_core/target/release/tg.exe` from a
prior build can be resolved by the native-binary launcher path instead of your fresh build.
**Fix:** `uv run tg doctor --json` reports these under `skipped_native_tg_binaries` with
`rust_binary_version_status`. Rebuild explicitly with
`cargo build --manifest-path rust_core/Cargo.toml --release`, or pin `TG_NATIVE_TG_BINARY` to the
exact binary path you intend to exercise. Source: `AGENTS.md:357`.

### 7. Very new CPython + the `abi3-py311` floor (candidate/open)

`rust_core/Cargo.toml:37` pins `pyo3 = { version = "0.29.0", features = ["anyhow", "abi3-py311"] }` —
a stable-ABI build meant to load unmodified on any CPython `>=3.11`. CI sets
`PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"` globally (`ci.yml:21`) so PyO3 doesn't refuse to compile
against a CPython release newer than the PyO3 crate itself recognizes as supported. If a local build
fails specifically on a bleeding-edge Python (3.14+) with an "unsupported Python version"-shaped PyO3
error, set the same env var before building: `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1`. **Labeled
candidate/open** — this is inferred from why CI sets the var, not from a confirmed local repro; verify
before treating it as settled.

### 8. Mock-based FFI tests can pass green while the real bridge is dead

Not a build step, but the verification step every Rust-side build change needs — mechanism and
receipt live in `tensor-grep-architecture-contract` ("The front door" / PyO3+GIL note) and the
general procedure in the global skill `dogfood-the-shipped-artifact`; don't re-derive it here.
Build-specific check: after any change touching `rust_core/src/*` and its Python caller
(`src/tensor_grep/backends/rust_backend.py`), run a live call into the compiled extension — step 4's
`import tensor_grep.rust_core` plus `tests/unit/test_rust_core.py` — not just mock-patched unit tests.

### 9. A worktree agent's "tests pass" is a HYPOTHESIS, not proof (harvest pattern)

**Symptom:** a background/subagent working in an isolated `git worktree` reports its local test suite
green, but the change misbehaves (or a real Rust-extension bridge is dead — Trap 8) once it lands on
the main checkout.
**Cause:** a fresh worktree does not inherit a built venv — it may run against a **copied or absent**
native extension, a stale `.venv`, or skip the Rust rebuild step entirely, so "pytest passed" in that
worktree can be true for the wrong reason (mocked/stubbed backend, no real `rust_core` import).
**Fix — the harvest procedure:** never merge a worktree's own green run as the proof. Cherry-pick its
commit onto a fresh branch off `origin/main` (or the current integration branch), then **re-verify in
the real, fully-built venv**: `uv run maturin develop` (or a full `uv pip install -e ".[dev,ast]"`),
`uv run ruff check . && uv run ruff format --check --preview .`, `uv run mypy src/tensor_grep`,
`uv run pytest -q`, plus a live smoke (`uv run tg --version`, a real search) — only then does it clear
the gate for PR. Cleanup after harvesting: `git checkout main; git reset --hard origin/main;
git worktree remove --force <path>`. This caught 3/3 real issues in a 2026-07-03 worktree-council
verify pass (a Linux-reasoning agent's Windows-FS-blind concurrency claim among them) — see
`tensor-grep-worktree-council-verify-caught-3of3-2026-07-03` memory / `tensor-grep-change-control`'s
change-control gates for where this sits in the merge pipeline.

### 10. A cuda-only test helper needs BOTH itself and every helper it calls co-gated, or `clippy -D warnings` reds the release gate

**Symptom:** `cargo clippy -- -D warnings` (the default-feature build `static-analysis` runs) fails
with a `dead_code` warning on a function that is only ever called from inside `#[cfg(feature =
"cuda")]` test code.
**Cause:** gating a test's *own* `#[cfg(feature = "cuda")]` attribute is not enough by itself -- a
default `cargo test` (no `--features cuda`, what CI's `test-rust-core` runs) then silently never
compiles OR runs that test at all (audit #172, gate-nit NIT-4/MF-1: 3 `classify_gpu_route_failure_*`
tests in `rust_core/src/main.rs` were `#[cfg(feature = "cuda")]`-gated on top of their enclosing
`#[cfg(test)] mod tests`, so plain `cargo test` skipped them with zero signal). The naive fix --
un-gate just the tests -- doesn't compile, because the helper functions they call
(`classify_gpu_route_failure`, `sanitize_cuda_detail`, `GpuRouteFailureKind`, `GpuRouteFailure`)
are still absent by default; the naive fix in the other direction -- un-gate just the helpers -- leaves
them with zero callers in the default (non-cuda) build and reds `cargo clippy -- -D warnings` on
`dead_code`.
**Fix:** gate the helper definitions themselves `#[cfg(any(feature = "cuda", test))]` (present
whenever cuda is enabled -- unchanged production behavior -- OR whenever `cfg(test)` is set, so the
tests have something to call and are not dead code), and drop the redundant per-test
`#[cfg(feature = "cuda")]` so the tests run under plain `cargo test`/`cargo clippy` too. Verify with
both `cargo test --no-default-features` (tests now compile+run) and `cargo clippy --
-D warnings` (default features, no `--tests` -- the release-gating `static-analysis` job's exact
invocation) locally before pushing. Source: PR #597 (`3fd3af7`, shipped v1.75.4).

### 11. rustup's pinned-toolchain fetch has no built-in retry

**Symptom:** a `cargo`/`rustc` invocation inside `rust_core/` (or even the setup step's `rustup
default 1.96.0`) fails outright on a flaky network with no automatic retry — distinct from Trap 2's
LTO "hang," which eventually completes on its own.
**Cause:** `rust_core/rust-toolchain.toml` pins channel `1.96.0`, so the first `cargo`/`rustc`
invocation inside `rust_core/` after that pin is set triggers rustup's own on-demand toolchain
download if `1.96.0` isn't already installed. Unlike this repo's `curl | sh` rustup-bootstrap
invocations (`--retry 10 --retry-connrefused`, e.g. `.github/workflows/ci.yml`'s Setup Rust steps),
rustup's own toolchain-fetch logic has no retry of its own. This red-failed CI on 2 consecutive
macOS runners (#720, #721) before being fixed with a 3-attempt/15s-backoff retry loop wrapped around
the pinned-toolchain fetch (`.github/workflows/ci.yml:449-459`, PR #722, commit `714fbc8`, shipped
in the same release wave as v1.93.10/v1.94.0).
**Fix:** if a toolchain fetch times out locally, it's a transient network blip, not a broken pin —
just retry the command (`rustup default 1.96.0`, or re-run the `cargo build`/`cargo test` that
triggered the on-demand fetch). For a scripted or CI-style bootstrap where you can't rely on a human
to notice and retry, wrap the first `cargo`/`rustc` invocation after the pin is set in a small
retry loop, mirroring `test-rust-core`'s Setup Rust step in `.github/workflows/ci.yml`.

### 12. Never point WSL `uv` at the Windows checkout's `.venv` (A60)

**Symptom:** a "quick dependency check" from WSL against the Windows tree (`uv run --no-sync
--project /mnt/c/...`) — and afterwards the Windows venv is broken or empty.
**Cause:** WSL `uv` treats the Windows venv as incompatible, **removes it**, and creates an empty
Linux venv at the same path. A read-shaped probe becomes a shared-environment mutation and forces a
locked Windows rebuild (`uv sync --frozen` from PowerShell).
**Fix:** WSL/Cursor worktrees use a **WSL-local venv** (or CI); Windows verification runs from
PowerShell in the canonical Windows checkout. Never cross those environment roots. If it already
happened: move the incompatible venv aside, recreate it from Windows with `uv sync --frozen`,
verify imports/version, and only then resume gates. Source: `AGENTS.md` — grep "A60".

### 13. Never run bare `uv` inside an isolated worktree (A116)

**Symptom:** a worktree acquires an empty `.venv`, then `uv run` fails or validates against the
wrong environment. **Cause:** a worktree is not a self-contained development environment; bare
`uv run` can initialize its own empty virtual environment. **Fix:** run the canonical checkout's
main venv while targeting paths in the worktree; do not create or repair a worktree-local `.venv`
for verification.

### 13b. Never recurse `$TEMP` looking for skill drafts

**Symptom:** a status/discovery probe hangs ~100s+ and exits `-1` (`4294967295`) with empty
output. **Cause:** `Get-ChildItem -Recurse $env:TEMP` walks locked Windows temp trees. **Fix:**
assert the worktree path (`Test-Path .claude/skills/<skill>/SKILL.md` + `git status --porcelain`
on that path). Top-level `$TEMP` filter only; never full recurse.

### 13. `uv lock` churns ~280 unrelated lines — hand-splice a new dep instead

**Symptom:** adding one dependency and running a raw `uv lock` produces a huge diff of unrelated
reformatted GPU/CUDA marker expressions (a local-vs-CI uv-version mismatch), which poisons review
and the lockfile-drift gates.
**Fix:** hand-splice ONLY the new dependency's `[[package]]` block (alphabetical position) plus its
requires-dist / optional-dependency refs. Then VERIFY the splice with the exact command the
`Dependency & License Audit` gate runs — it must exit 0:

```bash
uv export --format requirements.txt --all-extras --no-emit-project --locked
```

Source: `AGENTS.md` Local Dev Gotchas — grep "hand-splice a new dep instead".

### 14. A WSL interpreter can have a broken stdlib — probe before trusting it (2026-08-12)

**Symptom:** a WSL `python` looks usable (`--version` answers) but dies on basic stdlib work, so
anything you conclude about packages/venvs through it is built on a broken instrument.
**Fix:** before trusting a WSL interpreter, run the probe: `python -c "import shutil"` — non-zero
means broken; prefer a WSL-local managed venv over a bare system interpreter. **Record the CHECK
you ran, not the CONCLUSION**: host state is volatile, so "WSL python failed `import shutil` on
2026-08-12" is a dated snapshot, not a durable fact a later session may inherit as current.

## CI parity cheat sheet

What `.github/workflows/ci.yml` actually runs, and the closest local reproduction. Release/publish
jobs are intentionally omitted here — see `tensor-grep-release-and-positioning`. **CPU-SAFE scope
applies** (see the note under "Rebuilding after a Rust-side change"): the compile rows in the
"Local equivalent" column (`cargo build`/`test`/`clippy`/`check`) are CI/cloud work on this shared
desktop, not local commands; only `rustfmt --check` and the Python-side rows run locally.

| CI job | Checks | Local equivalent |
|---|---|---|
| `smoke` | Rust core builds (`cargo build --no-default-features`), package installs, one golden search | `cargo build --manifest-path rust_core/Cargo.toml --no-default-features`; `uv pip install -e .`; `tg search ERROR tests/golden/fixture_data --format rg --sort path` |
| `static-analysis` | `cargo fmt -- --check`, `cargo clippy -- -D warnings`, `ruff check .`, `ruff format --check --preview .`, `mypy src/tensor_grep`, registration-completeness | see "Local validation" above |
| `test-python` (3 OS × py3.11/3.12) | `uv run pytest tests -v --tb=short` | `uv run pytest -q` |
| `test-rust-core` (3 OS × stable/nightly) | `cargo test --verbose --no-default-features` | `cargo test --manifest-path rust_core/Cargo.toml --no-default-features` |
| `search-golden-parity` (windows) | `cargo test --test test_search_golden` | `cargo test --manifest-path rust_core/Cargo.toml --test test_search_golden` |
| `cuda-feature-check` (ubuntu + windows, added #594/v1.75.1) | `cargo check --features cuda` -- a fast anti-bit-rot gate (no CUDA toolkit required, no build/test) so the `cuda` cargo feature can't rot silently between the rare times it actually compiles inside `build-release-native-assets`'s gated nvidia legs | `cargo check --manifest-path rust_core/Cargo.toml --features cuda` |
| `native-build-smoke` (4 OS) | `cargo build --release --no-default-features` then `tg --version`/`--help`/one search on the built binary | same, run from `rust_core/` |
| `agent-readiness` | `scripts/agent_readiness.py --no-shell-probes --no-wsl-probe` (the 13-check repo-local contract gate) | `python scripts/agent_readiness.py --no-shell-probes --no-wsl-probe --output artifacts/agent_readiness.json`; see `tensor-grep-diagnostics-and-tooling` |
| `windows-agent-readiness` | `scripts/agent_readiness.py --only-shell-probes --no-wsl-probe` (public shell-probe gate only — a **disjoint** check set from `agent-readiness`, none of the 13 repo-local checks) | `python scripts/agent_readiness.py --only-shell-probes --no-wsl-probe --output artifacts/agent_readiness.json`; see `tensor-grep-diagnostics-and-tooling` |
| `benchmark-regression` | perf regression gates vs. base revision | see `tensor-grep-benchmark-and-proof-toolkit` — do not eyeball timings without that skill's noise-floor rules |
| `release` and everything after it | semantic-release + PyPI/GitHub/package-manager publish | see `tensor-grep-release-and-positioning` — not locally reproducible, don't try |

## Provenance and maintenance

Volatile facts stated above and how to re-check them if this skill feels stale:

- **Version pins** (Python floor, uv, maturin, Rust toolchain, ruff, mypy, pyo3):
  `grep -nE "requires-python|version|channel" pyproject.toml rust_core/Cargo.toml rust_core/rust-toolchain.toml`
- **uv version CI pins**: `grep -n "uv==" .github/workflows/ci.yml`
- **Test file counts** (RE-RUN the `git ls-files | wc -l` commands -- 327/22/16 on 2026-08-02, up
  from 291/21/16 — `tests/eval/` now has 2 files, `test_agent_accuracy.py` +
  `test_retrieval_quality_regression.py` (up from 1), still not part of this count):
  `find tests/unit tests/e2e tests/integration tests/eval -name "test_*.py" | wc -l` run per
  directory, or one combined `find tests -name "test_*.py" | wc -l` for the total file count.
  Note this counts *files*, not individual `def test_*` cases — the suite has thousands of the latter.
  **Re-run this yourself before trusting the stamped numbers** — they drift every session.
- **CI job names/order**: read `.github/workflows/ci.yml` directly (`grep -n "^  [a-z][a-z-]*:$" .github/workflows/ci.yml`).
- **LTO / release-profile setting**: `grep -n "profile.release" -A2 rust_core/Cargo.toml` (was `:463-464`, now `:467-468` — re-run before trusting either)
- **Registration-completeness gate presence**: `ls .tg-registration.toml` and
  `grep -n "registration_check" .github/workflows/ci.yml`
- **Current versions re-verified 2026-07-08, toolchain pins RE-CONFIRMED unchanged 2026-07-16, again
  2026-07-22, and again 2026-07-24**: tensor-grep `v1.95.0`, Rust toolchain `1.96.0`, uv `0.11.25`,
  ruff `==0.15.20`, mypy `==1.19.1`, pyo3 `0.29.0`, maturin build-system pin `>=1.5,<2.0`, Python
  floor `>=3.11` — all other version pins verified current, no change from the v1.93.2 pass.

