Problem
Savings Plans is identified inconsistently across the stack:
| Layer |
File |
Identifier |
| Frontend dropdown |
frontend/src/index.html:112,703 |
"savingsplans" |
Frontend SERVICE_FIELDS |
frontend/src/settings.ts:32 |
"savingsplans" |
Frontend commitmentConfigs |
frontend/src/commitmentOptions.ts:58 |
"savingsplans" (after #63) |
| Backend canonical |
pkg/common/types.go:49 |
"savings-plans" |
| Backend CLI flag |
cmd/main.go:92 |
"savingsplans" |
| Backend purchase normaliser |
internal/purchase/execution.go:384 |
accepts both via case "savings-plans", "savingsplans": |
The frontend persists "savingsplans" to the service_configs.service column. The Go ServiceType constant is "savings-plans". The two sides only line up because execution.go does a dual-case branch on the way to common.ServiceSavingsPlans. Other services don't have the same compatibility shim, so it's load-bearing for Savings Plans alone.
Why it matters
- Any new code path that compares
ServiceConfig.service against common.ServiceSavingsPlans directly (without the normaliser) will silently miss Savings Plans rows.
- Adding a third identifier form (e.g.,
"savings_plans") anywhere is now extra-cheap and an easy oversight.
- The
execution.go case "savings-plans", "savingsplans": branch suggests historical drift — pinning a single canonical removes the foot-gun.
Out of scope (handled in #63)
#63 addressed a downstream consequence — the frontend commitmentOptions.ts had registered the AWS Savings Plans config under "savings-plans" while every other frontend call site used "savingsplans", causing all lookups to fall through to _default. That's now fixed within the frontend's own naming scheme; this issue tracks the broader frontend↔backend split.
Suggested approach
Pick one canonical and migrate the other side, with care for persisted rows:
- Option A — converge on
"savings-plans" (backend canonical): requires a one-shot SQL migration to rename service_configs.service rows from "savingsplans" → "savings-plans", plus updating the dropdown option values, SERVICE_FIELDS, and commitmentConfigs. Aligns with the pkg/common/types.go constant.
- Option B — converge on
"savingsplans" (frontend canonical): change pkg/common/types.go:49 to "savingsplans" (Go-side enum-style change is safer than a DB migration), drop the case "savings-plans" branch from execution.go:384, audit Go callers for hardcoded "savings-plans" strings.
Either option ends with a single normaliser at the API boundary and removes the dual-case branch.
Acceptance
- One canonical service identifier for Savings Plans across frontend + backend + DB.
- The dual-case branch in
execution.go:384 removed.
- A regression test that fails if a future commit reintroduces a divergent identifier.
Discovered while sweeping CodeRabbit nitpicks in #63.
Problem
Savings Plansis identified inconsistently across the stack:frontend/src/index.html:112,703"savingsplans"SERVICE_FIELDSfrontend/src/settings.ts:32"savingsplans"commitmentConfigsfrontend/src/commitmentOptions.ts:58"savingsplans"(after #63)pkg/common/types.go:49"savings-plans"cmd/main.go:92"savingsplans"internal/purchase/execution.go:384case "savings-plans", "savingsplans":The frontend persists
"savingsplans"to theservice_configs.servicecolumn. The GoServiceTypeconstant is"savings-plans". The two sides only line up becauseexecution.godoes a dual-case branch on the way tocommon.ServiceSavingsPlans. Other services don't have the same compatibility shim, so it's load-bearing for Savings Plans alone.Why it matters
ServiceConfig.serviceagainstcommon.ServiceSavingsPlansdirectly (without the normaliser) will silently miss Savings Plans rows."savings_plans") anywhere is now extra-cheap and an easy oversight.execution.gocase "savings-plans", "savingsplans":branch suggests historical drift — pinning a single canonical removes the foot-gun.Out of scope (handled in #63)
#63 addressed a downstream consequence — the frontend
commitmentOptions.tshad registered the AWS Savings Plans config under"savings-plans"while every other frontend call site used"savingsplans", causing all lookups to fall through to_default. That's now fixed within the frontend's own naming scheme; this issue tracks the broader frontend↔backend split.Suggested approach
Pick one canonical and migrate the other side, with care for persisted rows:
"savings-plans"(backend canonical): requires a one-shot SQL migration to renameservice_configs.servicerows from"savingsplans"→"savings-plans", plus updating the dropdown option values,SERVICE_FIELDS, andcommitmentConfigs. Aligns with thepkg/common/types.goconstant."savingsplans"(frontend canonical): changepkg/common/types.go:49to"savingsplans"(Go-side enum-style change is safer than a DB migration), drop thecase "savings-plans"branch fromexecution.go:384, audit Go callers for hardcoded"savings-plans"strings.Either option ends with a single normaliser at the API boundary and removes the dual-case branch.
Acceptance
execution.go:384removed.Discovered while sweeping CodeRabbit nitpicks in #63.