fix: enforce the memory cap on write, never truncate on read #182
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/reflection-rewrites-truncated-memories"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What's in
MemoryStore.read()truncated what it returned at 6000 chars (…[truncated]), while the reflection prompt demanded "FULL replacement: merge the current memories" andwrite()accepted anything. Once the file reached the cap — model miscounting, or a hand-edit past it — the agent merged against a partial view and "reproduced as much as available", wiping the tail. The #177 console trace made the failure visible (a reasoning log shows the agent noticing the truncation, weighing the risk, and writing anyway).read()returns the whole file. The only over-cap file possible after this PR is one the user hand-edited themselves — and hiding the user's own notes from the assistant (and the reflection agent that must merge them) would be the wrong repair. An oversized state is seen and then fixed, not silently cut.write()refuses content whose trimmed length exceeds 6000 chars and writes nothing (anArgumentErrorinvariant — the file cannot drift over the cap through the app again). Thewrite_memoriestool pre-checks and answers a coaching refusal instead of surfacing the invariant: merge against the FULL current memories, prune stale or superseded notes, tighten wording, and graduate durable how-to knowledge to a skill (write_skill), keeping only a one-line pointer in the memories.Verification
scripts/verify.sh appexecuted on a clean tree ate5e4074:All tests passed!(1402 tests), analyze clean with infos fatal,== verify OK (app) ==. Backend untouched.write_skillcontract; plus the trim-boundary test added after a mutation probe survived).if (false)— red onwrite refuses over-cap content;trimbefore the length check removed — red onwrite accepts content that fits once trimmed; tool pre-check →if (false)— red on the coaching-refusal test; the prompt's ENFORCED-cap line dropped — red on the prompt-contract test.backend/changed, soscripts/verify.sh backendwas not run.Closes the truncation-destroy failure mode surfaced by #159/#177.
Flutter Coverage
Total: 86.2% (11135 of 12916)
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A PR that fixes a silent data-destruction bug by moving the invariant to the only side that can actually destroy data~ The console trace from #177 where the agent noticed the truncation, weighed the risk, and wrote anyway gave me chills ♡ And now the tail of the user's memories can never again be wiped by a merge against a view that ended in
…[truncated]. Wonderful.Verdict: ✅ Looks good to me~
No blockers. I audited every angle I could sink my teeth into:
read()consumers traced (agent_service.dart:381reflection prompt,:600per-turn context message) — uncapping reads is safe for both, and the only over-cap file reachable now is one the user hand-edited. Hiding the user's own notes from the agent that must merge them would be the wrong repair; the ADR amendment owns this trade-off explicitly, in writing. *write()caller is the pre-checking tool — no path bypasses the coaching refusal, and the pre-check + store invariant pair mirrors the sibling layering exactly (WriteSkillToolvalidates kebab-case at the tool; the registry sink throws on traversal). Consistent~createSync, so a refused write leaves no file behind — and the test pinsexistsSync() == isFalse, not just the throw. Nothing-written is proven, not assumed. ♪stay under 6000is gone; prompt, tool description, schema field, and refusal message all interpolateMemoryStore.maxChars. Zero bare6000survives inapp/.…[truncated](sole remaining hit is the test asserting its absence).✅ What I liked~
write_skill) — cap pressure becomes a knowledge-management signal instead of a silent cliff. That's not a bug fix, that's teaching~6001 raw chars whose trimmed form fits) pins the subtlecontent.trim()-before-check semantics — the exact seam where a lazier guard would regress.Automated review by Jibril · 2026-09-20
CI/CD: absent for head
e5e4074(PR opened minutes ago, no coverage bot yet) · Local checks:flutter analyzeclean (25.1s), full suite 1402/1402 pass (matches PR body), 4/4 mutation probes red-on-own-test at head, tree restored pristine