Skip to content

Commit 1097873

Browse files
authored
Merge pull request #123 from jsilvanus/claude/subagents-deploy-phases-137-141-3d6vm5
Interface Parity Track: Phases 137–141
2 parents 225b92c + 470599a commit 1097873

28 files changed

Lines changed: 2288 additions & 183 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitsema": minor
3+
---
4+
5+
The HTTP API's `clusters`, `change-points`, `author`, `impact`, `semantic-diff`, `semantic-blame`, `triage`, and `workflow` analysis routes now accept the same `model`/`textModel`/`codeModel` embedding overrides already available as `--model`/`--text-model`/`--code-model` on their CLI equivalents, via a new shared request-scoped resolver.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitsema": minor
3+
---
4+
5+
`POST /analysis/author` (HTTP API) now supports the full `gitsema author` CLI flag surface: `since`, `detail`, `includeCommits`, `hybrid`, and `bm25Weight` are wired through to the same author-attribution logic the CLI uses, plus `chunks`/`level`/`vss` are accepted for flag-surface parity (no-op, matching the CLI's own behavior for these three). Breaking change: the response shape is now `{ authors, commits? }` instead of a bare array, to carry `includeCommits` results.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitsema": minor
3+
---
4+
5+
`gitsema code-search` now isolates its chunk and symbol candidate pools by default, returning them as separate, independently-ranked result lists instead of one shared-cutoff merged ranking — the default `--level symbol` was combining both pools on every call, which could let a file whose best evidence was chunk-framed get crowded out by symbol-framed matches (or vice versa) purely from embedding-framing bias. Pass `--merge-levels` to opt back into the previous single merged list. The MCP `code_search` tool and Guide's `code_search` tool adopt the same per-level separation, returning a `results_by_level` object (keyed by `file`/`chunk`/`symbol`) instead of a flat `results` array when multiple levels are active — a breaking response-shape change for existing callers, both of which gained a `merge_levels` parameter to opt back into the flat shape.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitsema": minor
3+
---
4+
5+
`POST /evolution/file` now accepts `level` (`file`/`symbol` per-symbol centroid evolution), `branch`, `model`/`textModel`/`codeModel` overrides, and `alerts` (top-N largest semantic jumps with author/commit), matching the CLI's `file-evolution` flag surface. `POST /evolution/concept` gains `branch` and model overrides, matching `evolution`/`concept-evolution`. `POST /graph/hotspots` gains `weightStructural`, matching CLI's `--weight-structural`. Branch filtering is now threaded through the core `computeEvolution()`/`computeConceptEvolution()` functions rather than being CLI-only.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitsema": minor
3+
---
4+
5+
MCP `semantic_search`/`first_seen` and HTTP `POST /search`/`POST /search/first-seen` now have full flag parity with CLI `search`/`first-seen`: per-level result separation (a new `module` search level, plus labeled per-level output or a `resultsByLevel` JSON shape when 2+ of chunk/symbol/module are active, with `merge_levels`/`mergeLevels` to opt back into one merged list), negative-example scoring (`not_like`/`notLike`, `lambda`), boolean query composition (`or`/`and`), `explain`, LLM provenance citations (`explain_llm`/`explainLlm`), query expansion (`expand_query`/`expandQuery`), cluster annotation (`annotate_clusters`/`annotateClusters`), HNSW ANN search (`vss`), multi-repo search (`repos`), per-request embedding model overrides (`model`/`text_model`/`code_model` on MCP, `model`/`textModel`/`codeModel` on HTTP), candidate-pool sampling (`early_cut`/`earlyCut`), and cache bypass (`no_cache`/`noCache`). This is a breaking change to the HTTP `POST /search` JSON response shape when 2+ search levels are active (now `{ resultsByLevel: {...} }` instead of a flat array), matching CLI's existing per-level behavior. `POST /api/v1/analysis/multi-repo-search` is now deprecated in favor of `POST /api/v1/search` with a `repos` param — the old route still works unchanged, with a `Deprecation` response header pointing at its replacement.

.github/skills/gitsema.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,10 @@ This section is generated from `src/core/narrator/interpretations.ts` (result in
661661

662662
**`code_search`** — Symbol/chunk-level search using the code embedding model.
663663

664-
- Usage: Search code using the code embedding model and return symbol/chunk-level matches.
665-
- Parameters: `snippet` (required) — Code snippet to embed and search for.; `top_k` — Maximum number of results to return (default 10, max 25).; `branch` — Restrict to blobs on this branch.
666-
- Result shape: Rendered "score path [blobHash]" lines (symbol level by default).
667-
- How to read it: Like semantic_search but embeds with the code model and targets symbols/chunks — better for finding specific functions/classes from a code snippet than from prose. Higher scores mean closer code-level similarity.
664+
- Usage: Search code using the code embedding model and return symbol/chunk-level matches. The chunk and symbol pools are searched in isolation by default and returned as a `results_by_level` object with independently-ranked lists per level (Phase 137) — pass merge_levels to opt back into one merged `results` array.
665+
- Parameters: `snippet` (required) — Code snippet to embed and search for.; `top_k` — Maximum number of results to return (default 10, max 25).; `branch` — Restrict to blobs on this branch.; `merge_levels` — Merge the chunk/symbol pools into one shared-cutoff results array instead of separate per-level results_by_level lists.
666+
- Result shape: { snippet, results_by_level: { file: [...], chunk: [...], symbol: [...] } } by default — chunk and symbol pools are searched in isolation and returned as separate, independently-ranked lists (Phase 137). Pass merge_levels to get the pre-Phase-137 shape instead: { snippet, results: [{ paths[], score, blobHash }] }.
667+
- How to read it: Like semantic_search but embeds with the code model and targets symbols/chunks — better for finding specific functions/classes from a code snippet than from prose. Higher scores mean closer code-level similarity within a level's own list. Don't rank across levels by raw score — chunk and symbol pools embed differently-framed text (raw excerpt vs. name+signature-annotated), so their scores are not on a directly comparable scale; read each level list separately.
668668

669669
**`cross_repo_similarity`** — Compare semantic search results for the same query across two separate repos.
670670

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,18 @@ Track semantic drift of a single file across its Git history.
534534

535535
`code-review` follows the same exit-code contract as `policy-check` (0 ok / 1 runtime error / 2 usage error / 3 gate failed).
536536

537+
#### `gitsema code-search <snippet> [options]`
538+
539+
| Flag | Default | Description |
540+
|---|---|---|
541+
| `-k, --top <n>` | `10` | Number of results |
542+
| `--level <level>` | `symbol` | Search granularity: `file` \| `chunk` \| `symbol` |
543+
| `--model <model>` || Embedding model override |
544+
| `--branch <branch>` || Restrict to blobs on this branch |
545+
| `--threshold <n>` | `0` | Minimum similarity score (0–1) |
546+
| `--merge-levels` | off | The default `symbol` level isolates the chunk and symbol candidate pools into separate labeled lists (Phase 137, mirroring `search`'s `--merge-levels`); pass this flag to merge them back into one shared-cutoff ranked list |
547+
| `--no-headings` || Don't print column header row |
548+
537549
### Knowledge Graph
538550

539551
| Command | Description |

docs/PLAN.md

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5250,7 +5250,52 @@ earlier over-narrowed draft of this phase):**
52505250
needs generalizing beyond `search.ts`), `docs/parity.md`, `docs/features.md`,
52515251
`README.md`, test files.
52525252

5253-
**Status:** not started.
5253+
**Status:** ✅ complete. Implemented as specified, reusing Phase 136's
5254+
`resolveExtraLevels()`/`isMultiLevelActive()`/`includeFiles` mechanism
5255+
directly (imported from `src/cli/commands/search.ts`) rather than
5256+
re-deriving it in each of the three call sites.
5257+
5258+
- CLI `gitsema code-search`: `codeSearchCommand()` now resolves
5259+
`resolveExtraLevels(searchChunksFlag, searchSymbolsFlag, false)` from the
5260+
`--level` argument (default `symbol` → both flags true → always
5261+
multi-level-active) and, when active and `--merge-levels` is not passed,
5262+
runs one isolated `vectorSearch()` call per level (`file`/`chunk`/`symbol`)
5263+
and renders them via `renderResultsByLevel()`. New `--merge-levels` flag
5264+
restores the pre-Phase-137 single merged call/`renderResults()` output.
5265+
- MCP `code_search` (`src/mcp/tools/search.ts`): same isolation; adds a
5266+
`merge_levels` boolean param (default `false`). When multi-level and not
5267+
merged, returns `{ snippet, results_by_level: { file, chunk, symbol } }`
5268+
(each level's array pared to `{ paths, score, blobHash, kind }`) instead of
5269+
the old flat `{ snippet, results: [...] }` — accepted breaking change per
5270+
`docs/parity.md` §4.
5271+
- Guide `code_search` tool (`src/core/narrator/guideTools.ts`): same shape
5272+
change (`results_by_level` vs. `results`), same `merge_levels` param. This
5273+
tool always searches both pools (no `level` param existed), so it always
5274+
hits the multi-level condition unless `merge_levels` is passed.
5275+
- `src/core/narrator/interpretations.ts`'s `code_search` entry updated to
5276+
document the new `results_by_level` shape and to warn against comparing
5277+
raw scores across levels (chunk vs. symbol embedding-framing bias); skill
5278+
regenerated via `pnpm gen:skill` (`skill/gitsema-ai-assistant.md` and
5279+
`.github/skills/gitsema.md` both updated).
5280+
- Tests: new `tests/integration/codeSearchLevelSeparation.test.ts` — proves
5281+
(a) the per-blob dedup invariant (a blob with both a chunk and a symbol
5282+
candidate appears at most once, in both a merged call and each isolated
5283+
per-level call), and (b) the crowding-out proof mirroring
5284+
`searchLevelSeparation.test.ts`, adapted to code-search's chunk-vs-symbol
5285+
pools (a weak lone chunk match survives its own isolated topK cutoff but
5286+
is crowded out of a small shared topK by two stronger symbol matches in
5287+
the merged/`--merge-levels` path). `pnpm build && pnpm test` green.
5288+
- `docs/parity.md` updated: the "`code-search` never received Phase 136's
5289+
treatment" gap note (§ Parity Observations) and its §6 roadmap mention are
5290+
resolved; Tool Matrix/flag tables unaffected (no new interface reached,
5291+
same five interfaces as before — CLI/Guide/MCP/LSP/Interactive — just a
5292+
shape/flag change within each).
5293+
- **Deviation from spec:** the phase entry's "Files likely touched" list
5294+
did not anticipate `src/core/narrator/interpretations.ts` needing an
5295+
update — included because `code_search`'s result shape changed and that
5296+
file is the single source of truth for "how to read" a tool's output
5297+
(enforced by the `docsSync` test).
5298+
- Changeset added (`code-search-per-level-results.md`, minor).
52545299

52555300
---
52565301

@@ -5311,7 +5356,45 @@ CLI's own implementation already threads these through
53115356
(`multi-repo-search`), `docs/parity.md`, `docs/features.md`, `README.md`,
53125357
test files.
53135358

5314-
**Status:** not started.
5359+
**Status:** ✅ complete.
5360+
5361+
**Deviations from the original spec:**
5362+
- **MCP per-level output shape:** the plan text said MCP `semantic_search`
5363+
would return `results_by_level`; MCP `registerTool()` handlers return
5364+
`{ content: [{ type: 'text', text }] }` (a rendered text blob, not
5365+
structured JSON — no MCP search tool returns JSON today), so the per-level
5366+
shape is expressed as labeled `== file ==` / `== chunk ==` / `== symbol ==`
5367+
/ `== module ==` text sections (via the existing `renderResultsByLevel()`
5368+
helper), mirroring CLI `search`'s own text-mode per-level output exactly.
5369+
A `merge_levels` param still opts back into one unlabeled list, matching
5370+
the CLI's `--merge-levels` semantics. HTTP `POST /search` does return the
5371+
literal `resultsByLevel` JSON key as specified, since that route already
5372+
returns structured JSON.
5373+
- **`multi-repo-search` resolution:** chose option (b) from the two
5374+
alternatives listed — `repos: string[]` was folded into `POST /search`
5375+
(and MCP `semantic_search`/`first_seen`) as the parity-complete
5376+
implementation, and `POST /analysis/multi-repo-search` was kept as a thin,
5377+
response-shape-unchanged **deprecated** alias over the same
5378+
`multiRepoSearch()` core call (adds a `Deprecation: true` header + a
5379+
`Link` header pointing at `/api/v1/search`) rather than duplicated or
5380+
enriched in place. Recorded in `docs/deprecations.md` §1.
5381+
- **Per-request model overrides implementation:** rather than mutate
5382+
`process.env` (CLI's `applyModelOverrides()` pattern, unsafe for a
5383+
long-running server handling concurrent requests), added
5384+
`hasModelOverride()`/`buildProviderForRequest()` to
5385+
`src/core/embedding/providerFactory.ts` as a small shared core-level
5386+
helper — resolves a per-request `ResolvedConfig` and calls the existing
5387+
`getTextProvider()`/`getCodeProvider()` factories directly, with no env
5388+
mutation. Both MCP `src/mcp/tools/search.ts` and HTTP
5389+
`src/server/routes/search.ts` use this same helper (no duplicated
5390+
resolution logic between the two interfaces). This helper is intentionally
5391+
narrow to `search`/`first-seen` for now; Phase 140 is scheduled to
5392+
generalize a model-override mechanism across the rest of the
5393+
`analysis.ts` HTTP routes and may fold this in or build alongside it.
5394+
- `code_search` (the third tool sharing `src/mcp/tools/search.ts`) was
5395+
**not** touched — that tool's per-level separation and flag parity is
5396+
Phase 137's scope (a separate, concurrently-developed phase), not
5397+
Phase 138's.
53155398

53165399
---
53175400

@@ -5332,13 +5415,17 @@ throughout this track — breaking changes are expected and accepted, not
53325415
avoided.
53335416

53345417
**Build order:** no hard dependencies between these phases; Phase 138 is
5335-
already in flight (above). The rest can ship in any order, though 140
5418+
✅ complete (above). The rest can ship in any order, though 140
53365419
(the model-override umbrella) is worth doing early since it's referenced by
5337-
several others (141, 143) as "don't duplicate this per-route, do it once."
5420+
several others (141, 143) as "don't duplicate this per-route, do it once" —
5421+
note Phase 138 already added a narrow `hasModelOverride()`/
5422+
`buildProviderForRequest()` helper to
5423+
`src/core/embedding/providerFactory.ts` scoped to `search`/`first-seen`;
5424+
Phase 140 should generalize or fold into that rather than starting fresh.
53385425

53395426
| Phase | Scope | Source finding |
53405427
|---|---|---|
5341-
| 138 | `search`/`first-seen`/`multi-repo-search` full parity (widened above) | Search/evolution/graph audit + analysis.ts audit |
5428+
| 138 | `search`/`first-seen`/`multi-repo-search` full parity (widened above) | Search/evolution/graph audit + analysis.ts audit |
53425429
| 139 | `evolution` (file/concept) + `hotspots` HTTP/MCP parity | Search/evolution/graph audit |
53435430
| 140 | Systemic `--model`/`--text-model`/`--code-model` override triplet, missing from nearly every `analysis.ts` HTTP route | analysis.ts audit |
53445431
| 141 | `author` HTTP route full parity (largest single-command gap) | analysis.ts audit |
@@ -5385,7 +5472,33 @@ Parity Track (see Phase 138's track table).
53855472
wherever `computeEvolution`/`computeConceptEvolution` live), `docs/parity.md`,
53865473
test files.
53875474

5388-
**Status:** not started.
5475+
**Status:** ✅ complete.
5476+
5477+
**Deviations from spec:**
5478+
- Scoped to **HTTP** parity, as the title's actual scope bullets describe
5479+
(all three bullets say "HTTP route"/route body schemas); MCP's `evolution`/
5480+
`concept_evolution`/`hotspots` tools were left untouched — they have their
5481+
own pre-existing branch/model-override gaps shared with several other MCP
5482+
tools, which is a wider, separately-scoped MCP audit, not folded in here.
5483+
- `hotspots`'s `weightStructural` is accepted on `POST /graph/hotspots` for
5484+
CLI flag-surface parity but is a **no-op**, exactly like the CLI's own
5485+
`hotspotsCommand` (`src/cli/commands/hotspots.ts` even says so in a
5486+
comment): `computeHotspots()`'s risk score is an unweighted geometric mean
5487+
over the active lens's signals with no weighting parameter anywhere in the
5488+
scoring function. Adding real weighting would be a `computeHotspots()`
5489+
behavior change beyond this phase's route-parity scope — documented as a
5490+
known no-op rather than silently pretended to work.
5491+
- `--narrate` on all three routes was **deferred**, not resolved in-scope:
5492+
it's LLM/infra-adjacent (needs a configured narrator model + redaction
5493+
path already used by `narrate`/`explain`) and is better done together with
5494+
Phase 144's narrator-route work than piecemeal here.
5495+
- The CLI's own `file-evolution --branch` post-filtered timeline entries
5496+
after calling `computeEvolution()`; this phase moved that filtering into
5497+
`computeEvolution()` itself (new `branch` option alongside the existing
5498+
`useSymbolLevel`), so the CLI command now delegates to the same
5499+
branch-aware core function the HTTP route uses — a small CLI-internal
5500+
refactor beyond the literal ask, done to avoid maintaining two branch-filter
5501+
implementations.
53895502

53905503
---
53915504

@@ -5417,7 +5530,36 @@ pattern rather than reinventing it per route.
54175530
**Files likely touched:** `src/server/routes/analysis.ts`, a new shared
54185531
helper module, `docs/parity.md`, test files.
54195532

5420-
**Status:** not started.
5533+
**Status:** ✅ complete *(completed vNEXT)*. Implemented as specified: a new
5534+
`src/server/lib/modelOverrides.ts` exports a `modelOverrideSchema` Zod
5535+
fragment (`{model, textModel, codeModel}`, spread into each affected route's
5536+
body schema via `...modelOverrideSchema.shape`) and a
5537+
`resolveRequestProvider(body, fallbackProvider)` helper applied to all 8
5538+
routes (`clusters`, `change-points`, `author`, `impact`, `semantic-diff`,
5539+
`semantic-blame`, `triage`, `workflow`). One deliberate deviation from
5540+
mirroring `resolveModels()`/`buildProviderOrExit()` verbatim: those CLI
5541+
helpers mutate `process.env` and call `process.exit()` on failure, both
5542+
unsafe for a long-running, concurrently-serving HTTP process (env mutation
5543+
from one request could leak into another in-flight request; `process.exit`
5544+
would kill the server). `resolveRequestProvider()` instead threads an
5545+
explicit `ResolvedConfig` through `buildProvider()` (already supported,
5546+
env-free) and throws a catchable `ModelOverrideError` that routes turn into
5547+
a 400 instead of exiting. `clusters` accepts and validates the triplet for
5548+
CLI/HTTP flag-surface parity, but — same as the CLI `clusters` command,
5549+
which also only calls `applyModelOverrides()` and never threads a provider
5550+
into `computeClusters()` — the override has no effect on clustering
5551+
behavior today, since `computeClusters()` clusters all stored embeddings
5552+
regardless of model; this is documented inline and in `docs/features.md`
5553+
rather than silently faked. `docs/parity.md` §2.3 item 3 updated to mark the
5554+
CLI-vs-HTTP gap closed. New `describe('Phase 140: model overrides on
5555+
analysis routes', ...)` block in `tests/serverRoutes.test.ts` (11 new tests)
5556+
covers all 8 routes, including a `providerFactory.js` mock that routes
5557+
`model` names containing `"override"` to a distinguishable provider so the
5558+
tests assert the override actually took effect, plus two tests asserting a
5559+
400 (not a crash or 502) when the override resolves to an invalid provider
5560+
config (`GITSEMA_PROVIDER=http` with no `GITSEMA_HTTP_URL`). `pnpm build &&
5561+
pnpm test` green. Changeset added (`analysis-routes-model-overrides.md`,
5562+
minor).
54215563

54225564
---
54235565

@@ -5439,7 +5581,21 @@ Update `docs/parity.md`, `docs/features.md`, `README.md`.
54395581
`src/core/search/authorSearch.ts` (or wherever author attribution lives),
54405582
`docs/parity.md`, test files.
54415583

5442-
**Status:** not started.
5584+
**Status:** ✅ complete. `AuthorBodySchema` in `src/server/routes/analysis.ts`
5585+
gained `since`, `detail`, `includeCommits`, `hybrid`, `bm25Weight` (all
5586+
threaded through to `computeAuthorContributions`/`hybridSearch`/
5587+
`searchCommits`, mirroring `src/cli/commands/author.ts` exactly) plus
5588+
`chunks`/`level`/`vss` for flag-surface parity — these three are accepted
5589+
but deliberately not wired to anything, matching the CLI's own dead-flag
5590+
behavior (`computeAuthorContributions` is blob-level only; the CLI's
5591+
`--vss` on `author` is itself just a warn-and-ignore no-op). Model-override
5592+
triplet (`model`/`textModel`/`codeModel`) intentionally excluded — tracked
5593+
separately in Phase 140. **Deviation from spec:** response shape changed
5594+
from a bare `AuthorContribution[]` array to `{ authors, commits? }` (the
5595+
`commits` key populated only when `includeCommits` is set) — a breaking
5596+
change, taken deliberately per `docs/parity.md` §4's "parity over API
5597+
response stability" rule, since a bare array had no room for
5598+
`includeCommits`'s second result set.
54435599

54445600
---
54455601

0 commit comments

Comments
 (0)