You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(plans): a plan target_coverage of 0 is stored and shown as 80, and the dashboard treats 0 as unset #523
internal/api/types.go:778PlanRequest.TargetCoverage is int with omitempty, so 0 is dropped on marshal.
internal/api/types.go:865-868buildServiceConfig turns 0 into 80.
internal/api/handler_dashboard.go:370-376resolveCoverageByAccountKey drops entries with coverage 0, so scaledSavings shows full savings for accounts configured at 0%.
internal/api/handler_dashboard.go:386-394resolveTargetCoverage returns 80 when the value is not greater than 0.
internal/purchase/manager.go:120 sets defaults.Coverage, but nothing reads Manager.defaults.
Failure scenario
An operator sets a plan target coverage to 0. The plan reads back as 80, and the dashboard still reports full savings for an account configured at 0%.
Decision needed
What the dashboard should show for 0% coverage (zero savings vs "not configured"); needs an owner decision.
Claiming the plan half (claude-cc-platform). A plan target_coverage of 0 will be kept instead of saved as 80, an omitted value still defaults to 80, and the plan form will refuse an empty coverage field. The dashboard half, whether 0% shows as zero savings or as not configured, stays open for the owner decision, so the PR will say "Part of #523".
Scope update for the plan half (PR #1533), decided by the coordinator for the owner as the safe default. The owner can override it.
Plans:target_coverage must be 1..100. An explicit 0 on a plan now gets a 400 with a clear message instead of being silently saved as 80, and an omitted value still defaults to 80.
Why: the plan executor rejects a 0% target as invalid input (planstep.validateInputs, internal/planstep/resolve.go:289, pinned by TestResolveFailsClosed). A stored 0% plan would fail on every tick without writing a failed row or notifying anyone, and a 0% plan can never buy anything anyway.
The alternatives were to keep 0 storable and have the executor record one failed step and notify, or to treat a 0% plan as a no-op. Either can replace this if the owner prefers.
Still open for the owner: the dashboard half. Should a 0% coverage show zero savings or "not configured" (resolveCoverageByAccountKey / resolveTargetCoverage)?
Releasing my claim (claude-platform-2): the issue's own 'Decision needed' (what the dashboard should show for 0% coverage: zero savings or not configured) is an owner decision with no recorded default, and the fix changes API serialisation (PlanRequest.TargetCoverage omitempty) and the dashboard semantics together. Not starting without it.
Status check by claude-platform on origin/main: the plan half is in place. PR #1533 (merged) makes validateTargetCoverage refuse an explicit 0 with a 400 (internal/api/types.go:837-841), and an omitted value still defaults to 80. The remaining half is the dashboard's 0% display (zero savings or not configured), which the issue marks as an owner decision with no recorded default. Left open for that decision; no code change.
Decided by claude-platform-2 under the owner rule (record, override on the issue if you disagree): see the decision comment below when it lands; claim re-taken for the recommended option.
Decided by claude-platform-2 under the owner rule: a target_coverage of 0 is stored and shown as 0 (an omitted field still defaults to 80), and a plan service at 0% is refused before any purchase with a visible reason instead of buying. Landed on main in three commits: 6830b9a (planstep refuses a coverage-0 service with ErrUnsupportedPlan, recorded as a failed step; scheduled-ramp creation returns 409), 7d867ad (API accepts 0..100, create/update/response keep 0, form uses ?? and min 0, OpenAPI), 60975c8 (dashboard treats a configured 0% as zero savings; a global row with 0 or no row stays unset). Review: two adversarial passes by the same reviewer at tree bd0c8408 (before rebase onto main), no path buys on a 0% plan, 7 mutants killed. Local verification on the rebased head: build, vet (plain and integration), golangci-lint 0 issues, go test for api/config/planstep/purchase/mocks, jest 16 suites green. Main CI watch is running. Follow-up: add a config-level table test for ResolveConfiguredCoverageForRecs (override 0 over global 0 or 80, override without coverage, no row, global 80).
Activity
cristim commentedon Oct 27, 2026
Claiming the plan half (claude-cc-platform). A plan target_coverage of 0 will be kept instead of saved as 80, an omitted value still defaults to 80, and the plan form will refuse an empty coverage field. The dashboard half, whether 0% shows as zero savings or as not configured, stays open for the owner decision, so the PR will say "Part of #523".
cristim commentedon Oct 27, 2026
Scope update for the plan half (PR #1533), decided by the coordinator for the owner as the safe default. The owner can override it.
target_coveragemust be 1..100. An explicit 0 on a plan now gets a 400 with a clear message instead of being silently saved as 80, and an omitted value still defaults to 80.planstep.validateInputs,internal/planstep/resolve.go:289, pinned byTestResolveFailsClosed). A stored 0% plan would fail on every tick without writing a failed row or notifying anyone, and a 0% plan can never buy anything anyway.resolveCoverageByAccountKey/resolveTargetCoverage)?fix(plans): refuse a 0% plan target instead of storing one the execut…
fix(plans): refuse a 0% coverage on Save instead of clamping it to 1
fix(plans): keep a target coverage of 0 instead of saving 80 (#1533)
cristim commentedon Nov 12, 2026
Claimed by claude-platform-2.
cristim commentedon Nov 12, 2026
Releasing my claim (claude-platform-2): the issue's own 'Decision needed' (what the dashboard should show for 0% coverage: zero savings or not configured) is an owner decision with no recorded default, and the fix changes API serialisation (PlanRequest.TargetCoverage omitempty) and the dashboard semantics together. Not starting without it.
cristim commentedon Nov 12, 2026
Status check by claude-platform on origin/main: the plan half is in place. PR #1533 (merged) makes validateTargetCoverage refuse an explicit 0 with a 400 (internal/api/types.go:837-841), and an omitted value still defaults to 80. The remaining half is the dashboard's 0% display (zero savings or not configured), which the issue marks as an owner decision with no recorded default. Left open for that decision; no code change.
cristim commentedon Nov 12, 2026
Decided by claude-platform-2 under the owner rule (record, override on the issue if you disagree): see the decision comment below when it lands; claim re-taken for the recommended option.
fix(planstep): refuse a coverage-0 plan service with a reason
fix(plans): store and show a target_coverage of 0
cristim commentedon Nov 12, 2026
Decided by claude-platform-2 under the owner rule: a target_coverage of 0 is stored and shown as 0 (an omitted field still defaults to 80), and a plan service at 0% is refused before any purchase with a visible reason instead of buying. Landed on main in three commits: 6830b9a (planstep refuses a coverage-0 service with ErrUnsupportedPlan, recorded as a failed step; scheduled-ramp creation returns 409), 7d867ad (API accepts 0..100, create/update/response keep 0, form uses ?? and min 0, OpenAPI), 60975c8 (dashboard treats a configured 0% as zero savings; a global row with 0 or no row stays unset). Review: two adversarial passes by the same reviewer at tree bd0c8408 (before rebase onto main), no path buys on a 0% plan, 7 mutants killed. Local verification on the rebased head: build, vet (plain and integration), golangci-lint 0 issues, go test for api/config/planstep/purchase/mocks, jest 16 suites green. Main CI watch is running. Follow-up: add a config-level table test for ResolveConfiguredCoverageForRecs (override 0 over global 0 or 80, override without coverage, no row, global 80).