fix: accept in-progress checkbox in goal roll-up - #102
Conversation
There was a problem hiding this comment.
The diff is small and I've read the relevant files. Here is the full adjudication:
Diff summary: 3 files — CHANGELOG.md, pkg/ops/complete.go, pkg/ops/complete_test.go
Scope: Bugfix — markGoalCheckbox only matched - [ ] checkboxes, skipping - [/] (in-progress) entries on goal files. Fixed by introducing a goalCheckboxMarker helper that accepts both markers.
Must Fix (Critical)
CHANGELOG.md:11— Version alignment breach.CHANGELOG.mdhas an## Unreleasedsection, which signals a pending release. Per CLAUDE.md, all four version strings must equal:CHANGELOG.md(top section header),.claude-plugin/plugin.json("version"), and both places in.claude-plugin/marketplace.json(metadata.versionandplugins[0].version). Currently all three plugin files are0.114.3while CHANGELOG is inUnreleasedstate. Either remove## Unreleased(not a real release yet) or bump all four files together. This blocksmake precommit.
Should Fix (Important)
pkg/ops/complete.go:325—goalCheckboxMarkeris an exported-but-package-local helper with no direct test. The mechanical funnel confirmed it is called only frommarkGoalCheckbox, which is exercised by the newContext("task with an in-progress goal checkbox"test — but that test covers only the caller's happy path. Edge cases (empty string, line with neither marker) are not verified. Either add unit tests for the helper directly or expand the integration test to cover the marker-not-found branch. The rulego-testing/no-bare-error-callflagged numerous mock setter calls in the test file, but all of those setter methods (FindTaskByNameReturns,WriteTaskReturns, etc.) are void-returning counterfeiter-generated methods — the findings are false positives from the rule's signature check; no action needed.
Nice to Have (Optional)
None.
Selector-mode traceability
- Candidates: rules matched by Step 4b-i glob filter — limited to this PR's changed files
- Applicable:
go-testing/no-bare-error-call,go-testing/suite-test-file-required,goalCheckboxMarkertest coverage - Skipped:
go-composition/no-package-function-calls-in-business-logic→ ops structs are the composition layer itself, calling domain setters is intentional (not a hidden dependency);go-context/cancel-check-in-loop→ SHOULD-tier, loops are bounded by line count (≤~20 iterations);go-logging/no-tight-loop-without-sampler→ SHOULD-tier, bounded loop with guarded log call
{
"verdict": "request-changes",
"summary": "Bugfix is correct and well-tested for the primary case, but the CHANGELOG has entered '## Unreleased' state without bumping the plugin version strings — a `make precommit` failure. The new `goalCheckboxMarker` helper also lacks direct unit tests.",
"comments": [
{
"file": "CHANGELOG.md",
"line": 11,
"severity": "critical",
"message": "Version alignment breach: CHANGELOG.md is in '## Unreleased' state but plugin manifests are at v0.114.3. Per CLAUDE.md, all four version strings must match. Either remove ## Unreleased or bump plugin.json + marketplace.json to a new version."
},
{
"file": "pkg/ops/complete.go",
"line": 325,
"severity": "major",
"message": "goalCheckboxMarker helper has no direct unit tests. It is exercised indirectly by the new 'task with an in-progress goal checkbox' integration test, but edge cases (empty input, no marker present) are not verified."
}
],
"concerns_addressed": [
"correctness: goalCheckboxMarker handles both - [ ] and - [/] correctly — verified by new integration test",
"version: CHANGELOG enters Unreleased but plugin files stay at 0.114.3 — not resolved, must fix before merge",
"testing: goalCheckboxMarker edge cases not directly tested — should add tests"
]
}|
Addressed the MAJOR; disputing the CRITICAL — I believe it is inverted for this repo. CRITICAL — version alignmentThe finding asks me to either drop
The alignment rule it cites ( The finding also states this is Complying would hand-bump manifests pre-merge and race the releaser — the exact failure that CLAUDE.md section exists to prevent. Leaving as-is; happy to be shown wrong if MAJOR — goalCheckboxMarker edge casesFair, and covered in
Tested through the public operation rather than as direct unit tests on the unexported helper: |
superseded by new automated review
There was a problem hiding this comment.
Now I have enough context. The counterfeiter directive is present (line 21) and the suite file exists — both are false positives from the mechanical funnel. Let me compile the findings.
Mechanical findings confirmed:
go-testing/no-bare-error-calloncomplete_test.golines 42, 58, 59 — the mockReturnscalls discard error returns. These are pre-existing in the unchanged part of the test, not introduced by this PR.
Code review of the diff:
The change is a targeted bugfix. Three new things:
goalCheckboxMarkersslice andgoalCheckboxMarker()helper- Updated
markGoalCheckboxto use the helper (accepting- [/]in addition to- [ ]) - Three new test cases covering the fixed behavior
Assessing the plan concerns:
- Sequential marker iteration:
goalCheckboxMarkers = ["- [ ]", "- [/]"]— the closed set means there's no ambiguity; each checkbox line can have only one state - Single replacement with
strings.Replace(..., 1)— correct marker == ""guard before the name check — intentional early-exit optimization, order is correctbreakafter first match — correct, only one checkbox per line
Correctness verdict: The bugfix is sound. No issues in the changed logic.
{
"verdict": "approve",
"summary": "Bugfix correctly extends goal checkbox matching from only `- [ ]` to both `- [ ]` and `- [/]` markers. The new helper and three test cases are well-structured. Pre-existing bare-error-call violations in the unchanged test setup are noted but outside the scope of this PR.",
"comments": [
{
"file": "pkg/ops/complete_test.go",
"line": 42,
"severity": "minor",
"message": "currentDateTime.SetNow(...) discards its return value. If SetNow returns (error), wrap in Gomega: Expect(currentDateTime.SetNow(...)).To(Succeed()). This is pre-existing in the unchanged BeforeEach setup, not introduced by this PR."
},
{
"file": "pkg/ops/complete_test.go",
"line": 58,
"severity": "minor",
"message": "mockTaskStorage.FindTaskByNameReturns(task, nil) discards its return value. If the mock's Returns method returns (error), wrap in Gomega. Pre-existing, not introduced by this PR."
},
{
"file": "pkg/ops/complete_test.go",
"line": 59,
"severity": "minor",
"message": "mockTaskStorage.WriteTaskReturns(nil) discards its return value. Pre-existing, not introduced by this PR."
}
],
"concerns_addressed": [
"correctness: goalCheckboxMarker iterates sequentially — addressed: the marker set is closed (only 2 entries), each checkbox line has exactly one state, so ordering is unambiguous",
"correctness: strings.Replace with 1 as n — addressed: correct single replacement scoped to one occurrence per line",
"correctness: marker==\"\" guard before task name check — addressed: intentional early-exit ordering, confirmed correct",
"go-testing/counterfeiter-directive-on-interface — not an issue: directive is present at line 21 of complete.go",
"go-testing/suite-test-file-required — not an issue: pkg/ops/ops_suite_test.go exists"
]
}
task completemarks the task's checkbox on its parent goal. The matcher only accepted- [ ]:A goal entry written as
- [/]— the in-progress marker this tool itself emits fromtask-statusgrouped-checkbox output — never matched. Completion then failed the roll-up with:The task still completed, so nothing looked broken until someone read the goal and found it claiming less progress than reality.
The sharp edge: a task being completed has usually been worked, and a worked task is marked
[/]on its goal. So the roll-up failed on exactly the tasks most likely to be completed. Hit twice on 2026-08-21 completing two migration tasks against one goal; both goal lines had to be hand-edited.Change
goalCheckboxMarkerreturns whichever unchecked marker a line carries (- [ ]or- [/]), empty when neithermarkGoalCheckboxreplaces the marker it matched, so both roll up to- [x]- [/]Verification
make precommit— PASS (17.8s), tests included.Known adjacent gap, deliberately not fixed here: a goal using a non-checkbox list (plain numbered entries) still fails, and a task never registered on its goal still fails. Both are documented separately.