@brandry/claude-jsonl-compressor 1.0.0-rc.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,593 +1,666 @@
1
- # Claude JSONL Compressor
2
-
3
- Strict, model-assisted compression for one Claude Code session transcript, plus an independent byte-preserving compatibility repair for historical `Read.pages` records.
4
-
5
- **Release:** [`1.0.0-rc.1`](CHANGELOG.md)<br>
6
- **Engine:** `v10`<br>
7
- **Model-pack schema:** `v11`<br>
8
- **License:** GPL-3.0-only<br>
9
- **Repository:** [brandrylabs/claude-jsonl-compressor](https://github.com/brandrylabs/claude-jsonl-compressor)
10
-
11
- This project is not affiliated with Anthropic. Claude Code's transcript JSONL is an observed internal format, not a published stable storage API. Always keep the original file or a verified backup.
12
-
13
- ## What It Does
14
-
15
- - Compresses one Claude Code JSONL into one current compact-style summary pair plus a recent raw active suffix.
16
- - Uses a model-authored semantic summary by default, with deterministic evidence selection and validation around it.
17
- - Excludes rewound/inactive branch text from every Claude-readable output layer.
18
- - Preserves recent conversation records for Claude rewind.
19
- - Projects one final `last-prompt` while retaining unknown source fields.
20
- - Validates UUIDs, parents, sessions, compact metadata and API-level tool pairing.
21
- - Supports candidate output and transactional replacement of one live `.claude/projects` session.
22
- - Handles repeated compression, including an explicit prior-summary verbatim mode.
23
- - Offers an independent byte-level repair that removes unsupported historical `Read.pages` members without reserializing the JSONL.
24
- - Runs with Python's standard library. No tokenizer or YAML dependency is required.
25
-
26
- ## Quick Start
27
-
28
- With this repository installed as a Codex skill, ask Codex:
29
-
30
- ```text
31
- Use the claude-jsonl-compressor skill on exactly one Claude Code JSONL.
32
- Input: C:\data\session.jsonl
33
- Output: C:\data\session.compressed.jsonl
34
- Target: about 150k estimated Messages tokens.
35
- Keep recent raw records for rewind, use the default model-assisted summary, and run validation.
36
- ```
37
-
38
- For a live `.claude/projects` file, explicitly request a numbered backup and in-place replacement, confirm that the session is closed, and provide a work directory outside `.claude`. The detailed two-pass CLI workflow appears below.
39
-
40
- ## Why Model-Assisted By Default
41
-
42
- Deterministic code can select topology and validate bytes, but it cannot decide which historical arguments, legal distinctions, design rationale or research conclusions matter. Python therefore freezes the active branch and builds a bounded, source-anchored evidence pack; a host model writes the summary; Python then verifies request/evidence digests, anchors, required source excerpts and the final JSONL.
43
-
44
- The script itself never calls a model or the network. The evidence pack bridges the practical 1M-session-versus-smaller-summarizer gap by including every non-empty older active human message and assistant `text`/`thinking` message in full while excluding inactive branches, recent raw records and low-value structural repetition. U+FFFD is reported without discarding the rest of a mandatory record. If mandatory evidence exceeds either pack ceiling, generation stops instead of sampling semantic history.
45
-
46
- ## Safety Properties
47
-
48
- ### Strict resume authority
49
-
50
- The physically last `type: "last-prompt"` record is authoritative in automatic mode. A malformed latest pointer is an error; the program does not search backward for an older valid pointer and accidentally revive an obsolete branch.
51
-
52
- Strict active mode rejects:
53
-
54
- - missing or malformed authority
55
- - missing leaf or parent
56
- - parent loops or malformed non-string/empty `parentUuid` values
57
- - ordinary-message/non-attachment physical parent inversion
58
- - recurring, pointer-mismatched or otherwise unsafe session lineage
59
- - duplicate UUIDs anywhere in the file
60
- - unsafe post-pointer extension
61
-
62
- Use `--resume-leaf UUID` only for an explicit recovery decision. It is reported as `active-chain-manual-override`, distinct from default strict `active-chain`. Use `--preserve-physical-tail` only as an explicit compatibility mode; it does not provide inactive-branch isolation.
63
-
64
- An unusual or ambiguous topology is a stop, not an automatic fallback. The CLI exits before creating a pack, candidate, report, backup or other sidecar. A hosting agent may explain one applicable explicit recovery control and ask the user to confirm it in a new instruction; it must not infer that confirmation from the original compression request. Manually spliced transcripts generally require physical-tail compatibility and therefore lose branch/rewind isolation.
65
-
66
- Current Claude Code reconstructs a conversation from a UUID map and parent links, so physical line order is not universally chronological. This project accepts only same-session `attachment -> attachment` physical inversions on an otherwise complete acyclic chain and writes them back in logical parent order. It also accepts one-way A->B (or A->B->C) session lineage only when a session never recurs and the final leaf and pointer match the final session. All earlier-session records become summary evidence; recent raw records remain entirely in the final session. A tool pair crossing that forced cut is a hard stop.
67
-
68
- ### Rewound branches stay out
69
-
70
- The source indexes are partitioned into mutually exclusive sets:
71
-
72
- | Set | Meaning | May enter summary? | May remain raw? |
73
- | --- | --- | --- | --- |
74
- | `summaryIndexes` | Older active-chain records | Yes | No |
75
- | `rawKeepIndexes` | Recent active-chain records | No | Yes |
76
- | `sideKeepIndexes` | Policy-approved checkpoint side records | No | Side records only |
77
- | `controlProjectionIndexes` | Pointer and safe global control records | No | Projected only |
78
- | `excludedBranchIndexes` | Inactive UUID branches | No | No |
79
- | `excludedUnattributedIndexes` | Unattributed non-chain records | No | No |
80
-
81
- Excluded records appear in reports only as counts and digests. Their text is not copied into the model pack, compact summary, deterministic appendix, verbatim prior-summary block or output message chain.
82
-
83
- ### Transactional writes
84
-
85
- - Input and candidate bytes are bound by full SHA-256; candidates are staged, flushed, validated and atomically published.
86
- - Numbered backups use exclusive creation and byte verification. Live replacement also captures and verifies the actual old target before installing the candidate.
87
- - Failed post-replacement validation restores the captured original. A rollback failure is raised prominently while verified recovery assets remain available.
88
- - Concurrent target recreation preserves the external target and recovery backups, then fails without publishing the candidate.
89
- - Parent-directory fsync is best effort and reported; this is not a cross-platform power-loss guarantee.
90
- - If the live JSONL commits but final report publication fails, the CLI does not undo valid committed data. It prints a `committed-report-failed` receipt with hashes and backup/candidate labels and exits with code 3.
91
-
92
- ## Requirements
93
-
94
- - Python 3.10 or newer
95
- - Node.js 22 or newer only when using the npm command wrappers
96
- - Claude Code is optional; it is needed only for an explicitly requested runtime `/resume` or `/context` smoke test
97
-
98
- No Python package installation is required.
99
-
100
- ## Installation
101
-
102
- ### Install As A Codex Skill
103
-
104
- Clone the repository into the Codex skill directory:
105
-
106
- ```bash
107
- skill="${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
108
- mkdir -p "$(dirname "$skill")"
109
- git clone https://github.com/brandrylabs/claude-jsonl-compressor.git "$skill"
110
- ```
111
-
112
- Windows PowerShell:
113
-
114
- ```powershell
115
- $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $env:USERPROFILE '.codex' }
116
- $skill = Join-Path $codexHome 'skills\claude-jsonl-compressor'
117
- New-Item -ItemType Directory -Force (Split-Path -Parent $skill) | Out-Null
118
- git clone https://github.com/brandrylabs/claude-jsonl-compressor.git $skill
119
- ```
120
-
121
- Update or uninstall the skill:
122
-
123
- ```bash
124
- git -C "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor" pull --ff-only
125
- rm -rf "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
126
- ```
127
-
128
- ```powershell
129
- git -C $skill pull --ff-only
130
- Remove-Item -LiteralPath $skill -Recurse -Force
131
- ```
132
-
133
- The installed directory must contain `SKILL.md`, `scripts/`, `config/`, `templates/` and `references/`.
134
-
135
- ### Install The npm CLI
136
-
137
- After the RC is published:
138
-
139
- ```bash
140
- npm install --global @brandry/claude-jsonl-compressor@rc
141
- ```
142
-
143
- This installs two commands:
144
-
145
- ```text
146
- claude-jsonl-compressor
147
- claude-jsonl-repair-read-pages
148
- ```
149
-
150
- The npm package is a zero-dependency Node shim over the bundled Python implementation. It forwards arguments, stdio, exit codes and signals with `shell: false`. The tarball also contains `SKILL.md`, `agents/` and `references/`, but npm installation does not register the directory as a Codex skill; skill installation remains a separate copy/link step.
151
-
152
- Upgrade or uninstall the global CLI:
153
-
154
- ```bash
155
- npm install --global @brandry/claude-jsonl-compressor@rc
156
- npm update --global @brandry/claude-jsonl-compressor
157
- npm uninstall --global @brandry/claude-jsonl-compressor
158
- ```
159
-
160
- Local development install and invocation:
161
-
162
- ```bash
163
- npm install --save-dev @brandry/claude-jsonl-compressor@rc
164
- npm update @brandry/claude-jsonl-compressor
165
- npm exec -- claude-jsonl-compressor --version
166
- npm exec -- claude-jsonl-repair-read-pages --version
167
- npm uninstall @brandry/claude-jsonl-compressor
168
- ```
169
-
170
- Run without retaining an installation:
171
-
172
- ```bash
173
- npx --yes --package @brandry/claude-jsonl-compressor@rc claude-jsonl-compressor --version
174
- npx --yes --package @brandry/claude-jsonl-compressor@rc claude-jsonl-repair-read-pages --version
175
- ```
176
-
177
- Actual npm/npx operations use the same Python CLI options:
178
-
179
- ```bash
180
- npx --yes --package @brandry/claude-jsonl-compressor@rc claude-jsonl-compressor --input session.jsonl --write-model-pack run/session.model-pack.md
181
- npx --yes --package @brandry/claude-jsonl-compressor@rc claude-jsonl-repair-read-pages --input session.jsonl --scan-only
182
- ```
183
-
184
- ### Use From Source Without Installing
185
-
186
- ```bash
187
- python scripts/compress_claude_jsonl.py --version
188
- python scripts/repair_claude_jsonl.py --version
189
- ```
190
-
191
- ### Can Claude Code Install This Skill?
192
-
193
- `SKILL.md` is a Codex skill definition, not a native Claude Code skill/plugin format. Claude Code can still run the Python or npm commands when instructed, but installing this directory into Claude's configuration does not automatically create an equivalent Claude-native skill.
194
-
195
- ## Detailed Workflow
196
-
197
- The examples below use PowerShell and a local skill installation:
198
-
199
- ```powershell
200
- $skill = "$env:USERPROFILE\.codex\skills\claude-jsonl-compressor"
201
- ```
202
-
203
- ### 1. Analyze The Resume Path
204
-
205
- ```powershell
206
- python "$skill\scripts\compress_claude_jsonl.py" `
207
- --input "C:\data\session.jsonl" `
208
- --analyze-resume-path
209
- ```
210
-
211
- This is read-only. A nonzero result must be resolved before model-pack generation.
212
-
213
- ### 2. Generate A Model Evidence Pack
214
-
215
- ```powershell
216
- python "$skill\scripts\compress_claude_jsonl.py" `
217
- --input "C:\data\session.jsonl" `
218
- --write-model-pack "C:\work\run\session.model-pack.md" `
219
- --target-ratio 0.30 `
220
- --min-recent-records 120 `
221
- --summary-char-budget 60000 `
222
- --target-estimated-tokens 150000 `
223
- --model-pack-char-budget 500000 `
224
- --model-pack-estimated-token-budget 150000
225
- ```
226
-
227
- The evidence pack has two independent default ceilings: 500,000 characters
228
- and a conservative 150,000-token local estimate. The token ceiling leaves
229
- working room in a typical 200k summarizer context. Mandatory human/assistant
230
- semantic records, prior compact summaries, handoff lines, and required coverage
231
- groups are never sampled or clipped; generation stops if they do not fit.
232
- Optional source/tool/system/error evidence is added by importance and chronology
233
- until either ceiling is reached, and the pack/report state whether that optional
234
- evidence was truncated. Do not install a tokenizer to change this workflow.
235
-
236
- `--target-ratio` is an approximate byte-ratio planning input, not a hard release gate. For a hard local Messages estimate ceiling, use:
237
-
238
- ```powershell
239
- --target-estimated-tokens 150000
240
- ```
241
-
242
- This candidate-output estimate is separate from the model-pack reading ceiling.
243
- It covers complete retained structured message payloads, including full thinking,
244
- `tool_use.input`, `tool_result`, and `toolUseResult` data. It does not include
245
- Claude's system prompt, tool schemas, MCP servers, agents, skills, memory files
246
- or runtime-loaded context. It is not a promise about total `/context` usage.
247
-
248
- `--summary-char-budget` has a hard minimum of 4000 characters. A smaller value or a blank compact summary is rejected instead of publishing unusable memory.
249
-
250
- ### 3. Write The Model Summary
251
-
252
- The model reads the pack and writes `session.model-summary.md`.
253
-
254
- The first HTML comment must be copied exactly and contains:
255
-
256
- ```text
257
- source_sha256
258
- summary_source_sha256
259
- evidence_anchor_lines_digest
260
- required_anchor_groups_digest
261
- handoff_summary_digest
262
- pack_request_digest
263
- required_claim_sources_digest
264
- ```
265
-
266
- Every substantive transcript claim needs a displayed `L<number>` anchor. Every external-handoff claim needs a displayed `H<number>` anchor. The validator rejects invented or hidden anchors. It also requires at least one cited anchor from every generated coverage group and an anchored body under each of the nine exact headings printed in the pack. Only the exact leading metadata comment and exact required headings are exempt from line grounding; extra HTML comments or headings are errors. The exact whole line `Unknown from provided anchors.` is the only unanchored uncertainty placeholder; adding other text to that line removes the exemption.
267
-
268
- Schema v11 assigns a required full-text L-anchor group to every non-empty older active human message and every older active assistant `text`/`thinking` message. It binds every selection/resource option through `pack_request_digest`. Under the exact `### Mandatory Evidence Coverage` subsection, the model must provide exactly one line per mandatory semantic/prior-summary record:
269
-
270
- ```text
271
- - L42 support_text_json="exact source substring" disposition=covered
272
- ```
273
-
274
- The JSON string must decode to a meaningful exact substring of that L record. This mechanical gate blocks anchor-only boilerplate and leaves a checkable source excerpt; it does not prove that all natural-language interpretation is correct. Schema v11 also reserves early/middle/late/latest, source/tool and prior-summary coverage. Prior compact summaries and every physical line of an explicitly supplied handoff enter the pack in full; handoff early/middle/late/latest H groups must be cited. Pack generation stops instead of truncating or sampling mandatory evidence when either the character or estimated-token ceiling is insufficient. Raise `--model-pack-char-budget` or `--model-pack-estimated-token-budget` only when the summarizing model can read the resulting pack.
275
-
276
- The summary should preserve:
277
-
278
- - current state
279
- - chronology and supersessions
280
- - user constraints and wording
281
- - assistant/model research decisions and reasons
282
- - evidence provenance
283
- - rejected alternatives
284
- - risks, unknowns and follow-ups
285
- - recent raw boundary
286
-
287
- Later events control current state, but earlier decisions and their reasons remain as superseded history.
288
-
289
- ### 4. Build A Candidate
290
-
291
- ```powershell
292
- python "$skill\scripts\compress_claude_jsonl.py" `
293
- --input "C:\data\session.jsonl" `
294
- --output "C:\data\session.compressed.jsonl" `
295
- --target-ratio 0.30 `
296
- --min-recent-records 120 `
297
- --summary-char-budget 60000 `
298
- --target-estimated-tokens 150000 `
299
- --model-pack-char-budget 500000 `
300
- --model-pack-estimated-token-budget 150000 `
301
- --model-summary "C:\work\run\session.model-summary.md"
302
- ```
303
-
304
- Pass exactly the same selection options used for the model pack. In particular,
305
- repeat both non-default model-pack ceilings so the second pass regenerates the
306
- same evidence contract.
307
-
308
- The command writes:
309
-
310
- ```text
311
- session.compressed.jsonl
312
- session.compressed.jsonl.validation.json
313
- session.compressed.jsonl.report.md
314
- ```
315
-
316
- The input remains unchanged.
317
-
318
- ## Live Session Replacement
319
-
320
- Close the Claude Code process using that session before replacement. The live target must be an existing regular `.jsonl` file under `.claude/projects`.
321
-
322
- Generate the model pack and model summary outside `.claude`, then run:
323
-
324
- ```powershell
325
- python "$skill\scripts\compress_claude_jsonl.py" `
326
- --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
327
- --replace-original `
328
- --confirm-session-closed `
329
- --work-dir "C:\work\claude-compression\SESSION-TIMESTAMP" `
330
- --model-pack-estimated-token-budget 150000 `
331
- --target-estimated-tokens 150000 `
332
- --model-summary "C:\work\claude-compression\SESSION-TIMESTAMP\session.model-summary.md"
333
- ```
334
-
335
- The default backup is placed beside the live file:
336
-
337
- ```text
338
- SESSION.jsonl.backup
339
- SESSION.jsonl.backup1
340
- SESSION.jsonl.backup2
341
- ```
342
-
343
- To keep backups outside `.claude`:
344
-
345
- ```powershell
346
- --backup-dir "C:\work\claude-compression\SESSION-TIMESTAMP\backups"
347
- ```
348
-
349
- Candidate, report, validation, model pack and model summary files remain under the external work directory. Do not manually copy a refused candidate over a live session. Exit code 3 with `committed-report-failed` means the live JSONL was already replaced and validated but final report publication failed; inspect the printed hashes and numbered backup instead of rerunning blindly.
350
-
351
- ## Checkpoint And Rewind Behavior
352
-
353
- Conversation rewind and file rewind are separate mechanisms.
354
-
355
- Default:
356
-
357
- ```text
358
- --checkpoint-policy active-correlated
359
- ```
360
-
361
- It retains only UUID-less `file-history-snapshot` records with structural identifiers that correlate to recent retained active records.
362
-
363
- Other controls:
364
-
365
- ```text
366
- --checkpoint-policy none
367
- --max-file-history-snapshots N
368
- ```
369
-
370
- `--checkpoint-policy preserve-recent` is rejected in strict active-chain mode. It is available only together with explicit `--preserve-physical-tail`, which is labeled compatibility mode and does not isolate rewound branches. JSONL compression alone does not guarantee complete file-state rewind.
371
-
372
- ## Repeated Compression
373
-
374
- The default behavior folds previous compact summaries into one new current summary. Old decisions must be checked against later supersessions; old summary text is not automatically current truth.
375
-
376
- An older Codex compact boundary may retain a `preservedMessages` snapshot from the time it was created. If a later rewind diverges from that snapshot, source validation reports a historical-snapshot warning and follows only the current authoritative parent chain; the rewound tail stays excluded. Every newly generated candidate must rebuild this metadata to match its current chain exactly.
377
-
378
- For an explicit exact-text request:
379
-
380
- ```text
381
- --preserve-prior-summaries-verbatim
382
- ```
383
-
384
- Use the flag in both passes. The compressor allows up to 1.5 times the configured summary character budget. If the exact block still does not fit, it reports `fallback-folded` and uses normal semantic folding. It never leaves stacked old compact pairs on the current active chain.
385
-
386
- ## Deterministic Fallback
387
-
388
- Model-assisted summary is the default. Use deterministic fallback only on explicit request:
389
-
390
- ```powershell
391
- python "$skill\scripts\compress_claude_jsonl.py" `
392
- --input "C:\data\session.jsonl" `
393
- --output "C:\data\session.compressed.jsonl" `
394
- --deterministic-summary
395
- ```
396
-
397
- The CLI otherwise requires `--model-summary`.
398
-
399
- ## Read.pages Compatibility Repair
400
-
401
- Claude's native Read tool can legitimately use `pages` for long PDFs. This repair exists for a separate compatibility failure where a historical bridge cannot accept that member. Compression never runs it automatically.
402
-
403
- ### Scan
404
-
405
- ```powershell
406
- python "$skill\scripts\repair_claude_jsonl.py" `
407
- --input "C:\data\session.jsonl" `
408
- --scan-only
409
- ```
410
-
411
- ### Write A Candidate
412
-
413
- ```powershell
414
- python "$skill\scripts\repair_claude_jsonl.py" `
415
- --input "C:\data\session.jsonl" `
416
- --output "C:\data\session.repaired.jsonl" `
417
- --expect-matches 2
418
- ```
419
-
420
- ### Replace One Live File
421
-
422
- ```powershell
423
- python "$skill\scripts\repair_claude_jsonl.py" `
424
- --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
425
- --replace-original `
426
- --confirm-session-closed `
427
- --work-dir "C:\work\claude-repair\SESSION-TIMESTAMP" `
428
- --expect-matches 2
429
- ```
430
-
431
- Default scope is the strict active chain. `--scope all` must be explicit.
432
-
433
- The repair requires:
434
-
435
- - assistant API message
436
- - structured `tool_use`
437
- - exact tool name `Read`
438
- - object `input`
439
- - present `pages` member
440
- - non-empty `file_path`
441
- - exactly one later matching `tool_result` in scope
442
- - the same non-empty `sessionId` on use and result
443
- - result `sourceToolAssistantUUID` equal to the tool-use assistant UUID
444
-
445
- Pending calls and near matches are reported but unchanged. Duplicate JSON keys or ambiguous spans stop the run before editing. Candidate publication re-reads the actual published bytes, binds them to the expected SHA-256, validates the repair plan, requires an idempotent second scan, and runs the shared full-transcript UUID/parent/compact/tool validator. Exit code 3 with `operationState: committed-report-failed` has the same already-committed meaning as live compression.
446
-
447
- ## CLI Reference
448
-
449
- Important compression options:
450
-
451
- | Option | Purpose |
452
- | --- | --- |
453
- | `--analyze-resume-path` | Read-only strict topology report |
454
- | `--write-model-pack PATH` | Write bounded semantic evidence and stop |
455
- | `--model-summary PATH` | Validate and embed model-authored summary |
456
- | `--deterministic-summary` | Explicit model opt-out |
457
- | `--target-ratio R` | Approximate output byte-ratio planning value; not a hard gate |
458
- | `--target-estimated-tokens N` | Hard ceiling under the local complete-structure Messages estimate |
459
- | `--min-recent-records N` | Raw active-suffix floor |
460
- | `--summary-char-budget N` | Compact-summary character budget; minimum 4000 |
461
- | `--model-pack-char-budget N` | Evidence-pack character budget |
462
- | `--model-pack-estimated-token-budget N` | Evidence-pack local token estimate ceiling; default 150000 |
463
- | `--resume-leaf UUID` | Explicit recovery leaf override |
464
- | `--max-post-last-prompt-extension N` | Explicit complete tool-result-only closure limit; default 0 |
465
- | `--checkpoint-policy POLICY` | Strict mode: `active-correlated` or `none`; `preserve-recent` only with physical-tail compatibility |
466
- | `--preserve-prior-summaries-verbatim` | Explicit repeated-compression exact-text mode |
467
- | `--preserve-physical-tail` | Compatibility mode without branch-isolation guarantee |
468
- | `--replace-original` | Transactionally replace one live session |
469
- | `--confirm-session-closed` | Required caller acknowledgement for live replacement; not process-lock detection |
470
- | `--work-dir PATH` | External process directory for live replacement |
471
- | `--backup-dir PATH` | Optional external backup directory |
472
- | `--validate-only PATH` | Structural validation only |
473
-
474
- Run `--help` for the complete list.
475
-
476
- ## Validation Scope
477
-
478
- The validator checks:
479
-
480
- - JSON object per non-empty line
481
- - UUID uniqueness
482
- - parent existence and session consistency
483
- - final pointer target
484
- - active chain closure
485
- - narrow attachment-order and one-way session-lineage compatibility, with unsafe variants rejected
486
- - one current compact boundary and compact summary
487
- - compact metadata consistency
488
- - merged assistant fragments and split user tool results
489
- - API-level `tool_use` / `tool_result` order and pairing
490
- - non-empty, unique active tool IDs; partial multi-tool ordered subsets remain a reported branch-compatibility warning
491
- - absence of internal scratch fields
492
-
493
- Validation checks internal consistency under the observed-format rules; Claude Code versions may still build runtime context differently.
494
-
495
- When runtime testing is explicitly allowed, check these separately:
496
-
497
- 1. `/resume` lists and opens the session.
498
- 2. `/context` shows expected Messages usage.
499
- 3. Recent conversation rewind works.
500
- 4. Recent file rewind works for retained checkpoints.
501
-
502
- High total `/context` with low Messages can come from system prompt, tools, MCP, agents, skills, memory files or newly read content. Recompressing JSONL does not reduce those categories.
503
-
504
- ## Session Locator
505
-
506
- Locate exactly one file by filename or session ID without reading transcript bodies:
507
-
508
- ```powershell
509
- python "$skill\scripts\claude_session_tools.py" `
510
- --root "$env:USERPROFILE\.claude\projects" `
511
- --query "SESSION.jsonl"
512
- ```
513
-
514
- `--scan-titles` reads candidate files only when title matching is explicitly needed. Multiple matches are an error. The compressor never performs directory-wide multi-session compression.
515
-
516
- ## Development And Verification
517
-
518
- Run the complete standard-library suite:
519
-
520
- ```bash
521
- python -B -m unittest discover -s tests -v
522
- python -B tests/test_compressor.py
523
- python -B tests/test_repair.py
524
- python -B tests/test_package.py
525
- python -B tests/test_transaction_races.py
526
- python -B tests/test_semantic_evidence_contracts.py
527
- python -B tests/test_structural_safety_contracts.py
528
- python -B tests/test_protocol_contracts.py
529
- ```
530
-
531
- Additional release checks:
532
-
533
- ```bash
534
- pycache="$(mktemp -d)"
535
- if ! PYTHONPYCACHEPREFIX="$pycache" python -m compileall -q scripts tests; then
536
- rm -rf "$pycache"
537
- exit 1
538
- fi
539
- rm -rf "$pycache"
540
- python -B -I -S scripts/compress_claude_jsonl.py --version
541
- python -B -I -S scripts/repair_claude_jsonl.py --version
542
- npm test
543
- npm pack --dry-run --json
544
- npm publish --dry-run --access public --tag rc
545
- ```
546
-
547
- The release suite covers active/dead branch partitioning, fixed-seed topology transformations, strict pointer failures, dual model-pack budgets, complete structured token accounting, multilingual semantic ledgers and thinking, handoffs, request/claim digests, mandatory support excerpts, tool pairs, repeated compression, checkpoint policies, transaction races and committed-report states, exact byte repair, BOM/CRLF, npm tarball allowlisting and offline tarball installation.
548
-
549
- ### Maintainer RC Release Checklist
550
-
551
- 1. Confirm a clean public tree and matching `1.0.0-rc.1` values in `package.json`, Python version output, docs, and tests.
552
- 2. Run the Python, npm, isolated-Python, tarball, privacy, and offline-install gates above.
553
- 3. Inspect `npm pack --dry-run --json`; publish only the allowlisted files.
554
- 4. Require a clean worktree, create annotated tag `v1.0.0-rc.1`, and push the commit and tag.
555
- 5. For the first manual RC, publish from an authenticated maintainer machine with npm 2FA:
556
-
557
- ```bash
558
- npm publish --access public --tag rc
559
- ```
560
-
561
- 6. Verify the npm version and `rc` dist-tag, then create the GitHub prerelease from the already-pushed tag.
562
-
563
- Do not append `--provenance` to a local publish. npm provenance requires a supported cloud CI runner. For later releases, prefer npm trusted publishing from a public GitHub repository on a GitHub-hosted runner with `id-token: write`, a protected release tag, and a matching protected environment; trusted publishing generates provenance automatically.
564
-
565
- Registry ownership, npm trusted-publisher configuration, credentials, tag push, GitHub prerelease creation, and npm publication are external maintainer actions and are not claimed by the local test suite.
566
-
567
- ## Repository Layout
568
-
569
- ```text
570
- SKILL.md
571
- CHANGELOG.md
572
- README.md
573
- LICENSE
574
- package.json
575
- bin/
576
- config/
577
- scripts/
578
- templates/
579
- references/
580
- tests/
581
- ```
582
-
583
- ## Privacy
584
-
585
- - The public project contains only anonymous synthetic fixtures.
586
- - Model packs and candidate metadata use generic labels such as `SOURCE_JSONL`; generated reports expose basenames, never full local paths.
587
- - npm publication uses an extension-level file allowlist.
588
- - JSONL, backups, reports, model packs, model summaries, caches and compiled Python files are excluded from the package.
589
- - Review generated evidence packs before sharing them; they intentionally contain selected transcript evidence.
590
-
591
- ## License
592
-
593
- GPL-3.0-only. You may use, study, modify and redistribute the project under the GPL terms. Distribution of modified or incorporated versions may require corresponding source and the same license; review the license when integrating it into a distributed commercial product.
1
+ # Claude JSONL Compressor
2
+
3
+ [English](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.1.0/README.md) | [简体中文](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.1.0/docs/README.zh-CN.md) | [日本語](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.1.0/docs/README.ja.md)
4
+
5
+ Strict, model-assisted compression for one Claude Code session transcript, plus an independent byte-preserving compatibility repair for historical `Read.pages` records.
6
+
7
+ **Release:** [`1.1.0`](CHANGELOG.md)<br>
8
+ **Engine:** `v10`<br>
9
+ **Model-pack schema:** `v11`<br>
10
+ **License:** GPL-3.0-only<br>
11
+ **Repository:** [brandrylabs/claude-jsonl-compressor](https://github.com/brandrylabs/claude-jsonl-compressor)
12
+
13
+ This project is not affiliated with Anthropic. Claude Code's transcript JSONL is an observed internal format, not a published stable storage API. Always keep the original file or a verified backup.
14
+
15
+ ## Version 1.1.0: bounded research evidence
16
+
17
+ The goal is to reduce Claude context and cache costs without replacing them
18
+ with unbounded summarizer work. The CLI keeps `excerpt`, `legacy`, `fold`, and
19
+ zero protected human turns as compatible defaults. For new Codex workflows,
20
+ use scoped citations; select full tool evidence when research lives in tool
21
+ payloads. Run the same selection options in preflight and both passes:
22
+
23
+ ```text
24
+ --preflight --tool-evidence full --citation-style scoped
25
+ --min-recent-turns 2
26
+ --preserve-prior-summaries-verbatim --prior-summary-overflow error
27
+ ```
28
+
29
+ `--preflight` is read-only and replaces an output operation; the other flags
30
+ are selection settings. It separates pointer topology from tool closure,
31
+ reports a physical-tail candidate without selecting it, and checks the chosen
32
+ pack against both existing ceilings. `nothing-to-summarize` needs no model work.
33
+ Full evidence retains complete old active tool inputs/results and auxiliary
34
+ results, including mixed prose/tool records and replacement characters. Exact
35
+ long strings repeated within one record are referenced once. It does not merge
36
+ near-duplicates, fetch external documents, or reconstruct unrecorded reasoning.
37
+ An over-budget full pack stops; it never silently samples required payloads,
38
+ raises the ceiling, launches more agents, or splits itself into volumes.
39
+
40
+ `--citation-style scoped` uses `[@L42]` / `[@H3]` in summary prose; ordinary
41
+ document labels such as L73/H1 remain literal. The mandatory coverage subsection
42
+ keeps its printed L-prefix syntax. `--prior-summary-overflow error` requires
43
+ verbatim preservation and refuses folding, including known impossible old-text
44
+ size before model work. Pass 2 checks the actual new layer too. The legacy
45
+ `fold` policy remains available. Exact old text includes trailing whitespace.
46
+
47
+ `--min-recent-turns N` protects human-started turns after the latest compact
48
+ and in the final session only. It may enlarge raw context and prevent meeting
49
+ a token target; protection is never silently reduced. It is unsupported with
50
+ physical-tail compatibility. Counts are structural, not a guarantee of rewind
51
+ menu entries. File rewind still requires native checkpoints and their files;
52
+ Bash changes cannot acquire checkpoints merely through JSONL compression.
53
+
54
+ The final attributable custom title (or automatic title if none) is preserved,
55
+ including a rename after the final pointer. Lookup shares that resolver and
56
+ prefers exact path/ID matches over title scanning. Old names are not aliases.
57
+ Unowned titles in mixed-session files are reported instead of guessed.
58
+
59
+ After successful read-only preflight, make and verify a numbered source backup
60
+ before semantic work; check its hash against preflight. Live replacement still
61
+ creates its independent transaction backup. Default model work is one authored
62
+ summary plus focused self-review. Explicit user requests for retrospective,
63
+ independent, or subagent review are supported with their requested configuration.
64
+ Review and cite relevant complete evidence; do not turn every run into repeated
65
+ full-history review. Structural checks cannot establish semantic truth or actual
66
+ Claude runtime compatibility. Engine v10 / model-pack v11 / report 1 remain;
67
+ regenerate a two-pass pack after upgrading.
68
+
69
+
70
+ ## What It Does
71
+
72
+ - Compresses one Claude Code JSONL into one current compact-style summary pair plus a recent raw active suffix.
73
+ - Uses a model-authored semantic summary by default, with deterministic evidence selection and validation around it.
74
+ - Excludes rewound/inactive branch text from every Claude-readable output layer.
75
+ - Preserves recent conversation records for Claude rewind.
76
+ - Projects one final `last-prompt` while retaining unknown source fields.
77
+ - Validates UUIDs, parents, sessions, compact metadata and API-level tool pairing.
78
+ - Supports candidate output and transactional replacement of one live `.claude/projects` session.
79
+ - Handles repeated compression, including an explicit prior-summary verbatim mode.
80
+ - Offers an independent byte-level repair that removes unsupported historical `Read.pages` members without reserializing the JSONL.
81
+ - Runs with Python's standard library. No tokenizer or YAML dependency is required.
82
+
83
+ ## Quick Start
84
+
85
+ With this repository installed as a Codex skill, ask Codex:
86
+
87
+ ```text
88
+ Use the claude-jsonl-compressor skill on exactly one Claude Code JSONL.
89
+ Input: C:\data\session.jsonl
90
+ Output: C:\data\session.compressed.jsonl
91
+ Target: about 150k estimated Messages tokens.
92
+ Keep recent raw records for rewind, use the default model-assisted summary, and run validation.
93
+ ```
94
+
95
+ For a live `.claude/projects` file, explicitly request a numbered backup and in-place replacement, confirm that the session is closed, and provide a work directory outside `.claude`. The detailed two-pass CLI workflow appears below.
96
+
97
+ ## Why Model-Assisted By Default
98
+
99
+ Deterministic code can select topology and validate bytes, but it cannot decide which historical arguments, legal distinctions, design rationale or research conclusions matter. Python therefore freezes the active branch and builds a bounded, source-anchored evidence pack; a host model writes the summary; Python then verifies request/evidence digests, anchors, required source excerpts and the final JSONL.
100
+
101
+ The script itself never calls a model or the network. The evidence pack bridges the practical 1M-session-versus-smaller-summarizer gap by including every non-empty older active human message and assistant `text`/`thinking` message in full while excluding inactive branches, recent raw records and low-value structural repetition. U+FFFD is reported without discarding the rest of a mandatory record. If mandatory evidence exceeds either pack ceiling, generation stops instead of sampling semantic history.
102
+
103
+ ## Safety Properties
104
+
105
+ ### Strict resume authority
106
+
107
+ The physically last `type: "last-prompt"` record is authoritative in automatic mode. A malformed latest pointer is an error; the program does not search backward for an older valid pointer and accidentally revive an obsolete branch.
108
+
109
+ Strict active mode rejects:
110
+
111
+ - missing or malformed authority
112
+ - missing leaf or parent
113
+ - parent loops or malformed non-string/empty `parentUuid` values
114
+ - ordinary-message/non-attachment physical parent inversion
115
+ - recurring, pointer-mismatched or otherwise unsafe session lineage
116
+ - duplicate UUIDs anywhere in the file
117
+ - unsafe post-pointer extension
118
+
119
+ Use `--resume-leaf UUID` only for an explicit recovery decision. It is reported as `active-chain-manual-override`, distinct from default strict `active-chain`. Use `--preserve-physical-tail` only as an explicit compatibility mode; it does not provide inactive-branch isolation.
120
+
121
+ An unusual or ambiguous topology is a stop, not an automatic fallback. The CLI exits before creating a pack, candidate, report, backup or other sidecar. A hosting agent may explain one applicable explicit recovery control and ask the user to confirm it in a new instruction; it must not infer that confirmation from the original compression request. Manually spliced transcripts generally require physical-tail compatibility and therefore lose branch/rewind isolation.
122
+
123
+ Current Claude Code reconstructs a conversation from a UUID map and parent links, so physical line order is not universally chronological. This project accepts only same-session `attachment -> attachment` physical inversions on an otherwise complete acyclic chain and writes them back in logical parent order. It also accepts one-way A->B (or A->B->C) session lineage only when a session never recurs and the final leaf and pointer match the final session. All earlier-session records become summary evidence; recent raw records remain entirely in the final session. A tool pair crossing that forced cut is a hard stop.
124
+
125
+ ### Rewound branches stay out
126
+
127
+ The source indexes are partitioned into mutually exclusive sets:
128
+
129
+ | Set | Meaning | May enter summary? | May remain raw? |
130
+ | --- | --- | --- | --- |
131
+ | `summaryIndexes` | Older active-chain records | Yes | No |
132
+ | `rawKeepIndexes` | Recent active-chain records | No | Yes |
133
+ | `sideKeepIndexes` | Policy-approved checkpoint side records | No | Side records only |
134
+ | `controlProjectionIndexes` | Pointer and safe global control records | No | Projected only |
135
+ | `excludedBranchIndexes` | Inactive UUID branches | No | No |
136
+ | `excludedUnattributedIndexes` | Unattributed non-chain records | No | No |
137
+
138
+ Excluded records appear in reports only as counts and digests. Their text is not copied into the model pack, compact summary, deterministic appendix, verbatim prior-summary block or output message chain.
139
+
140
+ ### Transactional writes
141
+
142
+ - Input and candidate bytes are bound by full SHA-256; candidates are staged, flushed, validated and atomically published.
143
+ - Numbered backups use exclusive creation and byte verification. Live replacement also captures and verifies the actual old target before installing the candidate.
144
+ - Failed post-replacement validation restores the captured original. A rollback failure is raised prominently while verified recovery assets remain available.
145
+ - Concurrent target recreation preserves the external target and recovery backups, then fails without publishing the candidate.
146
+ - Parent-directory fsync is best effort and reported; this is not a cross-platform power-loss guarantee.
147
+ - If the live JSONL commits but final report publication fails, the CLI does not undo valid committed data. It prints a `committed-report-failed` receipt with hashes and backup/candidate labels and exits with code 3.
148
+
149
+ ## Requirements
150
+
151
+ - Python 3.10 or newer
152
+ - Node.js 22 or newer only when using the npm command wrappers
153
+ - Claude Code is optional; it is needed only for an explicitly requested runtime `/resume` or `/context` smoke test
154
+ - Hard-link support on the volume holding the target file, and on an explicitly configured backup directory, for `--replace-original` only
155
+
156
+ No Python package installation is required.
157
+
158
+ ### Hard-link requirement for live replacement
159
+
160
+ `--replace-original` publishes the candidate with `os.link` so that it never overwrites a concurrent claimant, and the rollback path restores the captured original the same way. The target directory must therefore support file hard links. An explicitly supplied `--backup-dir` also publishes numbered backups with a hard link and must support the same operation.
161
+
162
+ Before live replacement staging, backup payload publication, or target movement, the compressor runs a unique, same-directory hard-link probe on the target volume. The probe briefly creates two dot-prefixed temporary files and removes them; a cleanup failure is reported with the retained probe name and stops the live operation. If the capability probe fails, the target remains at its original path and byte content, and no live replacement stage, backup payload, or target move has started. This probe is a capability check, not a guarantee against a later permission, quota, network, or concurrency failure; late publication, rollback, and cleanup failures are reported with their transaction state and verified recovery assets.
163
+
164
+ Each unique live-transaction temporary path is checked against its recorded filesystem identity and frozen bytes before cleanup. A detected mismatch is retained and reported as `committed-cleanup-failed` after a committed candidate, or alongside the primary failure before commit. Portable Python cannot bind the final pathname `unlink` atomically to that earlier identity check across Windows, Linux, and macOS. Run live replacement only with the session closed and no other writer; hostile same-account directory manipulation is outside this guarantee.
165
+
166
+ NTFS, APFS, ext4, XFS, and btrfs commonly support file hard links, while FAT/exFAT, some network shares, overlay or bind-mounted filesystems, and Windows ReFS may reject them. The probe is authoritative for the actual directory and account; the filesystem name alone is not treated as a guarantee. The repository CI runs the probe and transaction tests on Windows, Linux, and macOS runner volumes.
167
+
168
+ Candidate output is unaffected: it publishes through `os.replace` and has no hard-link dependency.
169
+
170
+ ## Installation
171
+
172
+ ### Install As A Codex Skill
173
+
174
+ Clone the repository into the Codex skill directory:
175
+
176
+ ```bash
177
+ skill="${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
178
+ mkdir -p "$(dirname "$skill")"
179
+ git clone https://github.com/brandrylabs/claude-jsonl-compressor.git "$skill"
180
+ ```
181
+
182
+ Windows PowerShell:
183
+
184
+ ```powershell
185
+ $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $env:USERPROFILE '.codex' }
186
+ $skill = Join-Path $codexHome 'skills\claude-jsonl-compressor'
187
+ New-Item -ItemType Directory -Force (Split-Path -Parent $skill) | Out-Null
188
+ git clone https://github.com/brandrylabs/claude-jsonl-compressor.git $skill
189
+ ```
190
+
191
+ Update or uninstall the skill:
192
+
193
+ ```bash
194
+ git -C "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor" pull --ff-only
195
+ rm -rf "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
196
+ ```
197
+
198
+ ```powershell
199
+ git -C $skill pull --ff-only
200
+ Remove-Item -LiteralPath $skill -Recurse -Force
201
+ ```
202
+
203
+ The installed directory must contain `SKILL.md`, `scripts/`, `config/`, `templates/` and `references/`.
204
+
205
+ ### Install The npm CLI
206
+
207
+ After version 1.1.0 is published:
208
+
209
+ ```bash
210
+ npm install --global @brandry/claude-jsonl-compressor
211
+ ```
212
+
213
+ This installs two commands:
214
+
215
+ ```text
216
+ claude-jsonl-compressor
217
+ claude-jsonl-repair-read-pages
218
+ ```
219
+
220
+ The npm package is a zero-dependency Node shim over the bundled Python implementation. It forwards arguments, stdio, exit codes and signals with `shell: false`. The tarball also contains `SKILL.md`, `agents/` and `references/`, but npm installation does not register the directory as a Codex skill; skill installation remains a separate copy/link step.
221
+
222
+ Upgrade or uninstall the global CLI:
223
+
224
+ ```bash
225
+ npm install --global @brandry/claude-jsonl-compressor
226
+ npm update --global @brandry/claude-jsonl-compressor
227
+ npm uninstall --global @brandry/claude-jsonl-compressor
228
+ ```
229
+
230
+ Local development install and invocation:
231
+
232
+ ```bash
233
+ npm install --save-dev @brandry/claude-jsonl-compressor
234
+ npm update @brandry/claude-jsonl-compressor
235
+ npm exec -- claude-jsonl-compressor --version
236
+ npm exec -- claude-jsonl-repair-read-pages --version
237
+ npm uninstall @brandry/claude-jsonl-compressor
238
+ ```
239
+
240
+ Run without retaining an installation:
241
+
242
+ ```bash
243
+ npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-compressor --version
244
+ npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-repair-read-pages --version
245
+ ```
246
+
247
+ Actual npm/npx operations use the same Python CLI options:
248
+
249
+ ```bash
250
+ npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-compressor --input session.jsonl --write-model-pack run/session.model-pack.md
251
+ npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-repair-read-pages --input session.jsonl --scan-only
252
+ ```
253
+
254
+ ### Use From Source Without Installing
255
+
256
+ ```bash
257
+ python scripts/compress_claude_jsonl.py --version
258
+ python scripts/repair_claude_jsonl.py --version
259
+ ```
260
+
261
+ ### Can Claude Code Install This Skill?
262
+
263
+ `SKILL.md` is a Codex skill definition, not a native Claude Code skill/plugin format. Claude Code can still run the Python or npm commands when instructed, but installing this directory into Claude's configuration does not automatically create an equivalent Claude-native skill.
264
+
265
+ ## Detailed Workflow
266
+
267
+ The examples below use PowerShell and a local skill installation:
268
+
269
+ ```powershell
270
+ $skill = "$env:USERPROFILE\.codex\skills\claude-jsonl-compressor"
271
+ ```
272
+
273
+ ### 1. Analyze The Resume Path
274
+
275
+ ```powershell
276
+ python "$skill\scripts\compress_claude_jsonl.py" `
277
+ --input "C:\data\session.jsonl" `
278
+ --analyze-resume-path
279
+ ```
280
+
281
+ This is read-only. A nonzero result must be resolved before model-pack generation.
282
+ Use `reasonCode` for the exact machine-readable cause; `status` is only the
283
+ coarse category. The stable value table is in
284
+ [`references/claude-jsonl-compression-format.md`](references/claude-jsonl-compression-format.md#authoritative-last-prompt).
285
+
286
+ ### 2. Generate A Model Evidence Pack
287
+
288
+ ```powershell
289
+ python "$skill\scripts\compress_claude_jsonl.py" `
290
+ --input "C:\data\session.jsonl" `
291
+ --write-model-pack "C:\work\run\session.model-pack.md" `
292
+ --target-ratio 0.30 `
293
+ --min-recent-records 120 `
294
+ --summary-char-budget 60000 `
295
+ --target-estimated-tokens 150000 `
296
+ --model-pack-char-budget 500000 `
297
+ --model-pack-estimated-token-budget 150000
298
+ ```
299
+
300
+ The evidence pack has two independent default ceilings: 500,000 characters
301
+ and a conservative 150,000-token local estimate. The token ceiling leaves
302
+ working room in a typical 200k summarizer context. Mandatory human/assistant
303
+ semantic records, prior compact summaries, handoff lines, and required coverage
304
+ groups are never sampled or clipped; generation stops if they do not fit.
305
+ Optional source/tool/system/error evidence is added by importance and chronology
306
+ until either ceiling is reached, and the pack/report state whether that optional
307
+ evidence was truncated. Do not install a tokenizer to change this workflow.
308
+
309
+ `--target-ratio` is an approximate byte-ratio planning input, not a hard release gate. For a hard local Messages estimate ceiling, use:
310
+
311
+ ```powershell
312
+ --target-estimated-tokens 150000
313
+ ```
314
+
315
+ This candidate-output estimate is separate from the model-pack reading ceiling.
316
+ It covers complete retained structured message payloads, including full thinking,
317
+ `tool_use.input`, `tool_result`, and `toolUseResult` data. It does not include
318
+ Claude's system prompt, tool schemas, MCP servers, agents, skills, memory files
319
+ or runtime-loaded context. It is not a promise about total `/context` usage.
320
+
321
+ `--summary-char-budget` has a hard minimum of 4000 characters. A smaller value or a blank compact summary is rejected instead of publishing unusable memory.
322
+
323
+ ### 3. Write The Model Summary
324
+
325
+ The model reads the pack and writes `session.model-summary.md`.
326
+
327
+ The first HTML comment must be copied exactly and contains:
328
+
329
+ ```text
330
+ source_sha256
331
+ summary_source_sha256
332
+ evidence_anchor_lines_digest
333
+ required_anchor_groups_digest
334
+ handoff_summary_digest
335
+ pack_request_digest
336
+ required_claim_sources_digest
337
+ ```
338
+
339
+ Every substantive transcript claim needs a displayed `L<number>` anchor. Every external-handoff claim needs a displayed `H<number>` anchor. The validator rejects invented or hidden anchors. It also requires at least one cited anchor from every generated coverage group and an anchored body under each of the nine exact headings printed in the pack. Only the exact leading metadata comment and exact required headings are exempt from line grounding; extra HTML comments or headings are errors. The exact whole line `Unknown from provided anchors.` is the only unanchored uncertainty placeholder; adding other text to that line removes the exemption.
340
+
341
+ Schema v11 assigns a required full-text L-anchor group to every non-empty older active human message and every older active assistant `text`/`thinking` message. It binds every selection/resource option through `pack_request_digest`. Under the exact `### Mandatory Evidence Coverage` subsection, the model must provide exactly one line per mandatory semantic/prior-summary record:
342
+
343
+ ```text
344
+ - L42 support_text_json="exact source substring" disposition=covered
345
+ ```
346
+
347
+ The JSON string must decode to a meaningful exact substring of that L record. This mechanical gate blocks anchor-only boilerplate and leaves a checkable source excerpt; it does not prove that all natural-language interpretation is correct. Schema v11 also reserves early/middle/late/latest, source/tool and prior-summary coverage. Prior compact summaries and every physical line of an explicitly supplied handoff enter the pack in full; handoff early/middle/late/latest H groups must be cited. Pack generation stops instead of truncating or sampling mandatory evidence when either the character or estimated-token ceiling is insufficient. Raise `--model-pack-char-budget` or `--model-pack-estimated-token-budget` only when the summarizing model can read the resulting pack.
348
+
349
+ The summary should preserve:
350
+
351
+ - current state
352
+ - chronology and supersessions
353
+ - user constraints and wording
354
+ - assistant/model research decisions and reasons
355
+ - evidence provenance
356
+ - rejected alternatives
357
+ - risks, unknowns and follow-ups
358
+ - recent raw boundary
359
+
360
+ Later events control current state, but earlier decisions and their reasons remain as superseded history.
361
+
362
+ ### 4. Build A Candidate
363
+
364
+ ```powershell
365
+ python "$skill\scripts\compress_claude_jsonl.py" `
366
+ --input "C:\data\session.jsonl" `
367
+ --output "C:\data\session.compressed.jsonl" `
368
+ --target-ratio 0.30 `
369
+ --min-recent-records 120 `
370
+ --summary-char-budget 60000 `
371
+ --target-estimated-tokens 150000 `
372
+ --model-pack-char-budget 500000 `
373
+ --model-pack-estimated-token-budget 150000 `
374
+ --model-summary "C:\work\run\session.model-summary.md"
375
+ ```
376
+
377
+ Pass exactly the same selection options used for the model pack. In particular,
378
+ repeat both non-default model-pack ceilings so the second pass regenerates the
379
+ same evidence contract.
380
+
381
+ The command writes:
382
+
383
+ ```text
384
+ session.compressed.jsonl
385
+ session.compressed.jsonl.validation.json
386
+ session.compressed.jsonl.report.md
387
+ ```
388
+
389
+ The input remains unchanged.
390
+
391
+ ## Live Session Replacement
392
+
393
+ Close the Claude Code process using that session before replacement. The live target must be an existing regular `.jsonl` file under `.claude/projects`.
394
+
395
+ Generate the model pack and model summary outside `.claude`, then run:
396
+
397
+ ```powershell
398
+ python "$skill\scripts\compress_claude_jsonl.py" `
399
+ --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
400
+ --replace-original `
401
+ --confirm-session-closed `
402
+ --work-dir "C:\work\claude-compression\SESSION-TIMESTAMP" `
403
+ --model-pack-estimated-token-budget 150000 `
404
+ --target-estimated-tokens 150000 `
405
+ --model-summary "C:\work\claude-compression\SESSION-TIMESTAMP\session.model-summary.md"
406
+ ```
407
+
408
+ The default backup is placed beside the live file:
409
+
410
+ ```text
411
+ SESSION.jsonl.backup
412
+ SESSION.jsonl.backup1
413
+ SESSION.jsonl.backup2
414
+ ```
415
+
416
+ To keep backups outside `.claude`:
417
+
418
+ ```powershell
419
+ --backup-dir "C:\work\claude-compression\SESSION-TIMESTAMP\backups"
420
+ ```
421
+
422
+ Candidate, report, validation, model pack and model summary files remain under the external work directory. Do not manually copy a refused candidate over a live session. Exit code 3 with `committed-report-failed` means the live JSONL was already replaced and validated but final report publication failed; inspect the printed hashes and numbered backup instead of rerunning blindly.
423
+
424
+ ## Checkpoint And Rewind Behavior
425
+
426
+ Conversation rewind and file rewind are separate mechanisms.
427
+
428
+ Default:
429
+
430
+ ```text
431
+ --checkpoint-policy active-correlated
432
+ ```
433
+
434
+ It retains only UUID-less `file-history-snapshot` records with structural identifiers that correlate to recent retained active records.
435
+
436
+ Other controls:
437
+
438
+ ```text
439
+ --checkpoint-policy none
440
+ --max-file-history-snapshots N
441
+ ```
442
+
443
+ `--checkpoint-policy preserve-recent` is rejected in strict active-chain mode. It is available only together with explicit `--preserve-physical-tail`, which is labeled compatibility mode and does not isolate rewound branches. JSONL compression alone does not guarantee complete file-state rewind.
444
+
445
+ ## Repeated Compression
446
+
447
+ The default behavior folds previous compact summaries into one new current summary. Old decisions must be checked against later supersessions; old summary text is not automatically current truth.
448
+
449
+ An older Codex compact boundary may retain a `preservedMessages` snapshot from the time it was created. If a later rewind diverges from that snapshot, source validation reports a historical-snapshot warning and follows only the current authoritative parent chain; the rewound tail stays excluded. Every newly generated candidate must rebuild this metadata to match its current chain exactly.
450
+
451
+ For an explicit exact-text request:
452
+
453
+ ```text
454
+ --preserve-prior-summaries-verbatim --prior-summary-overflow error
455
+ ```
456
+
457
+ Use both flags in both passes. The compressor allows up to 1.5 times the configured summary character budget and stops if strict exact preservation cannot fit. Omitting the overflow flag retains the legacy `fold` behavior: an oversized block reports `fallback-folded` and uses normal semantic folding. It never leaves stacked old compact pairs on the current active chain.
458
+
459
+ ## Deterministic Fallback
460
+
461
+ Model-assisted summary is the default. Use deterministic fallback only on explicit request:
462
+
463
+ ```powershell
464
+ python "$skill\scripts\compress_claude_jsonl.py" `
465
+ --input "C:\data\session.jsonl" `
466
+ --output "C:\data\session.compressed.jsonl" `
467
+ --deterministic-summary
468
+ ```
469
+
470
+ The CLI otherwise requires `--model-summary`.
471
+
472
+ ## Read.pages Compatibility Repair
473
+
474
+ Claude's native Read tool can legitimately use `pages` for long PDFs. This repair exists for a separate compatibility failure where a historical bridge cannot accept that member. Compression never runs it automatically.
475
+
476
+ ### Scan
477
+
478
+ ```powershell
479
+ python "$skill\scripts\repair_claude_jsonl.py" `
480
+ --input "C:\data\session.jsonl" `
481
+ --scan-only
482
+ ```
483
+
484
+ ### Write A Candidate
485
+
486
+ ```powershell
487
+ python "$skill\scripts\repair_claude_jsonl.py" `
488
+ --input "C:\data\session.jsonl" `
489
+ --output "C:\data\session.repaired.jsonl" `
490
+ --expect-matches 2
491
+ ```
492
+
493
+ ### Replace One Live File
494
+
495
+ ```powershell
496
+ python "$skill\scripts\repair_claude_jsonl.py" `
497
+ --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
498
+ --replace-original `
499
+ --confirm-session-closed `
500
+ --work-dir "C:\work\claude-repair\SESSION-TIMESTAMP" `
501
+ --expect-matches 2
502
+ ```
503
+
504
+ Default scope is the strict active chain. `--scope all` must be explicit.
505
+
506
+ The repair requires:
507
+
508
+ - assistant API message
509
+ - structured `tool_use`
510
+ - exact tool name `Read`
511
+ - object `input`
512
+ - present `pages` member
513
+ - non-empty `file_path`
514
+ - exactly one later matching `tool_result` in scope
515
+ - the same non-empty `sessionId` on use and result
516
+ - result `sourceToolAssistantUUID` equal to the tool-use assistant UUID
517
+
518
+ Pending calls and near matches are reported but unchanged. Duplicate JSON keys or ambiguous spans stop the run before editing. Candidate publication re-reads the actual published bytes, binds them to the expected SHA-256, validates the repair plan, requires an idempotent second scan, and runs the shared full-transcript UUID/parent/compact/tool validator. Exit code 3 with `operationState: committed-report-failed` has the same already-committed meaning as live compression.
519
+
520
+ ## CLI Reference
521
+
522
+ Important compression options:
523
+
524
+ | Option | Purpose |
525
+ | --- | --- |
526
+ | `--analyze-resume-path` | Read-only strict topology report |
527
+ | `--write-model-pack PATH` | Write bounded semantic evidence and stop |
528
+ | `--model-summary PATH` | Validate and embed model-authored summary |
529
+ | `--deterministic-summary` | Explicit model opt-out |
530
+ | `--target-ratio R` | Approximate output byte-ratio planning value; not a hard gate |
531
+ | `--target-estimated-tokens N` | Hard ceiling under the local complete-structure Messages estimate |
532
+ | `--min-recent-records N` | Raw active-suffix floor |
533
+ | `--summary-char-budget N` | Compact-summary character budget; minimum 4000 |
534
+ | `--model-pack-char-budget N` | Evidence-pack character budget |
535
+ | `--model-pack-estimated-token-budget N` | Evidence-pack local token estimate ceiling; default 150000 |
536
+ | `--resume-leaf UUID` | Explicit recovery leaf override |
537
+ | `--max-post-last-prompt-extension N` | Explicit complete tool-result-only closure limit; default 0 |
538
+ | `--checkpoint-policy POLICY` | Strict mode: `active-correlated` or `none`; `preserve-recent` only with physical-tail compatibility |
539
+ | `--preserve-prior-summaries-verbatim` | Explicit repeated-compression exact-text mode |
540
+ | `--preserve-physical-tail` | Compatibility mode without branch-isolation guarantee |
541
+ | `--replace-original` | Transactionally replace one live session |
542
+ | `--confirm-session-closed` | Required caller acknowledgement for live replacement; not process-lock detection |
543
+ | `--work-dir PATH` | External process directory for live replacement |
544
+ | `--backup-dir PATH` | Optional external backup directory |
545
+ | `--validate-only PATH` | Structural validation only |
546
+
547
+ Run `--help` for the complete list.
548
+
549
+ ## Validation Scope
550
+
551
+ The validator checks:
552
+
553
+ - JSON object per non-empty line
554
+ - UUID uniqueness
555
+ - parent existence and session consistency
556
+ - final pointer target
557
+ - active chain closure
558
+ - narrow attachment-order and one-way session-lineage compatibility, with unsafe variants rejected
559
+ - one current compact boundary and compact summary
560
+ - compact metadata consistency
561
+ - merged assistant fragments and split user tool results
562
+ - API-level `tool_use` / `tool_result` order and pairing
563
+ - non-empty, unique active tool IDs; partial multi-tool ordered subsets remain a reported branch-compatibility warning
564
+ - absence of internal scratch fields
565
+
566
+ Validation checks internal consistency under the observed-format rules; Claude Code versions may still build runtime context differently.
567
+
568
+ When runtime testing is explicitly allowed, check these separately:
569
+
570
+ 1. `/resume` lists and opens the session.
571
+ 2. `/context` shows expected Messages usage.
572
+ 3. Recent conversation rewind works.
573
+ 4. Recent file rewind works for retained checkpoints.
574
+
575
+ High total `/context` with low Messages can come from system prompt, tools, MCP, agents, skills, memory files or newly read content. Recompressing JSONL does not reduce those categories.
576
+
577
+ ## Session Locator
578
+
579
+ Locate exactly one file by filename or session ID without reading transcript bodies:
580
+
581
+ ```powershell
582
+ python "$skill\scripts\claude_session_tools.py" `
583
+ --root "$env:USERPROFILE\.claude\projects" `
584
+ --query "SESSION.jsonl"
585
+ ```
586
+
587
+ `--scan-titles` reads candidate files only when title matching is explicitly needed. Multiple matches are an error. The compressor never performs directory-wide multi-session compression.
588
+
589
+ ## Development And Verification
590
+
591
+ Run the complete standard-library suite:
592
+
593
+ ```bash
594
+ python -B -m unittest discover -s tests -v
595
+ python -B tests/test_compressor.py
596
+ python -B tests/test_repair.py
597
+ python -B tests/test_package.py
598
+ python -B tests/test_transaction_races.py
599
+ python -B tests/test_semantic_evidence_contracts.py
600
+ python -B tests/test_structural_safety_contracts.py
601
+ python -B tests/test_protocol_contracts.py
602
+ ```
603
+
604
+ Additional release checks:
605
+
606
+ ```bash
607
+ pycache="$(mktemp -d)"
608
+ if ! PYTHONPYCACHEPREFIX="$pycache" python -m compileall -q scripts tests; then
609
+ rm -rf "$pycache"
610
+ exit 1
611
+ fi
612
+ rm -rf "$pycache"
613
+ python -B -I -S scripts/compress_claude_jsonl.py --version
614
+ python -B -I -S scripts/repair_claude_jsonl.py --version
615
+ npm test
616
+ npm pack --dry-run --json
617
+ npm publish --dry-run --access public --tag latest
618
+ ```
619
+
620
+ The release suite covers active/dead branch partitioning, fixed-seed topology transformations, strict pointer failures, dual model-pack budgets, complete structured token accounting, multilingual semantic ledgers and thinking, handoffs, request/claim digests, mandatory support excerpts, tool pairs, repeated compression, checkpoint policies, transaction races and committed-report states, exact byte repair, BOM/CRLF, npm tarball allowlisting and offline tarball installation.
621
+
622
+ ### Maintainer Stable Release Checklist
623
+
624
+ 1. Confirm a clean public tree and matching `1.1.0` values in `package.json`, Python version output, docs, and tests.
625
+ 2. Run the Python, npm, isolated-Python, tarball, privacy, and offline-install gates above.
626
+ 3. Inspect `npm pack --dry-run --json`; publish only the allowlisted files.
627
+ 4. Require a clean worktree, create annotated tag `v1.1.0`, and push the commit and tag.
628
+ 5. Publish from an authenticated maintainer environment with permission to publish this package:
629
+
630
+ ```bash
631
+ npm publish --access public --tag latest
632
+ ```
633
+
634
+ 6. Verify npm version `1.1.0` and the `latest` dist-tag, then create a GitHub release (not a prerelease) from the already-pushed tag.
635
+
636
+ Do not append `--provenance` to a local publish. npm provenance requires a supported cloud CI runner. For later releases, prefer npm trusted publishing from a public GitHub repository on a GitHub-hosted runner with `id-token: write`, a protected release tag, and a matching protected environment; trusted publishing generates provenance automatically.
637
+
638
+ Registry ownership, npm trusted-publisher configuration, credentials, tag push, GitHub prerelease creation, and npm publication are external maintainer actions and are not claimed by the local test suite.
639
+
640
+ ## Repository Layout
641
+
642
+ ```text
643
+ SKILL.md
644
+ CHANGELOG.md
645
+ README.md
646
+ LICENSE
647
+ package.json
648
+ bin/
649
+ config/
650
+ scripts/
651
+ templates/
652
+ references/
653
+ tests/
654
+ ```
655
+
656
+ ## Privacy
657
+
658
+ - The public project contains only anonymous synthetic fixtures.
659
+ - Model packs and candidate metadata use generic labels such as `SOURCE_JSONL`; generated reports expose basenames, never full local paths.
660
+ - npm publication uses an extension-level file allowlist.
661
+ - JSONL, backups, reports, model packs, model summaries, caches and compiled Python files are excluded from the package.
662
+ - Review generated evidence packs before sharing them; they intentionally contain selected transcript evidence.
663
+
664
+ ## License
665
+
666
+ GPL-3.0-only. You may use, study, modify and redistribute the project under the GPL terms. Distribution of modified or incorporated versions may require corresponding source and the same license; review the license when integrating it into a distributed commercial product.