Skip to content

Commit 07bbb0c

Browse files
jgravelleclaude
andcommitted
get_parity_map: correspondence-aware migration parity map (v1.108.111)
New standard-tier tool + `parity` CLI mapping port parity between a source and target symbol tree (two subpaths of one repo, or two repos). Per source symbol: ported / ported_diverged / unported / orphaned / added, with rename-aware matching (reuses find_similar_symbols similarity primitives) and a dependency-ordered port plan (SCC-grouped via get_dependency_cycles, Kahn topo, unblocked/blocking_deps). Read-only and plan-only. Reserved parity_axes slot for P3 jdoc/jdata suite axes. No INDEX_VERSION bump; core_compact unchanged. Registered on all surfaces; schema_baseline refreshed; tool count 88 full. tests/test_parity_map.py (17). Full suite 5159 passed / 7 skipped; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e8d5a30 commit 07bbb0c

10 files changed

Lines changed: 1064 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,40 @@
22

33
All notable changes to jcodemunch-mcp are documented here.
44

5+
## [1.108.111] - 2026-07-07 - New tool: get_parity_map (migration parity map)
6+
7+
### Added
8+
9+
- **`get_parity_map`** — correspondence-aware migration/port parity between a
10+
SOURCE symbol tree and a TARGET tree (two subpaths of one repo, or two repos).
11+
For each source function/method/class it reports **ported** (an equivalent
12+
counterpart exists), **ported_diverged** (a counterpart exists but its
13+
signature/body drifted — the failure a name-only "exists in both trees" check
14+
reports as done), **unported** (no counterpart, still referenced by source
15+
code), **orphaned** (unmatched with no in-scope caller — an entry point or a
16+
possible intentional drop), or **added** (target-only surface). Rename-aware:
17+
a ported-and-renamed symbol (`getUserById``fetch_user`) is matched by a
18+
structural+behavioral similarity blend (signature-token Jaccard + callee-name
19+
Jaccard + byte-length ratio), not reported as a false unported+added pair.
20+
- When `include_port_plan` is set, the unmatched source symbols are ordered by
21+
the source dependency graph (leaves first) so nothing is ported before what it
22+
calls; import cycles are collapsed to an `scc_group` (ported together) via the
23+
existing Kosaraju SCC pass, and each entry carries `unblocked` +
24+
`blocking_deps` so the agent can pick the next actionable symbol.
25+
- **Read-only and plan-only.** It maps the migration and orders the work; it
26+
never edits, moves, or ports anything — that stays the caller's keystroke.
27+
`parity_pct` is a labelled estimate (`ported / (ported + ported_diverged +
28+
unported + orphaned)`); `added` is excluded from parity. All reuse — no new
29+
index tables, **no INDEX_VERSION bump**. Standard tier (`core_compact`
30+
unchanged at 3969). Divergence policy `signature` (default) / `signature+body`
31+
/ `name_only`; rename matching on by default with an auto-disable + honest note
32+
past the pair budget on very large scopes.
33+
- New `parity` CLI: `parity <source> <target> [--source-path] [--target-path]
34+
[--match-threshold] [--divergence] [--no-rename] [--no-port-plan] [--json]`.
35+
- `parity_axes` is a reserved (empty in P1) slot for the P3 suite axes (jdoc
36+
doc-parity, jdata schema-parity). Tool count **88** in `full`. New
37+
`tests/test_parity_map.py` (17). See `docs/prd-parity-map.md`.
38+
539
## [1.108.110] - 2026-07-07 - check_embedding_drift marked mutating (readOnlyHint suite parity)
640

741
### Changed

CLAUDE.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# jcodemunch-mcp — Project Brief
22

