Skip to content

Commit 6f0ceb2

Browse files
committed
Tool audit sweep: 4 BLOCKERS, 12 HIGH, 6 MEDIUM fixed across 23 files
After 4 parallel agent audits covering 47 tool surfaces, found and fixed: BLOCKERS (4): - Display banner CharBadge used 30/90 hardcoded limits for every size regardless of which banner (Mobile Banner = 22/28, Wide Skyscraper = 25/80, etc.). Imports DISPLAY_SIZES and looks up per-size h_max/d_max now. - Google Shopping policy_warnings render was unreachable: outer `?.length` truthy gate meant the zero-warnings "clean" message never fired. Now uses Array.isArray() check. - Google Shopping `category` field smart-fill aliased to brain.industry, pre-populating "E-commerce" into the product-taxonomy field. Renamed to product_category so smart-fill leaves it blank for explicit user input. - Hashtags tool: HashtagOutput used config.initial.platform (frozen as "instagram") to key into the recommended-set bucket. Non-IG platforms always showed empty recommendations. Now discovers the key dynamically with Object.keys().find(k => k.startsWith("recommended_set_for_")). HIGH (12): - Unconditional "IF AN IMAGE IS ATTACHED" prompt blocks in 5 optimizer prompts (ad-fatigue, bid-strategy, ctr-optimizer, keyword-strategy, landing-page) — now gated on `input.<screenshot_field>` matching the fix already applied to audience-targeting. Each Input interface gains its `?_screenshot?: unknown` field. - LinkedIn `format.replace("_", " ")` only replaced first underscore; "lead_gen_form" became "lead gen_form". Now uses /_/g regex. - YouTube copy-button dropped beat timestamps; in-stream script lines copied without their time markers. Now includes [t] in the copy text. - Budget-planner buildTitle hardcoded `$${total_monthly}/mo`; INR user would see "$50000" in saved history. Dropped the `$`. - Budget-planner renderer + budget renderer: 12 hardcoded `$` literals in AI-generated figures (waste, recovery, monthly_usd, etc.). All now use getCurrency().symbol. Prompts also prefix amounts with currency code so the AI generates figures in the user's chosen currency. - Audience optimizer label "Budget / mo (USD)" → "Budget / mo" with generic "Your selected currency" hint. - Suggestions auto-run race: setTimeout(() => run(), 400) captured stale null brain via closure, causing "No active Brand Brain" error on load when a brain existed. New runWithBrain(b) accepts the brain directly. - Launch wizard fallback retry dropped master abort signal; Stop button couldn't cancel during fallback. Now uses anySignal(args.signal, fallbackCtl.signal). - Wizard + GeneratorShell + suggestions + competitors + frameworks + learn/[concept] + batch.tsx: ALL now use `res.text || stream.text` fallback so empty res.text from Gemini doesn't produce blank saved ads. - Batch.tsx: no AbortController, no Stop button — N parallel calls couldn't be cancelled. Now has master signal threaded to each llmStream + Stop button visible while running. - learn/[concept]/Client.tsx: no AbortController; streaming continued writing to state after unmount. Added abortRef + useEffect cleanup. - BrandBrainForm: extract() did direct spread merge, destroying manual edits. Now uses mergeFillEmpty. Also wired AbortController to all 3 AI call paths. save() redirect changed from "/" to "/brand". - Landing-page tool: URL field was a dead string the AI couldn't fetch. Updated label + hint to make this explicit; user pastes copy in the textarea below. - Spark-ads + suggestions: `$` hardcoded in scale-rules cap and campaign budget display. Both now use getCurrency().symbol. MEDIUM (6): - campaign-kit prompt now imports BANNED_WORDS_RULE + HONESTY_CLAUSE matching meta-ads / google-ads / linkedin-ads. - content-calendar prompt computes estimated entry count (days × platforms × cadence/wk) and instructs a HARD_ENTRY_CAP=35 with truncation_notice to prevent silent JSON truncation on 1-month multi-platform requests that previously exceeded the 6500-token budget. - google-shopping price field placeholder no longer says "$24.99"; prompt guard: "(not provided — do not invent pricing)". - Display, hashtags, google-shopping minor cleanup. Verification: typecheck clean, 43/43 tests pass, next build compiles from scratch. Touched 23 source files across app/, components/, lib/. (Several audit findings were intentionally NOT acted on after re-inspection: the "busyRef leak" in brand/new/page.tsx was incorrect — quickAddFromGoogleInternal owns its own busyRef lifecycle including the finally cleanup, so the reroute path is safe. Several optional-rendering nitpicks were also skipped as low-value.)
1 parent dcea432 commit 6f0ceb2

27 files changed

Lines changed: 612 additions & 79 deletions

File tree

