You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v1.108.70: bounded-source mode for get_symbol_source (#340)
Add an optional, explicitly-labeled source slice to get_symbol_source so a
large symbol or heavy symbol_ids batch can't hand the agent a silently
partial body when the client/model context clips the response after a
successful call. The source-retrieval analogue of search_symbols(token_budget=)
from #328.
New optional params: source_start_line/source_end_line (absolute file lines,
clamped to the symbol body), max_source_lines, max_source_bytes (UTF-8-safe),
and max_total_source_bytes (batch cap; oversized symbols come back partial,
never dropped). When a bound shortens the body, the entry carries
server-authored source_truncated / source_range / source_total_range /
source_total_lines / source_total_bytes / source_truncated_reason /
source_is_bounded_view.
Contract (per the issue's accepted scope):
- default (no bound) is byte-for-byte unchanged; bounded fields appear only
when a bound is requested;
- verify=true still hashes the full indexed body; a bounded entry is flagged
source_is_bounded_view so the slice is never mistaken for the verified bytes;
- context_lines + any bound is rejected (cannot expand past the bound);
- max_total_source_bytes batch cap.
Pure helpers _utf8_safe_truncate + _bound_source (range -> max_lines ->
max_bytes -> batch-remaining precedence). Bounded params hidden under
compact_schemas (still callable) to protect core_compact (3969, under 4000).
schema_baseline.json refreshed. New tests/test_v1_108_70.py (19). Full suite
4690 passed / 10 skipped. Reported by @mmashwani.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,7 @@
1
1
# jcodemunch-mcp — Project Brief
2
2
3
3
## Current State
4
+
- **Version:** 1.108.70 — Bounded-source mode for `get_symbol_source` (#340, reported by @mmashwani; OP went AWOL so self-implemented against jjg's accepted-in-principle 5-point scope ruling). `get_symbol_source` always returned the full indexed body — fragile when a very large symbol or heavy `symbol_ids` batch gets clipped by the MCP client/model context AFTER a successful call (silent partial). New optional bounds: `source_start_line`/`source_end_line` (absolute file lines, clamped to the symbol body), `max_source_lines`, `max_source_bytes` (UTF-8-safe), `max_total_source_bytes` (batch cap — oversized symbols come back PARTIAL not dropped). When a bound shortens the body the entry carries server-authored `source_truncated`/`source_range`/`source_total_range`/`source_total_lines`/`source_total_bytes`/`source_truncated_reason`/`source_is_bounded_view`. **Contract held to jjg's 5 points:** (1) default with no bound is byte-for-byte unchanged (bounded fields appear only when requested); (2) metadata authored on the slice, absolute line frame; (3) `verify=true` still hashes the FULL body, bounded entry flagged `source_is_bounded_view` so the slice isn't mistaken for verified bytes; (4) `context_lines` + any bound is REJECTED (can't silently expand past the bound); (5) `max_total_source_bytes` batch cap. New pure helpers `_utf8_safe_truncate` + `_bound_source` (range→max_lines→max_bytes→batch-remaining precedence, later/tighter bound wins the reason) in `tools/get_symbol.py`; `source` var stays the full body so verify is unaffected. Bounded params added to `_COMPACT_STRIP_PARAMS["get_symbol_source"]` (hidden under compact, still callable) to protect core_compact (now 3969, under §10 ≤4000). `schema_baseline.json` refreshed (compact keys ~unchanged; *_full grew for the new params). The source-retrieval analogue of `search_symbols(token_budget=)` from #328. Scoped to `tools/get_symbol.py` + `server.py` schema. New `tests/test_v1_108_70.py` (19). Full suite 4690 passed / 10 skipped.
4
5
- **Version:** 1.108.69 — Durable-change delivery (`get_delivery_metrics` tool + `delivery` CLI). The suite shows tokens used + tokens **saved** (input); it had no honest **output** measure — the gap that lets teams reward raw volume (tokenmaxxing) over outcomes. New `tools/get_delivery_metrics.py` mines local git over a window and classifies each non-merge commit into ONE mutually-exclusive bucket by precedence: `revert_authored` (undoes work) → `reverted` (a later commit reverts it) → `reworked` (a file it touched was re-touched by a later commit within `rework_horizon_days` = churn-back) → `durable` (landed and stuck). `commits_durable` is the honest **numerator for a cost-per-outcome ratio** — divide AI spend over the same window by it. **Hub-file exclusion is the honesty fix:** files co-touched by ≥max(4, 20%) of window commits (CHANGELOG, version, a monolithic dispatch module) are shared ledgers, not rework evidence, so they're dropped from the rework signal (auditable via `_meta.hub_files_excluded`); without it, hot files flagged 93% of this repo's own commits as rework, with it 38%. Durability is **trailing** — commits inside the horizon are surfaced as `commits_provisional` (counted durable, not yet settled). Reuses `get_symbol_provenance._classify_commit` for the `by_category` breakdown; pure read via `_run_git` (no index write, no new tables). Diagnostic trend, NOT a score to chase (rewarding the number re-invents the proxy-gaming it replaces). `delivery [repo] [--window-days] [--rework-horizon-days] [--cost DOLLARS] [--json]` — `--cost` prints the headline cost-per-durable-change. Standard tier (alongside get_churn_rate/get_hotspots), so `core_compact` unaffected. Tool count 84→85 (full). New `tests/test_delivery_metrics.py` (7: bucket partition, hub exclusion, horizon-zero, honest no-git/empty-window) with date-controlled commits (same-second commits dodge the `0 < delta` rework check). The numerator half; the jMunch Console cost-per-outcome **tile** (join its window token-spend with this) is the buildable follow-up. See memory `project_jmunch_console`.
5
6
- **Version:** 1.108.68 — Retrieval-regret loop (`suggest_corrections` tool + `reflect` CLI). The `ranking_events` telemetry ledger fed only `WeightTuner`; it also carries an unread, louder signal — when retrieval failed and the agent had to re-ask. New `retrieval/regret.py::analyze_regret` mines SIX regret signals over the ledger (re-query churn, low confidence, thin/zero result, ambiguous top, stale-at-query, vocabulary gap = identity-miss-rescued-by-semantic) as severity-ranked clusters (pure read via the existing `token_tracker.ranking_db_query` loader; no new tables). `tools/suggest_corrections.py` fuses clusters + `audit_agent_config` stale-config findings + a dry-run `WeightTuner` proposal into prioritized, explainable corrections: CLAUDE.md routing/glossary lines as **unified-diff previews** (difflib), index-freshness hints (reads `get_watch_status` service.active), the weight proposal. **Charter: NEVER writes a user file** — suggestions only, applying a patch is the user's keystroke; the only persistable state is the `tuning.jsonc` weights sidecar behind explicit `apply_weights`. Honest no-telemetry hint when `perf_telemetry_enabled` off (ledger only exists then). Clean-room "suggest, don't write" is the differentiator ([[feedback_jcm_read_only_charter]]). Surfaces: `suggest_corrections` MCP tool (standard tier, alongside tune_weights/analyze_perf), `reflect` CLI (`reflect [repo] [--all] [--apply-weights] [--json]`), one-line digest integration. Tool count 83→84 (full) / standard +1. `schema_baseline.json` refreshed (core_compact still 3956, suggest_corrections is standard not core). New `tests/test_suggest_corrections.py` (12) incl. the charter no-write gate. See memory `project_retrieval_regret_prd`.
6
7
- **Version:** 1.108.67 — Demote the `language` enum under `compact_schemas`. The `language` filter's full LANGUAGE_REGISTRY enum (~76 values, ~200 tokens of mechanical names an agent already knows) is dropped to a plain `type:string` filter under compact (capability preserved — the tool accepts any language string; unknown → empty match); the full surface keeps the enum. Brings `core_compact` back under the §10 ≤4000 line (4162→**3956**, ~44 margin) after 1.108.55-66 tool additions pushed it to 4146; also trims `standard_compact` (16339→16149). New `_COMPACT_DEMOTE_ENUM_PARAMS` frozenset (keyed by PARAM name, so search_symbols/search_ast/etc. all benefit) + an enum-demotion pass alongside the existing `_COMPACT_STRIP_PARAMS` loop in `_build_tools_list`. `schema_baseline.json` refreshed to honest live counts (the wholesale `capture_schema_baseline.py` run is now CORRECT — it writes 3956, the very thing that made it wrong at v1.108.66 when it wrote 4146). New `test_compact_demotes_language_enum_keeps_capability` pins it (enum gone under compact, present in full). Closes the open follow-up from the Counter release. No behavior change on the full tier.
@@ -116,7 +117,7 @@ src/jcodemunch_mcp/
116
117
tools/
117
118
index_folder.py # Local indexer (sync → asyncio.to_thread in server.py). v1.108.0 adds `paths=[...]` arg via new `resolve_explicit_paths()` helper to skip the directory walk when the caller supplies an explicit file/subdir list; security matches the walk path (outside-root / traversal / symlink-escape / oversize / unsupported-ext all warn-and-skip with per-entry warnings). v1.108.6 adds `identity_mode: "config"|"local"|"git"` arg — delegates to `storage/git_root.resolve_index_identity()` which is the single source of truth for local-folder → repo-ID resolution (replacing duplicated logic across watcher.py / resolve_repo.py / index_folder.py).
Copy file name to clipboardExpand all lines: README.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -110,9 +110,9 @@ is a byte the agent doesn't pay to read.
110
110
<!-- WHATSNEW:START -->
111
111
#### What's new
112
112
113
-
-**[v1.108.61](https://github.com/jgravelle/jcodemunch-mcp/releases/tag/v1.108.61)** (2026-06-18) — Every config key is now documented (no more "Other")
description="Get full source of one symbol (symbol_id → flat object) or many (symbol_ids[] → {symbols, errors}). Supports verify, context_lines, and fqn (PHP FQN via PSR-4).",
1326
+
description="Get full source of one symbol (symbol_id → flat object) or many (symbol_ids[] → {symbols, errors}). Supports verify, context_lines, fqn (PHP FQN via PSR-4), and an optional bounded mode that caps returned source for large symbols/batches.",
"description": "PHP fully-qualified class name (e.g. 'App\\Models\\User'). Resolves to symbol_id via PSR-4. Alternative to symbol_id."
1362
+
},
1363
+
"source_start_line": {
1364
+
"type": "integer",
1365
+
"description": "Bounded mode: absolute file line (1-based, same frame as `line`/`end_line`) to start the returned source slice; clamped to the symbol body."
1366
+
},
1367
+
"source_end_line": {
1368
+
"type": "integer",
1369
+
"description": "Bounded mode: absolute file line (1-based, inclusive) to end the returned source slice; clamped to the symbol body."
1370
+
},
1371
+
"max_source_lines": {
1372
+
"type": "integer",
1373
+
"description": "Bounded mode: keep at most the first N lines of the (ranged) slice. Sets source_truncated + metadata when it shortens the body."
1374
+
},
1375
+
"max_source_bytes": {
1376
+
"type": "integer",
1377
+
"description": "Bounded mode: UTF-8-safe per-symbol byte cap on the returned source. Verify still hashes the full body."
1378
+
},
1379
+
"max_total_source_bytes": {
1380
+
"type": "integer",
1381
+
"description": "Bounded mode (batch): cap on total returned source bytes across all symbols. Oversized symbols come back partial (source_truncated) rather than dropped, preventing an N×per-symbol blowup."
0 commit comments