33
## Current State
4+
- **Version:** 1.108.111 — **New tool `get_parity_map` (migration parity map; F-15 #2 from the tokensave review, clean-room per [[feedback_clean_room_innovation]] — competitor unnamed in shipped artifacts).** Correspondence-aware port parity between a SOURCE and TARGET symbol tree (two subpaths of one repo, or two repos). Per source function/method/class: **ported** (equivalent counterpart), **ported_diverged** (counterpart exists but signature/body drifted — the failure a name-only "exists in both trees" check reports as done), **unported** (no counterpart, still referenced by source), **orphaned** (unmatched with no in-scope caller — entry point or possible drop), **added** (target-only). **Rename-aware:** unmatched source symbols matched to same-kind target candidates by a structural+behavioral blend (`_signature_tokens`/`_callee_set`/`_jaccard`/`_byte_ratio` reused from `find_similar_symbols`), so a ported-and-renamed symbol isn't a false unported+added pair. **Port plan** (`include_port_plan`): dependency-ordered over ALL unmatched source (adjacency from `_callee_set`), Kosaraju SCC grouping via `get_dependency_cycles._find_cycles` (cycles → one `scc_group`, ported together), Kahn topo over the condensed graph → `order_index`, per-symbol `unblocked`/`blocking_deps`. **Read-only / plan-only** (never edits or ports — [[feedback_jcm_read_only_charter]]); `parity_pct = ported/(ported+ported_diverged+unported+orphaned)`, labelled estimate, `added` excluded. Divergence policy `signature` (default, compares param_count + name-stripped signature tokens so a pure rename isn't flagged) / `signature+body` (also content_hash) / `name_only`. New `tools/get_parity_map.py` + `cli/parity.py` + `parity` CLI subcommand; standard tier (**core_compact unchanged 3969**), registered on all surfaces, `schema_baseline.json` refreshed, tool count **88** full. `parity_axes` reserved slot for P3 suite axes (jdoc doc-parity / jdata schema-parity, [[feedback_suite_parity_for_agent_contracts]]). NO INDEX_VERSION bump (pure read). New `tests/test_parity_map.py` (17). Design note: initial "orphaned = no *ported* caller" starved the port plan (swallowed the pending graph); corrected to "no *in-scope* caller" with the plan spanning all unmatched. See `docs/prd-parity-map.md`.
45
- **Version:** 1.108.110 — **`check_embedding_drift` flipped to `readOnlyHint=False` — closes the suite readOnlyHint divergence.** jcm was the outlier: jdoc v1.93.0 / jdata v1.17.0 already marked it mutating (`force=true` re-pins the drift canary = a write); jcm reported it read-only, so Claude Code plan mode ran `check_embedding_drift(force=true)` silently. Fix: new `_ANNOTATION_ONLY_WRITERS` frozenset (`{check_embedding_drift}`) unioned into `_NON_READONLY_TOOLS` on top of `counter.STATE_CHANGING_ACTIONS | {order, route}`. **Deliberately NOT added to STATE_CHANGING_ACTIONS** — that set gates the counter's `order()` allow_state_change opt-in, and the tool's DEFAULT path is a pure drift report; adding it there would break `order("check_embedding_drift")` reads. So the annotation write-set and the order-gate write-set now diverge by exactly this one dual-mode tool (documented at the def). Purely an annotation change; NO INDEX_VERSION bump, no wire/behavior change. New `tests/test_v1_108_110.py` (4: marked mutating / not in STATE_CHANGING / order allows read without opt-in / in annotation write-set) + extended `test_readonly_annotations.py` (check_embedding_drift in write spot-check + annotation-only-writers-not-in-STATE_CHANGING guard); updated `test_v1_108_104.py` exact-equality assert to account for the new union member. **Suite readOnlyHint parity now fully CLOSED across jcm/jdoc/jdata.**
56
- **Version:** 1.108.109 — **Svelte (.svelte) language support (community PR #362, @thomasmodeneis).** `.svelte` files were dropped at discovery as `wrong_extension`; infra was already ~60% wired (JS-family ext set, `$lib`/`.svelte-kit` aliases, hook nudge), the gap was the parser layer. New `_parse_svelte_symbols` (extractor.py) mirrors the tree-sitter Vue parser `_parse_vue_symbols` — bundled `svelte` grammar produces the same `document -> script_element -> raw_text` shape as Vue, so each `<script>` block's raw_text is re-parsed with the JS/TS grammar. Surfaces: synthetic component symbol (kind=class, from filename), top-level fn/class/interface/type, **Svelte 5 runes** (`let x = $state(...)`/`$derived(...)`, incl. `$derived.by` member form + destructured `let {a,b} = $props()`), **Svelte 4 props** (`export let`/`export const`), **Svelte 4 reactive labels** (`$: doubled = ...`) — last three as kind=constant, all parented to the component. Instance + module (`context="module"`) blocks both parsed, each with its own line offset. `language="svelte"` (Vue parity). New `_extract_svelte_imports` (imports.py) mirrors `_extract_astro_imports`: whole-file ESM scan + PascalCase component-usage edges over HTML-comment-masked body, with `<script>`/`<style>` bodies stripped first so TS generics (`identity<T>`/`Array<Item>`) aren't misread as component tags. Registration: `.svelte`->`svelte` in LANGUAGE_EXTENSIONS + new `SVELTE_SPEC`/LANGUAGE_REGISTRY entry (auto-flows into the MCP language enum + config validation); svelte added to package_registry.py JS-family tuples + plan_refactoring.py import/decl patterns. **NO INDEX_VERSION bump** — additive, previously-skipped files just gain symbols on re-index (Astro/template precedent). New `tests/test_svelte.py` (14) + `tests/fixtures/svelte/`. Verified: grammar bundled, PR branch based on current HEAD (no rebase needed), scope reconciled (8 files, no hidden CI edits), read-only charter respected, full suite **5137 passed / 7 skipped**. Second contributor language feature after Astro (#311) / templates (#337).
67
- **Version:** 1.108.108 — **Audit WS-5 tail (atomic content writes + 4-path persistence test) — WS-5 COMPLETE.** `_write_cached_text` (sqlite_store.py) wrote bodies straight to the final path → a crash mid-write left a truncated body that `_SOURCE_DUMP_TOOLS` would serve as source. Now writes to a `.tmp.<pid>` sibling + `os.replace` (same-dir → atomic on all platforms), cleans temp on failure, leaves existing body intact; covers all 3 content-write sites (full/incremental/migrate) via the single chokepoint. **4-path test:** one parametrized `tests/test_v1_108_108.py` asserts save_index/incremental_save/save_branch_delta/scip-ingest ALL take the `indexwrite` lock keyed on the repo (the V9 invariant) — a future write path added without the lock now fails loudly. Plus atomic-write regression tests (roundtrip, no temp leftover, overwrite-clean, failure-leaves-original). NO INDEX_VERSION bump. **WS-5 fully done (V9 + W7 + W1 + atomic + 4-path).** Remaining audit P2s: WS-8 V12-license transport (backend coord), WS-7 benchmark V1 (jjg's canonical number). Open parity follow-up: jdoc/jdata readOnlyHint.
@@ -105,7 +106,7 @@
105106
- **INDEX_VERSION:** 17
106107
- **Tests:** 4432 passed, 10 skipped (1.108.25 — full count varies by optional-dep availability)
107108
- **Python:** >=3.10
108-
- **Tool count:** 86 in `full` (front door hidden; +1 v1.108.69 `get_delivery_metrics`, +1 v1.108.90 `get_endpoint_impact`); `tool_surface=counter` exposes a 3-tool front door (`order`/`menu`/`route`) instead
109+
- **Tool count:** 88 in `full` (front door hidden; +1 v1.108.90 `get_endpoint_impact`, +1 v1.108.94 `index_dependency`, +1 v1.108.111 `get_parity_map`); `tool_surface=counter` exposes a 3-tool front door (`order`/`menu`/`route`) instead
109110

110111
## Key Files
111112
```
@@ -174,6 +175,7 @@ src/jcodemunch_mcp/
174175
get_delivery_metrics.py # (v1.108.69) get_delivery_metrics: durable-change delivery over a window. Classifies each non-merge commit into one bucket (revert_authored/reverted/reworked/durable) via _run_git; commits_durable is the numerator for cost-per-outcome (the `delivery` CLI's --cost divides AI spend by it). Hub files (CHANGELOG/version/monolithic dispatch, co-touched by >=max(4,20%) of commits) excluded from the rework signal (auditable via _meta.hub_files_excluded); commits_provisional flags the trailing tail. Reuses get_symbol_provenance._classify_commit for by_category. Read-only, no new tables
175176
get_symbol_provenance.py # get_symbol_provenance: full git archaeology per symbol — authorship lineage, semantic commit classification, evolution narrative. Phase 5: optional stack_frequency block reading runtime_stack_events over a 30-day window — per-severity counts + first/last seen; narrative gains an appended sentence when error count >= 3
176177
get_pr_risk_profile.py # get_pr_risk_profile: unified PR/branch risk assessment — fuses blast radius + complexity + churn + test gaps + volume into composite score. Phase 7: when runtime traces have been ingested, adds a 6th signal (runtime_traffic; W=0.15 with the static five rebalanced to 0.85 of their original weights) plus a runtime_dark_code_introduced flag for PRs that add code in files with zero runtime evidence. Static-only callers (no traces) keep the historical 5-signal mix bit-for-bit.
178+
get_parity_map.py # (v1.108.111) get_parity_map: correspondence-aware migration parity between a SOURCE and TARGET symbol tree (two subpaths of one repo, or two repos). Exact + rename matching (reuses find_similar_symbols _signature_tokens/_callee_set/_jaccard/_byte_ratio), status per source symbol (ported/ported_diverged/unported/orphaned/added), dependency-ordered port_plan (adjacency from _callee_set, SCC grouping via get_dependency_cycles._find_cycles, Kahn topo, unblocked/blocking_deps). Read-only/plan-only; parity_axes reserved for P3 suite axes. Standard tier
177179
get_hotspots.py # get_hotspots: top-N high-risk symbols by complexity x churn
178180
get_repo_map.py # get_repo_map: query-less, token-budgeted, signature-level repo overview ranked by PageRank — cold-start orientation. Reuses cached PageRank, emits signatures only (no bodies), greedy-packs per-file under token_budget
179181
find_similar_symbols.py # find_similar_symbols: multi-signal consolidation detection — semantic (embeddings) + structural (signature/size) + behavioral (callee Jaccard); union-find clustering, verdict tier (near_duplicate / similar_logic / parallel_implementation), canonical pick by PageRank, differs_by breakdown. BM25 inverted-index pre-filter for sub-N^2 cost. Skips tests/dunders/generated by default.
@@ -259,6 +261,7 @@ src/jcodemunch_mcp/
259261
| `digest` | Agent stand-up briefing — composes since-last-session delta + risk surface + dead-code candidates; tracks per-repo last-seen SHA at `~/.code-index/digest_state/`; also exposed as MCP tool `digest`. v1.108.68 adds a one-line retrieval-regret summary when the ledger has clusters |
260262
| `reflect` | (v1.108.68) Surface retrieval regret as SUGGESTED config corrections — `reflect [repo] [--project-path] [--window-days N] [--all] [--apply-weights] [--json]`. Thin CLI over the `suggest_corrections` tool; read-only (only `--apply-weights` writes, and only the tuning.jsonc sidecar) |
261263
| `delivery` | (v1.108.69) Print durable-change delivery metrics for a window — `delivery [repo] [--window-days N] [--rework-horizon-days N] [--cost DOLLARS] [--json]`. Thin CLI over `get_delivery_metrics`; `--cost` prints the headline cost-per-durable-change (how much got done for how little). Read-only git archaeology |
264+
| `parity` | (v1.108.111) Map migration parity between two symbol trees — `parity <source> <target> [--source-path P] [--target-path P] [--match-threshold F] [--divergence signature\|signature+body\|name_only] [--no-rename] [--no-port-plan] [--json]`. Thin CLI over `get_parity_map`: ported/diverged/unported/orphaned/added counts + dependency-ordered port plan. Read-only/plan-only |
262265
| `health` | Print `get_repo_health` JSON to stdout (includes six-axis radar). For CI/scripting; `--radar-only` for just the radar sub-field. Used by the v1.88.0 health-radar GitHub Action |
263266
| `file-risk` | Print per-symbol risk JSON for a file (composite score + four-axis breakdown). Used by the v0.2.0 VS Code risk-density gutter |
264267
| `observatory build\|init` | Public OSS code-health observatory pipeline — clones, indexes, scores a configured repo list; writes static HTML + RSS + JSON to an output dir. v1.90.0; CI repo-id bug fixed in v1.90.1. Live at https://jgravelle.github.io/jcodemunch-observatory/ |

benchmarks/schema_baseline.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"core_compact": 3969,
3-
"core_full": 5328,
4-
"standard_compact": 17173,
5-
"standard_full": 18769,
6-
"full_compact": 18483,
7-
"full_full": 20079
3+
"core_full": 5331,
4+
"standard_compact": 17616,
5+
"standard_full": 19215,
6+
"full_compact": 18926,
7+
"full_full": 20525
88
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "jcodemunch-mcp"
3-
version = "1.108.110"
3+
version = "1.108.111"
44
description = "Token-efficient MCP server for source code exploration via tree-sitter AST parsing"
55
readme = "README.md"
66
requires-python = ">=3.10"

0 commit comments

Comments
 (0)