From 838f8d6ff6d21fd4da72de8839ee5321471de6e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 13:06:41 +0000 Subject: [PATCH 01/20] fix: stop silently substituting bids below the real minimum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setAdGroupDefaultBid (and setTargetBid, which had the same pattern) validated only that a bid was finite/non-negative, then silently rewrote anything below Amazon's real $0.02 minimum to $0.02 — a caller requesting $0.00 got $0.02 back with a history entry claiming that's what they asked for, contradicting the engine's documented fail-fast convention. Added assertValidBid (finite, non-negative, and >= the real minimum) for these "set an explicit bid on an existing entity" actions, and removed the clamp. Left addTarget/normalizeCampaign's clamping alone — those are creation/normalization paths filling in defaults for otherwise-incomplete data, a different and still-valid use case for clamping. Tightened the two UI call sites' pre-submit guards to match the new floor so they no longer throw uncaught. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B --- .../AdConsole/details/AdGroupsTab.tsx | 6 +++++- src/components/AdConsole/details/TargetsTab.tsx | 3 ++- .../ad-console/core/__tests__/adgroup.test.ts | 14 +++++++++++++- .../ad-console/core/__tests__/engine.test.ts | 6 ++++++ src/engine/ad-console/core/engine/adgroup.ts | 8 ++++---- src/engine/ad-console/core/engine/target.ts | 8 ++++---- src/lib/validation.ts | 17 +++++++++++++++++ 7 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/components/AdConsole/details/AdGroupsTab.tsx b/src/components/AdConsole/details/AdGroupsTab.tsx index 4283c02..1423be9 100644 --- a/src/components/AdConsole/details/AdGroupsTab.tsx +++ b/src/components/AdConsole/details/AdGroupsTab.tsx @@ -10,6 +10,7 @@ import { TextInput } from '@astryxdesign/core/TextInput'; import type { Campaign } from '@/engine/ad-console/types'; import { useAdConsoleStore } from '@/engine/ad-console/store'; import { calc, formatMoney, formatWhole, formatPercent, formatBid, acosClass } from '@/engine/ad-console/core/engine'; +import { MIN_BID } from '@/lib/validation'; import { EmptyState } from './EmptyState'; interface Props { @@ -62,7 +63,10 @@ export function AdGroupsTab({ campaign: c }: Props) { />