Skip to content

Commit 64e2585

Browse files
author
SqlRush
committed
Accept tombstone id aliases
1 parent 4bca9bc commit 64e2585

5 files changed

Lines changed: 23 additions & 3 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ M6 补充:嵌套 contract message 现在接受 `messageId`/`messageID`/`messag
4040

4141
M6 补充:基础 `SessionEntry` JSONL loader 现在接受 `role`/`entryType`/`messageType`、message ID/UUID、parent ID/UUID 和 `sessionID`/`session`/session UUID 别名,旧 entry 文件可通过 `session.Load` 保留类型、parent 和 session。
4242

43+
M6 补充:tombstone metadata target/parent 现在接受 `targetId`/`deletedId`/`messageId``parentId`/`parentMessageId` 系列 ID/UUID 别名,删除/重连 replay 不会因旧字段拼写丢失 tombstone 目标或 parent。
44+
4345
M6 补充:remote history GraphQL/connection 分页现在接受 `hasPrevious`/`hasPreviousPage``hasOlder`/`more` 继续分页标记,以及 `previousCursor`/`prevCursor`/`beforeCursor`/`olderCursor` 等 before-id cursor 别名,避免只返回第一页历史。
4446

4547
M6 补充:remote history response parser 现在会递归解包 `data.session.events``data.projectSession.eventConnection``conversation``remoteHistory` 等 GraphQL/session wrapper,继续复用 `nodes`/`edges[].node``pageInfo` pagination 解析。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ test/parity/ # golden tests against TS/official behavior
199199
- 本轮补充:嵌套 contract message 接受 `parentUUID``parentId`/`parentID`/`parent_id``parentMessageId`/`parentMessageID`/`parent_message_id` 和 parent-message UUID 别名,transcript/remote history payload 自带 parent alias 时不会丢失嵌套 parent。
200200
- 本轮补充:嵌套 contract message 接受 `messageId`/`messageID`/`message_id``messageUuid`/`messageUUID`/`message_uuid` 作为自身 ID/UUID 别名,indexed resume 会保留 payload 自带的 nested message id。
201201
- 本轮补充:基础 `SessionEntry` JSONL loader 接受 `role`/`entryType`/`messageType`、message ID/UUID、parent ID/UUID 和 `sessionID`/`session`/session UUID 别名,旧 entry 文件可通过 `session.Load` 保留类型、parent 和 session。
202+
- 本轮补充:tombstone metadata target/parent 加宽到 `targetId`/`deletedId`/`messageId``parentId`/`parentMessageId` ID/UUID 别名,delete/relink replay 可兼容旧字段拼写。
202203
- 本轮补充:remote history `SDKEvent` 解码接受顶层 `sessionID` 作为事件 session id 别名,materialize transcript 时会填充 record 与嵌套 message 的 session id(覆盖测试:`TestRemoteHistoryTranscriptMessagesAcceptsSessionIDUpperAlias`)。
203204
- 本轮补充:remote history `SDKEvent` 解码接受 `parentUUID``parentId`/`parentID`/`parent_id``parentMessageId`/`parentMessageID`/`parent_message_id` parent aliases,materialize transcript 时会保留 parent chain(覆盖测试:`TestRemoteHistoryTranscriptMessagesAcceptsParentIDAliases`)。
204205
- 本轮补充:memory 层补齐官方 `memoryAge`/freshness note 语义,`ReadDocumentsWithOptions` 可为超过 1 天的 memory 文档前缀 system-reminder,提示模型把 memory 当作 point-in-time observation 并核对当前代码。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ M6 progress now includes:
133133
- `internal/contracts`/`internal/session`: nested contract messages now accept parent aliases such as `parentUUID`, `parentId`/`parentID`/`parent_id`, `parentMessageId`/`parentMessageID`/`parent_message_id`, and parent-message UUID aliases, preserving payload-supplied parents in transcript and remote-history resume paths.
134134
- `internal/contracts`/`internal/session`: nested contract messages now accept `messageId`/`messageID`/`message_id` and `messageUuid`/`messageUUID`/`message_uuid` as their own ID/UUID aliases, preserving payload-supplied nested message IDs in indexed resume output.
135135
- `internal/contracts`/`internal/session`: the base `SessionEntry` JSONL loader now accepts role/type, message ID/UUID, parent ID/UUID, and session ID/UUID aliases so older entry files keep type, parent, and session metadata when loaded through `session.Load`.
136+
- `internal/session`: tombstone metadata target/parent loading now accepts target/deleted/message ID aliases and parent ID/message ID aliases, preserving tombstone delete/relink replay across older field spellings.
136137
- `internal/session`: remote-history parsing now also accepts connection-style `history`/`messages` wrappers with `nodes`/`edges[].node` event lists and `pageInfo`/`page_info` `hasNextPage`/`endCursor`/`startCursor` pagination aliases.
137138
- `internal/session`: remote-history connection edges now use `edges[].cursor` as the event cursor when the nested node lacks an event ID, preserving pagination even when pageInfo omits a cursor.
138139
- `internal/session`: remote-history parsing now also accepts `eventList`/`event_list`, `sessionEvents`/`session_events`, and connection aliases such as `connection`, `eventConnection`, and `sessionEventsConnection`.

internal/session/transcript_metadata_fields.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,25 @@ func parseTombstoneMetadata(line []byte) (TombstoneEntry, bool) {
186186
return TombstoneEntry{}, false
187187
}
188188
if entry.TargetUUID == "" {
189-
entry.TargetUUID = fields.idValue("targetUuid", "targetUUID", "target_uuid", "deletedUuid", "deletedUUID", "deleted_uuid", "messageUuid", "messageUUID", "message_uuid")
189+
entry.TargetUUID = fields.idValue(
190+
"targetUuid", "targetUUID", "target_uuid",
191+
"targetId", "targetID", "target_id",
192+
"deletedUuid", "deletedUUID", "deleted_uuid",
193+
"deletedId", "deletedID", "deleted_id",
194+
"messageUuid", "messageUUID", "message_uuid",
195+
"messageId", "messageID", "message_id",
196+
)
190197
}
191198
if entry.TargetUUID == "" {
192199
entry.TargetUUID = entry.UUID
193200
}
194201
if entry.ParentUUID == nil {
195-
if parent := fields.idValue("parentUuid", "parentUUID", "parent_uuid"); parent != "" {
202+
if parent := fields.idValue(
203+
"parentUuid", "parentUUID", "parent_uuid",
204+
"parentId", "parentID", "parent_id",
205+
"parentMessageUuid", "parentMessageUUID", "parent_message_uuid",
206+
"parentMessageId", "parentMessageID", "parent_message_id",
207+
); parent != "" {
196208
entry.ParentUUID = &parent
197209
}
198210
}

internal/session/transcript_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ func TestLoadTranscriptAppliesTombstonesAndRelinks(t *testing.T) {
403403
`{"type":"tombstone","targetUuid":"a1","parentUuid":"u1","sessionId":"s1","reason":"deleted","timestamp":"2026-01-01T00:00:03Z"}`,
404404
`{"type":"tombstone","deleted_uuid":"ghost","parent_uuid":"u2","session_uuid":"s2","deleted_reason":"alias deleted"}`,
405405
`{"type":"tombstone","uuid":"legacy","parent_uuid":"u2","sessionId":"s3","reason":"legacy fallback"}`,
406+
`{"type":"tombstone","targetID":"old","parentMessageID":"u1","sessionID":"s4","deletedReason":"id alias deleted"}`,
406407
})
407408

408409
transcript, err := LoadTranscript(path)
@@ -428,12 +429,15 @@ func TestLoadTranscriptAppliesTombstonesAndRelinks(t *testing.T) {
428429
if entry := transcript.Tombstones["legacy"]; entry.SessionID != "s3" || entry.Reason != "legacy fallback" || entry.TargetUUID != "legacy" {
429430
t.Fatalf("legacy tombstone entry = %#v", entry)
430431
}
432+
if entry := transcript.Tombstones["old"]; entry.SessionID != "s4" || entry.Reason != "id alias deleted" || entry.ParentUUID == nil || *entry.ParentUUID != "u1" {
433+
t.Fatalf("id aliased tombstone entry = %#v", entry)
434+
}
431435

432436
metadata, err := LoadTranscriptMetadata(path)
433437
if err != nil {
434438
t.Fatal(err)
435439
}
436-
if metadata.Tombstones["a1"].Reason != "deleted" || metadata.Tombstones["ghost"].SessionID != "s2" || metadata.Tombstones["legacy"].TargetUUID != "legacy" {
440+
if metadata.Tombstones["a1"].Reason != "deleted" || metadata.Tombstones["ghost"].SessionID != "s2" || metadata.Tombstones["legacy"].TargetUUID != "legacy" || metadata.Tombstones["old"].ParentUUID == nil || *metadata.Tombstones["old"].ParentUUID != "u1" {
437441
t.Fatalf("metadata tombstones = %#v", metadata.Tombstones)
438442
}
439443
}

0 commit comments

Comments
 (0)