Fix paused boulder session resolution
This commit is contained in:
@@ -97,6 +97,74 @@ describe("resolveActiveBoulderSession", () => {
|
||||
expect(result?.boulderState.session_ids).toContain("ses_appended")
|
||||
})
|
||||
|
||||
test("returns null for tracked session when mirror boulder is paused", async () => {
|
||||
// given
|
||||
const planPath = join(testDirectory, "paused-plan.md")
|
||||
writeFileSync(planPath, "# Plan\n- [ ] Task 1\n", "utf-8")
|
||||
writeBoulderState(testDirectory, {
|
||||
active_plan: planPath,
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
status: "paused",
|
||||
session_ids: ["ses_tracked"],
|
||||
session_origins: { ses_tracked: "direct" },
|
||||
plan_name: "paused-plan",
|
||||
})
|
||||
|
||||
// when
|
||||
const result = await resolveActiveBoulderSession({
|
||||
client: { session: { get: async () => ({ data: {} }) } } as never,
|
||||
directory: testDirectory,
|
||||
sessionID: "ses_tracked",
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
test("returns null for tracked work session when resolved work is abandoned", async () => {
|
||||
// given
|
||||
const activePlanPath = join(testDirectory, "active-work-plan.md")
|
||||
const abandonedPlanPath = join(testDirectory, "abandoned-work-plan.md")
|
||||
writeFileSync(activePlanPath, "# Plan\n- [ ] Active task\n", "utf-8")
|
||||
writeFileSync(abandonedPlanPath, "# Plan\n- [ ] Abandoned task\n", "utf-8")
|
||||
writeBoulderState(testDirectory, {
|
||||
schema_version: 2,
|
||||
active_work_id: "work-active",
|
||||
active_plan: activePlanPath,
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: ["ses_active"],
|
||||
plan_name: "active-work-plan",
|
||||
works: {
|
||||
"work-active": {
|
||||
work_id: "work-active",
|
||||
active_plan: activePlanPath,
|
||||
plan_name: "active-work-plan",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: ["ses_active"],
|
||||
status: "active",
|
||||
},
|
||||
"work-abandoned": {
|
||||
work_id: "work-abandoned",
|
||||
active_plan: abandonedPlanPath,
|
||||
plan_name: "abandoned-work-plan",
|
||||
started_at: "2026-01-02T10:05:00Z",
|
||||
session_ids: ["ses_abandoned"],
|
||||
status: "abandoned",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// when
|
||||
const result = await resolveActiveBoulderSession({
|
||||
client: { session: { get: async () => ({ data: {} }) } } as never,
|
||||
directory: testDirectory,
|
||||
sessionID: "ses_abandoned",
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
test("returns complete progress when a mirrored worktree plan is complete", async () => {
|
||||
// given
|
||||
const mainPlanPath = join(testDirectory, ".omo", "plans", "worktree-plan.md")
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
} from "../../features/boulder-state"
|
||||
import type { BoulderState, PlanProgress } from "../../features/boulder-state"
|
||||
|
||||
function isInactiveBoulderStatus(status: BoulderState["status"]): boolean {
|
||||
return status === "paused" || status === "abandoned"
|
||||
}
|
||||
|
||||
export async function resolveActiveBoulderSession(input: {
|
||||
client: PluginInput["client"]
|
||||
directory: string
|
||||
@@ -45,6 +49,10 @@ export async function resolveActiveBoulderSession(input: {
|
||||
}
|
||||
: boulderState
|
||||
|
||||
if (isInactiveBoulderStatus(nextBoulderState.status)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const progress = getPlanProgress(
|
||||
sessionWork
|
||||
? resolveBoulderPlanPathForWork(input.directory, sessionWork)
|
||||
|
||||
@@ -291,6 +291,156 @@ describe("createToolExecuteAfterHandler task timers", () => {
|
||||
expect((taskSession?.elapsed_ms ?? 0) > 0).toBe(true)
|
||||
})
|
||||
|
||||
it("does not end task timer when only a nested checkbox with the same label is checked", async () => {
|
||||
// given
|
||||
const parentSessionID = "ses_parent_nested_checkbox"
|
||||
const planDirectory = join(testDirectory, ".omo", "plans")
|
||||
mkdirSync(planDirectory, { recursive: true })
|
||||
const planPath = join(planDirectory, "task-timer-nested-checkbox-plan.md")
|
||||
writeFileSync(planPath, "# Plan\n\n## TODOs\n- [ ] 1. Implement auth flow\n - [ ] 1. nested evidence\n", "utf-8")
|
||||
writeBoulderState(testDirectory, {
|
||||
schema_version: 2,
|
||||
active_work_id: "work-1",
|
||||
active_plan: planPath,
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: [parentSessionID],
|
||||
plan_name: "task-timer-nested-checkbox-plan",
|
||||
task_sessions: {
|
||||
"todo:1": {
|
||||
task_key: "todo:1",
|
||||
task_label: "1",
|
||||
task_title: "Implement auth flow",
|
||||
session_id: "ses_child_nested_checkbox",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
status: "running",
|
||||
updated_at: "2026-01-02T10:00:00Z",
|
||||
},
|
||||
},
|
||||
works: {
|
||||
"work-1": {
|
||||
work_id: "work-1",
|
||||
active_plan: planPath,
|
||||
plan_name: "task-timer-nested-checkbox-plan",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: [parentSessionID],
|
||||
status: "active",
|
||||
task_sessions: {
|
||||
"todo:1": {
|
||||
task_key: "todo:1",
|
||||
task_label: "1",
|
||||
task_title: "Implement auth flow",
|
||||
session_id: "ses_child_nested_checkbox",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
status: "running",
|
||||
updated_at: "2026-01-02T10:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const { beforeHandler, afterHandler } = createHandlers()
|
||||
|
||||
await beforeHandler(
|
||||
{ tool: "edit", sessionID: parentSessionID, callID: "call-task-timer-edit-nested" },
|
||||
{ args: { filePath: planPath, oldString: " - [ ] 1. nested evidence", newString: " - [x] 1. nested evidence" } },
|
||||
)
|
||||
|
||||
writeFileSync(planPath, "# Plan\n\n## TODOs\n- [ ] 1. Implement auth flow\n - [x] 1. nested evidence\n", "utf-8")
|
||||
|
||||
// when
|
||||
await afterHandler(
|
||||
{ tool: "edit", sessionID: parentSessionID, callID: "call-task-timer-edit-nested" },
|
||||
{
|
||||
title: "Edit",
|
||||
output: "Updated file",
|
||||
metadata: {
|
||||
filePath: planPath,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// then
|
||||
const taskSession = readBoulderState(testDirectory)?.works?.["work-1"]?.task_sessions?.["todo:1"]
|
||||
expect(taskSession).toBeDefined()
|
||||
expect(taskSession?.ended_at).toBeUndefined()
|
||||
expect(taskSession?.status).toBe("running")
|
||||
})
|
||||
|
||||
it("ends task timer when a top-level checkbox with the tracked label is checked", async () => {
|
||||
// given
|
||||
const parentSessionID = "ses_parent_top_level_checkbox"
|
||||
const planDirectory = join(testDirectory, ".omo", "plans")
|
||||
mkdirSync(planDirectory, { recursive: true })
|
||||
const planPath = join(planDirectory, "task-timer-top-level-checkbox-plan.md")
|
||||
writeFileSync(planPath, "# Plan\n\n## TODOs\n- [ ] 1. Implement auth flow\n - [ ] 1. nested evidence\n", "utf-8")
|
||||
writeBoulderState(testDirectory, {
|
||||
schema_version: 2,
|
||||
active_work_id: "work-1",
|
||||
active_plan: planPath,
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: [parentSessionID],
|
||||
plan_name: "task-timer-top-level-checkbox-plan",
|
||||
task_sessions: {
|
||||
"todo:1": {
|
||||
task_key: "todo:1",
|
||||
task_label: "1",
|
||||
task_title: "Implement auth flow",
|
||||
session_id: "ses_child_top_level_checkbox",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
status: "running",
|
||||
updated_at: "2026-01-02T10:00:00Z",
|
||||
},
|
||||
},
|
||||
works: {
|
||||
"work-1": {
|
||||
work_id: "work-1",
|
||||
active_plan: planPath,
|
||||
plan_name: "task-timer-top-level-checkbox-plan",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
session_ids: [parentSessionID],
|
||||
status: "active",
|
||||
task_sessions: {
|
||||
"todo:1": {
|
||||
task_key: "todo:1",
|
||||
task_label: "1",
|
||||
task_title: "Implement auth flow",
|
||||
session_id: "ses_child_top_level_checkbox",
|
||||
started_at: "2026-01-02T10:00:00Z",
|
||||
status: "running",
|
||||
updated_at: "2026-01-02T10:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
const { beforeHandler, afterHandler } = createHandlers()
|
||||
|
||||
await beforeHandler(
|
||||
{ tool: "edit", sessionID: parentSessionID, callID: "call-task-timer-edit-top-level" },
|
||||
{ args: { filePath: planPath, oldString: "- [ ] 1. Implement auth flow", newString: "- [x] 1. Implement auth flow" } },
|
||||
)
|
||||
|
||||
writeFileSync(planPath, "# Plan\n\n## TODOs\n- [x] 1. Implement auth flow\n - [ ] 1. nested evidence\n", "utf-8")
|
||||
|
||||
// when
|
||||
await afterHandler(
|
||||
{ tool: "edit", sessionID: parentSessionID, callID: "call-task-timer-edit-top-level" },
|
||||
{
|
||||
title: "Edit",
|
||||
output: "Updated file",
|
||||
metadata: {
|
||||
filePath: planPath,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// then
|
||||
const taskSession = readBoulderState(testDirectory)?.works?.["work-1"]?.task_sessions?.["todo:1"]
|
||||
expect(taskSession).toBeDefined()
|
||||
expect(taskSession?.ended_at).toBeString()
|
||||
expect(taskSession?.status).toBe("completed")
|
||||
})
|
||||
|
||||
it("tracks parallel delegated tasks by task label from TASK section", async () => {
|
||||
// given
|
||||
const parentSessionID = "ses_parent_parallel"
|
||||
|
||||
Reference in New Issue
Block a user