From 61fdd3d995cae16d539e09087525707a0e640419 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 28 Apr 2026 02:01:12 +0200 Subject: [PATCH 1/3] docs(README): document per-account service overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #117. Per-account service overrides have been a feature since migration 000011_cloud_accounts.up.sql (mid-2024) and now have full UI coverage via #72 (inline payment edit) and #106 (create modal). Adds a new "Per-Account Service Overrides" section to README between "Coverage Percentage" and "Safety Features" covering: - Concept + when to use vs the global Settings → Purchasing card. - Web-UI walkthrough (Settings → Accounts → expand → Service overrides). - AWS-only V1 boundary callout pointing at #109 for Azure/GCP. - How to edit existing overrides — inline Payment per #72, Reset, and the #110 follow-up for inline Term/Coverage/Enabled. - "Inherit" semantics: blank fields are not stored as a sentinel; the PUT request stays sparse and the engine reads the global default at evaluation time. - API parity note: the override modal targets the same endpoint as scripted setups; both write the same row. Also disables MD060 (table-column-style) in .markdownlint.yaml — same rationale as PR #169 (landing here too in case the PRs merge in a different order; both diffs are idempotent). --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index ac42b32dc..541dcd960 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,69 @@ The coverage percentage controls what portion of recommendations to act on: | 25% | Quarter of recommendations | Testing/validation | | 0% | Skip service entirely | Exclude from processing | +## Per-Account Service Overrides + +Per-account service overrides let you tweak the global Settings → Purchasing +defaults (term, payment, coverage) on a per-account, per-service basis. + +### When to use them + +Use overrides when an account's purchasing policy differs from the rest of +your fleet: + +- **Dev/staging accounts**: prefer 1-year, no-upfront for RDS to keep + flexibility cheap, while production uses the global 3-year all-upfront. +- **Workload-shape outliers**: an account that runs a steady ElastiCache + cluster can override to 3-year all-upfront for ElastiCache only, while + the same account inherits the global default for everything else. +- **Pilot rollouts**: enable a new SP plan-type on one account first, + leave it disabled globally, then promote when proven. + +If every account should get the same change, edit the **global** Settings → +Purchasing card instead — that propagates without per-account work. + +### How to create an override (web UI) + +1. Open the dashboard → **Settings → Accounts**. +2. Find the account row → click the row to expand it → click + **Service overrides**. +3. The override modal opens. Pick the (provider, service) pair you want + to override and fill in any of `term`, `payment`, `coverage`, + `enabled`. Fields you leave blank inherit the global default — see + "What 'Inherit' means" below. +4. Click **Save**. The override row appears under the account; the + recommendation engine reads it on the next refresh. + +> **AWS-only V1 boundary**: the override modal currently lists AWS +> services. Azure and GCP override UIs are tracked in #109. The +> backend already accepts overrides for any provider via the API, so +> scripted setups work today; only the UI is gated. + +### How to edit an existing override + +- **Inline edit Payment** on an existing override row (per #72) — pick + the new value from the dropdown and it persists immediately. +- **Reset** deletes the override entirely; the account falls back to + the global default for that (provider, service) pair. +- Inline edit for **Term / Coverage / Enabled** is tracked in #110 — + for now those fields require deleting and recreating the override. + +### What "Inherit" means + +A blank field on an override is **not stored** as a sentinel value — the +PUT request omits the field, the row stays sparse, and the recommendation +engine reads the global default at evaluation time. So if you set the +global default from `3yr no-upfront` to `1yr no-upfront`, every override +that left `term` blank starts producing 1-year recommendations +automatically. Overrides that explicitly set `term: 3yr` keep that. + +### API parity + +The override modal targets the same endpoint as scripted setups: +`PUT /api/accounts/{id}/service-overrides/{provider}/{service}`. Existing +automation continues to work without change — the UI and the API write to +the same `account_service_overrides` row. + ## Safety Features CUDly includes multiple safety mechanisms to prevent unintended purchases: From 5dd5533007965e024dee689fba88734787271af3 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Sat, 6 Jun 2026 08:50:41 +0200 Subject: [PATCH 2/3] docs(readme): correct stale override edit/delete wording to shipped UI (refs #117) - Replace stale "#110 tracked / delete-and-recreate" limitation with the shipped reality: AWS overrides support inline edit of Term, Payment, Coverage, and Enabled directly on the row; non-AWS providers are read-only. - Rename "Reset" to "Delete" throughout the override-editing section to match the current button label and confirm dialog title (per #114). --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 541dcd960..6d3f8bbe1 100644 --- a/README.md +++ b/README.md @@ -351,12 +351,12 @@ Purchasing card instead — that propagates without per-account work. ### How to edit an existing override -- **Inline edit Payment** on an existing override row (per #72) — pick - the new value from the dropdown and it persists immediately. -- **Reset** deletes the override entirely; the account falls back to +- **AWS overrides support inline edit** of Term, Payment, Coverage, and + Enabled directly on the row — each field persists immediately on change. +- **Non-AWS overrides** (Azure, GCP) display field values as read-only text; + per-provider editing is tracked in #109. +- **Delete** removes the override entirely; the account falls back to the global default for that (provider, service) pair. -- Inline edit for **Term / Coverage / Enabled** is tracked in #110 — - for now those fields require deleting and recreating the override. ### What "Inherit" means From cc91cadd22cc4799d166909da8547e0373dddabf Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Fri, 19 Jun 2026 17:34:09 +0200 Subject: [PATCH 3/3] docs(README): fix stale provider-boundary and em-dash issues in override section - Replace the stale "AWS-only V1 boundary" callout with accurate text: the override modal is provider-aware and lists services for the account's provider (AWS, Azure, GCP). Issues #109 and #110 shipped and both the create modal and inline-edit work for all providers. - Replace "Non-AWS overrides display field values as read-only text" with the correct statement that all override rows support inline edit (Term, Payment, Coverage, Enabled). References to tracking issue #109 removed as it is closed. - Replace five U+2014 em-dashes in the new section with punctuation that matches the project prose style (semicolons, parentheses, period). --- README.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6d3f8bbe1..3e47e79be 100644 --- a/README.md +++ b/README.md @@ -329,8 +329,8 @@ your fleet: - **Pilot rollouts**: enable a new SP plan-type on one account first, leave it disabled globally, then promote when proven. -If every account should get the same change, edit the **global** Settings → -Purchasing card instead — that propagates without per-account work. +If every account should get the same change, edit the **global** Settings -> +Purchasing card instead; that propagates without per-account work. ### How to create an override (web UI) @@ -339,28 +339,25 @@ Purchasing card instead — that propagates without per-account work. **Service overrides**. 3. The override modal opens. Pick the (provider, service) pair you want to override and fill in any of `term`, `payment`, `coverage`, - `enabled`. Fields you leave blank inherit the global default — see - "What 'Inherit' means" below. + `enabled`. Fields you leave blank inherit the global default (see + "What 'Inherit' means" below). 4. Click **Save**. The override row appears under the account; the recommendation engine reads it on the next refresh. -> **AWS-only V1 boundary**: the override modal currently lists AWS -> services. Azure and GCP override UIs are tracked in #109. The -> backend already accepts overrides for any provider via the API, so -> scripted setups work today; only the UI is gated. +> **All providers supported**: the override modal lists services for +> the account's provider (AWS, Azure, GCP). The UI and the backend +> both accept overrides for any provider. ### How to edit an existing override -- **AWS overrides support inline edit** of Term, Payment, Coverage, and - Enabled directly on the row — each field persists immediately on change. -- **Non-AWS overrides** (Azure, GCP) display field values as read-only text; - per-provider editing is tracked in #109. +- All override rows support **inline edit** of Term, Payment, Coverage, and + Enabled directly on the row. Each field persists immediately on change. - **Delete** removes the override entirely; the account falls back to the global default for that (provider, service) pair. ### What "Inherit" means -A blank field on an override is **not stored** as a sentinel value — the +A blank field on an override is **not stored** as a sentinel value. The PUT request omits the field, the row stays sparse, and the recommendation engine reads the global default at evaluation time. So if you set the global default from `3yr no-upfront` to `1yr no-upfront`, every override @@ -371,7 +368,7 @@ automatically. Overrides that explicitly set `term: 3yr` keep that. The override modal targets the same endpoint as scripted setups: `PUT /api/accounts/{id}/service-overrides/{provider}/{service}`. Existing -automation continues to work without change — the UI and the API write to +automation continues to work without change; the UI and the API write to the same `account_service_overrides` row. ## Safety Features