Skip to content

fix(cli): count every Savings Plan and RI type in the run summary - #2239

Merged
cristim merged 1 commit into
mainfrom
fix/sp-summary-plan-types
Oct 16, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/sp-summary-plan-types

Conversation

@cristim

@cristim cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member

Refs #1613 (finding M9). The other remaining findings are tracked on the issue and in #2236 / #2237.

Summary

The run summary in cmd/multi_service_stats_helpers.go silently dropped some Savings Plan and RI recommendations, so the counts and savings it printed could be short of what the run contained:

  • collectSPSavings skipped SageMaker plans.
  • Both SP helpers dropped plans whose PlanType was unknown, lowercase or empty. They also dropped umbrella-slug rows with no details, which is what an --input-csv row loads as.
  • collectRISavings keyed only on the legacy service constants, so the canonical compute, relational-db, cache and data-warehouse types (which can arrive from a CSV) were missed.

The fix:

  • A single classifier, spPlanKind, checks the per-plan-type service slug first and then PlanType, case-insensitively.
  • collectSPSavings now goes through categorizeSPRecommendations, which gains SageMaker and Other buckets. Anything it does not recognise is printed on an "Other SP" line with its count and savings, so every row lands in exactly one bucket and the lines add up to the total.
  • collectRISavings accepts the canonical RI types as well as the legacy ones.

This change only affects the summary. The purchase path keys on the slug through PlanTypeForServiceType, and the changed helpers are called only from the summary and comparison print paths. One visible effect: canonical RI rows now count toward the DB and EC2 RI savings used in the comparison section's options.

Evidence (local, macOS, GOTOOLCHAIN=go1.26.9, unit tests)

  • Fail before / pass after: with the origin/main helpers restored (only the new struct fields stubbed), 13 cases in the new cmd/multi_service_stats_plan_types_test.go fail, covering SageMaker (parser, umbrella and lowercase forms), canonical slug without details, unknown plan type, umbrella without details, totals, the Other print, and RI compute, relational-db, cache and data-warehouse. With the fix, all pass.
  • Reviewer mutation probes, all caught:
    • dropping the slug-first lookup;
    • removing cache and data-warehouse from the DB bucket;
    • dropping the case folding.
  • Checks:
    • make build, go vet ./... and go test -race ./cmd/... pass.
    • golangci-lint v2.10.1 reports 0 issues.
    • scripts/test-git-secrets-allowlist.sh passes 44/44.
  • Review: two passes by an independent non-author reviewer (sonnet, high) found no actionable findings at f90da9dd. They checked parser and CSV inputs, double counting, callers and file sizes.

Pre-existing and out of scope: OpenSearch RIs are in neither RI savings bucket.

🤖 Generated with Claude Code

collectSPSavings ignored SageMaker plans and silently dropped any plan
type it did not know. categorizeSPRecommendations dropped unknown types
and keyed only on the details PlanType string. collectRISavings keyed
only on the legacy service constants, so canonical services read from a
CSV (compute, relational-db, cache, data-warehouse) were not counted.

Resolve the plan type from the per-plan-type service slug first, then a
case-insensitive PlanType, share one classifier between both helpers, and
count anything unrecognized in an explicit "Other SP" line so counts and
savings always add up. collectRISavings now accepts the canonical
service types too. Display only; purchase logic is unchanged.

Refs #1613

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristim cristim added priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/eventually No deadline impact/internal Team-internal only effort/l Weeks type/chore Maintenance / non-user-visible triaged Item has been triaged labels Oct 16, 2026
@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 2 billable files and costs up to $0.50.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 23 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 65 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-cli/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: bc2b0cb0-f6ad-4af7-acd6-40889c304b39
📥 Commits

Reviewing files that changed from the base of the PR and between 64c8c70 and 633bd90.

📒 Files selected for processing: 2
  • cmd/multi_service_stats_helpers.go
  • cmd/multi_service_stats_plan_types_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

Independent review verdict: CLEAN at 633bd90

Reviewer: fresh Claude agent (Sonnet, high effort), not the author, cold brief. The PR body was treated as a claim. Coordinator: claude-cc-reviewer.

Blocking findings: none. This PR changes only the savings aggregation and display (collectSPSavings, collectRISavings, categorizeSPRecommendations, printSPTypeSummaries). Success and failure counts are untouched.

How rows are classified (checked against the pinned pkg/common/types.go, :118 and :126-129):

  • SP rows: the four per-plan-type slugs are bucketed by slug. The umbrella savingsplans falls back to PlanType, case-insensitive. Each row lands in exactly one bucket.
  • Unknown SP rows: an unknown PlanType, or the umbrella with no details, goes to "Other SP", which is counted and printed. Nothing is dropped silently, and the per-bucket lines sum to the total.
  • RI rows:
    • The EC2 bucket covers ec2 and compute.
    • The DB bucket covers rds, relational-db, elasticache, cache, memorydb, redshift and data-warehouse.
    • Not bucketed: opensearch, search, nosql and storage. These were missing before this PR too, the body acknowledges opensearch, and it is display only, so it is a follow-up.
  • Money:
    • An RI-only run gives the same totals for ec2, rds, elasticache, memorydb and redshift.
    • The EC2 = became +=, which matters only when ec2 and compute rows coexist. The old behaviour dropped one of them.

Overlap with #2238:

What was attacked and how (AWS_CONFIG_FILE=/dev/null, GOTOOLCHAIN=go1.26.9):

  • go vet ./cmd/... is clean. go test -race -count=1 ./cmd/... passes 1519.
  • Fail-before: with the main helpers restored, the 13 new cases fail: SageMaker, canonical slug, lowercase, unknown, umbrella-without-details, and RI cache/compute/relational-db/data-warehouse. The pre-existing TestPrintSavingsPlansSection/Prints_SageMaker_Savings_Plans also fails against the old helpers.
  • Mutation probes, both killed:
    • removing the sagemaker fallback (4 failures);
    • removing ServiceCompute from the EC2 RI bucket (2 failures).

Gate: there is no linked issue, and the PR carries its own triage labels. Merge once CI is green at this SHA.

@cristim

cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

CR waived: quota, adversarial review + local verification + green CI (per CLAUDE.md:172-173). CodeRabbit posted 'Review limit reached' on this PR. Gate at 633bd90: CLEAN independent verdict above, CI green, mergeability CLEAN. (claude-cc-reviewer)

@cristim
cristim merged commit 6a15af9 into main Oct 16, 2026
12 checks passed
@cristim
cristim deleted the fix/sp-summary-plan-types branch October 16, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/l Weeks impact/internal Team-internal only priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/chore Maintenance / non-user-visible urgency/eventually No deadline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant