docs(session): distinguish converter support from target compatibility

The compatibility obligation belongs to the released target format.
An incoming converter can be fixed or support more historical inputs after
the target ships, as long as its output preserves that target's meaning.
Alpha releases may reject valid historical cases without an implemented,
evidenced mapping instead of expanding the format for speculative inputs.

Record the native-writer oracle, pre-normalization comparison, supported
flat V4 tool-result representation, uniform plugin naming, owner collision
rules, retained source files and read-only private corpus handling in the
migration cookbook. Align the existing architecture note with this rule;
keep native target changes and source conversion fixes distinct.

This updates contributor guidance only. It does not change finalization
checkpoint machinery, existing V4 schemas, or any released codec.
This commit is contained in:
Tianyi Cui committed 2026-09-19 17:20:53 +08:00
1 parent 1e7686b2c9
commit e90c55e0ab
6 files changed
+20 -8

No files matched your search

@@ -2,5 +2,5 @@
# 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 .agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.md
2026-08-31-released-session-format-migrations.md: 754ffae3a7f4bf58d25fe2fc7174593bd01042d5
2026-08-31-released-session-format-migrations.zh.md: 7de89ddf32da3bcbfc25df19d37d67a322028c85
2026-08-31-released-session-format-migrations.md: a25b8f76b2ce5bff9d5d431ceec66b4b9b65631b
2026-08-31-released-session-format-migrations.zh.md: 7201d953f3f3e1a1e2497fcd32416d7bb3a11801
@@ -79,7 +79,7 @@ Preset renames cover the creation header and every selection event because the l
A source inherited count can be unknown before EOF: V2 derives it from seed markers, and V1→V2 can change cardinality. The chain passes that absence to the next stage instead of fabricating a count. The [V2-to-V3 inheritance rules](../../../../packages/session/session-format-v2-to-v3/README.md#sequence-references) support this case; older stages that require a header-supplied count still refuse when it is absent. This permits seeded multi-hop restoration without retaining an intermediate artifact array.
The [version and release-status reference](../../../../docs/session-format-status.md) owns the published-format record and identifies the code’s writer authority. Released formats retain their semantics; committed generations remain byte-preserved during migration. A subsequent structural change requires the next adjacent edge under the [versioning rule](2026-08-10-session-log-version-mechanism.md), not an amendment to a released conversion. Ordinary event additions follow that rule’s required-event refusal mechanism rather than automatically allocating a version. A current-format file does not rerun its incoming migration; integration tests use isolated disposable homes and unchanged historical inputs.
The [version and release-status reference](../../../../docs/session-format-status.md) owns the published-format record and identifies the code’s writer authority. Released formats retain their semantics; committed generations remain byte-preserved during migration. A breaking change to the released target format requires the next adjacent edge under the [versioning rule](2026-08-10-session-log-version-mechanism.md). Incoming converters may be corrected or accept additional historical cases while preserving that target format; alpha converters can refuse cases without an implemented mapping. Ordinary event additions follow that rule’s required-event refusal mechanism rather than automatically allocating a version. A current-format file does not rerun its incoming migration; integration tests use isolated disposable homes and unchanged historical inputs.
The [committed-corpus inventory](../../../../packages/test-support/llm-replay/tests/session-format-corpus-inventory.ts) identifies deliberately unsupported historical conversions by source path, generation, and exact refusal reason. Retaining those artifacts must not force chronology-changing migration or permit a blanket skip: every listed artifact must still raise the typed migration refusal, and unlisted artifacts must restore. Native current-generation fixtures cannot be classified as unsupported, because they do not traverse an incoming edge. Headerless test-harness protocol examples remain a separate explicit class. The corpus test checks source bytes after both successful and refused restoration; it does not rewrite historical evidence to satisfy the current reader.
@@ -79,7 +79,7 @@ Chain 中不存在 `flatMap`、spread expansion、中间 event array 或 schedul
源继承数量在 EOF 前可能未知:V2 从种子标记推导它,而 V1→V2 可以改变事件数量。迁移链将这种缺失传递给下一个 Stage,而不伪造数量。[V2 到 V3 继承规则](../../../../packages/session/session-format-v2-to-v3/README.zh.md#sequence-references)支持此情况;需要 header 提供数量的旧 Stage 仍在数量缺失时拒绝。这使有种子的多跳恢复无需保留中间产物数组。
[版本与发布状态参考](../../../../docs/session-format-status.zh.md)拥有已发布格式记录,并指明代码中的写入器真源。已发布格式保留其语义;迁移期间已提交代际的字节保持不变。后续结构性变更必须按[版本规则](2026-08-10-session-log-version-mechanism.zh.md)添加下一条相邻迁移边,而非修改已发布转换。普通事件新增遵循该规则的必需事件拒绝机制,而非自动分配版本。当前格式文件不会重新执行入边迁移;集成测试使用隔离、可丢弃的 home 和未变更的历史输入。
[版本与发布状态参考](../../../../docs/session-format-status.zh.md)拥有已发布格式记录,并指明代码中的写入器真源。已发布格式保留其语义;迁移期间已提交代际的字节保持不变。对已发布目标格式的破坏性变更必须按[版本规则](2026-08-10-session-log-version-mechanism.zh.md)添加下一条相邻迁移边。传入转换器可以修复或增加历史场景支持,同时保持目标格式兼容;alpha 转换器可以拒绝尚无已实现映射的场景。普通事件新增遵循该规则的必需事件拒绝机制,而非自动分配版本。当前格式文件不会重新执行入边迁移;集成测试使用隔离、可丢弃的 home 和未变更的历史输入。
[已提交语料清单](../../../../packages/test-support/llm-replay/tests/session-format-corpus-inventory.ts) 按源路径、代际与精确拒绝原因标识有意不支持的历史转换。保留这些产物不能迫使迁移改变时序,也不能允许统一跳过:每个清单中的产物仍必须抛出类型化迁移拒绝,未列入的产物必须还原。原生当前代际 fixture 不经过入边,因此不能被归为不支持。没有版本 header 的测试框架协议示例保持为独立的显式类别。语料测试在还原成功和拒绝后都检查源字节;它不通过改写历史证据来满足当前 reader。
@@ -2,5 +2,5 @@
# 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 docs/cookbook/adding-a-session-format-version.md
adding-a-session-format-version.md: 472bc944261474e913abecff302fcd5cef474d14
adding-a-session-format-version.zh.md: fe4a97fa3f90027c7914aaa575866bedb4e3a483
adding-a-session-format-version.md: 19604237a0c1374fe97924c81b570084de1aadbd
adding-a-session-format-version.zh.md: 738ddaa39231d03af6305a1fd1266612a82f7f13
@@ -25,7 +25,7 @@ Let N be the accepted or published version in [version/status](../session-format
Use a shared `release/*` integration base for N+1. The base change adds the writer, codec, catalog wiring, identity migration, and verification. Create each independent child branch from that base and target its PR at the release branch, not another independent child’s branch. Each child adds its structural transformation, validators, consumers, and tests to the same adjacent migration package. Do not allocate extra versions just to represent review order. Merge reviewed children into the release branch through PRs, then validate the combined result before release. Honor release-branch force-push and deletion protections; do not force-sync it.
Released codecs and accepted migration meanings remain stable. An unaccepted N→N+1 transition may combine coordinated breaking changes. Once accepted, preserve that transition: later compatible evolution uses new same-version records, while new breaking changes require the next edge and their own header increase.
Released codecs and accepted target-format meanings remain stable. The N→N+1 converter may receive fixes or support additional historical cases after N+1 ships, without a version bump, if its output remains compatible with N+1. Changing the established target representation incompatibly requires the next version; improving the incoming conversion does not.
Use disposable, isolated Harness homes while N+1 remains open for integration. An interim N+1 file already has the target writer version, so a later edit to N→N+1 will not migrate that file again. Re-run from unchanged historical input in a fresh test home; never repair this by rewriting a committed generation or reusing a real user's home.
@@ -57,6 +57,12 @@ Treat the inherited cut as a logical event count, not a physical row count. Expo
Define the new edge's event admission and transformation rules explicitly. The [V2-to-V3 source audit](../../packages/session/session-format-v2-to-v3/README.md#source-audit) and [alpha V0→V1 rule](../../.agents/notes/implemented/architecture/2026-08-31-alpha-historical-unknown-event-refusal.md) own the policies of those released edges, not the new edge. Do not generalize either to every edge. A change to structure or event positions requires classifying source events, payload members, and references, and explicitly deciding whether opaque data can remain valid. [Equal-version retention](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.md) alone does not prove a structural transformation safe. Validate target semantics and give each newly accepted case a rejecting counterexample; never widen older edges to hide an unsupported transformation.
Use native writer output as the conversion oracle: replay the same recorded LLM messages, tool outputs, and other inputs through the old and target writers. Compare migrated old output with raw target output before any format-normalizing helper; mask only documented volatile fields. For V4, ordinary tool results become flat tool-role messages with ordinary content blocks. A theoretically permitted old structure alone does not justify a new core content type.
Alpha converters may explicitly refuse historical cases without an implemented, evidenced mapping, including otherwise valid logs. Keep the source unchanged and publish no partial successor. Prefer real corpora and reproducible writers when extending support; inspect user corpora read-only and create sanitized regression fixtures.
Use the `plugin:` convention for historical unknown identifiers and conflicting extension-field names. Preserve complete original names and payloads, distinguish source and field owners to prevent collisions, and leave known core identities and opaque nested data alone. Document each exact mapping in the edge README.
Maintain the new edge's README as its single complete transformation and admission catalog, following the [V2-to-V3 specification](../../packages/session/session-format-v2-to-v3/README.md#v2-to-v3-specification). Enumerate every converted header, event, message slot, and payload field; exact rename or collision rules; preserved ids, coordinates, references, and inherited cuts; external prerequisites; refusal and opaque-data policies; and unsupported or deferred behavior. Separate historical source conversion from native target admission, and state which codec, restorer, installed validator, and recovery policy performs each check. Record any validation that is deliberately narrower than the declared schema. Update this catalog in the same change as its code; a generated schema inventory or PR description does not replace it.
Prove strict restoration through `sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation: 'current' })`, feeding rows in order and calling `finish()`. This exercises physical decoding, the complete chain, and installed current Session validation. Production's recoverable/transformed policy is not a replacement for strict fixture and publication verification. Preserve documented historical validation exceptions rather than claiming stricter source validation than the edge actually performs.
@@ -25,7 +25,7 @@
为 N+1 使用共享的 `release/*` 集成基线。基线变更添加写入器、codec、catalog 接线、恒等迁移与验证。从该基线创建各个独立子分支,并将其 PR(Pull Request)的目标设为发布分支,而非另一个独立子分支。每个子分支在同一个相邻迁移包内添加自身的结构变换、校验器、消费方和测试。不要只为表示评审顺序而分配额外版本。通过 PR 将评审后的子分支合入发布分支,并在发布前验证组合结果。遵守发布分支的强制推送与删除保护;不要强制同步该分支。
已发布 codec 与已接受迁移的含义保持稳定。尚未接受的 N→N+1 转换可以合并协同的破坏性变更。接受后须保留该转换:后续兼容演进使用新的同版本记录,新的破坏性变更则要求下一条迁移边及自身的头版本递增。
已发布 codec 和已接受的目标格式含义保持稳定。N+1 发布后,N→N+1 转换器仍可修复或增加历史场景支持;只要输出仍兼容 N+1,就不必提高版本。不兼容地改变既定目标表示需要下一版本,改进传入转换本身不需要。
N+1 尚处于开放集成阶段时,应使用可丢弃、相互隔离的 Harness home。中间版本产生的 N+1 文件已标为目标写入器版本,因此后续对 N→N+1 的修改不会再次迁移该文件。请在全新测试 home 中从未变更的历史输入重新运行;绝不通过改写已提交代际或复用真实用户 home 来修复这个问题。
@@ -57,6 +57,12 @@ pnpm run gen-session-format-catalog
显式定义新迁移边的事件准入与变换规则。[V2 到 V3 源审计](../../packages/session/session-format-v2-to-v3/README.zh.md#source-audit)和 [Alpha V0→V1 规则](../../.agents/notes/implemented/architecture/2026-08-31-alpha-historical-unknown-event-refusal.zh.md)分别负责对应已发布迁移边的策略,而非新迁移边的策略。不要将任一策略推广到所有迁移边。结构或事件位置变化时,必须分类源事件、载荷成员与引用,并显式判断不透明数据能否保持有效。[同版本保留](../../.agents/notes/implemented/architecture/2026-08-30-retain-ignorable-external-session-events.zh.md)本身不能证明结构变换安全。校验目标语义,并为每个新增可接受案例提供一个被拒绝的反例;绝不放宽旧迁移边来掩盖不受支持的转换。
以原生 writer 输出作为转换对照:用相同录制的 LLM 消息、工具输出和其他输入回放旧 writer 与目标 writer。任何格式归一化 helper 运行前,比较迁移后的旧输出与目标原始输出,只屏蔽已说明的易变字段。V4 的普通工具结果变为包含普通内容块的平铺 tool-role 消息。旧类型理论上允许某种结构,本身不足以支持新增核心内容类型。
Alpha 转换器可以明确拒绝尚无已实现、有证据映射的历史场景,包括原本有效的日志。源文件保持不变,且不发布部分 successor。扩展支持时优先使用真实语料和可复现 writer;只读检查用户语料,并创建脱敏回归 fixture。
历史未知标识符和冲突的扩展字段名使用 `plugin:` 约定。保留完整原名称与载荷,区分来源及字段 owner 以防冲突;不改变已知核心身份或不透明嵌套数据。每个精确映射写入迁移边 README。
参照 [V2 到 V3 规范](../../packages/session/session-format-v2-to-v3/README.zh.md#v2-to-v3-specification),将新迁移边的 README 维护为唯一完整的转换与接纳目录。枚举每个被转换的 header、事件、消息槽和载荷字段,精确重命名或冲突规则,保留的 id、坐标、引用与继承截点,外部前置证据,拒绝与不透明数据策略,以及不支持或延后的行为。将历史源转换与原生目标接纳分开,说明每项检查由哪个 codec、restorer、已安装校验器和恢复策略执行。记录刻意窄于声明 schema 的校验范围。代码变更必须同时更新此目录;生成的 schema 库存或 PR 描述不能替代它。
通过 `sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation: 'current' })` 验证严格恢复,按顺序传入各行并调用 `finish()`。这会执行物理解码、完整迁移链与已安装当前 Session 校验。生产环境的 recoverable/transformed 策略不能替代 fixture(测试前置数据)和发布验证所需的严格校验。保留已记录的历史校验例外,不要宣称源校验比迁移边实际执行的更严格。