Skip to content

Commit df7031d

Browse files
author
SqlRush
committed
Accept sidechain resource lifecycle attributes
1 parent 4a679ea commit df7031d

5 files changed

Lines changed: 57 additions & 1 deletion

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ M6 补充:microcompact disk cache loader 的 `cached`/`fromCache`/`cacheHit`/`
4242

4343
M6 补充:sidechain/subagent lifecycle state loader 现在接受 `subagent_started``agentStarted``task_failed``sidechainCompleted` 等相邻 subtype aliases,支持 `taskID`/`workerId`/`runId``agentName`/`kind``resultText`/`finalMessage` 等字段,并在 failed/cancelled subtype 没有显式 status 时自动归一状态。
4444

45+
M6 补充:sidechain/subagent lifecycle content 读取现在接受 JSON:API/resource-style `resource`/`attributes`/`properties` wrapper,外层 resource `id` 可作为 sidechain ID fallback,内层 agent metadata、status/outcome 和 summary 字段仍能恢复到 state/list/resume。
46+
4547
M6 补充:transcript resume 的嵌套 content block 现在接受 `toolUseId`/`toolUseID``isError``cacheControl``cacheReference` 字段别名,并保留 cache edit 的 `cacheReference`
4648

4749
M6 补充:transcript resume 的 nested content block `id`/`tool_use_id`/`toolUseId` 现在接受 JSON number,并保留为字符串 tool-use ID。

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ test/parity/ # golden tests against TS/official behavior
184184
- 已落地 JSONL/session 基础、resume/search/title 支撑、prompt history lock/buffered flush/field aliases、official subagent transcript layout、agent metadata sidecar/field aliases、sidechain runtime start/append/finish/cancel/fail 和 parent-chain append/finish 初版、sidechain manager orchestration 初版、sidechain manifest 聚合、sidechain state/list/resume/content-field aliases 初版、sidechain resume context builder、sidechain conversation/content-replacement reconstruction、transcript tail/window/metadata/index loaders、byte-budget transcript tail loader、agent-scoped content replacement metadata/record field-alias loading、session-scoped metadata reappend including AI-title/last-prompt/task-summary、transcript line offset index/window/byte-budget-window/parent-chain/resume/tail/byte-budget-tail loaders、extended transcript metadata entries/type/field aliases、transcript message/session UUID field aliases including top-level `messageUuid`/`messageId`/`id` record IDs and `role`/`entry_type`/`messageType`/`createdAt` timestamp aliases、transcript tombstone metadata delete/relink、transcript resume conversation builder、index 文本预览和 AI-title/last-prompt/task metadata 字段、流式 transcript 搜索、session list pagination、remote history token refresh、remote history 全量分页抓取/page-field/event-list/records/entries/eventList/sessionEvents/last-id/cursor/event-id/has-next alias/wrapped-data/links/paging/bare-array/connection/eventConnection/sessionEventsConnection wrapper response/edge-cursor fallback/max-pages 截断状态/before_id 续抓、remote event transcript materialization/fallback field fill/去重追加/duplicate parent guard、remote history 一步 sync 到 transcript、CLAUDE.md/memdir 扫描、team-memory secret guard、compact runner/boundary plan、conversation auto-compact、失败熔断、microcompact/cache 初版、persistent cached microcompact 初版、cache digest structural/rich-content metadata 覆盖、cache version/TTL/prune、in-memory micro cache prune、memory-cache write-through 到磁盘、atomic cache write、坏缓存默认 fail-open、session memory summary/frontmatter aliases/recall 初版、model-ranked recall session-id selection 和 invalid-selection fallback 初版、recall agent alternate/camel response keys/fenced-prose JSON extraction/scalar id parsing/nested/wrapped/collection-alias selection parsing、resume context model-assisted recall 接入、session memory rollup/prune compaction、rollup archive exclusion/merge、rune-safe rollup truncation、resume context + session memory recall、conversation recall 注入、deterministic/model-backed memory extraction 初版、extraction agent fenced-prose JSON/wrapped facts/alternate/structured field/nested source object/nested response/fact kind alias parsing 和 turn-end memory extraction 落盘。
185185
- 本轮补充:sidechain agent metadata sidecar 读取接受 `type`/`subagentType`/`agentName`/`name``workspacePath`/`workspace`/`path`/`directory``taskDescription`/`prompt`/`input`/`command`/`title` 等字段别名,避免历史或第三方 subagent sidecar 在 resume/list 时丢失 agent 类型、worktree 路径或任务描述。
186186
- 本轮补充:sidechain/subagent lifecycle content 读取会递归解包 `payload`/`data`/`body`/`result`/`response`/`metadata` 等 wrapper,嵌套的 subagent ID、status/outcome、summary、agent type、workspace path 和 task description 都可参与 state/list/resume 恢复。
187+
- 本轮补充:sidechain/subagent lifecycle content 读取现在也接受 JSON:API/resource-style 的 `resource`/`attributes`/`properties` wrapper;外层 resource `id` 可作为 sidechain ID fallback,内层 metadata、status/outcome 和 summary 仍会参与 state/list/resume 恢复。
187188
- 本轮补充:sidechain/subagent lifecycle 的 ID 等 string-like 字段接受 JSON number 和 Go 数字标量,numeric subagent ID 会保留为字符串并可用于 state/list/resume 查找。
188189
- 本轮补充:sidechain/subagent lifecycle subtype 现在接受 `subagent_started``agentStarted``task_failed``sidechainCompleted` 等相邻事件名,并从 `taskID`/`workerId`/`runId``agentName`/`kind``resultText`/`finalMessage` 等字段恢复 state/list/resume 信息;`*_failed`/`*_cancelled` subtype 无显式 status 时会归一到 failed/cancelled。
189190
- 本轮补充:transcript metadata loader 为 file-history snapshot 和 attribution snapshot 建立 `messageId` 索引,并接受 `message_id`/`messageUuid`/`id` 等字段别名,保留 raw list 的同时支持按消息恢复 snapshot。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ M6 progress now includes:
174174
- `internal/session`: sidechain state loading now accepts legacy subagent/agent/task start and finish subtype aliases, broader sidechain/subagent ID and type fields, summary aliases, and common running/completed/cancelled/failed status aliases.
175175
- `internal/session`: sidechain agent metadata sidecar loading now accepts broader agent type, workspace/worktree path, and task-description field aliases such as `subagentType`, `agentName`, `workspacePath`, `taskDescription`, `prompt`, and `title`.
176176
- `internal/session`: sidechain lifecycle content loading now recurses through wrapper objects such as `payload`, `data`, `result`, `response`, and `metadata`, preserving nested subagent IDs, status, summaries, agent type, worktree path, and task descriptions during state/list/resume recovery.
177+
- `internal/session`: sidechain lifecycle content loading now also accepts JSON:API/resource-style `resource`/`attributes`/`properties` wrappers, preserving outer resource IDs as sidechain ID fallback while recovering nested metadata, status, and summary fields.
177178
- `internal/session`: sidechain lifecycle scalar fields now accept JSON number values for IDs and other string-like fields, preserving numeric subagent IDs as strings across state/list/resume recovery.
178179
- `internal/session`: sidechain runtime finish now canonicalizes status aliases such as `success`, `error`, and `canceled` before writing summary records, keeping sidechain and main transcript lifecycle output stable.
179180
- `internal/session`: sidechain runtime now rejects duplicate starts while the same sidechain ID is running, and state loading treats a later start as a fresh lifecycle by clearing stale summary/end metadata.

internal/session/sidechain_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ func firstStringFieldDepth(value any, keys []string, depth int) string {
350350
}
351351
}
352352
}
353-
for _, key := range []string{"payload", "data", "body", "content", "result", "response", "record", "entry", "item", "event", "metadata", "details"} {
353+
for _, key := range []string{"payload", "data", "body", "content", "result", "response", "record", "entry", "item", "event", "resource", "attributes", "properties", "metadata", "details", "value", "output"} {
354354
if raw, ok := fields[key]; ok {
355355
if value := firstStringFieldDepth(raw, keys, depth+1); value != "" {
356356
return value

internal/session/sidechain_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,58 @@ func TestLoadSidechainStateAcceptsWrappedLifecycleContent(t *testing.T) {
355355
}
356356
}
357357

358+
func TestLoadSidechainStateAcceptsResourceLifecycleAttributes(t *testing.T) {
359+
sessionPath := filepath.Join(t.TempDir(), "session.jsonl")
360+
sessionID := contracts.ID("sess_1")
361+
if err := AppendSidechainMessage(sessionPath, sessionID, "resource-fallback", TranscriptMessage{
362+
Type: "system",
363+
UUID: "start_1",
364+
Timestamp: "2026-01-01T00:00:01Z",
365+
Subtype: "sidechain_start",
366+
Content: map[string]any{
367+
"id": "resource_agent",
368+
"type": "sidechain-lifecycle",
369+
"attributes": map[string]any{
370+
"agentName": "architect",
371+
"workspacePath": "/tmp/architect-worktree",
372+
"taskDescription": "design the refactor",
373+
"lifecycleState": "active",
374+
},
375+
},
376+
}); err != nil {
377+
t.Fatal(err)
378+
}
379+
if err := AppendSidechainMessage(sessionPath, sessionID, "resource-fallback", TranscriptMessage{
380+
Type: "system",
381+
UUID: "summary_1",
382+
Timestamp: "2026-01-01T00:00:02Z",
383+
Subtype: "sidechainCompleted",
384+
Content: map[string]any{
385+
"properties": map[string]any{
386+
"outcome": "success",
387+
"finalMessage": "resource lifecycle complete",
388+
},
389+
},
390+
}); err != nil {
391+
t.Fatal(err)
392+
}
393+
394+
states, err := ListSidechainStates(sessionPath, sessionID)
395+
if err != nil {
396+
t.Fatal(err)
397+
}
398+
if len(states) != 1 {
399+
t.Fatalf("states = %#v", states)
400+
}
401+
state := states[0]
402+
if state.ID != "resource_agent" || state.Status != SidechainStatusCompleted || state.Summary != "resource lifecycle complete" {
403+
t.Fatalf("state = %#v", state)
404+
}
405+
if state.Metadata.AgentType != "architect" || state.Metadata.WorktreePath != "/tmp/architect-worktree" || state.Metadata.Description != "design the refactor" {
406+
t.Fatalf("metadata = %#v", state.Metadata)
407+
}
408+
}
409+
358410
func TestLoadSidechainStateAcceptsNumericLifecycleIDs(t *testing.T) {
359411
sessionPath := filepath.Join(t.TempDir(), "session.jsonl")
360412
sessionID := contracts.ID("sess_1")

0 commit comments

Comments
 (0)