Master removed the stdio demo (#702c8cc30) — accept the deletion; this
branch's packChunks passthrough survives in acp-demo (auto-merged), and
cli-demo/tui-demo arrived from master without one (the follow-up snapshot
PR decides which demos expose the switch). Generated catalogs regenerated
over merged sources; the hand-written session.md durability paragraph
re-weaves this branch's lossless-encoding wording with master's invariant-
companion sentence.
Master's Zstandard physical encoding (#416) and this branch's packed chunk
rows compose as orthogonal layers: packChunks shapes the logical storage
records, compression shapes the physical bytes. The backend keeps both
config keys; eventLines(events, packChunks) replaces master's singular
eventLine helper and feeds encodeMaterialization/encodeEventBatch so packed
rows flow through either encoding. Demo apps carry both passthroughs. The
packed-row layout tests pin compression: 'none' (they assert textual line
tags) and read through rawLogPath; zstd spec fixtures inline
JSON.stringify for verbatim lines.
Conflicts: the two generated catalog docs (regenerated over merged sources)
and the jsonl backend README storage-layout bullets — master's required
delegationDepth header field weaves with this branch's storage-record/packed
row wording.
Identity validation must reject a header id that cannot derive a path, and only ENOENT may mean that storage is absent. Other root or per-path failures must remain visible instead of becoming an empty list or false miss.
Exercise those branches with narrow storage-mechanics cases, preserving per-file 100% coverage without restoring the flat-layout or multi-writer tests removed from the replacement design.
A configured root that already exists as a file or unreadable directory cannot host cwd buckets, but the backend previously mounted and deferred that deterministic configuration error until a later list or write.
Probe the resolved root while the plugin loads, surface every error except ENOENT, and keep an absent root valid for lazy first materialization. Document the timing contract, regenerate the config catalog, and pin the non-directory case at the load boundary.
JSONL discovered a log by the requested session id but later routed repair and append from the parsed header. A log selected for session A could therefore declare session B and redirect mutation to B.
Validate the requested id and exact header-derived cwd-bucket path before returning a stored prefix, reject duplicate ids across buckets, and repeat the id/cwd guards in the coordinator before repair or state publication. Collapse the redundant loadLive hook into loadStored while retaining the existing bucket layout and one-live-writer topology, avoiding flat-layout churn and a locator generic that SQLite and test backends do not need.
Conflicts: the four generated catalog docs (regenerated over merged sources),
session index.ts exports (keep chunk-rows exports + master's SessionSurface
re-export), stdio/acp demo config schema and persistence wiring (thread
packChunks through master's DEFAULT_PERSISTENCE_ROOT/UI shape), stdio README
config table, and the jsonl spec import line. The packed-chunk fixture also
gains the provenance field master made required on assistant/message.
Declare MoveFileExW's return value as Koffi int and model it as a numeric 32-bit Win32 BOOL. The previous Koffi bool declaration represented a one-byte C boolean and could read the native return register with the wrong ABI.
Test zero and nonzero results explicitly and assert the binding result type while preserving the existing write-through flags, error translation, and durable directory race behavior.
Providers stream token-sized deltas, so a session log stores hundreds of
near-identical assistant/chunk lines whose JSON envelopes dwarf their
payloads (~56x measured on a real DeepSeek session, 73% of file bytes).
Add a lossless storage codec to dsh-session: packChunkRuns() folds each
run of >=3 consecutive same-block delta chunks into one storage row --
text-chunks / reasoning-chunks / tool-call-chunks, bare slash-less tags
like the header line's 'session' so rows cannot be confused with session
events -- and decodeStorageRecord() expands rows back to the exact
original events (seq0/time0 + dt gap array reconstruct every member's
seq/time; tool-call rows carry the run-constant id/name). The encoder
whitelists exact shapes and stores anything unrecognized verbatim; the
decoder validates row-tagged values and fails loud on malformation.
The JSONL backend gains a packChunks config (default false). Writing
packs only when enabled -- default-off output stays byte-identical to
the previous layout, so snapshot goldens are untouched. Reading is
layout-blind: scanLog always decodes rows and now checks seq contiguity
with a cursor instead of the line index, so packed, unpacked, and mixed
files all load identically. Fixture readers (llm-replay parseSessionLog,
acp-snapshot normalizeSessionLog) share the codec; the normalizer zeroes
a row's time0/dt exactly like an event's time. The two demo bundles
plumb packChunks from cordis.yml to the backend.
Measured on a real coding session: 105 KB -> 42 KB (-60%), 475 lines ->
74, with reasoning/tool-call heavy sessions saving the most. Covered by
example + fast-check round-trip codec tests, backend packed/mixed/torn-
tail specs, and an end-to-end demo run loading a packed log through a
default-config backend.