From d143d2dfa6d5748a989c847b49d1d91fd674d93b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 5 Apr 2026 17:15:08 +0900 Subject: [PATCH] feat(run): integrate session origins and agent detection into continuation state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/cli/run/completion-continuation.test.ts | 34 +++++++++++++++++++++ src/cli/run/continuation-state.ts | 2 ++ 2 files changed, 36 insertions(+) diff --git a/src/cli/run/completion-continuation.test.ts b/src/cli/run/completion-continuation.test.ts index 20758c344..795a32ff0 100644 --- a/src/cli/run/completion-continuation.test.ts +++ b/src/cli/run/completion-continuation.test.ts @@ -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(() => {}) diff --git a/src/cli/run/continuation-state.ts b/src/cli/run/continuation-state.ts index d827af5e3..07b530812 100644 --- a/src/cli/run/continuation-state.ts +++ b/src/cli/run/continuation-state.ts @@ -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 }