From be3f23a42247144f0276b6ed74a0017ebd313abf Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:42:29 +0800 Subject: [PATCH] docs(session): propose packed chunk rows by default --- ...-26-packed-chunk-rows-by-default.i18n.yaml | 6 ++ ...2026-07-26-packed-chunk-rows-by-default.md | 56 +++++++++++++++++++ ...6-07-26-packed-chunk-rows-by-default.zh.md | 56 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 .agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.i18n.yaml create mode 100644 .agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.md create mode 100644 .agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.zh.md diff --git a/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.i18n.yaml b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.i18n.yaml new file mode 100644 index 0000000000..d0e159ec6c --- /dev/null +++ b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write +2026-07-26-packed-chunk-rows-by-default.md: a4ac43280f83fdb1a75057d8a0d5633c33b89b36 +2026-07-26-packed-chunk-rows-by-default.zh.md: 05909c5f8aecc9f57c8145f87f9c908fd2118867 diff --git a/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.md b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.md new file mode 100644 index 0000000000..a4ac43280f --- /dev/null +++ b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.md @@ -0,0 +1,56 @@ +# Agent Note: Make packed chunk rows the default JSONL layout + +Status: proposed + +English | [中文](2026-07-26-packed-chunk-rows-by-default.zh.md) + +## Problem + +The JSONL persistence backend can losslessly replace a run of at least three consecutive same-block `assistant/chunk` delta events with one `text-chunks`, `reasoning-chunks`, or `tool-call-chunks` storage row. Loading expands that row back into the exact events, including sequence numbers, timestamps, and chunk boundaries. The codec therefore reduces repeated JSON envelopes without changing the authoritative logical session log. + +`packChunks` nevertheless defaults to `false` in both `dsh-session-persistence-jsonl` and the ACP demo composition. That default was chosen so the first packed-row implementation could land without rewriting the snapshot corpus. It now makes the ordinary write path, most tests, and almost every committed session fixture exercise the larger one-event-per-line representation, while only one dedicated ACP scenario exercises packing. + +The snapshot corpus is part of the default contract, not disposable test data. ACP and headless snapshots harvest physical persistence files, but the TUI snapshot writer serializes `Session.events` directly and bypasses the backend encoder. Flipping one schema default would therefore leave different products and test tiers with different physical layouts, and future fixtures could silently return to unpacked rows. + +This proposal changes only the physical storage representation. Every provider chunk remains one logical `assistant/chunk` session event, is delivered live through `session/event`, occupies its own sequence number, and remains addressable by `sourceEventSeqs` after load. Coalescing live events before `Session.append()` is outside this proposal because it would change UI streaming, cancellation evidence, provenance, and replay semantics established by the [session-persistence decision](../../implemented/architecture/2026-06-14-session-persistence.md). + +## Proposal + +Packed chunk rows become the default physical layout for every JSONL writer, shipping composition, default-path test, and committed session-log fixture. The JSONL backend resolves omitted `packChunks` to `true`; the ACP demo's pass-through config does the same; CLI, TUI, headless, and other compositions that omit the option inherit the backend default. + +`packChunks: false` remains an explicit write-side opt-out for line-per-event diagnostics and compatibility tests. Reading stays unconditional and layout-blind, so packed, unpacked, and mixed existing logs continue to load without migration or a session-format version change. The option controls only newly appended batches; it does not select a reader mode. + +The packed codec remains at the `dsh-session` storage seam. Persistence, fixture producers, normalizers, and replay readers share `packChunkRuns()` and `decodeStorageRecord()` rather than introducing a snapshot-only encoding. Packing remains per durable append batch and retains the existing minimum run length and exact-shape allowlist. + +## Implementation plan + +1. Change `SessionPersistenceJsonl.Config.packChunks` and the ACP demo wrapper default to `true`. Update their JSDoc, bilingual READMEs, generated config catalog, and every current-state statement that calls packed rows opt-in. Keep the explicit boolean so deployments can request unpacked writes without coupling that choice to `compression: 'none'`. +2. Make the JSONL backend's default-path tests assert packed output without passing `packChunks: true`. Retain narrowly named tests for `packChunks: false`, byte-identical unpacked writes, mixed-layout reads, malformed packed rows, and torn tails. Tests whose subject is unrelated persistence behavior omit the flag and therefore exercise the shipping default. +3. Make every snapshot fixture producer emit the same physical layout. ACP and headless suites harvest the backend's packed raw-mode artifacts. The TUI snapshot writer applies the shared codec instead of mapping `session.events` directly to lines. Raw `compression: 'none'` remains necessary for reviewable fixtures but no longer implies one logical event per physical line. +4. Re-encode every committed session-format JSONL fixture by decoding its current records and packing the recovered event list after the unchanged header. This includes parent and child `session*.jsonl` files plus replay and expected-session files whose first record is `session`. The migration must prove exact decoded event equality before and after; it does not call a model or regenerate transcript content. +5. Remove the `packed-chunks.cordis.yml` and replay overlay because packing no longer needs a special composition. Keep the authored `packed-chunks` scenario as the all-row-kinds contract under the ordinary config: it must contain `text-chunks`, `reasoning-chunks`, and `tool-call-chunks`, decode event-for-event equal to its independent source fixture, and re-persist identically through the assembled application. +6. Add an inventory-free check to the keyless snapshot gate that discovers session-format JSONL fixtures by their `session` header, decodes them, and rejects any fixture whose physical records differ from the canonical packed encoding. This covers future scenarios and child logs without a hand-maintained path list. Explicit unpacked and mixed-layout compatibility inputs stay in focused package tests, not the default snapshot corpus. +7. Update the implemented session-persistence and snapshot Agent Notes to distinguish logical events from storage records and to describe packed fixtures as the ordinary layout. Run focused codec and JSONL persistence coverage, every snapshot suite, documentation synchronization, lint, and whitespace validation. + +## Alternatives considered + +**Flip only the backend schema default.** This would change most runtime writes but leave the ACP wrapper's resolved default, TUI's direct serializer, existing fixtures, and future fixture policy inconsistent. A default is credible only when shipping compositions and the tests that represent them share it. + +**Keep snapshots unpacked for readability.** The decoder and normalizer already understand packed rows, and one row retains every chunk boundary and timestamp explicitly. Keeping the largest committed consumer on the legacy layout would make snapshot coverage avoid the shipping write path and preserve the original reason the default stayed off. + +**Remove `packChunks` and always pack.** One canonical writer is simpler, but an explicit unpacked form remains useful for line-oriented diagnostics and for proving mixed-layout compatibility. The pre-release stance permits removing the option later if those concrete uses disappear; changing the default does not require that additional decision. + +**Batch chunks as logical session events.** This would reduce event count rather than only storage envelopes, but it would also delay or reshape live `session/event` delivery, renumber provenance, and require every UI and replay consumer to understand a second streaming unit. The storage codec already obtains the size benefit behind a smaller interface without changing those contracts. + +## Acceptance criteria + +- Omitting `packChunks` writes eligible runs as packed rows in the JSONL backend and every shipping app composition. +- `packChunks: false` still writes one event per line, while both configurations read packed, unpacked, and mixed logs into identical contiguous `SessionEvent[]` values. +- Every committed session-format snapshot fixture is in canonical packed form, and a keyless top-level snapshot check prevents unpacked packable runs from returning. +- ACP, headless, and TUI snapshot recording or refresh preserves the packed layout without changing the decoded event stream, model script, transcript, or expected user output. +- The ordinary packed scenario retains all three row kinds and exact decoded equality with its source fixture without a packing-specific config overlay. +- Current documentation consistently calls packed rows the default physical JSONL layout and preserves the distinction between storage rows and logical `assistant/chunk` events. + +## Risks + +The implementation creates a large fixture diff even though logical behavior is unchanged; reviewers must use decoded equality and the canonical-layout check rather than inspect thousands of mechanical line replacements. Tools that read raw JSONL and assume every post-header line is a `SessionEvent` will encounter storage-row tags more often, although that assumption is already outside the documented format and the repository readers decode rows unconditionally. Packed rows also make a raw file less convenient for per-token line processing; `packChunks: false` remains the deliberate escape hatch. diff --git a/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.zh.md b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.zh.md new file mode 100644 index 0000000000..05909c5f8a --- /dev/null +++ b/.agents/notes/proposed/architecture/2026-07-26-packed-chunk-rows-by-default.zh.md @@ -0,0 +1,56 @@ +# Agent Note: 将打包分片行设为默认 JSONL 布局 + +Status: proposed + +[English](2026-07-26-packed-chunk-rows-by-default.md) | 中文 + +## 问题 + +JSONL 持久化后端可将一段至少包含 3 个连续、同属一个块的 `assistant/chunk` 增量事件,无损替换为一条 `text-chunks`、`reasoning-chunks` 或 `tool-call-chunks` 存储行。加载时,后端会将该存储行展开为完全一致的事件,包括序列号、时间戳和分片边界。因此,该编解码器可减少重复的 JSON 封装,而不会改变作为权威依据的逻辑会话日志。 + +然而,`packChunks` 仍默认为 `false`,`dsh-session-persistence-jsonl` 和 ACP(Agent Client Protocol)演示组合都是如此。选择这一默认值,是为了让首个打包行实现在不重写快照语料库的情况下合入。目前,常规写入路径、大多数测试以及几乎所有签入仓库的会话 fixture(测试前置数据)都会使用体积更大的每事件一行表示,只有一个专用 ACP 场景覆盖打包行为。 + +快照语料库属于默认契约,而非可随意丢弃的测试数据。ACP 和 headless 快照采集物理持久化文件,但 TUI 快照写入器会直接序列化 `Session.events`,绕过后端编码器。因此,仅翻转一个 schema 默认值,会让不同产品和测试层级采用不同的物理布局,后续 fixture 也可能在无人察觉的情况下退回非打包行。 + +本提案仅改变物理存储表示。每个提供方分片仍是一个逻辑 `assistant/chunk` 会话事件,经 `session/event` 实时传递,各自占用一个序列号,并在加载后仍可由 `sourceEventSeqs` 寻址。在 `Session.append()` 之前合并实时事件不在本提案范围内,因为这会改变 UI 流式输出、取消证据、溯源信息以及[会话持久化决策](../../implemented/architecture/2026-06-14-session-persistence.md)确立的回放语义。 + +## 提案 + +打包分片行成为所有 JSONL 写入器、已交付组合、默认路径测试和签入仓库的会话日志 fixture 所采用的默认物理布局。省略 `packChunks` 时,JSONL 后端将其解析为 `true`;ACP 演示的透传配置同样如此;CLI(命令行界面)、TUI、headless 及其他省略该选项的组合会继承后端默认值。 + +`packChunks: false` 继续作为写入侧显式停用选项,用于每事件一行的诊断和兼容性测试。读取仍不受该选项控制且与布局无关,因此现有的打包、非打包和混合日志无需迁移或更改会话格式版本,仍可继续加载。该选项只控制新追加的批次,不会选择读取器模式。 + +打包编解码器仍位于 `dsh-session` 的存储 seam。持久化、fixture 生成器、规范化器和回放读取器共享 `packChunkRuns()` 与 `decodeStorageRecord()`,而不引入仅供快照使用的编码。打包仍以每个持久追加批次为单位,并保留现有的最小连续段长度和精确形态允许列表。 + +## 实施计划 + +1. 将 `SessionPersistenceJsonl.Config.packChunks` 和 ACP 演示包装层的默认值改为 `true`。更新其 JSDoc、双语 README、生成的配置目录,以及每处将打包行称为可选启用项的现状说明。保留显式布尔值,使部署可以请求非打包写入,而无需将这一选择与 `compression: 'none'` 绑定。 +2. 让 JSONL 后端的默认路径测试在不传入 `packChunks: true` 的情况下断言打包输出。保留名称明确且范围聚焦的测试,以覆盖 `packChunks: false`、逐字节相同的非打包写入、混合布局读取、畸形打包行和撕裂尾部。主题与打包无关、关注其他持久化行为的测试省略该标志,从而覆盖实际交付的默认值。 +3. 让每个快照 fixture 生成器都输出相同的物理布局。ACP 和 headless 套件采集后端在原始模式下生成的打包产物。TUI 快照写入器改用共享编解码器,不再直接将 `session.events` 映射为行。为了让 fixture 便于评审,仍需使用原始模式 `compression: 'none'`,但这不再意味着每个逻辑事件对应一条物理行。 +4. 重新编码每个签入仓库的会话格式 JSONL fixture:先解码其当前记录,再在保持 header 不变的前提下打包还原出的事件列表。范围包括父级和子级 `session*.jsonl` 文件,以及首条记录为 `session` 的回放文件和预期会话文件。迁移必须证明前后解码出的事件完全相等;它不会调用模型,也不会重新生成 transcript(文本记录)内容。 +5. 移除 `packed-chunks.cordis.yml` 及其回放 overlay,因为打包不再需要专用组合。保留人工编写的 `packed-chunks` 场景,在普通配置下继续作为覆盖所有行种类的契约:它必须包含 `text-chunks`、`reasoning-chunks` 和 `tool-call-chunks`,解码出的事件必须与其独立源 fixture 逐事件相等,并且通过组装后的应用重新持久化时保持完全一致。 +6. 在无密钥快照门禁中增加一项无需清单的检查:通过 `session` header 发现会话格式 JSONL fixture,解码后拒绝物理记录与规范打包编码不同的任何 fixture。这样无需手工维护路径列表,即可覆盖未来场景和子级日志。显式的非打包与混合布局兼容性输入仍保留在聚焦的包(package)级测试中,不进入默认快照语料库。 +7. 更新已实现的会话持久化与快照 Agent Note(agent 决策记录),区分逻辑事件与存储记录,并说明打包 fixture 是常规布局。运行聚焦的编解码器与 JSONL 持久化覆盖率、全部快照套件、文档同步、lint 和空白校验。 + +## 备选方案 + +**仅翻转后端 schema 默认值。** 这会改变大多数运行时写入,但 ACP 包装层解析后的默认值、TUI 的直接序列化器、现有 fixture 和未来 fixture 政策仍会彼此不一致。只有已交付组合及代表这些组合的测试采用相同默认值时,该默认值才可信。 + +**快照继续使用非打包格式以便阅读。** 解码器和规范化器已经能够理解打包行,而且一条存储行仍会显式保留每个分片边界与时间戳。如果让规模最大的已签入消费方继续使用旧布局,快照覆盖就会绕开已交付的写入路径,也会保留当初未启用该默认值的原因。 + +**删除 `packChunks` 并始终打包。** 只保留一个规范写入器更简单,但显式的非打包形式仍适用于面向行的诊断,也可用于证明混合布局兼容性。预发布立场允许在这些具体用途消失后移除该选项;更改默认值不要求同时作出这一额外决策。 + +**把分片批量合并为逻辑会话事件。** 这会减少事件数量,但也会延迟或重塑 `session/event` 的实时传递,改变溯源信息所引用的序号,并要求每个 UI 和回放消费方理解第二种流式单位。存储编解码器已经通过更窄的接口获得体积收益,无需改变这些契约。 + +## 验收标准 + +- 省略 `packChunks` 时,JSONL 后端和每个已交付应用组合都会将符合条件的连续段写为打包行。 +- `packChunks: false` 仍会按每事件一行的形式写入;无论采用哪种配置,读取打包、非打包和混合日志时,都会得到完全相同且连续的 `SessionEvent[]` 值。 +- 每个签入仓库的会话格式快照 fixture 都采用规范打包形式;一项无密钥顶层快照检查会防止可打包的非打包连续段再次出现。 +- ACP、headless 和 TUI 的快照录制或刷新会保留打包布局,而不会改变解码后的事件流、模型脚本、transcript 或预期用户输出。 +- 普通配置下的打包场景保留全部 3 种行,并在没有打包专用配置 overlay 的情况下,与其源 fixture 保持精确的解码事件相等性。 +- 当前文档统一将打包行称为默认物理 JSONL 布局,并保留存储行与逻辑 `assistant/chunk` 事件之间的区别。 + +## 风险 + +尽管逻辑行为不变,实现仍会产生大规模 fixture diff;评审人必须依据解码后的相等性和规范布局检查进行评审,而不是检查数千处机械行替换。读取原始 JSONL 并假定 header 后每一行都是 `SessionEvent` 的工具,会更频繁地遇到带存储行 tag 的记录;不过,这一假设本就不属于成文格式契约,仓库中的读取器也始终无条件解码记录。打包行还会降低原始文件按 token 逐行处理的便利性;`packChunks: false` 是有意保留的退路。