|
2 | 2 |
|
3 | 3 | All notable changes to jcodemunch-mcp are documented here. |
4 | 4 |
|
| 5 | +## [1.108.187] - 2026-07-26 - both fusion exits record the features they measured |
| 6 | + |
| 7 | +### Fixed |
| 8 | + |
| 9 | +- ⚠ **`get_ranked_context`'s fusion exit passed no `extract_ledger_features` payload |
| 10 | + at all**, so `top1_score`, `top2_score` and `identity_hit` were recorded as their |
| 11 | + defaults on every ranking-ledger row it ever wrote. Three of regret's six signals |
| 12 | + read those columns, and a default is indistinguishable from a measurement once it |
| 13 | + is in the ledger. Reported as an adjacent finding in 1.108.186 rather than folded |
| 14 | + in; this is the follow-through. |
| 15 | + |
| 16 | + Identity comes from the identity channel's own `raw_scores` rather than being left |
| 17 | + to default. That exit BUILDS the identity channel, so unlike its sibling it knows |
| 18 | + per symbol whether identity matched, and `build_identity_channel` admits only |
| 19 | + symbols scoring above zero. |
| 20 | + |
| 21 | +- ⚠ **`search_symbols`' fusion exit had the narrower half of the same defect.** It |
| 22 | + passed the features but built their input as `{"score": s}` with no `identity` key, |
| 23 | + so `identity_hit` was recorded False on every fusion row regardless of what the |
| 24 | + identity channel found. Both fusion producers are fixed together: shipping one |
| 25 | + honest and one false would have guaranteed a third release on the same column. |
| 26 | + |
| 27 | +- ⚠ **A pre-fix single-result row was clustered as `thin_result` on the strength of a |
| 28 | + default.** That detector treats `top1_score is None` as thin, which reads a MISSING |
| 29 | + measurement as a weak one — correct where the score was genuinely absent, wrong for |
| 30 | + rows that recorded no features at all. Only those rows are skipped; every other |
| 31 | + producer's behaviour is unchanged, pinned by tests for the empty-result, |
| 32 | + weak-score and other-tool cases. |
| 33 | + |
| 34 | +### Changed |
| 35 | + |
| 36 | +- **The ledger input is now SEPARATE from the confidence input, and a failing test is |
| 37 | + what found this.** `compute_confidence` sniffs the same `identity` key when no |
| 38 | + `has_identity_match` is passed, scoring it 1.0 known-true / 0.7 unknown / 0.6 |
| 39 | + known-false — so feeding identity through the shared input would have moved the |
| 40 | + confidence of every fusion search (measured: 0.742 -> 0.783 on one fixture). That |
| 41 | + is a ranking-adjacent change with its own justification, not a side effect of |
| 42 | + recording a column, so the confidence call keeps the input it has always had. The |
| 43 | + separation is pinned behaviourally: `attach_confidence` must receive rows without |
| 44 | + an identity key on the same call where the ledger features receive rows with one. |
| 45 | + |
| 46 | +- **`ledger_trust.identity_label_is_trustworthy`, and its discriminator is weaker |
| 47 | + than the semantic one on purpose.** `identity_hit` has no value a pre-fix row alone |
| 48 | + can carry — pre-fix is always `0`, and `0` is also an honest post-fix answer, so |
| 49 | + matching on the column would discard good evidence forever. What IS exact: a row |
| 50 | + that returned symbols while recording no `top1_score` can only have come from the |
| 51 | + exit that passed no features. Post-fix a non-empty result always carries a fused |
| 52 | + score, and an empty result carries no `returned_ids` to match on. |
| 53 | + |
| 54 | +- ⚠ **`search_symbols_fusion`'s history is NOT separable, and no heuristic is used.** |
| 55 | + It always recorded top scores and only omitted the `identity` key, so its pre-fix |
| 56 | + rows are indistinguishable from honest post-fix rows that had no identity match. |
| 57 | + There is no version or timestamp column to bound it by. The recency window is the |
| 58 | + only remedy and that is stated rather than papered over. |
| 59 | + |
| 60 | +- ⚠ **An identity guard on regret's `vocabulary_gap` was UNREACHABLE and was |
| 61 | + removed** — found by a failing non-vacuity test, not by reading. That signal |
| 62 | + requires `semantic_used`, and a `get_ranked_context_fusion` row with |
| 63 | + `semantic_used=1` is already refused by the v1.108.186 rule, so the rows are |
| 64 | + suppressed either way. An unreachable guard reads like protection and is not. |
| 65 | + |
| 66 | +- Disclosure follows the 1.108.186 pattern: `events_without_ledger_features` on |
| 67 | + `analyze_regret`, `no_ledger_features` per repo in `analyze_perf --ledger`, and |
| 68 | + `identity_hits` no longer counts a default as a miss. Both keys are absent — not |
| 69 | + zero — for a caller whose ledger holds no such row. |
| 70 | + |
| 71 | +### Notes |
| 72 | + |
| 73 | +- New `tests/test_v1_108_187.py` (24). Non-vacuity shown on both exits: reverting |
| 74 | + either producer fails four tests. Compatibility pinned — a pre-fix ledger stays |
| 75 | + readable and unrewritten. |
| 76 | +- No tool-count change, no `INDEX_VERSION` change, no schema change. Replay gate |
| 77 | + green (mrr/ndcg/recall 1.0), which matters here because the confidence separation |
| 78 | + is the thing that kept ranking output still. |
| 79 | +- Still open from the 1.108.186 findings: `token_tracker._ensure_loaded(None)` on the |
| 80 | + first savings write repins the perf db to `~/.code-index` even when a tool was |
| 81 | + handed a `storage_path`. |
| 82 | + |
5 | 83 | ## [1.108.186] - 2026-07-26 - the ledger stops claiming a channel that never ran |
6 | 84 |
|
7 | 85 | ### Fixed |
|
0 commit comments