|
2 | 2 |
|
3 | 3 | All notable changes to jcodemunch-mcp are documented here. |
4 | 4 |
|
| 5 | +## [1.108.169] - 2026-07-25 - the retrieval verdict survives compaction |
| 6 | + |
| 7 | +### Fixed |
| 8 | + |
| 9 | +- **Every compact encoder silently dropped `_meta.verdict`.** `schema_driven.encode` |
| 10 | + filters `_meta` through a strict allowlist (`for k in meta_keys: if k in meta`), |
| 11 | + and `verdict` appeared in **none of the 15** schema files. Under MUNCH encoding |
| 12 | + the whole retrieval-verdict contract was invisible: `state`, `channels`, |
| 13 | + `coverage`, `scorer`, and the `did_you_mean` near-misses never reached the |
| 14 | + caller. Tools *without* a custom encoder (`get_file_content`) kept their |
| 15 | + verdict, so the signal was present exactly where it was least needed and |
| 16 | + absent on `search_symbols` / `search_text` / `get_ranked_context` — the three |
| 17 | + tools the absence contract is built on. |
| 18 | + |
| 19 | + **The sharpest consequence was on absence evidence.** `server.py`'s chokepoint |
| 20 | + mints the citable `absent:<sha>` ref onto `_meta.verdict.evidence_ref`, and in |
| 21 | + compact mode the caller never received it — so a proof the server had already |
| 22 | + recorded could not be cited, and the in-band `absence_citable:false` / |
| 23 | + `absence_blocked_by` refusal reason was discarded with it. The token-saving |
| 24 | + layer was eating the safety layer. |
| 25 | + |
| 26 | + Fix: new `meta_json_blobs` parameter on `schema_driven.encode`/`decode`, |
| 27 | + mirroring the existing `json_blobs` mechanism. A structured `_meta` value rides |
| 28 | + as `__json._meta.<key>` and round-trips as a real dict. **`meta_keys` could not |
| 29 | + be reused** — that path flattens to a scalar, which would have stringified the |
| 30 | + verdict into a Python repr. All 15 schemas now declare `_META_JSON = ("verdict",)`. |
| 31 | + |
| 32 | +- **`search_text` was never wired for the v1.108.168 rebuilding rule.** That |
| 33 | + release added `index_changed=index_changed_since_load(index)` to |
| 34 | + `search_symbols` and `get_ranked_context` only. The absence chokepoint is |
| 35 | + **generic** — it fires on any `_meta.verdict` dict — so `search_text` could |
| 36 | + reach `absent` and mint a citable ref while structurally unable to detect a |
| 37 | + rewrite underneath its own scan. Same hole as the one .168 closed, left open in |
| 38 | + the third tool. Now passes `index_changed=`. |
| 39 | + |
| 40 | +### Notes |
| 41 | + |
| 42 | +- **Honest cost:** carrying the verdict adds ~58 tokens to a 20-result encoded |
| 43 | + search (+18.6%) and ~89 tokens to a zero-result response. The zero-result case |
| 44 | + is where the value is — that is precisely the response an agent would otherwise |
| 45 | + read as proven absence. The verdict is carried at **full fidelity**: trimming it |
| 46 | + inside the encoder would make compact and JSON disagree, which is the exact |
| 47 | + class of bug being fixed here. If the `ok` verdict is too chatty (`note` |
| 48 | + duplicates `state` in prose), that is a verdict-construction change for its own |
| 49 | + release, not something the compaction layer gets to decide. |
| 50 | +- No new tool, no schema/tool-count/INDEX_VERSION change. New |
| 51 | + `tests/test_v1_108_169.py` (10), including a guard that every schema declaring |
| 52 | + `_META` also declares `verdict` in `_META_JSON`, so a future encoder cannot |
| 53 | + reintroduce the drop. |
| 54 | + |
5 | 55 | ## [1.108.168] - 2026-07-24 - a rebuild underneath a scan cannot prove absence (5th refusal rule) |
6 | 56 |
|
7 | 57 | ### Fixed |
|
0 commit comments