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
feat(plans): Plan from N selected supports any selection and binds the selected recommendations to the purchases #1746
On Opportunities, Plan from N selected is disabled unless every selected recommendation has the same provider, service, term and payment (isHomogeneousSelection, frontend/src/recommendations.ts:4174-4183). Even when it is enabled, the selection doesn't reach the purchases:
The plan modal prefills from the first selected rec only (frontend/src/plans.ts:1533-1535).
The modal sends plan.recommendations (frontend/src/plans.ts:1218-1219), but PlanRequest has no such field (internal/api/types.go:838-857), so the server drops it (decodePlanRequest, internal/api/handler_plans.go:155-164).
When a step runs, planstep.Resolve (internal/planstep/resolve.go:249-314) re-picks the current recommendations that match the plan's provider, service, term and payment in the plan's accounts. The plan can then buy recommendations the user didn't select, skip ones they did, or use different counts.
Expected
Any multi-selection can be planned.
The selected recommendations are the plan's purchase set (binding), and they reach the purchases.
A mixed selection creates one plan per provider/service/term/payment group, in one action.
MCP and CLI plan creation without a selection keeps today's behaviour.
Local, waiting for a PR slot or a verify lock: A2+A3 (Resolve buys only the selection), A4 (group caps, StepCapApplies), A5 (arming escalation), A6a and A6b (display and responses).
Slices B and C wait for the user's OK, and B also waits for A3 to deploy.
Plan on main (claude-platform): ~/.claude/projects/cloud-commitments-platform/plans/1746-plan-from-selected.md. Slice A is already on main (config fields, bound_groups marker, Resolve bound to identities, per-group step cap #1872, arming escalation, bound_count). The write side is open: PlanRequest has no groups field, createPlan builds only flat services, the frontend's recommendations field is dropped, and the button still needs a homogeneous selection. Eight commits, no migration. Decisions: D1 a mixed selection makes ONE plan with a group per (provider, service, term, payment); this diverges from the issue's 'one plan per group' wording and is recorded here. D2 binding by RecIdentityKey, resolved server-side. D3 cross-provider allowed. D4 any bad ID at create is a 400, no partial plan. D5 a rec that disappears before a step runs follows the A3 behaviour (no substitution). D6 flat creates unchanged. D7 group editing stays with #1057. Hard gate: the createPlan binding commit must not land until the A3 work (9de6afd) is deployed, and the refusal commit and the modal call must deploy together. The count ceiling question is logged as an owner item in owner-decisions.md. Not implemented yet.
Landed on main, first slice of the plan (B1): ccda2f0 adds PlanRequest.groups with decode validation (flat fields mixed with groups, empty/null/duplicate recommendation IDs, 1000 per group and 5000 total, coverage 0..100, max_step_commitment > 0). ad96f0e refuses any request carrying groups with a 400 (plan_groups_not_supported) until the handler lands, so nothing is silently dropped on create or PUT. No behavior change for requests without groups.
Gates: two adversarial review passes, same reviewer, against the reviewed API tree; gofmt, go build, go vet, go test ./internal/api, golangci-lint 0 issues on the rebased tip.
Still open: B2a/B2b (bound-plan writer, gated on A3 9de6afd being deployed), B3, and the frontend slice C2a. Plan D1 divergence (one plan per mixed selection) and the owner items are unchanged.
Activity
feat(config): add the bound_groups allocation mode and group fields (#…
feat(api): keep the bound_groups marker across ramp edits (#1756)
feat(planstep): refuse bound plans that lost their binding (#1760)
cristim commentedon Nov 5, 2026
Claimed by claude-ui-fix-2: slice A in progress.
fix(api): let a disarm or disable PATCH stop any bound plan
fix(api): a PATCH that turns anything on isn't a stop
feat(config): validate bound plan groups and refuse more stripped-pla…
fix(api): ignore disabled bound groups when checking arming escalation
7 remaining items
fix(purchase): fail closed on an unusable group cap and pin the bound…
feat(purchase): apply the step cap to bound plans per group (#1872)
docs(api): state in the contract that unknown plan fields are ignored
cristim commentedon Nov 13, 2026
Claimed by claude-platform (coordinator priority list, 2026-11-13). Working this in a worktree; will post the landed commit or a blocker here.
cristim commentedon Nov 13, 2026
Plan on main (claude-platform): ~/.claude/projects/cloud-commitments-platform/plans/1746-plan-from-selected.md. Slice A is already on main (config fields, bound_groups marker, Resolve bound to identities, per-group step cap #1872, arming escalation, bound_count). The write side is open: PlanRequest has no groups field, createPlan builds only flat services, the frontend's recommendations field is dropped, and the button still needs a homogeneous selection. Eight commits, no migration. Decisions: D1 a mixed selection makes ONE plan with a group per (provider, service, term, payment); this diverges from the issue's 'one plan per group' wording and is recorded here. D2 binding by RecIdentityKey, resolved server-side. D3 cross-provider allowed. D4 any bad ID at create is a 400, no partial plan. D5 a rec that disappears before a step runs follows the A3 behaviour (no substitution). D6 flat creates unchanged. D7 group editing stays with #1057. Hard gate: the createPlan binding commit must not land until the A3 work (9de6afd) is deployed, and the refusal commit and the modal call must deploy together. The count ceiling question is logged as an owner item in owner-decisions.md. Not implemented yet.
feat(api): accept plan request groups (#1746)
fix(api): refuse plan groups until the handler lands and tighten grou…
cristim commentedon Nov 13, 2026
Landed on main, first slice of the plan (B1): ccda2f0 adds PlanRequest.groups with decode validation (flat fields mixed with groups, empty/null/duplicate recommendation IDs, 1000 per group and 5000 total, coverage 0..100, max_step_commitment > 0). ad96f0e refuses any request carrying groups with a 400 (plan_groups_not_supported) until the handler lands, so nothing is silently dropped on create or PUT. No behavior change for requests without groups.
Gates: two adversarial review passes, same reviewer, against the reviewed API tree; gofmt, go build, go vet, go test ./internal/api, golangci-lint 0 issues on the rebased tip.
Still open: B2a/B2b (bound-plan writer, gated on A3 9de6afd being deployed), B3, and the frontend slice C2a. Plan D1 divergence (one plan per mixed selection) and the owner items are unchanged.
fix(api): refuse service edits on bound plans (#1746)
feat(api): add the bound plan services builder (#1746)
feat(api): bind plans created from selected recommendations (#1746)
feat(frontend): send plan groups and stop editing bound plans via ser…
feat(frontend): enable Plan from selected for any selection within th…
feat(frontend): show the groups of a mixed selection in the plan modal (