Compaction summary corrupts `$` sequences in quoted code — fingerprint of an unescaped String.replace replacement pattern
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
After context compaction, code quoted inside the model-written summary arrives corrupted in a very specific pattern: $$ collapses to a single $, and ` $ ` (dollar immediately followed by a closing backtick) disappears entirely, replaced by what looks like the text preceding the insertion point. Meanwhile ${...}, $name, and $(...)` survive untouched.
This is exactly the semantics of JavaScript's String.prototype.replace() replacement patterns ($$ → literal $, ` $ ` → "portion of the string preceding the match", $ before a letter/brace/paren → not special). It strongly suggests the summary text is inserted into the continuation-message template via .replace() without escaping $` in the replacement string — the model writes the code correctly, and the assembly pipeline mangles it.
Environment
- Claude Code 2.1.246, macOS desktop app 1.37937.1, macOS (Darwin 24.6.0)
- Observed in auto-compaction summaries (
isCompactSummary: truerecords in the session transcript jsonl)
Evidence (three real compactions, 25–26 Aug)
- A fenced SQL block quoting a PostgreSQL function verbatim. On disk (migration file, intact, already applied):
... LANGUAGE plpgsql SECURITY DEFINER SET search_path = myschema AS $$
DECLARE
...
END;
$$;
In the compaction summary, both $$ became a single $:
... AS $
DECLARE
...
END; $;
AS $ does not open a dollar-quote — the summary presents syntactically invalid SQL as a verbatim quote of a migration that already ran in production.
- Inline code `
DO $verify$` (4 occurrences across 3 summaries) became:
the migration's own `DO $verify
blocks), then closed the cap gate...
The $ + closing-backtick sequence vanished and was replaced by two newlines — cutting the sentence mid-word and unbalancing the markdown code spans that follow. This matches ` $ ` being interpreted as "preceding portion" where the portion before the placeholder was \n\n`.
- Unharmed in the same summaries:
${...}inside TypeScript template literals (8 occurrences),$sha$,$spec,$(grep ...)— precisely the sequences that are not special in a replacement string. That selectivity is the fingerprint.
Impact
- Deterministic for any codebase using PostgreSQL dollar-quoting (
$$,DO $tag$ ... $tag$) — i.e. essentially every Postgres/Supabase migration. - An agent resuming after compaction sees broken code presented as a verbatim quote of files that are intact on disk. Likely failure modes: the agent "repairs" a healthy file to match the mangled quote, or copies the mangled SQL into a new migration. (We now inject a post-compaction warning about this on our side, but the root cause is upstream.)
Suggested fix
Escape the replacement (summary.replace(/\$/g, '$$$$')) — or use the function form, template.replace(PLACEHOLDER, () => summary), which bypasses replacement-pattern interpretation entirely.
Repro sketch
- Run a session long enough to auto-compact, whose work involves SQL containing
$$/DO $tag$(e.g. reviewing a Postgres migration), so the summary quotes it. - Read the
isCompactSummary: truerecord in the transcript jsonl. - Observe
$$→$and `$`→ preceding-text insertion, while${,$word,$(` survive.
What Should Happen?
Quoted code should survive compaction byte-for-byte: the summary-assembly step must not reinterpret $ sequences in the summary text. $$ should remain $$ and ` $ ` should remain $ `, so quoted PostgreSQL dollar-quoting (AS $$ ... $$;, DO $tag$ ... $tag$;`) stays syntactically valid and matches the file on disk. Apart from the summarizer's own deliberate elisions, no character-level changes should be introduced by the assembly pipeline.
Error Messages/Logs
Steps to Reproduce
- Create a folder with one file,
demo.sql, containing minimal dollar-quoted PostgreSQL:
CREATE OR REPLACE FUNCTION demo_fn() RETURNS int
LANGUAGE plpgsql AS $$
BEGIN
RETURN 1;
END;
$$;
DO $verify$
BEGIN
RAISE NOTICE 'ok';
END;
$verify$;
- Start a Claude Code session in that folder and spend a few turns on the file, so the eventual compaction summary will quote it — e.g.: "Read demo.sql, quote the full function definition verbatim in a fenced sql block, and explain the
DO $verify$block", followed by a couple of follow-up questions about the same code.
- Trigger a compaction. We observed the corruption on automatic compaction (context-window overflow; three separate real summaries on v2.1.246). The quickest route is running
/compact, which produces the same kind of summary record; if manual compaction does not show it, fill the context until auto-compaction fires.
- Read the summary record back from the session transcript:
grep -m1 '"isCompactSummary":true' ~/.claude/projects/<encoded-project-dir>/<session-id>.jsonl | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['message']['content'])"
- Observe, wherever the summary quoted the SQL:
AS $$arrives asAS $and the closing$$;as$;— syntactically invalid SQL presented as a verbatim quote — and the inline formDO $verify$immediately followed by a closing backtick loses the$+backtick sequence (in our transcripts it was replaced by two newlines, cutting the sentence mid-word). Meanwhile${...},$name, and$(...)in the same summary survive untouched. That selectivity matches JavaScriptString.replacereplacement-pattern semantics exactly ($$→ literal$, `$`` → the portion preceding the match).
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
1.1260x.x
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_