Repository navigation
Merged
Conversation
…d a config-aware oversize notice SessionStart's handoff + REMEMBER legend + now/today/recent/archive sections had no total cap, only memory_inject_max_bytes (per-file). An ordinary store (six individually healthy files, 11-14.7KB combined) routinely crossed Claude Code's own ~10,000-character preview/persist threshold, so the model never saw === MEMORY === at all -- the preview cut off inside === LAST HANDOFF ===. - thresholds.session_start_max_bytes (default 9000, bytes not characters -- see the new _remember_apply_session_start_budget header for why bytes are the conservative choice). Fills in priority order handoff -> now -> recent -> today -> archive; whatever does not fit is listed by name and size, never silently dropped, reusing the same convention source=compact already uses for its own deferred files. Malformed values fall back to the default and are logged (#834's own convention). 0 disables the budget. - thresholds.handoff_max_redeliveries (default 3): once the same unchanged handoff content has been delivered this many times, a further delivery is listed by path instead of re-injected in full -- re-sending the same ~2KB note forever was most of what was pushing stores over the new budget. The delivery record still advances past the cap, so a later redelivery still reports how long the note has been pending, and /remember still replaces it immediately. - The memory_inject_max_bytes over-cap notice now says "capped by config" and drops the /remember:doctor suggestion when the configured cap sits below the bundled 200000 default -- that is a deliberate choice (often to stay under the new total budget), not evidence of a broken store. Measured on a realistic fixture matching the issue's own repro: before this fix, 12,890 bytes; after, 6,740 bytes with the lowest-priority sections listed instead of injected. Docs: docs/configuration.md and config.example.json document both new keys; docs/windows-skip-triage.md records the three new modules' blanket win32 skip, same template and verdict as the existing session-start-hook.sh subprocess fixtures they are modelled on. Fixed a latent bash 3.2 parsing bug surfaced by wrapping the handoff+memory render in command substitution: two pre-existing `case` blocks without a leading `(` on their patterns (a `case "$_hkey" in fingerprint) ...` fed from a `while read` loop, and `case "$DELIVERIES" in ''|*[!0-9]*) ...`) parse fine standalone but not nested inside `$( { ... } )` under bash 3.2 -- added the leading `(` this codebase already uses elsewhere for exactly this reason. Deferred (per the brief): session_start.inject_files (request 2) and the config-cache `-nt`-on-deletion bug (split to #843, scripts/log.sh and scripts/lib-env-cache.sh -- untouched here). Co-Authored-By: Claude Sonnet 5 <[email protected]>
… the bundled cap Review of #845 found three defects in the SessionStart budget; each now has a failing-first test. 1. Quadratic cut. _remember_apply_session_start_budget located each memory section with ${text%%"$h"*} / ${text#*"$h"} over the whole captured body, which bash 3.2 and 5 evaluate in time quadratic in the body size, once per dropped section, and only ever on an over-budget (i.e. large) store. Measured on a ~300 KB store over the default 9000-byte budget: 55.3s (bash 5.3) / 45.7s (bash 3.2) before, 0.35s / 0.44s after. 2. Content steered the cut. The first "--- <basename> ---" match was taken anywhere in the body, so a header-shaped line in the handoff (file content, the #721 threat model) or in an earlier memory file moved the cut there: it removed the handoff's tail, its random-token END fence and "=== MEMORY ===", and listed files as dropped that were still injected. Both fixed by never searching content. The renderer now decides inclusion per section before emitting it (_REMEMBER_BUDGET_EXCLUDE, set only inside the budget's own render subshell, unset at lib load so the environment cannot inject it), and lists what it left out with the size it already measured. The head is recovered by length (body minus a fresh render of the MEMORY tail), trusted only after the tail compares byte-identical; a mismatch leaves the body as rendered and logs it. The under-budget path still returns at the length check: no new render, fork or copy. 3. "Capped by config" hid a malformed file. With memory_inject_max_bytes below the bundled 200000, every over-cap file was called deliberate, including a file larger than 200000 itself (the #346 shape). The config wording now applies only while every over-cap file is within the bundled default; past it, the original warning and /remember:doctor stay. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This was referenced Oct 2, 2026
fdaviddpt
added a commit
that referenced
this pull request
Oct 2, 2026
… guess on v0.38.0 (#871) * docs(#864): narrow (not confirm) the RUNTIME_FETCH_EXEC third-finding guess on v0.38.0 #861's rewording of pipeline/shell.py and scripts/log.sh did not clear the directory portal's RUNTIME_FETCH_EXEC warning; v0.38.0 raised the count from 2 to 3 instead. Which third file matched is only visible behind the expanded row in the portal's web UI, which this lane has no access to, so the real trigger stays unconfirmed. Source-only follow-up: grepped the two largest v0.37.0->v0.38.0 diffs (scripts/lib-memory-context.sh, scripts/session-start-hook.sh, both from #842/#845) for curl, wget, download, fetch, eval and exec and found nothing in either -- the existing doc guess that #842/#845's SessionStart budget code is the new third match has no textual support. scripts/log.sh is the one file here with real eval calls on validated input, the strongest literal (still unconfirmed) candidate for why it keeps matching. No code changed. docs/releasing.md records the narrowed state and flags that a human with portal access still needs to expand the row and record the file/line before this can be closed. Co-Authored-By: Claude Sonnet 5 <[email protected]> * docs(#864): precise churn figures, clarify grep scope per self-review Explore review flagged two accuracy issues in 4ccd6dd's docs/releasing.md text: "+164"/"+74 lines" were git diff --stat churn totals (insertions plus deletions), not insertion counts, misleading by ~2 and ~11 lines respectively -- replaced with explicit insertions/deletions. The "found nothing" grep claim was ambiguous between grepping the whole shipped file vs only the diff's added lines; under the whole-file reading it is false (scripts/session-start-hook.sh has 9 pre-existing `exec` hits). Clarified that only the added (+) lines were grepped, gave the exact command, and noted why the whole-file reading is misleading (those hits pre-date v0.38.0 and cannot explain a count that only rose between versions). Co-Authored-By: Claude Sonnet 5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SessionStart had no limit on its total output. Claude Code saves any hook output over roughly 10,000 characters to a file and shows the model only a short preview. On ordinary stores (11–14.7KB, measured by the reporter), that preview ends inside the handoff, so the whole MEMORY section (now, recent, today, archive) never reaches the model.
This covers requests 1, 3 and 4 from #842. Request 2 (
session_start.inject_files) is deferred. The config-cache bug from the same report is #843 / #844.thresholds.session_start_max_bytes, default 9000;0turns it off.source=compactalready lists files. Nothing is truncated partway through a file.source=compactis unaffected.thresholds.handoff_max_redeliveries, default 3. Once the same handoff has been shown in full N times, later starts list it by path and size instead of re-injecting it. The delivery count still goes up, and/rememberstill replaces the handoff as before.memory_inject_max_bytesis set below the bundled default, the notice says "capped by config" and no longer points to/remember:doctor.docs/configuration.mdandconfig.example.json.tests/test_config_contract.pypasses.Closes #842
Measured
On a test store sized like the one in the report:
Testing
23 new tests, each written first and watched fail before the change: 11 of 23 failed then, and all 23 pass now.
tests/test_session_start_total_budget_842.py(11). Covers: under budget (control), drop order, dropped files listed with their size, total stays under the budget, every file just under its per-file cap, a store with lots of multi-byte text,0turning the budget off, a malformed value, andcompactunchanged (control).tests/test_handoff_max_redeliveries_842.py(8). Covers: below the threshold (control), listed instead of injected on the 4th delivery, the count still going up, a new handoff resetting the count (control),0turning the cap off, a lower configured threshold, and a malformed value.tests/test_memory_inject_capped_notice_842.py(4). Covers: a lowered cap gets the new wording; at or above the default keeps the original wording (controls).pytest: 2820 passed, 59 skipped, 0 failed.$( { … } )broke two existingcaseblocks under the stock bash 3.2.57, a parse error caught while developing this. They now use the leading(pattern form this file already uses elsewhere for the same reason.Review notes
session-start-hook.shnow run inside$( … ). I followed the 38 functions called from it. No variable set inside is read after it, no background job starts inside it, and nothing inside writes to the real terminal output directly or callsexecorexit.after_session_startplugin output. The default of 9000 leaves about 1000 bytes of room under the 10K cap for these.additionalContexttext.Cross-platform claims
/bin/bash3.2.57.docs/windows-skip-triage.md. CI covers the matrix.🤖 Generated with Claude Code
[AI-generated]