Skip to content

feat(plans): plan steps resolve real recommendations and show real counts and prices (tier B of #609) #631

Closed

Description

@cristim

Tier B of #609. The tier A stopgap (#618, #616, merged) made empty plan steps fail loudly and show "No recommendations attached". It did not make plans work: until this issue lands, no plan can complete a step, and "Add Purchases" always returns 409.

Goal

A plan created from recommendations (provider, service, term, payment, target accounts, target coverage, ramp) schedules steps that purchase real recommendations, and the planned-purchases list shows real counts, resource types, upfront cost and savings instead of placeholders.

Proposed shape

  • Resolve the matching stored recommendations for the plan's provider, service, term, payment and target accounts at execution time, not at plan creation, so a step buys what is current when it runs.
  • Size each step from the ramp: PercentPerStep of the resolved set, per the target-coverage meaning chosen under D1.
  • Savings Plans steps are sized by HourlyCommitment, not instance count.
  • Store the resolved recommendations on the execution, so approval, history and retry see the same rows.
  • Planned-purchases preview: compute count, resource types, upfront and savings for pending steps from the current matching recommendations, labelled as an estimate until the step runs.
  • Reuse createPurchaseExecutionsTx (ramp lock chore(deps): Bump style-loader from 3.3.4 to 4.0.0 in /frontend #1861, creator stamp sec(auth): API key permission constraints are silently dropped on create #950), which tier A kept without a production caller. Re-enable "Add Purchases" through it.
  • Decide the dropped recommendations field of PlanRequest: honour it (per D2) or stop sending it from savePlan (frontend/src/plans.ts).
  • Keep the tier A guards as the fallback: a step that resolves to nothing fails once and does not advance the ramp.

Open product decisions (block the design)

  • D1. Does "target coverage 80%" mean "buy 80% of the matching recommendations" or "reach 80% account coverage"? The second needs a coverage source, which is unknown for Azure.
  • D2. Is the snapshot sent by "Plan from N selected" binding, or does the plan filter win?

Known gaps to cover

Acceptance

  • A plan like the one reported (Azure compute, 1 year, 80% target coverage, weekly 25% ramp, one target account, Auto-Purchase on) produces steps with non-zero counts and real upfront and savings figures, and a scheduled step executes against real recommendations.
  • A step with nothing to buy fails once, with one failed row, and the ramp does not advance.
  • Regression tests use the real data shape of that plan and fail on current main.

Activity

  1. 79 remaining items

  2. cristim commented on Oct 27, 2026

    @cristim
    MemberAuthor

    Follow-ups from the cold review of #1083 (merged as b4994d2). They're low-severity coverage gaps in the clock characterization and didn't block the merge.

    • The target-met row's completed_at and its error note are only checked as set, not by value. Shifting completed_at by an hour, or changing the note, still passes. Bracket CompletedAt with time.Now() before and after the call, and pin the note text.
    • The plan's last_execution_date, which completion writes at store_postgres.go ~815-816, is never checked.
  3. cristim commented on Nov 4, 2026

    @cristim
    MemberAuthor

    UI observation from production (2026-11-04): a plan whose only step says "No recommendations attached" (Count, Upfront and Est. Savings blank) still shows a green "Health: 100" badge on the plan card, and its Pending step has an enabled primary "Run now" button. With nothing attached, the health score and the enabled Run now both suggest the step is ready to buy.

  4. cristim commented on Nov 4, 2026

    @cristim
    MemberAuthor

    Carried over from #1708 (item 3), to do when plan steps can carry recommendations:

  5. cristim commented on Nov 5, 2026

    @cristim
    MemberAuthor

    Claimed by claude-qa-ui-4: S1 merged (#1761). The S2 detailed plan converged and waits on #1746 slice A (purchase.StepCapApplies plus bound-group Resolve) merging. S5 follows S2.

  6. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Claimed by claude-platform (coordinator priority list, 2026-11-13). Working this in a worktree; will post the landed commit or a blocker here.

  7. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Plan on main (claude-platform): ~/.claude/projects/cloud-commitments-platform/plans/631-plan-step-real-recs.md. Most of this issue is on main already (resolution at attach, resolved recs stored on the execution, Add Purchases re-enabled, one failed row with no ramp advance). Open item: a step that has not attached yet shows blank count, upfront and savings. Plan: estimate only the next unattached step, through the same planstep.Resolve path the purchase uses, labelled as an estimate; later steps show 'sized when step N-1 runs'. Four commits, no migration. Implementation starts now; this issue closes with the fourth commit.

  8. cristim commented on Nov 13, 2026

    @cristim
    MemberAuthor

    Landed on main (claude-platform): 22ee7d9, 402d2bb, 6d90c19 and a61acf5 (head a61acf5). The next unattached plan step now shows an estimate labelled as an estimate: the same planstep.Resolve path the attach uses, a view:recommendations gate, the cap check applied, and nothing persisted or read by approval, cap, retry or attach. Reviewed in two passes (approve), with integration tests against Docker showing the estimate equals what the tick later attaches. Open follow-ups, non-blocking: a unit test for an already-attached row at the next step (guards against double counting), and a test asserting one Resolve call per plan per request. The dropped PlanRequest recommendations field stays with #1746.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions