@brandry/claude-jsonl-compressor 1.0.0 → 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,611 +1,666 @@
1
- # Claude JSONL Compressor
2
-
3
- [English](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.0.0/README.md) | [简体中文](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.0.0/docs/README.zh-CN.md) | [日本語](https://github.com/brandrylabs/claude-jsonl-compressor/blob/v1.0.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.0.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
- ## What It Does
16
-
17
- - Compresses one Claude Code JSONL into one current compact-style summary pair plus a recent raw active suffix.
18
- - Uses a model-authored semantic summary by default, with deterministic evidence selection and validation around it.
19
- - Excludes rewound/inactive branch text from every Claude-readable output layer.
20
- - Preserves recent conversation records for Claude rewind.
21
- - Projects one final `last-prompt` while retaining unknown source fields.
22
- - Validates UUIDs, parents, sessions, compact metadata and API-level tool pairing.
23
- - Supports candidate output and transactional replacement of one live `.claude/projects` session.
24
- - Handles repeated compression, including an explicit prior-summary verbatim mode.
25
- - Offers an independent byte-level repair that removes unsupported historical `Read.pages` members without reserializing the JSONL.
26
- - Runs with Python's standard library. No tokenizer or YAML dependency is required.
27
-
28
- ## Quick Start
29
-
30
- With this repository installed as a Codex skill, ask Codex:
31
-
32
- ```text
33
- Use the claude-jsonl-compressor skill on exactly one Claude Code JSONL.
34
- Input: C:\data\session.jsonl
35
- Output: C:\data\session.compressed.jsonl
36
- Target: about 150k estimated Messages tokens.
37
- Keep recent raw records for rewind, use the default model-assisted summary, and run validation.
38
- ```
39
-
40
- 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.
41
-
42
- ## Why Model-Assisted By Default
43
-
44
- 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.
45
-
46
- 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.
47
-
48
- ## Safety Properties
49
-
50
- ### Strict resume authority
51
-
52
- 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.
53
-
54
- Strict active mode rejects:
55
-
56
- - missing or malformed authority
57
- - missing leaf or parent
58
- - parent loops or malformed non-string/empty `parentUuid` values
59
- - ordinary-message/non-attachment physical parent inversion
60
- - recurring, pointer-mismatched or otherwise unsafe session lineage
61
- - duplicate UUIDs anywhere in the file
62
- - unsafe post-pointer extension
63
-
64
- 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.
65
-
66
- 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.
67
-
68
- 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.
69
-
70
- ### Rewound branches stay out
71
-
72
- The source indexes are partitioned into mutually exclusive sets:
73
-
74
- | Set | Meaning | May enter summary? | May remain raw? |
75
- | --- | --- | --- | --- |
76
- | `summaryIndexes` | Older active-chain records | Yes | No |
77
- | `rawKeepIndexes` | Recent active-chain records | No | Yes |
78
- | `sideKeepIndexes` | Policy-approved checkpoint side records | No | Side records only |
79
- | `controlProjectionIndexes` | Pointer and safe global control records | No | Projected only |
80
- | `excludedBranchIndexes` | Inactive UUID branches | No | No |
81
- | `excludedUnattributedIndexes` | Unattributed non-chain records | No | No |
82
-
83
- 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.
84
-
85
- ### Transactional writes
86
-
87
- - Input and candidate bytes are bound by full SHA-256; candidates are staged, flushed, validated and atomically published.
88
- - Numbered backups use exclusive creation and byte verification. Live replacement also captures and verifies the actual old target before installing the candidate.
89
- - Failed post-replacement validation restores the captured original. A rollback failure is raised prominently while verified recovery assets remain available.
90
- - Concurrent target recreation preserves the external target and recovery backups, then fails without publishing the candidate.
91
- - Parent-directory fsync is best effort and reported; this is not a cross-platform power-loss guarantee.
92
- - 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.
93
-
94
- ## Requirements
95
-
96
- - Python 3.10 or newer
97
- - Node.js 22 or newer only when using the npm command wrappers
98
- - Claude Code is optional; it is needed only for an explicitly requested runtime `/resume` or `/context` smoke test
99
- - Hard-link support on the volume holding the target file, and on an explicitly configured backup directory, for `--replace-original` only
100
-
101
- No Python package installation is required.
102
-
103
- ### Hard-link requirement for live replacement
104
-
105
- `--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.
106
-
107
- 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.
108
-
109
- 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.
110
-
111
- 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.
112
-
113
- Candidate output is unaffected: it publishes through `os.replace` and has no hard-link dependency.
114
-
115
- ## Installation
116
-
117
- ### Install As A Codex Skill
118
-
119
- Clone the repository into the Codex skill directory:
120
-
121
- ```bash
122
- skill="${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
123
- mkdir -p "$(dirname "$skill")"
124
- git clone https://github.com/brandrylabs/claude-jsonl-compressor.git "$skill"
125
- ```
126
-
127
- Windows PowerShell:
128
-
129
- ```powershell
130
- $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $env:USERPROFILE '.codex' }
131
- $skill = Join-Path $codexHome 'skills\claude-jsonl-compressor'
132
- New-Item -ItemType Directory -Force (Split-Path -Parent $skill) | Out-Null
133
- git clone https://github.com/brandrylabs/claude-jsonl-compressor.git $skill
134
- ```
135
-
136
- Update or uninstall the skill:
137
-
138
- ```bash
139
- git -C "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor" pull --ff-only
140
- rm -rf "${CODEX_HOME:-$HOME/.codex}/skills/claude-jsonl-compressor"
141
- ```
142
-
143
- ```powershell
144
- git -C $skill pull --ff-only
145
- Remove-Item -LiteralPath $skill -Recurse -Force
146
- ```
147
-
148
- The installed directory must contain `SKILL.md`, `scripts/`, `config/`, `templates/` and `references/`.
149
-
150
- ### Install The npm CLI
151
-
152
- After version 1.0.0 is published:
153
-
154
- ```bash
155
- npm install --global @brandry/claude-jsonl-compressor
156
- ```
157
-
158
- This installs two commands:
159
-
160
- ```text
161
- claude-jsonl-compressor
162
- claude-jsonl-repair-read-pages
163
- ```
164
-
165
- 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.
166
-
167
- Upgrade or uninstall the global CLI:
168
-
169
- ```bash
170
- npm install --global @brandry/claude-jsonl-compressor
171
- npm update --global @brandry/claude-jsonl-compressor
172
- npm uninstall --global @brandry/claude-jsonl-compressor
173
- ```
174
-
175
- Local development install and invocation:
176
-
177
- ```bash
178
- npm install --save-dev @brandry/claude-jsonl-compressor
179
- npm update @brandry/claude-jsonl-compressor
180
- npm exec -- claude-jsonl-compressor --version
181
- npm exec -- claude-jsonl-repair-read-pages --version
182
- npm uninstall @brandry/claude-jsonl-compressor
183
- ```
184
-
185
- Run without retaining an installation:
186
-
187
- ```bash
188
- npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-compressor --version
189
- npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-repair-read-pages --version
190
- ```
191
-
192
- Actual npm/npx operations use the same Python CLI options:
193
-
194
- ```bash
195
- npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-compressor --input session.jsonl --write-model-pack run/session.model-pack.md
196
- npx --yes --package @brandry/claude-jsonl-compressor claude-jsonl-repair-read-pages --input session.jsonl --scan-only
197
- ```
198
-
199
- ### Use From Source Without Installing
200
-
201
- ```bash
202
- python scripts/compress_claude_jsonl.py --version
203
- python scripts/repair_claude_jsonl.py --version
204
- ```
205
-
206
- ### Can Claude Code Install This Skill?
207
-
208
- `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.
209
-
210
- ## Detailed Workflow
211
-
212
- The examples below use PowerShell and a local skill installation:
213
-
214
- ```powershell
215
- $skill = "$env:USERPROFILE\.codex\skills\claude-jsonl-compressor"
216
- ```
217
-
218
- ### 1. Analyze The Resume Path
219
-
220
- ```powershell
221
- python "$skill\scripts\compress_claude_jsonl.py" `
222
- --input "C:\data\session.jsonl" `
223
- --analyze-resume-path
224
- ```
225
-
226
- This is read-only. A nonzero result must be resolved before model-pack generation.
227
- Use `reasonCode` for the exact machine-readable cause; `status` is only the
228
- coarse category. The stable value table is in
229
- [`references/claude-jsonl-compression-format.md`](references/claude-jsonl-compression-format.md#authoritative-last-prompt).
230
-
231
- ### 2. Generate A Model Evidence Pack
232
-
233
- ```powershell
234
- python "$skill\scripts\compress_claude_jsonl.py" `
235
- --input "C:\data\session.jsonl" `
236
- --write-model-pack "C:\work\run\session.model-pack.md" `
237
- --target-ratio 0.30 `
238
- --min-recent-records 120 `
239
- --summary-char-budget 60000 `
240
- --target-estimated-tokens 150000 `
241
- --model-pack-char-budget 500000 `
242
- --model-pack-estimated-token-budget 150000
243
- ```
244
-
245
- The evidence pack has two independent default ceilings: 500,000 characters
246
- and a conservative 150,000-token local estimate. The token ceiling leaves
247
- working room in a typical 200k summarizer context. Mandatory human/assistant
248
- semantic records, prior compact summaries, handoff lines, and required coverage
249
- groups are never sampled or clipped; generation stops if they do not fit.
250
- Optional source/tool/system/error evidence is added by importance and chronology
251
- until either ceiling is reached, and the pack/report state whether that optional
252
- evidence was truncated. Do not install a tokenizer to change this workflow.
253
-
254
- `--target-ratio` is an approximate byte-ratio planning input, not a hard release gate. For a hard local Messages estimate ceiling, use:
255
-
256
- ```powershell
257
- --target-estimated-tokens 150000
258
- ```
259
-
260
- This candidate-output estimate is separate from the model-pack reading ceiling.
261
- It covers complete retained structured message payloads, including full thinking,
262
- `tool_use.input`, `tool_result`, and `toolUseResult` data. It does not include
263
- Claude's system prompt, tool schemas, MCP servers, agents, skills, memory files
264
- or runtime-loaded context. It is not a promise about total `/context` usage.
265
-
266
- `--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.
267
-
268
- ### 3. Write The Model Summary
269
-
270
- The model reads the pack and writes `session.model-summary.md`.
271
-
272
- The first HTML comment must be copied exactly and contains:
273
-
274
- ```text
275
- source_sha256
276
- summary_source_sha256
277
- evidence_anchor_lines_digest
278
- required_anchor_groups_digest
279
- handoff_summary_digest
280
- pack_request_digest
281
- required_claim_sources_digest
282
- ```
283
-
284
- 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.
285
-
286
- 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:
287
-
288
- ```text
289
- - L42 support_text_json="exact source substring" disposition=covered
290
- ```
291
-
292
- 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.
293
-
294
- The summary should preserve:
295
-
296
- - current state
297
- - chronology and supersessions
298
- - user constraints and wording
299
- - assistant/model research decisions and reasons
300
- - evidence provenance
301
- - rejected alternatives
302
- - risks, unknowns and follow-ups
303
- - recent raw boundary
304
-
305
- Later events control current state, but earlier decisions and their reasons remain as superseded history.
306
-
307
- ### 4. Build A Candidate
308
-
309
- ```powershell
310
- python "$skill\scripts\compress_claude_jsonl.py" `
311
- --input "C:\data\session.jsonl" `
312
- --output "C:\data\session.compressed.jsonl" `
313
- --target-ratio 0.30 `
314
- --min-recent-records 120 `
315
- --summary-char-budget 60000 `
316
- --target-estimated-tokens 150000 `
317
- --model-pack-char-budget 500000 `
318
- --model-pack-estimated-token-budget 150000 `
319
- --model-summary "C:\work\run\session.model-summary.md"
320
- ```
321
-
322
- Pass exactly the same selection options used for the model pack. In particular,
323
- repeat both non-default model-pack ceilings so the second pass regenerates the
324
- same evidence contract.
325
-
326
- The command writes:
327
-
328
- ```text
329
- session.compressed.jsonl
330
- session.compressed.jsonl.validation.json
331
- session.compressed.jsonl.report.md
332
- ```
333
-
334
- The input remains unchanged.
335
-
336
- ## Live Session Replacement
337
-
338
- Close the Claude Code process using that session before replacement. The live target must be an existing regular `.jsonl` file under `.claude/projects`.
339
-
340
- Generate the model pack and model summary outside `.claude`, then run:
341
-
342
- ```powershell
343
- python "$skill\scripts\compress_claude_jsonl.py" `
344
- --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
345
- --replace-original `
346
- --confirm-session-closed `
347
- --work-dir "C:\work\claude-compression\SESSION-TIMESTAMP" `
348
- --model-pack-estimated-token-budget 150000 `
349
- --target-estimated-tokens 150000 `
350
- --model-summary "C:\work\claude-compression\SESSION-TIMESTAMP\session.model-summary.md"
351
- ```
352
-
353
- The default backup is placed beside the live file:
354
-
355
- ```text
356
- SESSION.jsonl.backup
357
- SESSION.jsonl.backup1
358
- SESSION.jsonl.backup2
359
- ```
360
-
361
- To keep backups outside `.claude`:
362
-
363
- ```powershell
364
- --backup-dir "C:\work\claude-compression\SESSION-TIMESTAMP\backups"
365
- ```
366
-
367
- 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.
368
-
369
- ## Checkpoint And Rewind Behavior
370
-
371
- Conversation rewind and file rewind are separate mechanisms.
372
-
373
- Default:
374
-
375
- ```text
376
- --checkpoint-policy active-correlated
377
- ```
378
-
379
- It retains only UUID-less `file-history-snapshot` records with structural identifiers that correlate to recent retained active records.
380
-
381
- Other controls:
382
-
383
- ```text
384
- --checkpoint-policy none
385
- --max-file-history-snapshots N
386
- ```
387
-
388
- `--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.
389
-
390
- ## Repeated Compression
391
-
392
- 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.
393
-
394
- 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.
395
-
396
- For an explicit exact-text request:
397
-
398
- ```text
399
- --preserve-prior-summaries-verbatim
400
- ```
401
-
402
- 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.
403
-
404
- ## Deterministic Fallback
405
-
406
- Model-assisted summary is the default. Use deterministic fallback only on explicit request:
407
-
408
- ```powershell
409
- python "$skill\scripts\compress_claude_jsonl.py" `
410
- --input "C:\data\session.jsonl" `
411
- --output "C:\data\session.compressed.jsonl" `
412
- --deterministic-summary
413
- ```
414
-
415
- The CLI otherwise requires `--model-summary`.
416
-
417
- ## Read.pages Compatibility Repair
418
-
419
- 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.
420
-
421
- ### Scan
422
-
423
- ```powershell
424
- python "$skill\scripts\repair_claude_jsonl.py" `
425
- --input "C:\data\session.jsonl" `
426
- --scan-only
427
- ```
428
-
429
- ### Write A Candidate
430
-
431
- ```powershell
432
- python "$skill\scripts\repair_claude_jsonl.py" `
433
- --input "C:\data\session.jsonl" `
434
- --output "C:\data\session.repaired.jsonl" `
435
- --expect-matches 2
436
- ```
437
-
438
- ### Replace One Live File
439
-
440
- ```powershell
441
- python "$skill\scripts\repair_claude_jsonl.py" `
442
- --input "$env:USERPROFILE\.claude\projects\PROJECT\SESSION.jsonl" `
443
- --replace-original `
444
- --confirm-session-closed `
445
- --work-dir "C:\work\claude-repair\SESSION-TIMESTAMP" `
446
- --expect-matches 2
447
- ```
448
-
449
- Default scope is the strict active chain. `--scope all` must be explicit.
450
-
451
- The repair requires:
452
-
453
- - assistant API message
454
- - structured `tool_use`
455
- - exact tool name `Read`
456
- - object `input`
457
- - present `pages` member
458
- - non-empty `file_path`
459
- - exactly one later matching `tool_result` in scope
460
- - the same non-empty `sessionId` on use and result
461
- - result `sourceToolAssistantUUID` equal to the tool-use assistant UUID
462
-
463
- 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.
464
-
465
- ## CLI Reference
466
-
467
- Important compression options:
468
-
469
- | Option | Purpose |
470
- | --- | --- |
471
- | `--analyze-resume-path` | Read-only strict topology report |
472
- | `--write-model-pack PATH` | Write bounded semantic evidence and stop |
473
- | `--model-summary PATH` | Validate and embed model-authored summary |
474
- | `--deterministic-summary` | Explicit model opt-out |
475
- | `--target-ratio R` | Approximate output byte-ratio planning value; not a hard gate |
476
- | `--target-estimated-tokens N` | Hard ceiling under the local complete-structure Messages estimate |
477
- | `--min-recent-records N` | Raw active-suffix floor |
478
- | `--summary-char-budget N` | Compact-summary character budget; minimum 4000 |
479
- | `--model-pack-char-budget N` | Evidence-pack character budget |
480
- | `--model-pack-estimated-token-budget N` | Evidence-pack local token estimate ceiling; default 150000 |
481
- | `--resume-leaf UUID` | Explicit recovery leaf override |
482
- | `--max-post-last-prompt-extension N` | Explicit complete tool-result-only closure limit; default 0 |
483
- | `--checkpoint-policy POLICY` | Strict mode: `active-correlated` or `none`; `preserve-recent` only with physical-tail compatibility |
484
- | `--preserve-prior-summaries-verbatim` | Explicit repeated-compression exact-text mode |
485
- | `--preserve-physical-tail` | Compatibility mode without branch-isolation guarantee |
486
- | `--replace-original` | Transactionally replace one live session |
487
- | `--confirm-session-closed` | Required caller acknowledgement for live replacement; not process-lock detection |
488
- | `--work-dir PATH` | External process directory for live replacement |
489
- | `--backup-dir PATH` | Optional external backup directory |
490
- | `--validate-only PATH` | Structural validation only |
491
-
492
- Run `--help` for the complete list.
493
-
494
- ## Validation Scope
495
-
496
- The validator checks:
497
-
498
- - JSON object per non-empty line
499
- - UUID uniqueness
500
- - parent existence and session consistency
501
- - final pointer target
502
- - active chain closure
503
- - narrow attachment-order and one-way session-lineage compatibility, with unsafe variants rejected
504
- - one current compact boundary and compact summary
505
- - compact metadata consistency
506
- - merged assistant fragments and split user tool results
507
- - API-level `tool_use` / `tool_result` order and pairing
508
- - non-empty, unique active tool IDs; partial multi-tool ordered subsets remain a reported branch-compatibility warning
509
- - absence of internal scratch fields
510
-
511
- Validation checks internal consistency under the observed-format rules; Claude Code versions may still build runtime context differently.
512
-
513
- When runtime testing is explicitly allowed, check these separately:
514
-
515
- 1. `/resume` lists and opens the session.
516
- 2. `/context` shows expected Messages usage.
517
- 3. Recent conversation rewind works.
518
- 4. Recent file rewind works for retained checkpoints.
519
-
520
- 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.
521
-
522
- ## Session Locator
523
-
524
- Locate exactly one file by filename or session ID without reading transcript bodies:
525
-
526
- ```powershell
527
- python "$skill\scripts\claude_session_tools.py" `
528
- --root "$env:USERPROFILE\.claude\projects" `
529
- --query "SESSION.jsonl"
530
- ```
531
-
532
- `--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.
533
-
534
- ## Development And Verification
535
-
536
- Run the complete standard-library suite:
537
-
538
- ```bash
539
- python -B -m unittest discover -s tests -v
540
- python -B tests/test_compressor.py
541
- python -B tests/test_repair.py
542
- python -B tests/test_package.py
543
- python -B tests/test_transaction_races.py
544
- python -B tests/test_semantic_evidence_contracts.py
545
- python -B tests/test_structural_safety_contracts.py
546
- python -B tests/test_protocol_contracts.py
547
- ```
548
-
549
- Additional release checks:
550
-
551
- ```bash
552
- pycache="$(mktemp -d)"
553
- if ! PYTHONPYCACHEPREFIX="$pycache" python -m compileall -q scripts tests; then
554
- rm -rf "$pycache"
555
- exit 1
556
- fi
557
- rm -rf "$pycache"
558
- python -B -I -S scripts/compress_claude_jsonl.py --version
559
- python -B -I -S scripts/repair_claude_jsonl.py --version
560
- npm test
561
- npm pack --dry-run --json
562
- npm publish --dry-run --access public --tag latest
563
- ```
564
-
565
- 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.
566
-
567
- ### Maintainer Stable Release Checklist
568
-
569
- 1. Confirm a clean public tree and matching `1.0.0` values in `package.json`, Python version output, docs, and tests.
570
- 2. Run the Python, npm, isolated-Python, tarball, privacy, and offline-install gates above.
571
- 3. Inspect `npm pack --dry-run --json`; publish only the allowlisted files.
572
- 4. Require a clean worktree, create annotated tag `v1.0.0`, and push the commit and tag.
573
- 5. Publish from an authenticated maintainer environment with permission to publish this package:
574
-
575
- ```bash
576
- npm publish --access public --tag latest
577
- ```
578
-
579
- 6. Verify npm version `1.0.0` and the `latest` dist-tag, then create a GitHub release (not a prerelease) from the already-pushed tag.
580
-
581
- 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.
582
-
583
- 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.
584
-
585
- ## Repository Layout
586
-
587
- ```text
588
- SKILL.md
589
- CHANGELOG.md
590
- README.md
591
- LICENSE
592
- package.json
593
- bin/
594
- config/
595
- scripts/
596
- templates/
597
- references/
598
- tests/
599
- ```
600
-
601
- ## Privacy
602
-
603
- - The public project contains only anonymous synthetic fixtures.
604
- - Model packs and candidate metadata use generic labels such as `SOURCE_JSONL`; generated reports expose basenames, never full local paths.
605
- - npm publication uses an extension-level file allowlist.
606
- - JSONL, backups, reports, model packs, model summaries, caches and compiled Python files are excluded from the package.
607
- - Review generated evidence packs before sharing them; they intentionally contain selected transcript evidence.
608
-
609
- ## License
610
-
611
- 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.