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

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.
@@ -0,0 +1,500 @@
1
+ # Claude Code JSONL Compression Format Notes
2
+
3
+ This document describes the empirical format handled by
4
+ `@brandry/claude-jsonl-compressor` package `1.0.0-rc.1` and engine `v10`.
5
+
6
+ Claude Code transcript JSONL is an observed internal format, not a published
7
+ stable storage API. The rules below are deliberately strict where ambiguity
8
+ could restore a rewound branch, break resume topology, or make a tool exchange
9
+ incoherent.
10
+
11
+ ## Version Domains
12
+
13
+ The project keeps four independent version domains:
14
+
15
+ | Domain | Current value | Meaning |
16
+ | --- | --- | --- |
17
+ | Package | `1.0.0-rc.1` | GitHub/npm release version |
18
+ | Compression engine | `v10` | Topology, partition, and rewrite behavior |
19
+ | Model-pack schema | `v11` | Evidence-pack and model-summary binding protocol |
20
+ | Report schema | `1` | Compression and repair report fields |
21
+
22
+ A change to one domain does not imply the other domains changed.
23
+
24
+ ## Three Physical and Logical Layers
25
+
26
+ A transcript can contain records that play very different roles:
27
+
28
+ 1. **Active API-message chain.** `user` and `assistant` records connected from
29
+ the selected resume leaf through `parentUuid`. This is the history that the
30
+ compressor may summarize or preserve verbatim.
31
+ 2. **Control and side records.** `last-prompt`, titles, modes, attachments,
32
+ hooks, and `file-history-snapshot` records may exist physically in the file
33
+ without becoming ordinary API messages. They require explicit projection or
34
+ attribution rules.
35
+ 3. **Inactive or unattributed records.** Rewound branches and records that
36
+ cannot be structurally attributed to the active branch remain useful only
37
+ for diagnostics. Engine v10 excludes their content from every Claude-readable
38
+ output layer and from every model-summary evidence channel.
39
+
40
+ Physical line order alone is therefore not a definition of current context.
41
+
42
+ ## Common Observed Record Types
43
+
44
+ | `type` | Observed purpose |
45
+ | --- | --- |
46
+ | `user` | User content or user-side `tool_result` blocks |
47
+ | `assistant` | Assistant text, thinking, `tool_use`, model metadata, and usage |
48
+ | `system` | Claude Code events, including `subtype: compact_boundary` |
49
+ | `attachment` | Hook, image, file, or environment attachment metadata |
50
+ | `last-prompt` | UI/session pointer containing the current `leafUuid` |
51
+ | `file-history-snapshot` | File checkpoint metadata, commonly without UUID links |
52
+ | `mode` / `permission-mode` | Session mode state |
53
+ | `ai-title` / `custom-title` | Conversation title metadata |
54
+ | `queue-operation` | Queued-input or editing metadata |
55
+ | `agent-name` | Agent label metadata |
56
+
57
+ Open-schema handling is required: unknown fields are preserved on retained
58
+ records unless the operation explicitly projects a control record.
59
+
60
+ ## UUID, Parent, Session, and Resume Relationships
61
+
62
+ ### UUID chain
63
+
64
+ Message-like records commonly carry:
65
+
66
+ - `uuid`: record identity;
67
+ - `parentUuid`: preceding record on that branch, or `null` at a root;
68
+ - `sessionId`: session identity.
69
+
70
+ Engine v10 treats duplicate UUIDs anywhere in the input as blocking because a
71
+ UUID index would be ambiguous. A non-null `parentUuid` must be a non-empty
72
+ string; any other value is malformed. A missing parent, malformed parent or loop
73
+ on the selected active chain is blocking. Physical inversion is blocking except
74
+ for same-session `attachment -> attachment` edges on an otherwise complete,
75
+ acyclic chain; accepted attachments are emitted in logical parent order.
76
+ Equivalent non-UUID-ambiguity damage wholly inside an excluded branch does not
77
+ revive that branch.
78
+
79
+ Mixed `sessionId` ancestry is not rejected merely because two IDs occur. A
80
+ lineage is accepted only when its contiguous session runs never return to an
81
+ earlier ID and the final run matches both the selected leaf and authoritative
82
+ pointer. Planning forces every earlier run into `summaryIndexes` and preserves
83
+ only the final run as recent raw records. A tool relationship that would move
84
+ the cut across the lineage transition stops compression. This covers observed
85
+ historical branch/resume files without admitting A-B-A or arbitrary cross-session
86
+ raw chains.
87
+
88
+ ### Authoritative `last-prompt`
89
+
90
+ Automatic active-chain mode uses the physically last record whose
91
+ `type` is `last-prompt`. Its `leafUuid` is authoritative. The engine does not
92
+ skip a malformed latest pointer to search for an older usable pointer because
93
+ that could resurrect a state the user already left.
94
+
95
+ Strict topology statuses include:
96
+
97
+ - `absent`
98
+ - `malformed`
99
+ - `malformed-parent`
100
+ - `duplicate-uuid`
101
+ - `dangling`
102
+ - `loop`
103
+ - `non-monotonic`
104
+ - `session-mismatch`
105
+ - `extension-limit`
106
+ - `extension-branch`
107
+ - `extension-unsafe`
108
+ - `valid`
109
+
110
+ `--resume-leaf UUID` is an explicit recovery override. It is reported as
111
+ `active-chain-manual-override`, distinct from default strict `active-chain`.
112
+ `--preserve-physical-tail` is a separate legacy compatibility
113
+ mode and does not provide active-branch exclusion guarantees.
114
+
115
+ Strict failures are zero-write outcomes. The CLI does not prompt or retry in a
116
+ different mode. A hosting agent may ask for a new, explicit user confirmation
117
+ when one diagnosed recovery control applies, but the initial compression request
118
+ is not confirmation. Manually spliced or otherwise ambiguous files normally
119
+ require physical-tail compatibility and therefore forfeit branch/rewind
120
+ isolation.
121
+
122
+ ### Records after `last-prompt`
123
+
124
+ The default `--max-post-last-prompt-extension 0` excludes all UUID records
125
+ physically after the authoritative pointer. A nonzero value is explicit and
126
+ accepts only a direct, physically later, same-session linear closure consisting
127
+ of tool-result-only user records that close every pending tool ID. Ordinary
128
+ conversation, system/hook records, unrelated results and partial closure are
129
+ rejected as unsafe.
130
+
131
+ ## Six-Way Source Partition
132
+
133
+ After strict topology succeeds, every source line belongs to exactly one set:
134
+
135
+ | Set | Meaning | May reach Claude-readable output? |
136
+ | --- | --- | --- |
137
+ | `summaryIndexes` | Older active-chain records selected for semantic summary | Yes, only through the new summary |
138
+ | `rawKeepIndexes` | Recent active-chain records kept byte-semantically as JSON objects | Yes, as recent raw history |
139
+ | `sideKeepIndexes` | Explicitly attributed side/checkpoint records | Yes, as non-chain side records |
140
+ | `controlProjectionIndexes` | Control metadata used to construct the final projected state | Yes, only through controlled projection |
141
+ | `excludedBranchIndexes` | UUID records outside the selected active branch | No |
142
+ | `excludedUnattributedIndexes` | Records with no accepted structural attribution | No |
143
+
144
+ The sets are mutually exclusive and cover every source index. The legacy
145
+ internal/report name `omittedIndexes` is only a compatibility alias for
146
+ `summaryIndexes`; it no longer means every physically discarded record.
147
+
148
+ Before a model pack or candidate is written, the engine copies only the
149
+ authoritative logical active chain, projects its selected leaf into one pointer,
150
+ and runs the shared transcript validator on that source view. Malformed old tool
151
+ exchanges, duplicate tool IDs, or compact metadata on the active chain therefore
152
+ cannot be hidden by summarization. Damage confined to an excluded inactive
153
+ branch remains excluded and does not block or enter the summary.
154
+
155
+ Inactive and unattributed record bodies are absent from:
156
+
157
+ - the model evidence pack;
158
+ - model-authored summary validation input;
159
+ - deterministic fallback summary input;
160
+ - prior-summary verbatim blocks;
161
+ - recent raw records;
162
+ - side records;
163
+ - the final candidate JSONL.
164
+
165
+ Reports may expose only counts and content-free line-set digests for excluded
166
+ sets.
167
+
168
+ ## Compact-Style Output Pair
169
+
170
+ The engine emits exactly one current Codex-created compact pair:
171
+
172
+ 1. a `system` record with `subtype: "compact_boundary"`;
173
+ 2. its direct `user` child with `isCompactSummary: true`.
174
+
175
+ Simplified synthetic shape:
176
+
177
+ ```json
178
+ {"type":"system","subtype":"compact_boundary","uuid":"BOUNDARY_UUID","parentUuid":null,"compactMetadata":{"codexOfflineCompression":true,"codexOfflineCompressionVersion":"v10","modelPackSchemaVersion":11,"reportSchemaVersion":1,"summaryUuid":"SUMMARY_UUID","preserveMode":"active-chain","resumeLeafInfo":{"status":"valid","selectedLeafUuid":"ACTIVE_LEAF"}}}
179
+ {"type":"user","uuid":"SUMMARY_UUID","parentUuid":"BOUNDARY_UUID","isCompactSummary":true,"message":{"role":"user","content":"SUMMARY_TEXT"}}
180
+ ```
181
+
182
+ The exact open-schema object contains additional observed metadata. The current
183
+ pair is followed by the recent active suffix. In strict active-chain mode, only
184
+ the first recent record has its parent changed to `SUMMARY_UUID`; every later
185
+ recent parent/session edge must already be coherent. A final `last-prompt` is
186
+ deep-copied from the authoritative source pointer so unknown fields survive,
187
+ then only its projected leaf/session values are updated.
188
+
189
+ Validation requires the final pointer's active chain to contain the exact
190
+ current boundary and summary, not merely an older compact pair.
191
+
192
+ ## Model-Assisted Summary Protocol v11
193
+
194
+ Model-assisted semantic summary is the default. Deterministic code owns all
195
+ topology, source partitioning, JSONL construction, UUID generation, parent
196
+ rewrites, and validation. The model receives only the frozen evidence pack and
197
+ writes summary prose.
198
+
199
+ ### Evidence binding
200
+
201
+ The generated pack contains:
202
+
203
+ - a generic `SOURCE_JSONL` label, never the original filename or full local path;
204
+ - full source-file SHA-256;
205
+ - SHA-256 of the exact active records selected for summary;
206
+ - the digest of the displayed evidence-anchor set;
207
+ - the digest of all required evidence-coverage groups;
208
+ - the digest of an optional external handoff summary;
209
+ - a canonical request digest binding all selection/budget/policy options and the
210
+ loaded importance/topic/template resource content;
211
+ - a digest of the mandatory claim-source map;
212
+ - a full-text `L<number>` record for every non-empty older active human message
213
+ and every older active assistant `text`/`thinking` message;
214
+ - selected line-numbered source/tool/error evidence;
215
+ - `H<number>` anchors for displayed handoff lines;
216
+ - explicit current-state, chronology, supersession, decision, rationale,
217
+ rejected-option, uncertainty, and recent-boundary instructions.
218
+
219
+ The model summary must begin with the exact comment copied from the pack:
220
+
221
+ ```html
222
+ <!-- claude-jsonl-compressor:model-summary v11
223
+ source_sha256: SOURCE_SHA256
224
+ summary_source_sha256: SUMMARY_SOURCE_SHA256
225
+ evidence_anchor_lines_digest: EVIDENCE_ANCHOR_LINES_DIGEST
226
+ required_anchor_groups_digest: REQUIRED_ANCHOR_GROUPS_DIGEST
227
+ handoff_summary_digest: HANDOFF_SUMMARY_DIGEST_OR_NONE
228
+ pack_request_digest: PACK_REQUEST_DIGEST
229
+ required_claim_sources_digest: REQUIRED_CLAIM_SOURCES_DIGEST
230
+ -->
231
+ ```
232
+
233
+ Substantive factual lines cite displayed `L<number>` and, when used, `H<number>`
234
+ anchors. The exact whole line `Unknown from provided anchors.` is the only
235
+ unanchored uncertainty placeholder; a longer line containing that text has no
236
+ exemption. Validation rejects wrong digests, invisible or out-of-range anchors,
237
+ unanchored substantive lines, too little evidence coverage, and common
238
+ no-access boilerplate. It requires at least one cited L anchor from each
239
+ generated group. Every non-empty human/assistant semantic record has its own
240
+ required L group, and prior summaries remain independently required. It also
241
+ requires all nine exact semantic sections printed in the pack, each with an L
242
+ or H anchor. A nonempty handoff generates early/middle/late/latest H coverage
243
+ groups that must all be cited. Handoff text is not trusted merely because it
244
+ contains a special phrase; only generated H anchors are accepted.
245
+
246
+ Only the exact leading metadata comment and the exact required headings are
247
+ exempt from line grounding. A second/unclosed HTML comment, an extra Markdown
248
+ heading, duplicate metadata, renamed/reordered sections, or unsupported body
249
+ line is an error. Under `## Evidence and Source Anchors`, the summary must contain
250
+ exactly one `### Mandatory Evidence Coverage` subsection. Every mandatory
251
+ semantic/prior-summary L anchor appears exactly once as:
252
+
253
+ ```text
254
+ - L42 support_text_json="exact source substring" disposition=covered
255
+ ```
256
+
257
+ The JSON string must decode to a meaningful exact substring of that source
258
+ record. This blocks a content-free anchor dump and leaves a mechanically
259
+ checkable excerpt; it does not prove perfect natural-language interpretation.
260
+
261
+ Every active-chain prior compact summary is inserted as a complete dedicated
262
+ evidence record. An explicitly supplied external handoff is inserted in full,
263
+ one JSON-escaped `H<number> full_text_json` line per source line. Two default
264
+ pack ceilings apply together: 500,000 characters and a conservative 150,000
265
+ local estimated tokens. The latter leaves working space in a typical 200k
266
+ summarizer context. If the complete semantic ledger, prior summaries, handoff,
267
+ and minimum structural coverage do not fit, pack generation stops; it does not
268
+ trim or sample mandatory evidence. Optional source/tool/system/error evidence
269
+ is ranked and added only while both ceilings permit, and truncation is reported.
270
+ The caller should raise `--model-pack-char-budget` or
271
+ `--model-pack-estimated-token-budget` only when the summarizing model can read
272
+ the resulting pack. No external tokenizer is installed. Once a model summary
273
+ passes validation, its text is not truncated to meet `--summary-char-budget`;
274
+ insufficient composition space is a hard error. The summary character budget
275
+ has a hard minimum of 4000, and blank compact summaries are invalid.
276
+
277
+ These controls reduce stale-summary and unsupported-claim risk. They do not
278
+ mathematically prove that a natural-language summary has perfect semantics.
279
+ The deterministic safety appendix and recent raw suffix remain independent
280
+ evidence layers.
281
+
282
+ ### Weighting and chronology
283
+
284
+ The evidence pack requires every non-empty older active human message and every
285
+ older active assistant `text`/`thinking` message, regardless of language or
286
+ keyword score. Source-text warnings such as U+FFFD are reported but do not make
287
+ mandatory records optional. Within the remaining optional source/tool capacity it gives
288
+ priority to:
289
+
290
+ 1. explicit user constraints and requested outcomes;
291
+ 2. final/current decisions and their reasons;
292
+ 3. supersessions and chronology needed to avoid reviving old decisions;
293
+ 4. model research conclusions, implementation decisions, and rationale;
294
+ 5. unresolved risks, exact identifiers, source references, and validation
295
+ results;
296
+ 6. file/tool content that materially supports the above;
297
+ 7. repetitive logs or mechanically recoverable detail.
298
+
299
+ Important evidence can be Chinese, English, Japanese, Korean, Arabic, Russian,
300
+ Hindi, Greek, Hebrew, Armenian, Thai, Georgian, Ethiopic, Bengali, Tamil,
301
+ Telugu, Malayalam, Spanish, French, German, Portuguese, or another language.
302
+ All assistant thinking is semantic evidence without a language-keyword gate;
303
+ multilingual terms and script detection only improve classification and optional
304
+ weighting. Language is not treated as a proxy for importance.
305
+
306
+ ## Token Ceilings
307
+
308
+ `--model-pack-estimated-token-budget N` limits the evidence pack read by the
309
+ summary-authoring model. It defaults to 150,000 and is enforced together with
310
+ the character budget. The same non-default value must be supplied when
311
+ generating the pack and when applying the model summary. Mandatory semantic and
312
+ handoff evidence cannot be dropped to satisfy it; optional evidence can be
313
+ truncated and this state is recorded in the pack, compact metadata, and report.
314
+
315
+ `--target-estimated-tokens N` uses a dependency-free heuristic over complete
316
+ retained structured message payloads, including full thinking,
317
+ `tool_use.input`, `tool_result`, and `toolUseResult` data. ASCII is estimated at
318
+ one token per four characters;
319
+ Han/Kana/Hangul at 1.3 each; non-BMP, symbol and combining-mark code points at
320
+ 1.5 each; other non-ASCII at 0.8 each; and each record adds a small allowance.
321
+ It narrows the byte-ratio plan and rejects a generated candidate whose
322
+ estimated message tokens exceed `N`. This candidate-output gate is independent
323
+ of the model-pack reading ceiling.
324
+
325
+ This estimate excludes system prompts, tools, MCP schemas, skills, runtime
326
+ injections, and any Claude-side accounting. It is a reproducible local planning
327
+ gate, not a promise that `/context` or an API relay will report the same total.
328
+ `--target-ratio` is only an approximate byte-ratio planning input and is not a
329
+ hard publication gate.
330
+
331
+ ## Tool Use and Tool Result Pairing
332
+
333
+ One API turn may be split across several JSONL records:
334
+
335
+ - multiple `tool_use` blocks in one assistant message;
336
+ - adjacent assistant fragments sharing one `message.id`;
337
+ - split user records carrying `tool_result` blocks;
338
+ - hook or attachment records between result fragments.
339
+
340
+ Validation rebuilds the selected active chain, filters API messages, merges
341
+ compatible assistant and user fragments, and checks tool IDs and order. It
342
+ rejects orphan results, wrong IDs, and out-of-order results. The compression cut
343
+ cannot divide a required tool-use/result relationship.
344
+
345
+ ## File-History Checkpoint Policies
346
+
347
+ Conversation topology and file checkpoint metadata are separate planes.
348
+ `file-history-snapshot` records commonly lack `uuid` and `parentUuid`, so they
349
+ cannot be relinked into the API-message chain.
350
+
351
+ Policies:
352
+
353
+ - `active-correlated` (default): retain a bounded set only when structural
354
+ identifiers correlate the snapshot to recent active records;
355
+ - `preserve-recent`: rejected in strict active-chain mode; accepted only with
356
+ explicit `--preserve-physical-tail`, which is labeled compatibility mode and
357
+ has no inactive-branch isolation guarantee;
358
+ - `none`: retain no snapshot side records.
359
+
360
+ Preserved snapshots are emitted as side records before the compact pair. JSONL
361
+ compression alone does not guarantee complete file-state rewind because Claude
362
+ Code checkpoint storage and lifecycle have behavior beyond the message chain.
363
+
364
+ ## Repeated Compression
365
+
366
+ Old compact pairs on the active chain are part of `summaryIndexes` when they
367
+ fall before the recent suffix. Normal repeated compression folds their useful
368
+ meaning into one new current summary rather than stacking live compact pairs.
369
+
370
+ `compactMetadata.preservedMessages` and `preservedSegment` describe the raw
371
+ suffix that existed when that compact pair was created. Later work can extend
372
+ that suffix, and a later rewind can leave part of the recorded snapshot on an
373
+ inactive branch. A structurally valid historical divergence is therefore a
374
+ source warning, not authority to restore those UUIDs and not a topology error.
375
+ Selection continues from the current `last-prompt` parent chain. Candidate
376
+ publication is stricter: the newly emitted snapshot must match the candidate's
377
+ current chain exactly or publication stops before writing.
378
+
379
+ `--preserve-prior-summaries-verbatim` is an explicit exception. It attempts to
380
+ embed prior `isCompactSummary.message.content` text verbatim inside the one new
381
+ summary and permits the summary text to grow to `1.5 * --summary-char-budget`.
382
+ If the prior summaries still do not fit, the engine uses the normal folded
383
+ model-summary path and reports the fallback reason. Excluded-branch summaries
384
+ are never eligible for either path.
385
+
386
+ ## Branch Session Files
387
+
388
+ Observed `/branch` files are not guaranteed to be full physical copies. They
389
+ may retain source UUIDs, rewrite `sessionId`, add `forkedFrom`, relink parents,
390
+ and omit source history outside the branch-relevant chain.
391
+
392
+ The compressor treats exactly one selected JSONL as authoritative. It does not
393
+ merge a branch file with its source. Compress the branch to continue the branch;
394
+ compress or archive the source separately when its physical history matters.
395
+
396
+ ## External References
397
+
398
+ JSONL may contain inline tool results, file excerpts, attachment metadata, or
399
+ paths/references to external artifacts such as tool-result storage. Offline
400
+ compression reads the selected JSONL and an explicitly supplied handoff file
401
+ only. It does not dereference, delete, or rewrite external project files,
402
+ `tool-results`, subagent artifacts, settings, or other sessions.
403
+
404
+ If a referenced artifact's contents are not embedded in the JSONL or handoff,
405
+ the model pack knows only the reference, not the external contents.
406
+
407
+ ## `Read.pages` Compatibility Repair
408
+
409
+ Claude Code legitimately uses `Read.input.pages`, including for paged document
410
+ reads. The independent repair command exists only for a known downstream
411
+ compatibility case where historical serialized `pages` members must be removed.
412
+ Compression never invokes this repair implicitly.
413
+
414
+ The repairer:
415
+
416
+ - matches only assistant `tool_use` blocks with exact tool name `Read` and an
417
+ `input` object containing `pages`;
418
+ - requires a nonempty `file_path`, exactly one later matching `tool_result`, the
419
+ same nonempty `sessionId`, and result `sourceToolAssistantUUID` equal to the
420
+ tool-use assistant UUID before automatic repair;
421
+ - reports pending calls without changing them;
422
+ - defaults to the strict active chain; `--scope all` is explicit;
423
+ - deletes the exact JSON member byte span without reserializing other data;
424
+ - blocks duplicate-key or overlapping-span ambiguity;
425
+ - preserves BOM, newline style, Unicode spelling, unknown fields, record count,
426
+ UUID/parent signature, and tool ID sequences;
427
+ - re-reads the actual published candidate, verifies exact expected bytes and
428
+ SHA-256, validates the repair invariants, requires an idempotent second scan
429
+ with zero remaining patches, and runs the shared full-transcript
430
+ UUID/parent/compact/tool validator on the published bytes.
431
+
432
+ Candidate and live replacement modes use the same atomic-write, full-hash,
433
+ numbered-backup, source-race, validation, and rollback principles as compression.
434
+
435
+ ## Candidate and Live Replacement Transactions
436
+
437
+ Candidate mode requires distinct input and output paths. Candidate JSONL,
438
+ reports, validation, model packs, summaries and work directories must remain
439
+ outside the entire `.claude` tree.
440
+
441
+ Live replacement mode requires one existing regular `.jsonl` input under
442
+ `.claude/projects`, an external `--work-dir`, and caller acknowledgement
443
+ `--confirm-session-closed`. The acknowledgement is an operational assertion,
444
+ not process-lock detection. It:
445
+
446
+ 1. reads and hashes the complete original bytes;
447
+ 2. writes and validates a candidate outside `.claude`;
448
+ 3. rechecks that the source bytes did not change;
449
+ 4. creates an exclusive `.backup`, `.backup1`, and so on;
450
+ 5. validates immutable candidate bytes and writes a unique same-directory stage
451
+ with flush/fsync;
452
+ 6. captures the actual old target, verifies its full bytes against the frozen
453
+ source, and publishes the staged candidate with an atomic no-clobber claim;
454
+ 7. verifies published bytes and structure, restoring the captured original on
455
+ failure. If restoration fails, the numbered verified backup remains and the
456
+ error is promoted.
457
+
458
+ If another process recreates the target after capture begins, the transaction
459
+ does not overwrite the external target. It preserves the verified numbered
460
+ backup and, when necessary, places the captured original in another numbered
461
+ backup, then fails without publishing the candidate.
462
+
463
+ Candidate-mode reports are written with the candidate. Live compression delays
464
+ its final sidecar/report until replacement metadata is available, so it does not
465
+ leave a stale pre-commit report. If the JSONL commits and validates but final
466
+ report publication fails, compression returns exit code 3 with
467
+ `operation_state: committed-report-failed`; repair uses the camelCase equivalent
468
+ `operationState`. The receipt includes public artifact labels and frozen,
469
+ candidate, and published hashes. This is a committed state and is not rolled
470
+ back or reported as an ordinary uncommitted failure.
471
+
472
+ Once a numbered backup is created and verified, later failure cleanup never
473
+ deletes that path. This preserves an audit/recovery asset even when publication
474
+ or rollback fails or another process races on a nearby name.
475
+
476
+ Claude Code should be closed for that session during replacement. No other
477
+ session or Claude settings file is part of the transaction. Parent-directory
478
+ fsync is best effort and reported; the project does not promise cross-platform
479
+ power-loss atomicity.
480
+
481
+ ## Validation Boundary
482
+
483
+ The validator checks observed-format coherence, including:
484
+
485
+ - parseability and object-per-line structure;
486
+ - unique UUIDs and resolvable parent/session links;
487
+ - exactly one current compact pair;
488
+ - current compact pair reachability from the final pointer;
489
+ - compact preserved-message metadata;
490
+ - non-empty, unique active tool IDs and tool-use/result pairing after fragment
491
+ merging; partial multi-tool ordered subsets are accepted only with an explicit
492
+ compatibility warning/count;
493
+ - absence of internal planning fields in output;
494
+ - source hashes, model-pack metadata, anchor sets, and target estimates where
495
+ applicable.
496
+
497
+ Passing these checks proves coherence under this project's empirical rules. It
498
+ does not make the private transcript format an Anthropic-supported public API.
499
+ When runtime testing is explicitly requested, `/resume`, `/context`, recent
500
+ conversation rewind, and recent file rewind are separate observations.