# Tensor Grep Large Repo Scale Campaign

> Use when tg hangs, stalls, or runs for minutes on a large/unscoped repo; when `--deadline` "seems ignored" and a symbol query still overruns its budget; when working task #52 (CLOSED, end-to-end deadline ineffective on a ~1800-file TS repo) or #390 (CLOSED, the daemon-path deadline gap) for historical context, caller-scan / build_repo_map latency, the unscoped-`tg search` hang, the generic >1500-file implicit-root fast-refuse (A9/#702), or the exit-2 partial-result semantics. The decision-gated campaign to finish agent-native SCALE HONESTY: tg must never hang and never silently lie (return an empty/partial result as if it were complete) on a customer-scale repo. Gives the reproduce -> phase-instrument (cProfile) -> ranked solution menu -> fail-closed build -> change-control promotion runbook with exact commands, expected numbers, and branch-on-mismatch forks. Verified against v1.110.14 (2026-08-11) on 2026-07-24 (#400/#413/#428/#702 fully shipped; #52 and #390 both CLOSED; the exit-code contract is exit-2-reg

- Skill: `oimiragieo/tensor-grep-large-repo-scale-campaign` (Agent Skill)
- Install (CLI): `npx skillmds@latest add oimiragieo/tensor-grep-large-repo-scale-campaign`
- Raw SKILL.md: https://api.skillmd.com/api/skills/oimiragieo/tensor-grep-large-repo-scale-campaign/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: oimiragieo (https://skillmd.com/u/oimiragieo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/oimiragieo/tensor-grep-large-repo-scale-campaign

---


# tensor-grep — Large-Repo Scale-Honesty Campaign

A decision-gated runbook for the project's hardest **live** problem: making tg
**bounded and honest at customer scale.** Two failure shapes, one contract:

1. **Never hang.** Every scan path has a wall-clock bound; when the bound trips it
   returns a **partial** result, never spins forever.
2. **Never silently lie.** A truncated/partial result is **flagged** (`result_incomplete` /
   `partial` in JSON, an exit-2 signal for symbol commands, a stderr warning) so an
   agent can tell "complete zero" from "gave up early." A quietly-empty result that
   reads as "no matches / no callers / dead code" is the one bug a context tool cannot
   ship.

This skill is the campaign map: what already shipped, what is still open, the exact
commands + expected numbers at each gate, the wrong paths that are fenced off, and how
promotion routes through change-control. **You ship nothing user-visible from this
skill without beating a measurable gate and a conscious flag-flip** (Phase 4).

---

## Why this is the live frontier (receipts)