AUDIT-PROMPT.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Tool Audit Prompt — for fix-cycle agents
2+
3+
You are auditing a specific batch of tools in the AdForge codebase. For every tool assigned to you, you must run the methodology below and produce a structured report of confirmed bugs.
4+
5+
## Inputs you receive
6+
7+
1. A list of tools to audit (e.g. `app/generate/meta`, `app/optimize/budget`)
8+
2. The companion `TEST-SCENARIOS.md` (per-tool expectations) and `AUDIT-FINDINGS.md` (recent fixes already shipped)
9+
3. The shared infrastructure these tools sit on:
10+
- `components/GeneratorShell.tsx` — generation orchestrator
11+
- `lib/llm.ts` — provider routing
12+
- `lib/quota-tracker.ts` — rate-limit tracking
13+
- `lib/smart-fill.ts` — brand-brain prefill heuristics
14+
- `lib/storage.ts` — IndexedDB persistence
15+
- `lib/brand-brain.ts` — schema + system prompt
16+
17+
## Methodology
18+
19+
For each tool, run these checks IN ORDER. Stop only when you've completed all six.
20+
21+
### 1. Static read
22+
- Open the page file (`app/<path>/page.tsx`) and its prompt builder (`lib/prompts/<...>.ts`)
23+
- Read every line — no skipping
24+
- Confirm: does it use `GeneratorShell`? If yes, you can skip most of the rate-limit / autosave checks. If no (custom impl), audit them in detail.
25+
26+
### 2. Happy-path trace
27+
Mentally simulate: user has active brand → opens tool → all fields auto-fill from brain → click Generate → output streams → saves to history.
28+
- Smart-fill matches: confirm the `name` of every input field in the page config matches one of the 40+ aliases in `lib/smart-fill.ts:suggestFromBrain`. List any unmatched field names.
29+
- buildPrompt: confirm the prompt builder produces a non-trivial prompt when given typical brain values + typical input values.
30+
31+
### 3. Empty-input check
32+
Mentally simulate: user with no active brand opens the tool blank → clicks Generate.
33+
- Required fields gating: which fields are `required: true`? Does the UI block submission cleanly when they're missing? Or does the AI receive `undefined` and confuse itself?
34+
- Empty system prompt: when brain is null, `buildBrandSystemPrompt(null)` returns a generic prompt. Confirm the tool's prompt builder gracefully handles undefined brain fields (no `brain.products[0]` without null check, etc.).
35+
36+
### 4. Streaming + autosave check (custom-implemented tools only)
37+
For tools that don't use GeneratorShell:
38+
- `const finalText = res.text || stream.text` fallback present? (audit-finding #recent)
39+
- `saveAd` called with the right brand_id and platform?
40+
- `addUsage` called with `(cost, input_tokens, output_tokens)` in the right order?
41+
- `estimateCostUsd` 3-arg form `(providerId, modelId, usage)`?
42+
- Abort signal threaded through `llmStream` / `llmCall`?
43+
- Rate-limit error parsing — does the catch detect 429-class errors and surface usefully?
44+
45+
### 5. Edge cases by tool type
46+
- **Generator with CharBadge** (google, meta, tiktok, linkedin, etc.): does the platform-limit validation gate auto-save? Should over-limit headlines not be auto-saved? (Currently they ARE saved — flag as low-priority issue.)
47+
- **Generator with image field** (vision-capable): is the field config `kind: "image"`? Does the prompt include an "IF IMAGE ATTACHED" block that's conditional on actual attachment?
48+
- **Optimizer with budget input**: is the label currency-agnostic (no hardcoded `$`)? Does the prompt prefix the budget with the currency code from `getCurrency()`?
49+
- **Tool with URL input** (landing-page, reel-teardown): is `looksLikeUrl()` called? Does ingestUrl get a signal for abort?
50+
- **Tool with comma-separated input** (keywords): does the parse handle whitespace, trailing commas, duplicates?
51+
52+
### 6. State-integration scenarios
53+
- Active brand switch mid-generation: does the tool use `runningRef` (or equivalent) to prevent brain state from mutating during an in-flight call?
54+
- Provider switch mid-generation: in-flight request should complete on the OLD provider; next request uses NEW.
55+
- Save while autosave is off: confirm the manual "Save" button is wired and works.
56+
57+
## Output format
58+
59+
Return a single Markdown block per tool, like this:
60+
61+
```
62+
### app/generate/meta · status: PASS / ISSUES_FOUND
63+
64+
Pass items: [list scenario IDs from TEST-SCENARIOS.md that you verified pass]
65+
66+
Issues found:
67+
1. **<severity>** [file:line]: <one-sentence symptom>
68+
Root cause: <what's wrong in code>
69+
Fix sketch: <how to fix in 1-2 lines of code>
70+
Confidence: 0-100
71+
72+
Open questions: <anything you couldn't verify statically and would need a running browser to confirm>
73+
```
74+
75+
Severity:
76+
- **BLOCKER**: tool crashes, silently fails, or produces wrong output
77+
- **HIGH**: tool works but UX is broken (no error feedback, wrong currency, etc.)
78+
- **MEDIUM**: edge case or nicety
79+
- **LOW**: nitpick, skip unless trivial
80+
81+
## Hard rules
82+
83+
- Do not invent issues. If you can't verify, mark it "open question."
84+
- Do not flag style preferences.
85+
- Confidence < 60 → don't include unless it's a security issue.
86+
- Cite file:line for every finding so the fix agent can navigate directly.
87+
88+
## What "done" looks like
89+
90+
- Every tool in your batch has a status block
91+
- Every BLOCKER and HIGH has enough detail to fix in <10 lines of code
92+
- The report is ready to hand to a fix agent

0 commit comments

Comments
 (0)