Repository navigation
ops(plans): clean up existing universal plans (DB rows with no plan_accounts entry) #742
Description
Context
PR "fix(plans): eliminate universal plans" closes the API hole that allowed
creating "universal plans" — rows in purchase_plans with no matching row in
plan_accounts. After that PR lands, the API rejects empty target_accounts
on POST /api/plans and the modal Save button stays disabled until the user
picks at least one account.
That PR does NOT touch existing universal plans already in the DB. Those rows
predate the enforcement and need operator-driven cleanup. This issue tracks
the cleanup decision + execution.
Diagnostic
Run the read-only diagnostic to enumerate every universal plan currently in
the DB (count + IDs + names + creation dates + services):
psql "$DB_URL" -f scripts/list_universal_plans.sqlThe script's header documents the three cleanup options inline so you can
decide per plan without leaving psql.
Three cleanup options
(a) Delete
Use when the plan is genuinely orphaned — no consumer, no executions, never
used. Safest when the row predates any intentional use.
DELETE FROM purchase_plans WHERE id = '<uuid>';(b) Fan out
Attach every enabled cloud_account whose provider matches the plan's
services map. Mimics the historical "blank == all provider accounts"
intent; concrete, explicit, and recoverable.
INSERT INTO plan_accounts (plan_id, account_id)
SELECT pp.id, ca.id
FROM purchase_plans pp
CROSS JOIN LATERAL jsonb_each(pp.services) AS svc(k, v)
JOIN cloud_accounts ca ON ca.provider = v->>'provider'
WHERE pp.id = '<uuid>'
ON CONFLICT DO NOTHING;(c) Manual review
Pull the plan up in the UI, talk to the owner if known, and assign the right
account(s) via the (now-required-Target-Account) New/Edit Plan modal.
Recommended for plans with clear intent the SQL can't infer.
Recommendation
Default to (c) manual review per plan, unless the row is obviously orphaned
(zero executions, owner unreachable) — in which case (a) DELETE is fine. (b)
fan-out is safe but loud (creates a lot of plan_accounts rows in one go);
use it only after confirming the original intent.
Related
- PR (eliminate-universal-plans): fixes the source so no new universal plans
can be created. - PR fix(plans): include universal plans in Account filter (refs #705) #739 (still open): added a UNION branch to
buildListPlansQueryso
universal plans appeared in the account-filtered list. After eliminate-
universal-plans merges + this cleanup completes, that branch is dead code.
Activity
docs(scripts): fix issue ref + add enabled filter in list_universal_p…
fix(plans): eliminate universal plans — require target_accounts on cr…
cristim commentedon May 28, 2026
Verification: cannot-verify-autopilot. PR #743 (commit f5b76eb) is in feat/multicloud-web-frontend and enforces that new plans must have target_accounts -- that closes the source of future universal plans. However, issue #742 specifically tracks cleaning up EXISTING universal plan DB rows (options: delete, fan-out, or manual review per the issue body). That requires DB access and is an operator action, not verifiable from outside the DB. Leaving open for operator confirmation.
fix(migrations): renumber direct-execute-audit 000057 -> 000058
feat(api,recs): execute-any/own permission gate on direct execute (cl…
ops(plans): migration to clean up universal plans (closes #742) (#802)