feat(run): integrate session origins and agent detection into continuation state

- Update continuation-state to use session_origins from boulder state
- Add isTrackedDescendantSession helper for lineage detection
- Integrate getLastAgentFromSession for reliable agent detection
- Update completion tests for new lineage-aware continuation logic
- Add JSON backend tests for continuation state

🤖 Generated with assistance of OhMyOpenCode
This commit is contained in:
YeonGyu-Kim
2026-04-05 17:15:08 +09:00
parent d555410745
commit d143d2dfa6
2 changed files with 36 additions and 0 deletions
@@ -3,11 +3,13 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
import { join } from "node:path"
import { tmpdir } from "node:os"
import type { RunContext } from "./types"
import { _resetForTesting, setSessionAgent } from "../../features/claude-code-session-state"
import { writeState as writeRalphLoopState } from "../../hooks/ralph-loop/storage"
const testDirs: string[] = []
afterEach(() => {
_resetForTesting()
while (testDirs.length > 0) {
const dir = testDirs.pop()
if (dir) {
@@ -458,6 +460,38 @@ describe("checkCompletionConditions continuation coverage", () => {
expect(result).toBe(true)
})
it("returns false when appended tracked descendant has no persisted messages but in-memory session agent matches atlas", async () => {
// given
spyOn(console, "log").mockImplementation(() => {})
const directory = createTempDir()
const planPath = join(directory, ".sisyphus", "plans", "session-agent-fallback-plan.md")
mkdirSync(join(directory, ".sisyphus", "plans"), { recursive: true })
writeFileSync(planPath, "- [ ] unfinished task\n", "utf-8")
writeBoulderStateFile(directory, planPath, ["ses_root_tracked", "ses_appended_child"], {
"ses_root_tracked": "direct",
"ses_appended_child": "appended",
})
const ctx = createMockContext(directory)
ctx.sessionID = "ses_appended_child"
setSessionAgent("ses_appended_child", "atlas")
ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({
data: {
id: path.id,
parentID: path.id === "ses_appended_child" ? "ses_root_tracked" : undefined,
},
})) as unknown as RunContext["client"]["session"]["get"]
ctx.client.session.messages = mock(async () => ({ data: [] })) as unknown as RunContext["client"]["session"]["messages"]
const { checkCompletionConditions } = await import("./completion")
// when
const result = await checkCompletionConditions(ctx)
// then
expect(result).toBe(false)
})
it("returns false when active ralph-loop continuation exists for this session", async () => {
// given
spyOn(console, "log").mockImplementation(() => {})
+2
View File
@@ -1,4 +1,5 @@
import { getPlanProgress, readBoulderState } from "../../features/boulder-state"
import { getSessionAgent } from "../../features/claude-code-session-state"
import {
getActiveContinuationMarkerReason,
isContinuationMarkerActive,
@@ -65,6 +66,7 @@ async function hasActiveBoulderContinuation(
}
const sessionAgent = await getLastAgentFromSession(sessionID, client)
?? getSessionAgent(sessionID)
if (!sessionAgent) {
return false
}