mirror of
https://github.com/thedotmack/claude-mem.git
synced 2026-09-20 04:23:02 +08:00
1e284feb05
* fix(context): fit the session block to the hook output limit by selection Claude Code delivers a hook's stdout verbatim up to 10,000 characters, then writes the whole block to disk and hands the model a ~2KB preview stub instead -- while the hook still reports success. The context block was sized by item counts, so on an active project it crossed that line and the model silently received no context at all. Counts cannot fix it: an observation title runs ~150-320 characters, a session-summary line ~250-600, and the last-session summary block 1-5K on its own, so any count safe on a quiet project wastes the budget and any count that fills the budget there overflows on a busy one. The shipped default of 50 observations overflows on titles alone. Measure instead. Render, and while the result is over budget drop the most expensive thing still in it and render again: full narratives first (their titles stay), then the last-session summary block, then sessions, then observations. Whole items go, never half of one, so the block stays well-formed and the header, footer and at least one observation survive. --full is an explicit human request and is left unbounded. Fixes #3802 * fix(context): count the health warning and report what was delivered Addresses the three P1 findings on this PR. All three were real; the first two are fixed here with tests, the third by rebuilding the two bundles that actually carry the change. **The observer-health warning is now inside the measured block.** It was appended to the already-fitted text, so its characters were never counted: a block fitted to 9,998 came back at 10,585 whenever the observer was unhealthy, and over the limit Claude Code replaces the whole thing with the ~2KB preview stub — losing the context *and* the outage warning, which is exactly when that warning matters most. `readObserverHealth` is now read once rather than per render. `fitContextToBudget` calls its render repeatedly and that function touches state; re-reading it per reduction would let the measured length move under the loop. **Injection stats now describe the delivered block.** A run trimmed from seven observations to three still reported `observation_count: 7` and 9,646 tokens injected while the model received three observations and 4,134 tokens. That is the one direction this telemetry must not be wrong in — it read as healthy precisely when context was being dropped. `ContextInjectStats` already promised this ("computed from the same observation set that was rendered"); the code now matches its own contract. Summaries use `budget.config.sessionCount`, the same slice `buildContextOutput` takes for `displaySummaries`. Both live in a new exported `fitContextForDelivery`, split out of `generateContextWithStats` so the fitting and the reporting can be tested without a database — the module previously had no in-process test path at all, which is why neither defect was caught. **Bundles.** `plugin/scripts/worker-service.cjs` and `plugin/scripts/context-generator.cjs` are rebuilt: marketplace hooks execute the checked-in bundle, so the fix does not reach installs without them. `mcp-server.cjs` and `server-service.cjs` are deliberately NOT included, and that is measured rather than assumed. Rebuilding them from a clean tree produces byte-identical output to rebuilding them with this patch applied, and both differ from what is committed on `main` — so their diff is pre-existing drift, not this change, and carrying it here would bury the review in noise. Five cases in `tests/context/context-budget-3802.test.ts`, driven through `fitContextForDelivery` rather than through `fitContextToBudget`, because both defects were in what happens *around* the fitter. The fixture is chosen so it can tell the two apart: 47 observations render to 9,900 characters, which fits, and 9,900 plus a 600-character warning does not. An earlier version of this test used 50 observations and **passed under the mutation** — the fitter's reductions are coarse, so the fitted block landed far enough below the limit that a warning appended afterwards still fit. Measured, then fixed. - Moving the warning back outside the fitter fails `counts the observer-health warning against the budget`. - Passing the original arrays to `buildInjectStats` fails two of the stats cases. `bun test tests/context/` gives 83 pass / 4 fail; the four are `context database ownership`, which fail identically on `main` (a child-process stdout JSON parse, unrelated to this change). `bun run typecheck:root`, `lint:hook-io` and `lint:spawn-env` are clean. Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com> --------- Co-authored-by: Nguyen Thanh Dat <ntdat812@gmail.com> Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>