Skip to content

Commit 493bfd6

Browse files
author
SqlRush
committed
Accept compact sidechain lifecycle statuses
1 parent df7031d commit 493bfd6

5 files changed

Lines changed: 45 additions & 10 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ M6 补充:sidechain/subagent lifecycle state loader 现在接受 `subagent_sta
4444

4545
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。
4646

47+
M6 补充:sidechain/subagent lifecycle status 归一化现在接受 compact/camel aliases,包括 `inProgress``completedSuccessfully``cancelledByUser`/`canceledByUser``failedError`/`failedWithError``timedOut`,并保持 transcript/runtime 输出为 canonical running/completed/cancelled/failed。
48+
4749
M6 补充:transcript resume 的嵌套 content block 现在接受 `toolUseId`/`toolUseID``isError``cacheControl``cacheReference` 字段别名,并保留 cache edit 的 `cacheReference`
4850

4951
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
@@ -187,6 +187,7 @@ test/parity/ # golden tests against TS/official behavior
187187
- 本轮补充:sidechain/subagent lifecycle content 读取现在也接受 JSON:API/resource-style 的 `resource`/`attributes`/`properties` wrapper;外层 resource `id` 可作为 sidechain ID fallback,内层 metadata、status/outcome 和 summary 仍会参与 state/list/resume 恢复。
188188
- 本轮补充:sidechain/subagent lifecycle 的 ID 等 string-like 字段接受 JSON number 和 Go 数字标量,numeric subagent ID 会保留为字符串并可用于 state/list/resume 查找。
189189
- 本轮补充: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。
190+
- 本轮补充:sidechain/subagent lifecycle status 归一化现在接受 compact/camel aliases,包括 `inProgress``completedSuccessfully``cancelledByUser`/`canceledByUser``failedError`/`failedWithError``timedOut`,并继续写回 canonical running/completed/cancelled/failed 状态。
190191
- 本轮补充:transcript metadata loader 为 file-history snapshot 和 attribution snapshot 建立 `messageId` 索引,并接受 `message_id`/`messageUuid`/`id` 等字段别名,保留 raw list 的同时支持按消息恢复 snapshot。
191192
- 本轮补充:transcript message/index/session list 读取 `gitBranch`,兼容 `git_branch`/`branch` 别名,并让 session search 可以按分支名命中,贴近官方 lite metadata 的 branch 展示和检索行为。
192193
- 本轮补充:full transcript `TitleFromTranscript` 的标题优先级和 indexed/lite 路径对齐,按 custom title、AI title、首个用户 prompt、last-prompt、summary 顺序兜底。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ M6 progress now includes:
179179
- `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.
180180
- `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.
181181
- `internal/session`: sidechain lifecycle state loading now accepts adjacent subtype aliases such as `subagent_started`, `agentStarted`, `task_failed`, and `sidechainCompleted`, with broader task/worker/run ID fields plus `resultText`/`finalMessage` summaries and subtype-derived failed/cancelled defaults.
182+
- `internal/session`: sidechain lifecycle status normalization now accepts compact/camel aliases such as `inProgress`, `completedSuccessfully`, `cancelledByUser`/`canceledByUser`, `failedError`/`failedWithError`, and `timedOut`, while still writing canonical running/completed/cancelled/failed states.
182183
- `internal/session`: transcript metadata loading now indexes file-history and attribution snapshots by message ID, with aliases such as `message_id`, `messageUuid`, and `id`, while preserving raw snapshot lists.
183184
- `internal/session`: transcript indexes and session search now recover message `gitBranch` values, accept `git_branch`/`branch` aliases, and can match sessions by branch name.
184185
- `internal/session`: full transcript title derivation now matches indexed/lite fallback order: custom title, AI title, first user prompt, last-prompt metadata, then summary.

internal/session/sidechain_status.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,33 @@ func sidechainStatusField(value any, keys ...string) string {
238238
}
239239

240240
func normalizeSidechainStatus(status string) string {
241-
switch strings.ToLower(strings.TrimSpace(status)) {
241+
status = strings.TrimSpace(status)
242+
switch normalizeSidechainStatusAlias(status) {
242243
case "":
243244
return ""
244-
case SidechainStatusRunning, "started", "active", "in_progress", "in-progress", "pending":
245+
case SidechainStatusRunning, "started", "active", "inprogress", "pending", "queued":
245246
return SidechainStatusRunning
246-
case SidechainStatusCompleted, "complete", "completed_successfully", "success", "succeeded", "done", "ok":
247+
case SidechainStatusCompleted, "complete", "completedsuccessfully", "success", "successful", "succeeded", "done", "ok":
247248
return SidechainStatusCompleted
248-
case SidechainStatusCancelled, "canceled", "cancel", "cancelled_by_user", "canceled_by_user", "aborted", "stopped":
249+
case SidechainStatusCancelled, "canceled", "cancel", "cancelledbyuser", "canceledbyuser", "usercancelled", "usercanceled", "aborted", "stopped":
249250
return SidechainStatusCancelled
250-
case SidechainStatusFailed, "failure", "error", "errored", "failed_error":
251+
case SidechainStatusFailed, "failure", "error", "errored", "failederror", "failedwitherror", "timeout", "timedout":
251252
return SidechainStatusFailed
252253
default:
253-
return strings.TrimSpace(status)
254+
return status
254255
}
255256
}
256257

258+
func normalizeSidechainStatusAlias(status string) string {
259+
var builder strings.Builder
260+
for _, r := range strings.TrimSpace(status) {
261+
if unicode.IsLetter(r) || unicode.IsDigit(r) {
262+
builder.WriteRune(unicode.ToLower(r))
263+
}
264+
}
265+
return builder.String()
266+
}
267+
257268
func ListSidechainStates(sessionPath string, sessionID contracts.ID) ([]SidechainState, error) {
258269
infos, err := ListSidechainTranscripts(sessionPath, sessionID)
259270
if err != nil {

internal/session/sidechain_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func TestLoadSidechainStateAcceptsSubtypeAndStatusAliases(t *testing.T) {
189189
Content: map[string]any{
190190
"subagent_id": "subagent_42",
191191
"subagentType": "explorer",
192-
"lifecycleState": "active",
192+
"lifecycleState": "inProgress",
193193
},
194194
}); err != nil {
195195
t.Fatal(err)
@@ -201,7 +201,7 @@ func TestLoadSidechainStateAcceptsSubtypeAndStatusAliases(t *testing.T) {
201201
Subtype: "agent_finish",
202202
Content: map[string]any{
203203
"agentID": "subagent_42",
204-
"outcome": "success",
204+
"outcome": "completedSuccessfully",
205205
"finalSummary": "found the issue",
206206
},
207207
}); err != nil {
@@ -221,6 +221,26 @@ func TestLoadSidechainStateAcceptsSubtypeAndStatusAliases(t *testing.T) {
221221
}
222222
}
223223

224+
func TestNormalizeSidechainStatusAcceptsCompactAliases(t *testing.T) {
225+
cases := map[string]string{
226+
"inProgress": SidechainStatusRunning,
227+
"in-progress": SidechainStatusRunning,
228+
"completedSuccessfully": SidechainStatusCompleted,
229+
"successful": SidechainStatusCompleted,
230+
"cancelledByUser": SidechainStatusCancelled,
231+
"canceledByUser": SidechainStatusCancelled,
232+
"failedError": SidechainStatusFailed,
233+
"failed_with_error": SidechainStatusFailed,
234+
"timedOut": SidechainStatusFailed,
235+
"custom_state": "custom_state",
236+
}
237+
for input, want := range cases {
238+
if got := normalizeSidechainStatus(input); got != want {
239+
t.Fatalf("normalizeSidechainStatus(%q) = %q, want %q", input, got, want)
240+
}
241+
}
242+
}
243+
224244
func TestLoadSidechainStateAcceptsAdjacentLifecycleSubtypeAliases(t *testing.T) {
225245
sessionPath := filepath.Join(t.TempDir(), "session.jsonl")
226246
sessionID := contracts.ID("sess_1")
@@ -631,7 +651,7 @@ func TestSidechainFinishNormalizesStatusAliases(t *testing.T) {
631651
if _, err := manager.Start(SidechainOptions{ID: "alias-success", StartedAt: time.Unix(100, 0).UTC()}); err != nil {
632652
t.Fatal(err)
633653
}
634-
summary, err := manager.Finish("alias-success", "success", "done", time.Unix(110, 0).UTC())
654+
summary, err := manager.Finish("alias-success", "completedSuccessfully", "done", time.Unix(110, 0).UTC())
635655
if err != nil {
636656
t.Fatal(err)
637657
}
@@ -649,7 +669,7 @@ func TestSidechainFinishNormalizesStatusAliases(t *testing.T) {
649669
if _, err := manager.Start(SidechainOptions{ID: "alias-error", StartedAt: time.Unix(120, 0).UTC()}); err != nil {
650670
t.Fatal(err)
651671
}
652-
failed, err := manager.Finish("alias-error", "error", "boom", time.Unix(130, 0).UTC())
672+
failed, err := manager.Finish("alias-error", "failedError", "boom", time.Unix(130, 0).UTC())
653673
if err != nil {
654674
t.Fatal(err)
655675
}

0 commit comments

Comments
 (0)