Fix paused boulder session resolution
This commit is contained in:
@@ -97,6 +97,74 @@ describe("resolveActiveBoulderSession", () => {
|
|||||||
expect(result?.boulderState.session_ids).toContain("ses_appended")
|
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 () => {
|
test("returns complete progress when a mirrored worktree plan is complete", async () => {
|
||||||
// given
|
// given
|
||||||
const mainPlanPath = join(testDirectory, ".omo", "plans", "worktree-plan.md")
|
const mainPlanPath = join(testDirectory, ".omo", "plans", "worktree-plan.md")
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import {
|
|||||||
} from "../../features/boulder-state"
|
} from "../../features/boulder-state"
|
||||||
import type { BoulderState, PlanProgress } 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: {
|
export async function resolveActiveBoulderSession(input: {
|
||||||
client: PluginInput["client"]
|
client: PluginInput["client"]
|
||||||
directory: string
|
directory: string
|
||||||
@@ -45,6 +49,10 @@ export async function resolveActiveBoulderSession(input: {
|
|||||||
}
|
}
|
||||||
: boulderState
|
: boulderState
|
||||||
|
|
||||||
|
if (isInactiveBoulderStatus(nextBoulderState.status)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const progress = getPlanProgress(
|
const progress = getPlanProgress(
|
||||||
sessionWork
|
sessionWork
|
||||||
? resolveBoulderPlanPathForWork(input.directory, sessionWork)
|
? resolveBoulderPlanPathForWork(input.directory, sessionWork)
|
||||||
|
|||||||
@@ -291,6 +291,156 @@ describe("createToolExecuteAfterHandler task timers", () => {
|
|||||||
expect((taskSession?.elapsed_ms ?? 0) > 0).toBe(true)
|
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 () => {
|
it("tracks parallel delegated tasks by task label from TASK section", async () => {
|
||||||
// given
|
// given
|
||||||
const parentSessionID = "ses_parent_parallel"
|
const parentSessionID = "ses_parent_parallel"
|
||||||
|
|||||||
Reference in New Issue
Block a user