Skip to content

Commit 81ca1ca

Browse files
author
SqlRush
committed
Persist sidechain runtime metadata
1 parent b0125de commit 81ca1ca

3 files changed

Lines changed: 41 additions & 4 deletions

File tree

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ M6 progress now includes:
238238
- `internal/contracts`/`internal/session`: remote-history `SDKEvent.message` now accepts top-level string and content-block array payloads, wrapping them as message content before event decoding so provider-shaped assistant/user events still materialize into transcript messages (`TestSDKEventUnmarshalAcceptsScalarMessagePayload`, `TestRemoteHistoryTranscriptMessagesAcceptsScalarEventMessagePayload`).
239239
- `internal/contracts`/`internal/session`: remote-history `SDKEvent` status/error/result payloads now accept adjacent field aliases such as `statusMessage`, `progress_message`, `errorMessage`, `failure_reason`, `resultText`, `outputText`, and result object aliases including `output`, `response`, `value`, and `completion`, scoped to the matching canonical event type (`TestSDKEventUnmarshalAcceptsStatusErrorResultAliases`, `TestRemoteHistoryEventsAcceptStatusErrorResultAliases`).
240240
- `internal/contracts`/`internal/session`: remote-history `SDKEvent` type canonicalization now also accepts adjacent stream/provider aliases such as `assistant_delta`, `humanMessage`, `finalResult`, `response.completed`, `failureEvent`, and `statusMessage`, keeping transcript materialization stable across SDK event spellings.
241+
- `internal/session`: sidechain runtime start now persists agent type, worktree path, and task description into the lifecycle transcript payload as well as the metadata sidecar, so resume/list can recover subagent metadata even if the sidecar is missing.
241242
- `internal/memory`: memory age/freshness helpers now match official stale-memory guidance, and document loading can prefix old memory files with a system-reminder that they are point-in-time observations.
242243
- `internal/memory`: relevant-memory attachment primitives now match the official `relevant_memories` shape for stable headers, system-reminder rendering, surfaced path/byte scanning, 200-line/4096-byte surfacing reads with truncation notices, mark-after-filter duplicate attachment handling, last-non-meta-user/single-word/session-byte-cap prefetch gating, top-5 candidate filtering after read-state/surfaced de-dup, and recent successful tools collection excluding pending/failed/same-name-failed tools.
243244
- `internal/conversation`: request building now expands `relevant_memories` attachment messages into user/meta system-reminders before Anthropic API normalization.

internal/session/sidechain_runtime.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,12 @@ func (r SidechainRuntime) Start(options SidechainOptions) (SidechainRun, error)
9090
Subtype: "sidechain_start",
9191
IsSidechain: true,
9292
Content: map[string]any{
93-
"sidechainId": id,
94-
"agentId": id,
95-
"status": run.Status,
96-
"agentType": options.AgentType,
93+
"sidechainId": id,
94+
"agentId": id,
95+
"status": run.Status,
96+
"agentType": options.AgentType,
97+
"worktreePath": options.WorktreePath,
98+
"description": options.Description,
9799
},
98100
}); err != nil {
99101
return SidechainRun{}, err

internal/session/sidechain_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,40 @@ func TestSidechainRuntimeStartAppendFinish(t *testing.T) {
115115
}
116116
}
117117

118+
func TestSidechainRuntimeStartPersistsMetadataInLifecyclePayload(t *testing.T) {
119+
sessionPath := filepath.Join(t.TempDir(), "session.jsonl")
120+
sessionID := contracts.ID("sess_1")
121+
runtime := SidechainRuntime{SessionPath: sessionPath, SessionID: sessionID}
122+
run, err := runtime.Start(SidechainOptions{
123+
ID: "agent/meta",
124+
StartedAt: time.Unix(100, 0).UTC(),
125+
AgentType: "researcher",
126+
WorktreePath: "/tmp/research-worktree",
127+
Description: "research the migration",
128+
})
129+
if err != nil {
130+
t.Fatal(err)
131+
}
132+
if err := os.Remove(run.MetadataPath); err != nil {
133+
t.Fatal(err)
134+
}
135+
136+
state, err := FindSidechainState(sessionPath, sessionID, "agent/meta")
137+
if err != nil {
138+
t.Fatal(err)
139+
}
140+
if state.Status != SidechainStatusRunning {
141+
t.Fatalf("state = %#v", state)
142+
}
143+
if state.Metadata.AgentType != "researcher" || state.Metadata.WorktreePath != "/tmp/research-worktree" || state.Metadata.Description != "research the migration" {
144+
t.Fatalf("metadata recovered from lifecycle payload = %#v", state.Metadata)
145+
}
146+
resumed, ok := ResumeSidechainRunFromState(state)
147+
if !ok || resumed.Metadata.AgentType != "researcher" || resumed.Metadata.WorktreePath != "/tmp/research-worktree" || resumed.Metadata.Description != "research the migration" {
148+
t.Fatalf("resumed = %#v ok=%v", resumed, ok)
149+
}
150+
}
151+
118152
func TestLoadSidechainStateMarksOrphanTranscriptUnknown(t *testing.T) {
119153
sessionPath := filepath.Join(t.TempDir(), "session.jsonl")
120154
sessionID := contracts.ID("sess_1")

0 commit comments

Comments
 (0)