- **`--deadline` is now a hard end-to-end wall-clock bound on the graph commands
  (task #52, CLOSED by PR #478 / `67f9779`, shipped v1.54.3).**
  Receipt (2026-07-05, on a real ~1884-file TypeScript repo, **pre-#396**):
  `tg callers QueryEngine --deadline 10` took **~25 s** (not ~10 s), and a direct
  `deadline_seconds=8` call took **>90 s**. Root cause: `build_symbol_callers_from_map`
  (the caller-scan) **re-reads/re-parses** candidate files in an `any()`-loop per
  definition, so "each stage is bounded" did **not** make the pipeline bounded. #478
  closed the residual gap a verify-plan-against-code pass found after #396/#440: four
  loops still had no wall-clock bound -- (A) `_iter_repo_files`' file-tree walk (count-only
  bound, no deadline), (B) `_relevant_tests_for_symbol`'s two unguarded `any()` loops (the
  dominant cause on a high-fan-out symbol like `"main"`, confirmed a 3x-recurring P0), (C)
  `build_symbol_impact_from_map` had no deadline parameter at all, and (D) the `string_refs`
  second pass in `build_symbol_refs_from_map`. All four now fold into the existing
  `_DeadlineBreakFlag`/`partial`/`deadline_limit` machinery, guarded `if deadline_monotonic
  is not None` so an undeadlined caller sees byte-identical behavior. See S2 for what this
  does and does not close (the #390 daemon-path gap is explicitly separate).
- **#396 (v1.39.1) shipped a caller-scan re-parse cache + `Path.resolve()` memoization**
  — the code comment measures **~18 s of ~22 s** wall time was redundant `resolve()`
  churn (`src/tensor_grep/cli/repo_map.py:76-80`), claims **7.9x on central symbols.**
  So the pre-#396 receipt numbers are STALE — **Phase 0 re-measures at HEAD.**
- **The unscoped-`tg search` hang was real and is now fixed, not in-flight.** `AGENTS.md`
  (`grep -n "hangs ~600 s then errors" AGENTS.md`; was `:378`, now `:480` — drifted +102 lines this
  pass, same-size drift as the other `AGENTS.md` cites below, consistent with one insertion earlier
  in that file)
  still narrates the pre-fix symptom ("hangs ~600 s then errors" because tg's own index dirs +
  a vendored tree were not auto-excluded) — that doc lags. The fix, **#400**, shipped in
  **v1.40.4** (`bb14abe`) and was hardened further by **#413** (v1.42.0) and **#428**; see
  §1 below. Do not present this as an open bug or an unmerged PR.
- **Whole-repo GRAPH commands (agent / callers / blast-radius / orient) are SLOW AT SCALE, not
  hung — scope to a package root.** On a big tree the graph is O(files): `tg agent .` / `tg orient .`
  walk + parse the whole repo. Native Windows they COMPLETE (agent ~18s on an 872-file repo,
  workspace `orient .` ~48s on 50k files) but they are not fast. The agent-honest usage on large
  trees is a package root — `tg agent REPO/src "task"`, `tg callers REPO/src SYMBOL` — 3-5x faster.
  Two CPU latency wins shipped 2026-07-11 (v1.63.3 deweight #534 removed a per-file `resolve()` hot
  loop from the shared hot path; v1.63.4 parse-cache #535 deduped the 2-3x Python parse, **36%
  faster on a warm re-query**); the remaining scan cost is inherent and is the warm-daemon's job (#94).
- **A "whole-repo hang past 60-90s" reported from WSL over `/mnt/c` is a 9p-filesystem ARTIFACT,
  NOT a tg deadlock.** WSL reading a Windows-mounted tree is ~3-5x slower than native NTFS at the
  file-walk + `stat()`/`realpath()` these commands do, which tips scope-proportional work past a test
  timeout. **Reproduce a WSL latency report NATIVELY (Windows or native Linux, not `/mnt/c`) before
  treating it as a tg bug** — the 2026-07-11 v1.63.2 "10-timeout P0 regression" dogfood was exactly
  this (native: every flagged command completed). Memory:
  `tensor-grep-wsl-mnt-c-latency-artifact-2026-07-11`.
- **Real-world scale dogfood (2026-07-23, c:/dev workspace, 300k+ files across dozens of
  nested projects — not a synthetic reference repo) confirms the shipped mechanisms hold at
  genuinely large scale, not just on the TypeScript reference repo.** `tg orient` bounds itself
  via `scan_limit(2000)` + centrality scoring to a useful ~2910-token capsule in **4.9 s**; an
  unscoped `tg search` degrades gracefully instead of hanging — it returns a partial result with
  an honest "exceeded timeout" `incomplete_reason` and exits **124** (the #400 native-walk
  deadline doing its job, not a new mechanism); `tg inventory --deadline` bounds correctly
  **per-project**. See §2 below for the one known-open edge this same dogfood surfaced.

---

## 0. When to use this skill — and when to use a sibling instead

Use this skill when the task is **bounding/honesty at scale**: a hang, a `--deadline`
overrun, task #52, the #390 daemon gap, the exit-code partial contract, or a
latency profile of a graph command on a big repo.

| If you actually need to… | Use this sibling instead |
| --- | --- |
| A hang/slowness whose CAUSE is unknown — systematic bisection first | `tensor-grep-debugging-playbook` (then return here) |
| Single-file / small-repo micro-latency, or make a number claim-quality | `tensor-grep-benchmark-and-proof-toolkit` |
| The front door / routing / registration / backend contract | `tensor-grep-architecture-contract` |
| Register a new flag/command (2 front doors, 4 sites) | `tensor-grep-config-and-flags` |
| Merge/release/experimental-flag gates + the incidents behind them | `tensor-grep-change-control` |
| Learn a settled battle (FFI reverts, dep caps, mock-vs-real, golden-sensitivity) | `tensor-grep-failure-archaeology` |
| Build/run the toolchain (uv, maturin, cargo) | `tensor-grep-build-and-env` |
| Update README/AGENTS/docs after shipping | `tensor-grep-docs-and-writing` |
| Position externally (never "faster grep") | `tensor-grep-release-and-positioning` |

**This skill never routes around change-control.** It produces the *evidence*; the
flip is a `tensor-grep-change-control` decision (Phase 4).

---

## 1. What already shipped (verify each before you build on it)

The **P0-6 "moat" deadline program** (#384-#401) + the now-shipped unscoped-hang fix (#400,
#413, #428, #702). Every anchor below is at HEAD on 2026-07-24 (v1.96.0); re-verify line numbers,
they drift — `main.py` is 17032 lines and `repo_map.py` is 18752 lines as of this pass, do not
trust an older total. Both files grew substantially (repo_map.py by ~1850 lines) across the
Java/C#/PHP language-support campaign (#719-#726) landed since the last full citation pass at
v1.93.2 — several anchors below drifted by 1000+ lines and have been re-pinned in this pass.

| Ship | What it does | Verify |
| --- | --- | --- |
| **#384-#388** deadline threading | `deadline_seconds` -> `_deadline_monotonic_from_seconds` -> `build_repo_map(deadline_monotonic=...)`; converted once to an absolute `time.monotonic()` stamp so the scan can self-bound and return partial. | `grep -n deadline_seconds src/tensor_grep/cli/repo_map.py` (e.g. `_deadline_monotonic_from_seconds` at `grep -n "^def _deadline_monotonic_from_seconds" src/tensor_grep/cli/repo_map.py` (`:768` as of 2026-08-14), was `:752`; `build_repo_map`'s own `deadline_monotonic` param at `grep -n "^def build_repo_map" src/tensor_grep/cli/repo_map.py` (def `:7669` as of 2026-08-14), was `:7427` — no longer 1-line churn; re-grep rather than trust either number) |
| **#389/#393** graph-command CLI `--deadline` | `tg callers / refs / impact / blast-radius` gained `--deadline FLOAT`; #393 bounds the **caller-scan traversal** itself. | `tg callers --help` shows `--deadline FLOAT RANGE`; source: `grep -n "^def callers\|^def refs\|^def impact\|^def blast_radius" src/tensor_grep/cli/main.py` → `callers:12467, refs:12360, impact:12155, blast_radius:12853` (was `main.py:11629/11645/11440/12083` — all four drifted +715/+715/+715/+770 lines; re-grep, do not trust these numbers either) |
| **#394** payload `result_incomplete` | Truncation stamped at the payload layer so MCP/`_json` consumers see it, not just the CLI. | `grep -n result_incomplete src/tensor_grep/cli/repo_map.py` |
| **#395** `tg inventory --deadline` | inventory walk wall-clock bounded. | `tg inventory --help` shows `--deadline`; source `grep -n "^def inventory" src/tensor_grep/cli/main.py` → `:8823` (was `:8414`, drift +409), `build_inventory(..., deadline_seconds=...)` at `cli/inventory.py:185` (was `:183`, drift +2) |
| **#396** caller-scan cache | `_mtime_aware_cache` (mtime+size in key) + `_resolved_path_str` `lru_cache(8192)` + `_module_aliases_for_path` `lru_cache(16384)`->`frozenset` (PR #345). **7.9x on central symbols.** | `grep -n "_mtime_aware_cache\|_resolved_path_str\|_module_aliases_for_path" src/tensor_grep/cli/repo_map.py` → defs at `:108/:101/:8953` as of 2026-08-14 (was `repo_map.py:100/93/8693` at the 2026-07-24 pass, was `repo_map.py:99/92/8181` before that -- `_module_aliases_for_path` keeps drifting) |
| **#398/#399/#401** exit semantics | #398 exit 2 on ANY truncated partial; #399 walked it back to exit 2 only when the partial is **also EMPTY** (found-but-capped exited 0); **#401 reverted #399** — a UNANIMOUS design council restored exit 2 on ANY truncated/partial result **REGARDLESS of whether matches were found** ("truncation trumps found": an agent must never trust a capped caller-set as exhaustive). This is the CURRENT, final contract — do not describe #399's found-exits-0 behavior as current. | `grep -n "^def _emit_symbol_command_result" src/tensor_grep/cli/main.py` → `:11762` (function body runs to `:11832`; was `main.py:10931-11099`, drift +708 lines); `docs/CONTRACTS.md:157` (was `:156`, drift +1 — `grep -n "Symbol-command exit codes are a three-state agent contract" docs/CONTRACTS.md`) |
| **#400** unscoped-hang fix (e7f18b7) — **shipped v1.40.4** (`bb14abe`), hardened by **#413** (v1.42.0) and **#428** | (A) `_SKIP_DIR_NAMES` excludes `_tg_refs` / `.tg_semantic_index` / `external_repos` (`grep -n "^_SKIP_DIR_NAMES" src/tensor_grep/cli/repo_map.py` -- `:203` as of 2026-08-14, was `:195`, was `:194` before that); (B) **native per-file search walk** got a wall-clock bound — `compute_native_walk_deadline` / `native_walk_deadline_exceeded` (`backends/cpu_backend.py`), checked per file, breaks to a partial with `result_incomplete`+stderr warning (`grep -n native_walk_deadline_exceeded src/tensor_grep/cli/main.py` -- `:8627-8634` as of 2026-08-14, was `:8248-8270`, was `:7979-7992` before that); (C) `_should_refuse_unbounded_vendored_root_scan` (`grep -n "^def _should_refuse_unbounded_vendored_root_scan" src/tensor_grep/cli/main.py` -- `:5527` as of 2026-08-14, was `:5205`; backed by the O(top-level-entries)-only probe `_root_top_level_vendored_dir_names` at `grep -n "^def _root_top_level_vendored_dir_names" src/tensor_grep/cli/main.py` -- `:5508` as of 2026-08-14, was `:5186`; exit 2, <1s — never walks) refuses a root with `node_modules`/`vendor`/`external_repos`/`third_party` at top level, **duplicated by design** into `bootstrap.py`'s `_search_paths_include_vendored_root` (`grep -n "^def _search_paths_include_vendored_root" src/tensor_grep/cli/bootstrap.py` -- `:1082` as of 2026-08-14, was `:998`) because that front door fast-paths native/rg past `main.py` (the recurring "two front doors" class) — both guards import the same `UNBOUNDED_VENDORED_ROOT_DIR_NAMES` set, now DEFINED in `io/scan_limits.py:60` (re-verified exact this pass) and merely re-exported from `io/directory_scanner.py`, as the single source of truth so they cannot drift apart. #413 added a bounded-`scandir` instant-refusal for a large *single-project* root (no vendored top-level dir but still huge); #428 ported the same walk-deadline/refusal into the MCP surface (`tg_search`/`tg_ast_search` had never inherited it). | `tg --version` (expect >= 1.40.4); `git log --oneline --all \| grep -i '#400\|#413\|#428'` |
| **#478** (`67f9779`, shipped v1.54.3) -- **CLOSES #52**, the 4 residual unbounded loops | (A) `_iter_repo_files`' file-tree walk gains `deadline_monotonic`/`deadline_hit` params (was count-only bound); `build_inventory` now computes its deadline BEFORE the walk, not after. (B) `_relevant_tests_for_symbol`'s two unguarded `any()` loops (the dominant cause on a high-fan-out symbol like `"main"`) now break on a shared deadline. (C) `build_symbol_impact_from_map` (`grep -n "^def build_symbol_impact_from_map" src/tensor_grep/cli/repo_map.py` -- `:16797` as of 2026-08-14, was `:16458`, was `:16441` before that) gained a `deadline_monotonic` parameter it never had, threaded into its `_preferred_definition_files`/`_relevant_tests_for_symbol` calls, plus a new `partial`/`deadline_limit` payload block; `build_symbol_impact`/`build_symbol_blast_radius_from_map` updated to pass it through. (D) the `string_refs` second pass in `build_symbol_refs_from_map` folds into the existing `refs_scan_deadline_hit` local. All four guarded `if deadline_monotonic is not None` (byte-identical no-op otherwise). **Scope note:** the design doc explicitly keeps `session_store.py` (the daemon call sites) out of scope -- see #390 in S2, which #478 narrows but does not close. | `git show --stat 67f9779`; `grep -n "deadline_monotonic" src/tensor_grep/cli/repo_map.py | grep -i impact` |

| **`tg find`** (v1.77.0, #189, `main.py:4728-4837`, was `:4574-4676`, drift +147 lines) — whole-repo hybrid NL search, a NEW command that reuses this campaign's bounding shape from day one rather than retrofitting it later | Takes `--deadline`/`--max-repo-files` plus an internal corpus-wide chunk cap (`_FIND_CORPUS_CHUNK_CAP`); a truncated scan sets `result_incomplete=true` and exits 2 (found-but-truncated prints results THEN exits 2 -- same "truncation trumps found" rule as #401 below, not the found-exits-0 shape #399 walked back). **Unlike `tg search`, `tg find` does NOT get the instant vendored/workspace-root refusal (#400)** -- it always attempts the bounded scan rather than refusing outright, because ranking the whole repo (not raw-text matching it) is the command's entire point. | `tensor-grep-run-and-operate` §11c has the full exit-contract prose; `grep -n "^def find" src/tensor_grep/cli/main.py` → `:4721` |
| **#702** (v1.92.3, A9) -- a **4th** unscoped front-door fix alongside #400/#413/#428, closing a gap those three never covered | `bootstrap._run_rg_passthrough` (`bootstrap.py:1337`, was `:1088`, drift +249) -- the plain **flag-less** search front door that runs *before* `main.py`'s Typer app is ever reached -- had **NO walk ceiling at all** until this shipped; the three existing `main.py`-side refusal guards (#400/#413) never ran for this path. Fixed by adding `bootstrap._search_paths_include_oversized_implicit_root` (`bootstrap.py:1053`, was `:804`, drift +249, the same-size shift as `_run_rg_passthrough` above -- consistent with one insertion earlier in the file), gated on `paths_defaulted`, sharing the SAME `IMPLICIT_SEARCH_WALK_FILE_CEILING = 1500` constant (`io/scan_limits.py:106`, re-verified exact this pass, re-exported via `io/directory_scanner.py`) the other 3 doors already used -- natively reproduced, not a WSL artifact. An over-ceiling implicit root now refuses in ~1.7s instead of silently walking up to the 60s `TG_RG_TIMEOUT_SECONDS` backstop. | `grep -n _search_paths_include_oversized_implicit_root src/tensor_grep/cli/bootstrap.py`; `grep -n IMPLICIT_SEARCH_WALK_FILE_CEILING src/tensor_grep/io/scan_limits.py` |

**Merge/release state to stamp every session:** #400/#413/#428/#702 are all in the **installed
binary** as of v1.96.0 — this is not a source-only or in-flight fix. If a future session finds
a NEW in-flight PR referenced by this skill, do not describe it as shipped until
`git log --oneline origin/main | head` shows a `chore(release)` commit above it (see the
project's merge-gate guardrail: an open PR is guidance, not a receipt, until it lands on main).

---

## 2. Still open (the campaign's actual work)

- **#52 — end-to-end deadline ineffective on a large TS repo — CLOSED by PR #478
  (`67f9779`, shipped v1.54.3).** See the shipped-table row in S1 for the four loops it
  bounded (A: file-tree walk, B: `_relevant_tests_for_symbol`, C: `build_symbol_impact_from_map`,
  D: `string_refs` second pass). This supersedes the *likely-closed* framing this skill
  previously carried after #396/#440/the `CALLER_SCAN_FILE_CEILING` chokepoint alone — those
  three narrowed the gap but a verify-plan-against-code pass still found the four loops above
  live and unbounded; #478 is the commit that actually closes it. **Still do a one-time fresh
  Phase-0 re-measure** on a real large TS repo before citing a specific wall-clock number in a
  benchmark claim or PR -- "the mechanism is closed" and "I have re-confirmed the number on my
  reference repo" are different claims; S3 Phase 0 gives the exact command.
- **#390 — daemon-path deadline gap — CLOSED (task #203, shipped #647/#652/#653, v1.81.2-.4).**
  This was **framed OPEN in earlier passes of this skill — that framing is now STALE and risks a
  future session re-implementing already-shipped code.** Verify directly rather than trusting
  either framing: `session_store.py` (the daemon's request-dispatch module) now threads a real
  `deadline_monotonic` value into its `build_symbol_impact_from_map` (`session_store.py:1449`, was
  `:1276`, drift +173) and
  `build_symbol_callers_from_map` (`session_store.py:1490`, was `:1317`, drift +173) call sites — the code comment at
  `session_store.py:1484-1488` (was `:1311-1315`, drift +173) states explicitly: *"the exact #390 daemon-path shape this task
  (#203) closes."* The same threading was extended to `refs`/`blast-radius`/`file_importers`
  (search `session_store.py` for `WARM_DAEMON_DEFAULT_DEADLINE_SECONDS` to see every site). A
  daemon-served graph query on a cached session `repo_map` is now bounded the same way a fresh
  CLI invocation is — the #478 design doc's explicit "session/daemon territory out of scope" carve-out
  from **Candidate (c)** below has since been executed as its own follow-up, not left open.
  **Candidate (c) below is retained as the historical solution-menu entry that predicted this fix's
  shape, not as an open task — do not re-propose closing #390 as new work.**
- **Default budget.** The native-walk bound reuses `configured_ripgrep_timeout_seconds()`,
  which now defaults to **60 s** (`subprocess_policy.py:75`, confirmed exact this pass, was 600 s).
  `AGENTS.md:480` (was `:378`, drift +102 -- `grep -n "hangs ~600 s then errors" AGENTS.md`)
  still narrates the pre-#400 "600 s" symptom — that doc lags; the resolver is the source of
  truth.
- **NEW (surfaced by the 2026-07-23 c:/dev dogfood receipt above) — a pathological
  workspace-union root can still blow an `inventory --deadline` budget**, even though
  #478 bounded `_iter_repo_files`'s walk. `tg inventory` always calls `build_inventory`
  with an explicit integer `max_files` (default `DEFAULT_MAX_INVENTORY_FILES = 50_000`,
  `cli/inventory.py:42`/`188` — was `:40`/`186`, both +2 lines, never `None`), so `_iter_repo_files` (`grep -n "^def _iter_repo_files" src/tensor_grep/cli/repo_map.py` -- `:1183` as of 2026-08-14, was `:1144`)
  always takes its `if max_files is not None:` branch (`grep -n "if max_files is not None:" src/tensor_grep/cli/repo_map.py` -- `:1209` as of 2026-08-14, was `:1170`), whose FIRST
  step is a single blocking `list(os.scandir(normalized_root))` (`grep -n "list(os.scandir(normalized_root))" src/tensor_grep/cli/repo_map.py` -- `:1211` as of 2026-08-14, was `:1172`) with
  **no deadline check before or during that one call** — the per-bucket
  `deadline_monotonic` check (`grep -n "loop A" src/tensor_grep/cli/repo_map.py` -- `:1256-1260` as of 2026-08-14, was `:1220-1221`) only runs AFTER this initial
  top-level listing has already fully materialized. On an ordinary repo this is instant;
  on a workspace-union root whose TOP LEVEL itself fans out to a huge number of entries
  (e.g. a parent folder of many independently-cloned projects, not a normal nested tree),
  that single `scandir()` call can burn the whole `--deadline` budget before any check
  runs. **Triage: rare, and NOT worth a load-bearing fix right now** — the fix (a
  lazy/incremental `os.scandir` iterator instead of `list(...)`) would touch a hot,
  heavily-profiled walk path for an edge case that only bites an already-discouraged
  usage (scanning a multi-project workspace ROOT instead of a package root; `tg orient`
  / `tg agent` already advise narrowing via `suggested_scope`/`workspace_root_detected`,
  see `docs/CONTRACTS.md`). Do not silently re-open this as a new #52 — it is a
  narrower, already-diagnosed gap in a single branch of one function, not an
  end-to-end unbounded pipeline.

---

## 3. The phased runbook (decision-gated)

Run in order. Each gate states the expected observation and where to **branch**.
PowerShell is the dev-box shell; `uv run` is cross-platform. Use `uv run --no-sync`
so a bare `uv run` does not re-sync away the `[dev]` tree (`tensor-grep-build-and-env`).

### Phase 0 — Reproduce the baseline on a REAL large repo (NEVER skip)

You cannot claim "bounded" without the unbounded number, and you cannot claim a fix
without the pre-fix number. Use a **public** large TS repo so this is reproducible
(never a private customer path).

```powershell
# thousands of .ts files, deep import graph — a public customer-scale proxy
# (any large TS repo works; substitute one with >~1500 .ts files if you prefer)
git clone --depth 1 https://github.com/microsoft/TypeScript C:\tmp\ts-ref
$repo = "C:\tmp\ts-ref\src"

# Wall-clock the bounded command against its own budget (source or the installed >=v1.40.4
# binary both have #400/#440; either works, source keeps you at HEAD for a just-landed fix):
Measure-Command { uv run --no-sync python -m tensor_grep callers $repo Node --deadline 10 --json | Out-Null }
```

**Expected + gate:**
- **GATE 0 (the #52 test):** total elapsed should be **<= deadline + ~10%** (i.e.
  ~11 s for `--deadline 10`). Read the JSON: `partial`/`result_incomplete` must be
  `true` **iff** the scan was actually truncated, and a truncated result must carry a
  non-empty `incomplete_reason`/`caveat`.
- **If elapsed >> deadline (e.g. 25 s for a 10 s budget)** -> **a NEW regression, not #52
  reopened, and not #390 either** -- #52 is CLOSED at HEAD (#478/`67f9779`, S1/§2), built
  specifically to prevent exactly this shape across all four loops (A/B/C/D), and #390 (the
  daemon-path gap) is ALSO CLOSED (task #203, S1/§2 above) -- a *session-served* query is bounded
  the same way a fresh CLI one is now, so do not reach for "still-open #390" as the explanation.
  A live overrun here means a new unbounded loop was introduced since #478/#203; if you can
  reproduce it specifically on a session-served/daemon query and NOT on a fresh CLI one, that is a
  fresh, narrower finding (a regression in the #203 threading), not a reopening of #390 itself.
  Proceed to Phase 1 to find where the budget leaks.
- **If elapsed is bounded AND truncation is honestly flagged** -> matches the current
  expectation (§2: #52 closed). Do NOT just declare victory: re-run on a *second*
  large repo and a *central* symbol (highest fan-in), confirm the exit code matches
  `docs/CONTRACTS.md:157` (was `:156`, drift +1; exit 2 fires on ANY truncation, found or not — §5), then route
  promotion of the "closed" claim through Phase 4 / change-control.
- **If it HANGS (no return, no error) on an unscoped `tg search`** on a root with a
  vendored dir -> this is a NEW bug, not the old #400 shape: #400's instant refusal
  (`_should_refuse_unbounded_vendored_root_scan`, exit 2, <1s) plus the native-walk
  deadline are shipped in the installed `tg` binary (>=v1.40.4) as well as source, so a
  hang here on EITHER means the fix regressed -> `tensor-grep-debugging-playbook`.

### Phase 1 — Phase-instrument the ACTUAL slow command (do NOT guess)

Profile the command Phase 0 flagged, on the same repo. cProfile is the oracle here
(the graph commands do **not** all expose `--profile`; only `context-render` and
`blast-radius-render` do — verified against `tg --help`). This invocation is verified
to run and print stats:

```powershell
# tottime = internal (self) time -> finds the true hot function.
# tg's own output prints first; the profile table is APPENDED at the end -> tail it.
uv run --no-sync python -m cProfile -s tottime -m tensor_grep callers $repo Node --deadline 30 2>&1 | Select-Object -Last 30
```

**Expected + gate (verified shape on a real repo):** the top `tottime` rows are the
**per-file parse** — on Python targets `{built-in method builtins.compile}`, `ast.walk`,
and `repo_map.py:2127(_python_imports_and_symbols)` (was `:1921`, drift +206); on TS targets the analog is the
tree-sitter parse via `repo_map.py:2271(_typescript_parser)` (was `:2065`, drift +206) — invoked **many times**,
because `build_symbol_callers_from_map` re-parses candidate files in its `any()`-loop
(`repo_map.py:3639` documents the "N definitions -> N re-reads/re-parses" hazard, was `:3407-3409`,
drift +231 — `grep -n "N definitions means N re-reads" src/tensor_grep/cli/repo_map.py`).

- **GATE 1a — caller-scan re-parse dominates** (many parse calls, high `ncalls` on the
  parse/`resolve` functions): the leak is the **re-parse loop**, not the one-shot map
  build -> **Solution menu candidate (a) or (b)** in Phase 2.
- **GATE 1b — a single `build_repo_map` pass dominates** (parse called ~once per file,
  not per definition): the map build itself is the cost -> the fix is bounding/caching
  `build_repo_map`, which #384-#388 already partly did via `deadline_monotonic`;
  re-measure whether the deadline is honored INSIDE that pass -> different branch --
  a NEW gap, since #390 (the daemon-path deadline gap) is CLOSED (S1/§2 above), not an
  open "#390 gap" to attribute this to.
- **GATE 1c — `resolve()` / path work dominates** despite #396: the cache is being
  defeated (e.g. an uncached `resolve()` before the cache lookup — `repo_map.py:2712`
  warns about exactly this, was `:2506-2507`, drift +206 — `grep -n "this resolve() runs BEFORE the re_export_cache lookup" src/tensor_grep/cli/repo_map.py`) -> candidate (b), fix the cache ordering.

> **Redundancy measurement before you design a cache (candidate b obligation):**
> monkeypatch the parse function with a `collections.Counter` keyed by path and count
> how many times each file is re-parsed for one `callers` call. This is the technique
> that overturned a code-review guess in PR #345 (see fenced paths). Numbers first,
> then design the key.

### Phase 2 — Solution menu (RANKED), each with a proof obligation

Pick top-down. Each candidate carries an obligation you must **verify/measure**, not
assume, before building.

#### Candidate (a) — bound the caller-scan re-parse loop (preferred)
Apply the **same per-file deadline check** #400 used for the native search walk to the
caller-scan `any()`-loop: check `native_walk_deadline_exceeded` (or an equivalent
`deadline_monotonic`) once per file, and on expiry break to a partial.
- **Obligation:** the partial must be **fail-closed and flagged** — set
  `result_incomplete = True` + a concrete `incomplete_reason`, and let
  `_emit_symbol_command_result` (`grep -n "^def _emit_symbol_command_result" src/tensor_grep/cli/main.py` -- `:11722` as of 2026-08-14, was `:11762` at the 2026-07-24 pass, was `:11298` before that) apply the exit contract: per §5, ANY
  truncated/partial result exits **2**, regardless of whether it's empty or non-empty
  (#401 — do not build toward the old #399 "found-but-capped exits 0" shape). Mirror
  #400's shape (`main.py:8137-8270`, was `:7979-7992`): break, never return a clean empty.
- **Why preferred:** it directly closes #52 with a mechanism already proven in-tree; low
  blast radius; deterministic. **This is what #478 (`67f9779`) actually shipped** -- the same
  per-file-deadline-check shape, applied to all four residual loops (S1/S2). This menu entry
  stays useful as the template for the next #52-shaped finding, not just a historical proposal.

#### Candidate (b) — extend the #396 caching to the residual re-parse
If Phase 1 shows re-parses that #396's caches miss, widen coverage.
- **Obligation — cache-key correctness:** a repo-map cache MUST key on file
  **mtime+size** (use the existing `_mtime_aware_cache` (`grep -n "^def _mtime_aware_cache" src/tensor_grep/cli/repo_map.py` -- `:108` as of 2026-08-14, was `:100`), not a plain
  `lru_cache` keyed on path alone — a plain cache returns **stale** results in the
  long-lived daemon (the code comments at `repo_map.py:30-34` + the
  `_MTIME_CACHE_CLEAR_REGISTRY` declaration (`grep -n "^_MTIME_CACHE_CLEAR_REGISTRY" src/tensor_grep/cli/repo_map.py` -- `:64` as of 2026-08-14, was `:55`) document this trap
  and the sweep). Prove the redundancy with the
  Counter-monkeypatch (Phase 1) BEFORE adding the cache, and prove correctness with a
  mutate-file-then-re-query test.
- Caching bounds the *common* case but does **not** bound a pathological single file —
  ship it WITH candidate (a), not instead of it.

#### Candidate (c) — close the #390 daemon-path deadline gap — SHIPPED (task #203, #647/#652/#653, v1.81.2-.4); retained here as historical record, not an open candidate
This entry correctly predicted the shape of the eventual fix and is kept for that reason, but the
work itself is DONE — do not re-propose it as new. What actually shipped: `session_store.py`'s
daemon call sites (`build_symbol_impact_from_map` at `session_store.py:1449`, was `:1276`, drift +173,
`build_symbol_callers_from_map` at `session_store.py:1490`, was `:1317`, drift +173, plus `refs`/`blast-radius`/
`file_importers` siblings) now pass a real `deadline_monotonic` value
(`WARM_DAEMON_DEFAULT_DEADLINE_SECONDS`-derived) into every builder call — exactly the call-site
wiring this candidate originally scoped as the remaining work after #478's builder-side prerequisite.
See S2 above for the closing citation (`session_store.py:1484-1488`'s own code comment, was `:1311-1315`, names task
#203 as the closer). If a NEW daemon-served command is added in the future, use this shipped pattern
as the template — thread `deadline_monotonic` at the call site, mirroring the sibling commands in
the same file, rather than assuming the daemon path is unbounded by default going forward.

#### Candidate (d) — replace the regex/slow TS parse with tree-sitter (HIGHEST RISK — fenced)
Only if (a)+(b)+(c) leave the parse itself as the irreducible hotspot.
- **Obligation (mandatory, before any swap):** **golden parity across the parser corpus.**
  A parser change alters symbol/import extraction for every downstream command; prove
  byte-identical (or explicitly-diffed-and-accepted) output on the AST parity corpus
  first (`benchmarks/run_ast_parity_check.py`). Do this in a fenced branch; do not mix
  it with a bounding fix.

### Phase 3 — Build behind the fail-closed contract + measure the gate

- Write the failing test first (`tests/unit/test_repo_map_targets.py`,
  `tests/unit/test_cli_modes.py` are the patterns #400 used). TDD, then the smallest fix.
- Honor the **Backend Fail-Closed / partial contract** (§4): a bound that trips
  produces a **flagged partial**, never a silent empty, never a raw crash.
- **Re-run Phase 0 + Phase 1** on the reference repo after the fix.
- **A6 — anti-hang test discipline (a hang-class regression test can itself hang):** when the
  red test is "this used to hang/overrun," wrap it so a still-broken fix fails FAST instead of
  wedging the test run. (1) layer an OUTER shell timeout (`--kill-after=Ns`) around an INNER
  per-test timeout implemented via a thread/watchdog, not `signal` (signal-based timeouts are a
  no-op on Windows and under the GIL for CPU-bound native calls — this repo runs CI and dev on
  Windows). (2) either land the fix before the red test lands, or land the red test already
  wrapped in both timeouts. (3) never leave an unbounded loop/subprocess-spawn/backtracking
  regex in the TEST ITSELF without its own bound — the test must fail the same way the product
  bug does (bounded, flagged), not hang the CI runner. (4) distinguish "slow but protected" from
  "hung" by EXIT CODE (124/137 = killed) not elapsed wall-clock, which is noisy on a shared
  runner. (5) apply the same two-layer timeout in CI as locally — a hang that only reproduces in
  CI because the outer timeout was dev-box-only is the worst kind to debug.

**Promotion gate (all must hold, measured on the reference repo, same symbol):**

| Metric | Requirement |
| --- | --- |
| Wall-clock vs budget | elapsed **<= `--deadline` + ~10%** (the #52 bar) |
| Truncation honesty | `partial`/`result_incomplete` set **iff** truncated; non-empty `incomplete_reason` |
| Exit code | matches `docs/CONTRACTS.md:157` (was `:156`, drift +1) (0 complete / 1 complete-not-found / 2 incomplete — **regardless of whether anything was found**, per #401) |
| No regression | benchmark-regression CI gate green (`benchmarks/check_regression.py`); correctness identical on the AST parity corpus |
| Real binary | dogfood via `scripts/dogfood/` on the REAL artifact, not CliRunner |

- **GATE 3:** if the wall-clock still overruns the budget OR a partial isn't flagged,
  **do not ship** — the honesty contract is the whole point. Iterate or record the
  negative result.

### Phase 4 — Promote through change-control (never here)

This skill produces evidence; **`tensor-grep-change-control` owns the flip.**
1. One **release-bearing PR per tick** (respect the push-race / one-merge-per-tick rule,
   `tensor-grep-release-and-positioning`).
2. Attach Phase 3 evidence (before/after wall-clock table + exit-code proof + parity).
3. **Re-dogfood on the REAL large repo before declaring the contract done** — this is
   the #399 lesson: #398 shipped an exit-code rule that had to be walked back one release
   later because the real-repo behavior (every large-repo query exiting 2) was wrong.
4. Update `docs/CONTRACTS.md` / `AGENTS.md` / `SESSION_HANDOFF.md` if the contract
   changed (`tensor-grep-docs-and-writing`).

---

## 4. Fenced-off wrong paths (do NOT do these)

| Forbidden | Why | Do instead |
| --- | --- | --- |
| **Raise the default timeout** to "fix" a hang | Masks, doesn't bound. A bigger number still hangs on a bigger repo; it just moves the wall. | Add a real per-file wall-clock bound that returns a flagged partial (#400 pattern). |
| **Return a silent empty / clean 0-result** on timeout | Violates the Backend Fail-Closed Contract (`AGENTS.md`, `grep -n "^## Backend Fail-Closed Contract" AGENTS.md` → `:2090`; was `:438-440`, drift +1233 — a cross-file citation this far stale is worth a grep, not a re-stamped number). A partial that reads as "no matches / no callers / dead code" is the exact bug this campaign exists to kill. | `result_incomplete = True` + `incomplete_reason` + stderr warning + the exit-2-regardless-of-found contract (§5). |
| **Assume "each stage bounded => pipeline bounded"** | The #52 lesson: `build_repo_map` and caller-scan were each bounded in isolation, yet the end-to-end command overran because the caller-scan re-parses ~all files. | Measure the WHOLE command wall-clock (Phase 0), then profile it (Phase 1). |
| **Guess the hotspot from code review** | PR #345 receipt: a review council guessed a 3.6%-of-runtime path; the live profile found the real one (`_module_aliases_for_path` called ~1.4M times) -> `lru_cache`+`frozenset` cut the run **61.7 s -> 12.8 s (4.8x)**. | Profile the ACTUAL slow command at scale (Phase 1); the profiler is the oracle. |
| **"Fix" a golden to match your dev box** | #363 receipt: a "stale" golden was edited to include rg submatches, then reverted — the golden is backend-sensitive and CI has no rg. CI is the oracle. | Read the failing CI job's -/+ diff first; force a deterministic backend in the test. `tensor-grep-failure-archaeology`. |
| **A plain `lru_cache` keyed on path in the daemon** | Returns stale results across a long-lived session when a file changes (`repo_map.py:30-34`, `55`). | `_mtime_aware_cache` (mtime+size in key) + register its `cache_clear` in the sweep registry. |
| **Ship the parser swap (candidate d) with a bounding fix** | Conflates a correctness-risky change with a latency fix; a parity regression would hide behind the perf win. | Fence the parser swap in its own branch, gated on AST parity FIRST. |
| **Route around change-control / auto-merge** | Non-negotiable. Autonomy is draft-PR-only. | Produce evidence here; let change-control gate the flip. |

---

## 5. The contract you are defending (exit codes + fail-closed)

**Symbol-command exit codes are a 3-state agent contract** (`docs/CONTRACTS.md:157`, was `:156`, drift +1,
enforced at `main.py:11639-11832`, was `:11054-11099`, drift +708 — `grep -n "^def _emit_symbol_command_result" src/tensor_grep/cli/main.py`). **This is the current, FINAL shape — #401 reverted #399,**
so exit 2 fires on ANY truncated/partial result, whether or not it found something:

| Exit | Meaning | Agent action |
| --- | --- | --- |
| `0` | **complete** result — the scan was NOT truncated | trust the findings as exhaustive |
| `1` | genuine not-found on a **complete** scan | the symbol truly is absent |
| `2` | **INCOMPLETE** — truncated by `--deadline` (`partial: true`) or a `--max-repo-files`/scan cap (`result_incomplete: true`) — **REGARDLESS of whether anything was found** | do NOT treat as exhaustive even if it has results (a truncated caller-set is not a safe blast-radius); parse the JSON and retry with a larger budget or a narrower scope |

Do not describe a "found-but-scan-capped result exits 0" behavior anywhere — that was #399,
walked back by #401 after a unanimous design council concluded truncation must always trump
"found something," so a caller/blast-radius consumer can never mistake a capped result for a
complete one. The **native-walk bound** (#400) is the search-side analog: on expiry it sets
`all_results.result_incomplete = True` + `incomplete_reason`, writes a stderr warning,
and **breaks** (`main.py:8137-8270`, was `:7979-7992`) — a flagged partial, never a silent empty. Any new
bound you add MUST follow this shape (see `tensor-grep-architecture-contract` for the
full `BackendExecutionError` contract).

**`tg find` (v1.77.0, #189) followed this shape from its first shipped version** — a genuine
example of a new command adopting this campaign's contract instead of retrofitting it: any
`--deadline`/`--max-repo-files`/internal chunk-cap truncation sets `result_incomplete=true` and
exits 2, whether or not ranked matches were found. It is bounded but NOT refusal-gated the way
`tg search` is (#400's instant vendored/workspace-root refusal does not apply) — see the §1
shipped table.

---

## 6. When NOT to use this skill

- **A hang/slowness whose cause you don't yet know** — do the systematic bisection in
  `tensor-grep-debugging-playbook` first; come back here once it's 

…(truncated)
