From 3baef2799da7a5754920dfd592b98761eeb34a40 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Sun, 27 Sep 2026 23:35:44 +0200 Subject: [PATCH 1/5] docs(readme): re-apply agent-SEO safety positioning from #2095 Re-applies Eli Finer's PR #2095 (branch agent-seo-readme, based on the old monorepo history) onto the new CLI-only main after the CUDly -> cloud-commitments-cli split. Ports the safety-first framing and agent positioning: a Key Features list, a prominent Safety Features section, and an Implementation Status table. Drops the PR's "real, interactive terminal" purchase gate and "--yes retired" claims: cmd/helpers.go's ConfirmPurchase returns true for --yes before the TTY check ever runs, so no automation boundary exists on the purchase path today (confirmed by CodeRabbit's review on #2095 and tracked in #1943, already open). The README and purchase-safety.md now state that gap explicitly and link #1943 instead of describing unbuilt behavior as current. GAPS-VS-CLAIMS.md is not carried over: it audits the old monorepo README against a plan/approval design that was replaced before #2095 even opened, and against a repo-split-pending state that has since landed. Its one still-relevant finding (the --yes bypass) already has a live tracking issue (#1943), so it is linked from the docs instead of duplicated into a static file. Co-Authored-By: Eli Finer Co-Authored-By: claude-flow --- README.md | 40 ++++++++++++++++++++++++++++++------- docs/cli/purchase-safety.md | 9 ++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7aa55530a..f37eae344 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,39 @@ # CUDly CLI -The CLI discovers cloud commitment recommendations and can purchase AWS Reserved Instances, Savings Plans, and selected Azure and GCP commitments. Amazon RDS and ElastiCache are the tested AWS service paths. Other AWS services, Azure, and GCP support remain experimental. +CUDly is an open source CLI for discovering and purchasing cloud commitments — AWS Reserved Instances and Savings Plans, plus selected Azure and GCP commitments — in a single command. It is dry-run by default: nothing is purchased until you pass `--purchase`. + +It is also built to be driven by an AI agent for the discovery and analysis side: searching recommendations, sizing a plan, filtering by account or region. The purchase step still needs a human to review the numbers before committing money. **`--yes` currently skips the confirmation prompt outright, including for a non-interactive caller** (a script, a CI job, an agent driving the CLI as a subprocess) — see [Safety Features](#safety-features) and [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) before wiring `--purchase --yes` into anything unattended. The CLI depends on the published shared Go modules in [cloud-commitments-go](https://github.com/LeanerCloud/cloud-commitments-go), pinned to fixed versions in `go.mod`. No sibling checkout or parent workspace is needed for local development. +## Key Features + +- **Dry-run by default** - `--purchase` is the only opt-in that moves money; a bare invocation only prints results and writes a CSV. +- **Grounded recommendations** - built from the cloud provider's own recommendation APIs (AWS Cost Explorer, Azure Advisor, GCP recommender), not estimated locally. +- **Multi-cloud, one interface** - AWS, Azure, and GCP through the same command and flags. See [Implementation Status](#implementation-status) for per-provider maturity. +- **Coverage control** - purchase a percentage of what's recommended, or of actual historical usage via `--target-coverage`, instead of buying everything a provider suggests in one run. +- **CSV + audit log** - every dry run and every purchase is written to CSV and to a permanent JSONL audit log. + +## Safety Features + +1. **Dry-run by default** - no purchase without the explicit `--purchase` flag. +2. **Confirmation prompt** - `--purchase` prints a summary of instance count and estimated savings, then prompts for confirmation. `--yes` skips this prompt, including for a non-interactive caller - it is not currently an automation boundary. [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. +3. **Coverage and instance limits** - `--coverage`, `--target-coverage`, and `--max-instances` shape what a dry run recommends before there is anything to confirm. +4. **Instance type validation** - every recommendation is checked against known, valid instance types before it is shown. +5. **Full audit trail** - every recommendation, purchased or not, is written to the audit log before any purchase API call runs. +6. **Permanent CSV exports** of every dry run and every purchase. +7. **No CLI-side duplicate-purchase prevention yet** - `--idempotency-window` is accepted but has no effect on the CLI path; deduplication only runs in the self-hosted platform's server-side scheduler. Review the dry-run CSV and audit log before retrying a run. + +Full internals: [Purchase Safety](docs/cli/purchase-safety.md). + +## Implementation Status + +| Cloud | Status | +|---|---| +| AWS | Production - Amazon RDS and ElastiCache are the tested paths; other AWS services remain experimental. | +| Azure | Experimental - selected commitment types; maturity can vary by service and account. | +| GCP | Experimental - selected commitment types; maturity can vary by service and account. | + ## Build Use the Go version declared in `go.mod`. @@ -25,24 +55,20 @@ Preview RDS recommendations before enabling a purchase: ./cudly --services rds --profile default ``` -Use `--purchase` to enable a purchase operation. Use `--yes` to skip its confirmation prompt. A terminal prompt is not an automation boundary. Read the purchase-safety guide before using this mode. The CLI's `--idempotency-window` flag does not prevent duplicate purchases in the CLI path; review the dry-run output and audit log before retrying. - Export a reviewable report when you need to share results: ```bash ./cudly --services rds --profile default --output recommendations.csv ``` -All purchase operations can spend money. Check the account, region, quantity, and selected commitment before confirming. +All purchase operations can spend money. Check the account, region, quantity, and selected commitment before confirming - see [Safety Features](#safety-features) for what is and is not enforced today. ## Credentials and provider status Use the provider's supported credential chain. For AWS, select a profile with `--profile` and validate access before a purchase. Follow the cloud setup guide for Azure and GCP. -- Amazon RDS and ElastiCache are the tested AWS service paths. -- Other AWS service paths can change and are not covered by the same maturity claim. -- Azure and GCP support is experimental and can vary by service and account. - Recommendation data and purchase APIs can change outside this repository. +- See [Implementation Status](#implementation-status) for per-cloud maturity. ## Related components diff --git a/docs/cli/purchase-safety.md b/docs/cli/purchase-safety.md index b81dd289a..0d0b24072 100644 --- a/docs/cli/purchase-safety.md +++ b/docs/cli/purchase-safety.md @@ -2,6 +2,10 @@ CUDly is designed to be safe by default. Real purchases require multiple explicit opt-ins, and several mechanisms prevent duplicate or unintended buys. +## Automation and AI agents + +An AI agent (or any other non-interactive caller) can drive discovery, sizing, and filtering safely: none of that reads or writes cloud commitments. Purchasing is different. `--purchase --yes` executes a real purchase from any invocation - a script, a CI job, or an agent running `cudly` as a subprocess included - because `--yes` skips the confirmation prompt before the interactive-terminal check ever runs. There is currently no automation boundary on the purchase path; [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. Until it lands, treat `--purchase --yes` as unattended purchase automation, and keep it out of anything an agent can trigger on its own. + ## The purchase decision: --purchase ```text @@ -42,8 +46,10 @@ cudly --input-csv recs.csv --purchase When running in purchase mode (`isDryRun=false`), cudly prints a summary of the total instance count and estimated savings and prompts for confirmation before executing any purchase. Pass `--yes` to skip this prompt in automation. +`--yes` skips the prompt unconditionally - it is not gated on whether the process has a real, interactive terminal. A script, a CI job, or an agent driving `cudly` as a subprocess can pass `--yes` and execute a purchase exactly as a human at a terminal would. Treat `--purchase --yes` as fully unattended purchase automation, not as a convenience for a human who already confirmed elsewhere. See [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) for the tracked work to close this gap. + ```bash -# Unattended purchase (use with care): +# Unattended purchase (use with care - see the note above): cudly --services rds --purchase --yes ``` @@ -128,3 +134,4 @@ Before any real purchase run: 4. Narrow the scope with `--include-regions`, `--include-accounts`, or `--min-savings-pct` before buying across all services. 5. Consider `--max-instances` as a final safety cap for a first run. 6. Note that `--idempotency-window` does not prevent double-buying in the CLI path; use a dry-run review (run without `--purchase`) and audit-log inspection to guard against retried runs. +7. If an AI agent or other automation drives `cudly`, never pass `--yes` to it directly - have the agent hand off the dry-run recommendation to a human, who runs `--purchase` themselves. See [Automation and AI agents](#automation-and-ai-agents). From ce779c02d1995c94063f1282ede9f69de218d65a Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Sun, 27 Sep 2026 23:52:51 +0200 Subject: [PATCH 2/5] docs(readme): fix duplicate-purchase claim in purchase-safety intro CodeRabbit review on #2098 correctly flagged that the intro line ("several mechanisms prevent duplicate or unintended buys") contradicts the file's own later statement that the CLI has no idempotency check against previously-purchased commitments. Narrow the intro to the mechanisms that actually exist and point to the existing Duplicate purchase prevention section for the gap. Co-Authored-By: claude-flow --- docs/cli/purchase-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/purchase-safety.md b/docs/cli/purchase-safety.md index 0d0b24072..8bceb4691 100644 --- a/docs/cli/purchase-safety.md +++ b/docs/cli/purchase-safety.md @@ -1,6 +1,6 @@ # Purchase Safety -CUDly is designed to be safe by default. Real purchases require multiple explicit opt-ins, and several mechanisms prevent duplicate or unintended buys. +CUDly is designed to be safe by default. Real purchases require an explicit `--purchase` opt-in, and several mechanisms - coverage limits, instance-type validation, and a full audit trail - guard against unintended buys. Duplicate-purchase prevention is not one of them today: the CLI path has no idempotency check against previously-purchased commitments (see [Duplicate purchase prevention](#duplicate-purchase-prevention---idempotency-window) below). ## Automation and AI agents From e3d7d6e4637d7326c0a2b66604da793e570e50b7 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 28 Sep 2026 00:18:46 +0200 Subject: [PATCH 3/5] docs(readme): scope README to the AWS-only CLI workflow CodeRabbit's review on #2098 flagged the README's multi-cloud framing as unsupported: cmd/main.go's createServiceClient only switches on AWS service types (falls through to nil otherwise), and runToolMultiService always instantiates an AWS-only RecommendationsClient. docs/cli/cloud-setup.md already documents that configure-azure/configure-gcp only bootstrap credentials for the separate self-hosted platform, "not part of the regular analysis/purchase workflow." Rewrite the intro, Key Features, Implementation Status, and Credentials sections to describe the CLI's actual AWS-only recommend-and-purchase surface, and point Azure/GCP readers at configure-azure/configure-gcp and the self-hosted platform instead of implying this CLI purchases Azure/GCP commitments directly. Co-Authored-By: claude-flow --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f37eae344..0d0f063a4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CUDly CLI -CUDly is an open source CLI for discovering and purchasing cloud commitments — AWS Reserved Instances and Savings Plans, plus selected Azure and GCP commitments — in a single command. It is dry-run by default: nothing is purchased until you pass `--purchase`. +CUDly is an open source CLI for discovering and purchasing AWS Reserved Instances and Savings Plans in a single command. It is dry-run by default: nothing is purchased until you pass `--purchase`. `configure-azure` and `configure-gcp` bootstrap credentials for the separate [self-hosted platform](https://github.com/LeanerCloud/cloud-commitments-platform); this CLI's own recommend-and-purchase workflow is AWS-only today. See [cloud setup](docs/cli/cloud-setup.md). It is also built to be driven by an AI agent for the discovery and analysis side: searching recommendations, sizing a plan, filtering by account or region. The purchase step still needs a human to review the numbers before committing money. **`--yes` currently skips the confirmation prompt outright, including for a non-interactive caller** (a script, a CI job, an agent driving the CLI as a subprocess) — see [Safety Features](#safety-features) and [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) before wiring `--purchase --yes` into anything unattended. @@ -9,8 +9,8 @@ The CLI depends on the published shared Go modules in [cloud-commitments-go](htt ## Key Features - **Dry-run by default** - `--purchase` is the only opt-in that moves money; a bare invocation only prints results and writes a CSV. -- **Grounded recommendations** - built from the cloud provider's own recommendation APIs (AWS Cost Explorer, Azure Advisor, GCP recommender), not estimated locally. -- **Multi-cloud, one interface** - AWS, Azure, and GCP through the same command and flags. See [Implementation Status](#implementation-status) for per-provider maturity. +- **Grounded recommendations** - built from AWS Cost Explorer's own recommendation data, not estimated locally. +- **Multiple AWS services, one interface** - RDS, ElastiCache, EC2, OpenSearch, Redshift, MemoryDB, and Savings Plans through the same command and flags. See [Implementation Status](#implementation-status) for per-service maturity. - **Coverage control** - purchase a percentage of what's recommended, or of actual historical usage via `--target-coverage`, instead of buying everything a provider suggests in one run. - **CSV + audit log** - every dry run and every purchase is written to CSV and to a permanent JSONL audit log. @@ -28,11 +28,12 @@ Full internals: [Purchase Safety](docs/cli/purchase-safety.md). ## Implementation Status -| Cloud | Status | +| AWS service | Status | |---|---| -| AWS | Production - Amazon RDS and ElastiCache are the tested paths; other AWS services remain experimental. | -| Azure | Experimental - selected commitment types; maturity can vary by service and account. | -| GCP | Experimental - selected commitment types; maturity can vary by service and account. | +| RDS, ElastiCache | Production - the tested paths. | +| EC2, OpenSearch, Redshift, MemoryDB, Savings Plans | Experimental - implemented and functional, still accumulating real-world purchase validation. | + +Azure and GCP are not part of this CLI's recommend-and-purchase workflow; `configure-azure` and `configure-gcp` only bootstrap credentials for the [self-hosted platform](https://github.com/LeanerCloud/cloud-commitments-platform). ## Build @@ -65,10 +66,11 @@ All purchase operations can spend money. Check the account, region, quantity, an ## Credentials and provider status -Use the provider's supported credential chain. For AWS, select a profile with `--profile` and validate access before a purchase. Follow the cloud setup guide for Azure and GCP. +Use the AWS SDK's supported credential chain. Select a profile with `--profile` and validate access before a purchase. - Recommendation data and purchase APIs can change outside this repository. -- See [Implementation Status](#implementation-status) for per-cloud maturity. +- See [Implementation Status](#implementation-status) for per-service maturity. +- `configure-azure` and `configure-gcp` bootstrap credentials for the self-hosted platform, not for this CLI - see [cloud setup](docs/cli/cloud-setup.md). ## Related components From e1d55e01b829de8a7566e0f5fd06d67994d6f29c Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 28 Sep 2026 01:02:46 +0200 Subject: [PATCH 4/5] docs(readme): fix duplicate-check, audit-log-order, and instance-type claims Independent review of PR #2098 found three more inaccuracies against cmd/, verified directly before fixing: - Duplicate-purchase prevention is real, not server-only: checkDuplicates (cmd/multi_service_helpers.go:580) and adjustRecsForDuplicates (cmd/multi_service.go:545) both call recfilter.DuplicateChecker.AdjustRecommendationsForExisting*, which subtracts commitments purchased in the last 24h (DefaultDuplicateCheckLookbackHours) on every path. What's actually true: --idempotency-window's value is never read (NewDuplicateChecker is always called with 0, the hardcoded default) - #1262 - and a failed existing-commitments lookup lets the run continue un-deduplicated with a warning - #1941. Rewrote the README, the Duplicate purchase prevention section, and the safety checklist in docs/cli/purchase-safety.md around that reality instead of "no CLI-side prevention" / "server-side scheduler only". - Audit records are written after each purchase call returns (cmd/multi_service.go:399-404: WriteAuditRecord follows purchaseSingleRec), not before. Only the audit-log path's writability is checked up front (multi_service.go:107-109). Corrected both README.md and purchase-safety.md's audit-log section. - "Instance type validation against known types" doesn't exist - validators.go's validateInstanceTypes only checks for a '.' separator. Replaced the README bullet with the real RDS extended-support filter (multi_service_helpers.go, --include-extended-support). Also: removed an em-dash from README.md, corrected "not estimated locally" (contradicted by --target-coverage's local sizing), and clarified that agent-safe discovery reads existing commitments (it doesn't just avoid writes). Co-Authored-By: claude-flow --- README.md | 10 +++++----- docs/cli/purchase-safety.md | 18 +++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0d0f063a4..48f41d95c 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ CUDly is an open source CLI for discovering and purchasing AWS Reserved Instances and Savings Plans in a single command. It is dry-run by default: nothing is purchased until you pass `--purchase`. `configure-azure` and `configure-gcp` bootstrap credentials for the separate [self-hosted platform](https://github.com/LeanerCloud/cloud-commitments-platform); this CLI's own recommend-and-purchase workflow is AWS-only today. See [cloud setup](docs/cli/cloud-setup.md). -It is also built to be driven by an AI agent for the discovery and analysis side: searching recommendations, sizing a plan, filtering by account or region. The purchase step still needs a human to review the numbers before committing money. **`--yes` currently skips the confirmation prompt outright, including for a non-interactive caller** (a script, a CI job, an agent driving the CLI as a subprocess) — see [Safety Features](#safety-features) and [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) before wiring `--purchase --yes` into anything unattended. +It is also built to be driven by an AI agent for the discovery and analysis side: searching recommendations, sizing a plan, filtering by account or region. The purchase step still needs a human to review the numbers before committing money. **`--yes` currently skips the confirmation prompt outright, including for a non-interactive caller** (a script, a CI job, an agent driving the CLI as a subprocess): see [Safety Features](#safety-features) and [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) before wiring `--purchase --yes` into anything unattended. The CLI depends on the published shared Go modules in [cloud-commitments-go](https://github.com/LeanerCloud/cloud-commitments-go), pinned to fixed versions in `go.mod`. No sibling checkout or parent workspace is needed for local development. ## Key Features - **Dry-run by default** - `--purchase` is the only opt-in that moves money; a bare invocation only prints results and writes a CSV. -- **Grounded recommendations** - built from AWS Cost Explorer's own recommendation data, not estimated locally. +- **Grounded recommendations** - sized from AWS Cost Explorer's own recommendation and coverage data, not a locally-guessed baseline. - **Multiple AWS services, one interface** - RDS, ElastiCache, EC2, OpenSearch, Redshift, MemoryDB, and Savings Plans through the same command and flags. See [Implementation Status](#implementation-status) for per-service maturity. - **Coverage control** - purchase a percentage of what's recommended, or of actual historical usage via `--target-coverage`, instead of buying everything a provider suggests in one run. - **CSV + audit log** - every dry run and every purchase is written to CSV and to a permanent JSONL audit log. @@ -19,10 +19,10 @@ The CLI depends on the published shared Go modules in [cloud-commitments-go](htt 1. **Dry-run by default** - no purchase without the explicit `--purchase` flag. 2. **Confirmation prompt** - `--purchase` prints a summary of instance count and estimated savings, then prompts for confirmation. `--yes` skips this prompt, including for a non-interactive caller - it is not currently an automation boundary. [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. 3. **Coverage and instance limits** - `--coverage`, `--target-coverage`, and `--max-instances` shape what a dry run recommends before there is anything to confirm. -4. **Instance type validation** - every recommendation is checked against known, valid instance types before it is shown. -5. **Full audit trail** - every recommendation, purchased or not, is written to the audit log before any purchase API call runs. +4. **RDS extended-support filtering** - by default, recommendations for instances running an engine version in AWS Extended Support are excluded, since the surcharge can erase RI savings; pass `--include-extended-support` to include them. +5. **Audit log written per purchase** - the audit log path is checked for writability before any cloud API call; each recommendation's audit record (purchased or dry-run, with its result) is then written as soon as that purchase call returns. 6. **Permanent CSV exports** of every dry run and every purchase. -7. **No CLI-side duplicate-purchase prevention yet** - `--idempotency-window` is accepted but has no effect on the CLI path; deduplication only runs in the self-hosted platform's server-side scheduler. Review the dry-run CSV and audit log before retrying a run. +7. **Duplicate-purchase dedup, with a caveat** - every path (`--services` and `--input-csv`) subtracts commitments purchased in the last 24 hours before sizing a recommendation. `--idempotency-window` doesn't change that fixed 24h lookback yet ([#1262](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1262)), and if the existing-commitments API call itself fails, the run continues un-deduplicated with a warning ([#1941](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1941)). Full internals: [Purchase Safety](docs/cli/purchase-safety.md). diff --git a/docs/cli/purchase-safety.md b/docs/cli/purchase-safety.md index 8bceb4691..21e4af7b5 100644 --- a/docs/cli/purchase-safety.md +++ b/docs/cli/purchase-safety.md @@ -1,10 +1,10 @@ # Purchase Safety -CUDly is designed to be safe by default. Real purchases require an explicit `--purchase` opt-in, and several mechanisms - coverage limits, instance-type validation, and a full audit trail - guard against unintended buys. Duplicate-purchase prevention is not one of them today: the CLI path has no idempotency check against previously-purchased commitments (see [Duplicate purchase prevention](#duplicate-purchase-prevention---idempotency-window) below). +CUDly is designed to be safe by default. Real purchases require an explicit `--purchase` opt-in, and several mechanisms - coverage limits, a duplicate-purchase check, RDS extended-support filtering, and a full audit trail - guard against unintended or repeated buys. See [Duplicate purchase prevention](#duplicate-purchase-prevention---idempotency-window) below for what that check does and does not cover. ## Automation and AI agents -An AI agent (or any other non-interactive caller) can drive discovery, sizing, and filtering safely: none of that reads or writes cloud commitments. Purchasing is different. `--purchase --yes` executes a real purchase from any invocation - a script, a CI job, or an agent running `cudly` as a subprocess included - because `--yes` skips the confirmation prompt before the interactive-terminal check ever runs. There is currently no automation boundary on the purchase path; [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. Until it lands, treat `--purchase --yes` as unattended purchase automation, and keep it out of anything an agent can trigger on its own. +An AI agent (or any other non-interactive caller) can safely drive discovery, sizing, and filtering: that reads recommendations and existing commitments (for example, `--target-coverage`'s coverage lookup and the duplicate check that runs before every purchase), but never purchases anything on its own. Purchasing is different. `--purchase --yes` executes a real purchase from any invocation - a script, a CI job, or an agent running `cudly` as a subprocess included - because `--yes` skips the confirmation prompt before the interactive-terminal check ever runs. There is currently no automation boundary on the purchase path; [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. Until it lands, treat `--purchase --yes` as unattended purchase automation, and keep it out of anything an agent can trigger on its own. ## The purchase decision: --purchase @@ -59,7 +59,7 @@ cudly --services rds --purchase --yes --audit-log string default: ./cudly-audit.jsonl ``` -Every recommendation - whether purchased or dry-run - is written as a JSON line to the audit log file before any purchase API call is made. The audit record includes: +Every recommendation - whether purchased or dry-run - gets a JSON line in the audit log file: the audit log *path* is checked for writability before any cloud API call is made (see below), but each record itself is written right after that recommendation's purchase call returns (immediately, for a dry run). The audit record includes: - Run ID (UUID that groups all purchases in a single invocation) - Recommendation details (service, region, instance type, count, term, payment) @@ -84,12 +84,16 @@ The default path (`./cudly-audit.jsonl`) writes to the current working directory --idempotency-window string default: 24h ``` -This flag is accepted as a Go duration string (e.g. `24h`, `48h`, `1h30m`). The CLI does not validate the string at startup; it stores the raw value but does not yet subtract previously-purchased commitments from new recommendations based on this window. The deduction logic runs in the server-side scheduler path (where the duration IS parsed), not the CLI purchase loop. Passing this flag in CLI invocations has no effect on which recommendations are purchased. +A duplicate check runs before every purchase, on both the `--services` and `--input-csv` paths: it fetches existing commitments and subtracts anything purchased in the last 24 hours from each recommendation's count, so a retried run doesn't buy the same capacity twice. -The audit status value `skipped_covered` (idempotency hit) is defined in the audit record schema for use by the server path and is not emitted by the CLI. +That 24-hour lookback is fixed. This flag is accepted as a Go duration string (e.g. `24h`, `48h`, `1h30m`) and stored, but its value is never read by the check - passing `--idempotency-window 72h` (or any other value) has no effect on which recommendations are purchased ([#1262](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1262) tracks wiring it in). + +If the existing-commitments lookup itself fails (a transient API error), the check is skipped for that batch and the run continues un-deduplicated, with a warning printed to the log rather than the run stopping ([#1941](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1941)). Treat that warning as a signal to check the audit log for the run before trusting its purchase counts. + +The audit status value `skipped_covered` (idempotency hit) is defined in the audit record schema for use by the server-side scheduler path and is not emitted by this CLI's dedup check. ```bash -# Accepted but currently has no effect on CLI recommendation deduction: +# The dedup check always runs with a fixed 24h lookback; this flag's value is not applied: cudly --services rds --idempotency-window 72h ``` @@ -133,5 +137,5 @@ Before any real purchase run: 3. If using `--target-coverage`, verify `--rebuy-window-days` is set appropriately for your RI renewal cadence. 4. Narrow the scope with `--include-regions`, `--include-accounts`, or `--min-savings-pct` before buying across all services. 5. Consider `--max-instances` as a final safety cap for a first run. -6. Note that `--idempotency-window` does not prevent double-buying in the CLI path; use a dry-run review (run without `--purchase`) and audit-log inspection to guard against retried runs. +6. Note that `--idempotency-window`'s value is not applied - dedup always uses a fixed 24h lookback ([#1262](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1262)) - and that a failed existing-commitments lookup lets the run proceed un-deduplicated with a warning ([#1941](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1941)); watch the log for that warning and check the audit log afterward. 7. If an AI agent or other automation drives `cudly`, never pass `--yes` to it directly - have the agent hand off the dry-run recommendation to a human, who runs `--purchase` themselves. See [Automation and AI agents](#automation-and-ai-agents). From d61e5e29474ec771769950027f3dc35845577975 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 28 Sep 2026 01:14:09 +0200 Subject: [PATCH 5/5] docs(readme): distinguish dry-run vs purchase audit-record timing CodeRabbit review on #2098 (round 3) noted the "Audit log written per purchase" bullet blurred dry-run and real-purchase timing under one "as soon as that purchase call returns" phrase, but a dry run has no purchase call (cmd/multi_service.go:413-418 returns a locally-built result without touching cmd/multi_service.go:429's executePurchase). Rename the item to "per recommendation" and split the two cases, to match the wording already used in docs/cli/purchase-safety.md. Co-Authored-By: claude-flow --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48f41d95c..581303f8c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The CLI depends on the published shared Go modules in [cloud-commitments-go](htt 2. **Confirmation prompt** - `--purchase` prints a summary of instance count and estimated savings, then prompts for confirmation. `--yes` skips this prompt, including for a non-interactive caller - it is not currently an automation boundary. [#1943](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1943) tracks closing that gap. 3. **Coverage and instance limits** - `--coverage`, `--target-coverage`, and `--max-instances` shape what a dry run recommends before there is anything to confirm. 4. **RDS extended-support filtering** - by default, recommendations for instances running an engine version in AWS Extended Support are excluded, since the surcharge can erase RI savings; pass `--include-extended-support` to include them. -5. **Audit log written per purchase** - the audit log path is checked for writability before any cloud API call; each recommendation's audit record (purchased or dry-run, with its result) is then written as soon as that purchase call returns. +5. **Audit log written per recommendation** - the audit log path is checked for writability before any cloud API call. Each recommendation then gets its own audit record: for a dry run, written as soon as its (local, no-API-call) result is generated; for a real purchase, written after that purchase call returns. 6. **Permanent CSV exports** of every dry run and every purchase. 7. **Duplicate-purchase dedup, with a caveat** - every path (`--services` and `--input-csv`) subtracts commitments purchased in the last 24 hours before sizing a recommendation. `--idempotency-window` doesn't change that fixed 24h lookback yet ([#1262](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1262)), and if the existing-commitments API call itself fails, the run continues un-deduplicated with a warning ([#1941](https://github.com/LeanerCloud/cloud-commitments-cli/issues/1941)).