fix(boulder): count only top-level checkboxes in simple-mode plan progress
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -650,6 +650,65 @@ describe("boulder-state", () => {
|
|||||||
expect(progress.completed).toBe(1)
|
expect(progress.completed).toBe(1)
|
||||||
expect(progress.isComplete).toBe(false)
|
expect(progress.isComplete).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should count only top-level checkboxes for simple plans with nested tasks", () => {
|
||||||
|
// given
|
||||||
|
const planPath = join(TEST_DIR, "simple-nested-plan.md")
|
||||||
|
writeFileSync(planPath, `# Plan
|
||||||
|
|
||||||
|
- [ ] Top-level task 1
|
||||||
|
- [x] Nested task ignored
|
||||||
|
- [x] Top-level task 2
|
||||||
|
* [ ] Another nested task ignored
|
||||||
|
`)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const progress = getPlanProgress(planPath)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(progress.total).toBe(2)
|
||||||
|
expect(progress.completed).toBe(1)
|
||||||
|
expect(progress.isComplete).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should treat final-wave-only plans as structured mode", () => {
|
||||||
|
// given
|
||||||
|
const planPath = join(TEST_DIR, "final-wave-only-plan.md")
|
||||||
|
writeFileSync(planPath, `# Plan
|
||||||
|
|
||||||
|
## Final Verification Wave
|
||||||
|
- [ ] F1. Top-level final review
|
||||||
|
- [x] Nested verification detail ignored
|
||||||
|
`)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const progress = getPlanProgress(planPath)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(progress.total).toBe(1)
|
||||||
|
expect(progress.completed).toBe(0)
|
||||||
|
expect(progress.isComplete).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should ignore mixed indentation levels in simple plans", () => {
|
||||||
|
// given
|
||||||
|
const planPath = join(TEST_DIR, "simple-mixed-indentation-plan.md")
|
||||||
|
writeFileSync(planPath, `# Plan
|
||||||
|
|
||||||
|
* [x] Top-level star task
|
||||||
|
- [ ] Indented task ignored
|
||||||
|
- [x] Tab-indented task ignored
|
||||||
|
- [ ] Top-level dash task
|
||||||
|
`)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const progress = getPlanProgress(planPath)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(progress.total).toBe(2)
|
||||||
|
expect(progress.completed).toBe(1)
|
||||||
|
expect(progress.isComplete).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("getPlanName", () => {
|
describe("getPlanName", () => {
|
||||||
|
|||||||
@@ -226,7 +226,9 @@ export function getPlanProgress(planPath: string): PlanProgress {
|
|||||||
const lines = content.split(/\r?\n/)
|
const lines = content.split(/\r?\n/)
|
||||||
|
|
||||||
// Check if the plan has structured sections (## TODOs / ## Final Verification Wave)
|
// Check if the plan has structured sections (## TODOs / ## Final Verification Wave)
|
||||||
const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN.test(line))
|
const hasStructuredSections = lines.some(
|
||||||
|
(line) => TODO_HEADING_PATTERN.test(line) || FINAL_VERIFICATION_HEADING_PATTERN.test(line),
|
||||||
|
)
|
||||||
|
|
||||||
if (hasStructuredSections) {
|
if (hasStructuredSections) {
|
||||||
// Structured plan: only count top-level checkboxes with numbered labels
|
// Structured plan: only count top-level checkboxes with numbered labels
|
||||||
@@ -291,8 +293,8 @@ function getStructuredPlanProgress(lines: string[]): PlanProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getSimplePlanProgress(content: string): PlanProgress {
|
function getSimplePlanProgress(content: string): PlanProgress {
|
||||||
const uncheckedMatches = content.match(/^\s*[-*]\s*\[\s*\]/gm) || []
|
const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) || []
|
||||||
const checkedMatches = content.match(/^\s*[-*]\s*\[[xX]\]/gm) || []
|
const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) || []
|
||||||
|
|
||||||
const total = uncheckedMatches.length + checkedMatches.length
|
const total = uncheckedMatches.length + checkedMatches.length
|
||||||
const completed = checkedMatches.length
|
const completed = checkedMatches.length
|
||||||
|
|||||||
Reference in New Issue
Block a user