# App Audit — Passiv Ad Manager
**Date:** 2026-05-16  
**Auditor:** Base44 AI (full codebase review)

---

## Section 1 — Database

### Tables and Schemas

#### Campaign
- `id` — string (auto)
- `created_date` — datetime (auto)
- `updated_date` — datetime (auto)
- `created_by` — string (auto, user email)
- `name` — string, required
- `platform` — string enum: meta | google | tiktok, required
- `status` — string enum: active | paused | review | rejected | ended | demo, default: active
- `daily_budget` — number, default: 0
- `spend_today` — number, default: 0
- `primary_goal` — string enum: awareness | traffic | sales
- `platform_campaign_id` — string
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Demo data generated client-side; real records created when user launches an ad

#### AdMetric
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `campaign_id` — string, required
- `date` — date, required
- `reach` — number, default: 0
- `clicks` — number, default: 0
- `spend` — number, default: 0
- `conversions` — number, default: 0
- `impressions` — number, default: 0
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Demo data generated client-side; real records would come from platform sync (not yet wired)

#### Alert
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `message` — string, required
- `severity` — string enum: critical | warning | info, default: info
- `action_type` — string enum: pause | increase_budget | dismiss | fix_ad
- `campaign_id` — string
- `is_dismissed` — boolean, default: false
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Demo data generated client-side; real records created by system rules (not yet automated)

#### ChangeHistory
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `campaign_id` — string, required
- `action` — string enum: pause | resume | edit | delete | budget_change | schedule_change | duplicate | rollback, required
- `field_changed` — string
- `old_value` — string
- `new_value` — string
- `source` — string enum: manual | automated | system, default: manual
- `performed_by` — string
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Records created on pause/resume actions in MyAds page

#### NotificationInbox
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_email` — string, required
- `subject` — string, required
- `body` — string, required
- `type` — string enum: weekly_digest | budget_alert | performance_drop | info, default: info
- `is_read` — boolean, default: false
- `campaign_id` — string
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Empty unless manually created; no automated notification pipeline exists yet

#### ABTest
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `name` — string, required
- `variant_a_id` — string (campaign ID), required
- `variant_b_id` — string (campaign ID), required
- `metric_tested` — string enum: ctr | cpc | conversion_rate | roas | reach, required
- `status` — string enum: active | completed | paused, default: active, required
- `start_date` — date, required
- `end_date` — date
- `winning_variant` — string enum: a | b | none, default: none
- `confidence_level` — number
- `variant_a_metric` — number
- `variant_b_metric` — number
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Empty by default

#### AutomatedRule
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `name` — string, required
- `metric` — string enum: ctr | cpc | cpm | roas | reach | frequency | spend, required
- `operator` — string enum: greater_than | less_than | equals | greater_than_or_equal | less_than_or_equal, required
- `threshold` — number, required
- `action` — string enum: pause_campaign | resume_campaign | increase_budget | decrease_budget | pause_low_performers | notify_only, required
- `action_value` — number
- `campaign_id` — string
- `enabled` — boolean, default: true
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Empty by default; rules created by user but not automatically executed (execution engine not built)

#### PlatformConnection
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_id` — string, required
- `platform` — string enum: meta | google | tiktok, required
- `connected` — boolean, default: false
- `account_id` — string
- `account_name` — string
- `access_token` — string (OAuth token — stored in database, not encrypted separately)
- `refresh_token` — string
- `token_expires_at` — datetime
- `last_synced_at` — datetime
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Simulated connections stored here by the Settings mock UI

#### Pixel
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `platform` — string enum: meta | google | tiktok, required
- `status` — string enum: active | not_installed | checking, default: not_installed
- `pixel_id` — string
- `events_to_track` — array of strings
- `last_verified_at` — datetime
- `dismissed_until` — datetime
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created when user interacts with pixel setup flow

#### TeamMember
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `email` — string, required
- `role` — string enum: manager | viewer, default: viewer, required
- `invite_accepted` — boolean, default: false
- `invited_by` — string
- `workspace_owner_email` — string
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created when owner invites team members from Settings

#### BillingSettings
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_email` — string, required
- `monthly_spend_cap` — number
- `card_brand` — string
- `card_last4` — string
- `vat_number` — string
- `plan` — string enum: starter | pro | business, default: starter
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created by billing section; no real Stripe integration

#### ReportSchedule
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_email` — string
- `report_type` — string enum: weekly_performance | monthly_spend | audience_breakdown | platform_comparison | custom, required
- `frequency` — string enum: weekly | monthly | never, default: never, required
- `last_sent_at` — date
- `next_send_at` — date
- `report_name` — string
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created when user schedules a report; no actual email delivery pipeline exists

#### Goal
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `campaign_id` — string, required
- `metric` — string enum: website_visits | sales_signups | reach, required
- `target_value` — number, default: 0, required
- `current_value` — number, default: 0
- `period_start` — date
- `period_end` — date, required
- `status` — string enum: active | archived | achieved, default: active
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created at end of ad wizard (Step 7); also in demo data

#### SupportTicket
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_email` — string, required
- `conversation_history` — array of objects
- `trigger_question` — string, required
- `status` — string enum: open | in_progress | resolved, default: open, required
- `escalation_reason` — string enum: billing | unsatisfied | uncertain | request | frustration
- `sla_minutes` — number
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created by ChatWidget when AI escalation trigger fires

#### ChatSession
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `user_email` — string, required
- `session_start` — datetime, required
- `session_end` — datetime
- `messages` — array of objects
- `actions_taken` — array of objects
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created when user starts new conversation in ChatWidget

#### AssetLibrary
- `id`, `created_date`, `updated_date`, `created_by` — auto
- `file_name` — string, required
- `file_url` — string, required
- `file_type` — string enum: image | video, required
- `mime_type` — string
- `file_size_bytes` — number, default: 0
- `width` — number, default: 0
- `height` — number, default: 0
- `platform_formats` — array of strings
- `fatigue_status` — string enum: healthy | warming | fatigued, default: healthy
- `campaign_ids` — array of strings
- `is_archived` — boolean, default: false
- `tags` — array of strings
- **RLS:** Enabled — create/read/update/delete restricted to `created_by = user.email`
- **Data:** Created when user uploads files in Assets page

#### User (built-in, read-only schema)
- `id` — string (auto)
- `email` — string (immutable)
- `full_name` — string
- `role` — string enum: admin | user (Base44 built-in)
- `created_date` — datetime (auto)
- Custom fields stored via `base44.auth.updateMe()`:
  - `business_type` — string
  - `primary_goal` — string
  - `budget_range` — string
  - `demo_mode` — boolean
  - `advanced_mode` — boolean
  - `onboarding_complete` — boolean
  - `reset_onboarding` — boolean
  - `tutorial_complete` — boolean
  - `notify_weekly_digest` — boolean
  - `notify_budget_alerts` — boolean
  - `notify_performance_drops` — boolean
  - `copy_regen_count` — number
  - `copy_regen_reset_at` — datetime string
  - `platform_connections` — object (legacy; PlatformConnection entity is also used)
- **RLS:** Base44 built-in — users can only read/update their own record
- **Data:** Populated on signup

---

## Section 2 — Authentication

Authentication is fully managed by the Base44 platform. The app does not implement any custom auth backend.

### Sign Up Flow
- Handled entirely by Base44's auth infrastructure
- Fields collected: email, password (Base44 standard)
- Full name set post-signup via `updateMe()`
- After signup, user is redirected to the app
- `OnboardingGuard` in `App.jsx` checks `user.onboarding_complete` — if false, redirects to `/onboarding`
- Onboarding collects: business type, primary goal, budget range, platform selection (mock)
- On onboarding complete: sets `onboarding_complete: true`, `demo_mode: true`, `tutorial_complete: false`, redirects to `/tutorial`

### Log In Flow
- Handled entirely by Base44
- On successful login, `AuthProvider` provides auth state
- If `authError.type === 'auth_required'`, `navigateToLogin()` is called
- If `authError.type === 'user_not_registered'`, `UserNotRegisteredError` component is shown

### Session Handling
- Managed by Base44 platform (token-based)
- Session duration: controlled by Base44 infrastructure (not configurable in this app)
- Session stored in browser (Base44 SDK handles this)

### Password Reset Flow
- Handled by Base44 platform
- No custom password reset UI in this app

### Email Verification
- Handled by Base44 platform
- Not configurable from within this app

---

## Section 3 — Pages and Routes

### `/onboarding` — Onboarding
- **Status:** Complete
- **Renders:** 5-step wizard (business type → goal → budget → platform connect → finish)
- **Data read:** `base44.auth.me()`
- **Data written:** `business_type`, `primary_goal`, `budget_range` to User; marks `onboarding_complete: true` on finish
- **Actions:** Select business type, select goal, select budget tier, "connect" platforms (mock only), finish
- **Note:** Platform connection in Step 4 is purely cosmetic — clicking "Connect" toggles local state only, nothing is persisted

### `/tutorial` — Tutorial
- **Status:** Complete
- **Renders:** Full dashboard with an interactive spotlight overlay guiding through 7 slides
- **Data read:** `base44.auth.me()`, then renders `<Dashboard />` internally
- **Actions:** Next / Back through slides, Skip tutorial, Explore Dashboard, Go to Settings
- **On finish:** Sets `tutorial_complete: true`, hard redirects via `window.location.href`

### `/dashboard` — Dashboard
- **Status:** Complete (beginner mode), Complete (advanced mode)
- **Renders:** Summary bar, stat cards, weekly chart, best performer, alerts panel, active ads list, monthly spend tracker, pixel setup banner
- **Data read:** Campaigns, AdMetrics, Alerts, ChangeHistory (live mode); demo data (demo mode)
- **Actions:** Dismiss alerts, link to builder, link to settings
- **Advanced mode:** Renders `AdvancedDashboard` component instead of beginner layout

### `/my-ads` — My Ads
- **Status:** Complete
- **Renders:** Campaign cards with metrics, filters (All/Active/Paused/In Review), sort dropdown, export button
- **Data read:** Campaign, AdMetric (today's date only)
- **Actions:** Pause, Resume, Edit (EditAdPanel), Duplicate (prefills builder), AI Insight (AIInsightPanel), Export CSV
- **Note:** Delete button not shown in the card row — DeleteAdModal exists in components but is not triggered from MyAds (only from EditAdPanel)

### `/insights` — Insights
- **Status:** Complete
- **Renders:** Overview tab (BestPerformerInsightCard, SpendEfficiencyCard, AudienceHealthCard, RecommendationsCard); Attribution and Cohort tabs (advanced mode only)
- **Data read:** Campaigns, AdMetrics (last 7 days)
- **Actions:** Click budget on best performer card to open BudgetModal; tab switching

### `/reports` — Reports
- **Status:** Complete (download), Partial (scheduling)
- **Renders:** 4 report type cards (Weekly Performance, Monthly Spend, Audience Breakdown, Platform Comparison); Custom Report Builder (advanced mode only)
- **Data read:** Campaigns, AdMetrics
- **Actions:** Download CSV, Schedule (saves to ReportSchedule entity but no actual email delivery), Build Custom Report
- **Note:** Scheduled reports are stored in the database but no backend job sends the emails

### `/builder` — Ad Builder
- **Status:** Complete (7 steps functional)
- **Renders:** WizardShell with 7 steps
- **Data read:** `base44.auth.me()`
- **Actions:** Full wizard flow — see Section 4

### `/assets` — Assets
- **Status:** Complete
- **Renders:** Upload zone, asset grid with archive/use-in-ad actions, storage usage display
- **Data read:** AssetLibrary, BillingSettings
- **Actions:** Upload file (real upload via UploadFile integration), archive asset, use in ad (navigates to builder with prefill)

### `/audiences` — Audiences
- **Status:** Placeholder — not built
- **Renders:** "Audience management coming in a future build." message only
- **Data read:** None
- **Actions:** None

### `/rules` — Automated Rules
- **Status:** Complete (UI), Not built (execution engine)
- **Renders:** Rules table, create rule form, AI rule suggestion
- **Data read:** AutomatedRule, Campaign
- **Actions:** Create rule, toggle active/paused, delete rule, AI suggest rule
- **Note:** Rules are stored and displayed but there is no scheduled job or backend function that evaluates and executes them

### `/ab-tests` — A/B Tests
- **Status:** Complete (UI), Partial (logic)
- **Renders:** ABTest cards, CreateABTestModal
- **Data read:** ABTest, Campaign, AdMetric
- **Actions:** Create test, declare winner (pauses losing variant)
- **Note:** Statistical confidence is stored but not auto-calculated from real metric data — it relies on manually entered or pre-set values

### `/settings` — Settings
- **Status:** Complete
- **Renders:** Profile, Mode (Advanced toggle), Data Mode (Demo/Live toggle), Testing Tools (demo mode only), Notifications, Platform Connections, Billing, Team sections
- **Data read:** `base44.auth.me()`, PlatformConnections, BillingSettings, TeamMember
- **Actions:** Save name, toggle advanced mode, switch demo/live, reset onboarding, toggle notification prefs, connect platforms (mock OAuth), manage billing (no Stripe), invite/manage team
- **Note:** Platform OAuth connections are simulated — the UI calls the OAuthModal which is present but no real OAuth flow completes

### `/help` — Help Centre
- **Status:** Placeholder — content only, no links work
- **Renders:** 4 static cards (Getting Started Guide, Video Tutorials, Ad Glossary, Live Support Chat)
- **Data read:** None
- **Actions:** Cards are clickable (cursor-pointer) but none have `onClick` handlers — nothing happens

### `*` — 404
- **Status:** Complete
- **Renders:** PageNotFound component (lib/PageNotFound)

---

## Section 4 — App Features — What Is Actually Built

### Onboarding Flow
- **Step 1 — Business Type:** 6 options (Local Business, Online Store, Restaurant, Fitness, Creative Agency, Other). Selection saved to user record. ✅ Working.
- **Step 2 — Primary Goal:** 3 options (Awareness, Traffic, Sales). Saved to user record. ✅ Working.
- **Step 3 — Budget:** 4 tiers. Saved as `budget_range` on user record. ✅ Working.
- **Step 4 — Platform Connect:** Cosmetic only. Clicking "Connect" toggles local state with no persistence. The note in the UI says "OAuth connections are placeholders — wired in Build 02". ⚠️ Not functional.
- **Step 5 — Finish:** Sets `onboarding_complete: true`, `demo_mode: true`, `tutorial_complete: false`, redirects to `/tutorial`. ✅ Working.

### Tutorial
- 7-slide spotlight tour over the real dashboard. ✅ Working.
- Smooth spotlight overlay with progress bar and skip. ✅ Working.
- Sets `tutorial_complete: true` on finish. ✅ Working.

### Demo Mode
- Toggled via `user.demo_mode === true`. ✅ Working.
- All new users start in demo mode after onboarding.
- Demo data: 3 campaigns (Meta, TikTok, Google), 7 days of metrics with upward trend, 2 alerts, 2 goals. ✅ Positive, realistic data.
- Demo mode shown on Dashboard, MyAds, Insights, Reports. ✅ Working.
- Demo campaigns created in DB with `status: "demo"` when wizard is completed in demo mode. ✅ Working.

### Live Mode
- Toggled in Settings → Data Mode. ✅ Working.
- When switched to live with no real campaigns: Dashboard shows "No data yet — connect your ad platforms in Settings" empty state. ✅ Working.
- When switched to live with no metrics: stat cards show zeros. ✅ Working.

### Platform Connections — Meta, Google, TikTok
- Settings → Platform Connections section exists. ⚠️ Partially built.
- OAuthModal component exists and renders.
- No real OAuth token exchange occurs — clicking "Connect" updates the PlatformConnection entity with `connected: true` but no real API credentials are obtained.
- `user.platform_connections` object used in wizard Step 3 to check if connected — this works correctly with the simulated connection data.

### Data Sync
- **Not built.** No backend function, scheduled task, or API integration pulls real data from Meta, Google, or TikTok. All metric data in live mode must be manually created or is absent.

### Dashboard — Beginner Mode
- **SummaryBar:** Greeting, campaign health badge, AI summary text (static, based on demo data), budget utilisation. ✅ Working.
- **StatCards:** Reach, Clicks, Spend, Conversions with day-over-day comparison. ✅ Working.
- **MonthlySpendTracker:** Tracks spend against monthly cap. ✅ Working.
- **WeekChart:** 7-day area chart of reach, clicks, spend. ✅ Working.
- **BestPerformerCard:** Identifies highest CTR campaign. ✅ Working.
- **GoalProgressBar:** Shows progress toward goal targets. ✅ Working.
- **AlertsPanel:** Displays active alerts, dismiss action, fix_ad triggers RejectionFixPanel. ✅ Working.
- **ActiveAdsList:** List of campaigns with metrics and status. ✅ Working.
- **PixelSetupBanner:** Shown when pixel not installed. ✅ Working.

### Dashboard — Advanced Mode
- Renders `AdvancedDashboard` with extended metrics, campaign table, anomaly alerts, creative panel, funnel viz, platform comparison. ✅ Working (built).

### Ad Creation Wizard
- **Step 1 — Ad Info:** Ad name (auto-populated with date), description (max 200 chars), goal selection. ✅ Working.
- **Step 2 — AI Copy:** LLM generates 3 variants (headline, description, CTA, score). Select variant, mix & match dropdowns, char count validation per platform. Regen limit: 5 per 24 hours tracked on user record. ✅ Working.
- **Step 3 — Platform:** Demo mode shows all 3 platforms. Live mode shows only connected. Single select (beginner) or multi-select (advanced). Copy length warnings per platform. ✅ Working.
- **Step 4 — Budget:** Daily or Monthly toggle. Slider for daily ($1–$500). Number input for monthly. Start/end date pickers. Reach estimates. Platform minimum enforcement. ✅ Working.
- **Step 5 — Audience:** 8 preset tiles (Local Customers, Online Shoppers, Young Professionals, Health & Wellness, Food & Dining, Homeowners & Families, Style & Fashion, Business Owners). Interest tag toggling. Audience summary card. ✅ Working.
- **Step 6 — Review:** Ad phone mockup preview, checklist (headline length, description length, budget, audience, goal, connection), summary table, landing page health checker, estimated reach. Launch button creates Campaign entity. ✅ Working.
- **Step 7 — Goal:** Optional. Set metric target (website_visits, sales_signups, reach), target number, end date. Creates Goal entity. Skip option available. ✅ Working.
- **Creative upload:** Step 6 has `data.creative` check in checklist but there is no creative upload step in the wizard — the wizard goes directly from Audience to Review with no image/video upload step. ⚠️ Bug — checklist item references a field that is never collected.

### Ad Management
- **Pause / Resume:** MyAds page, updates Campaign status, creates ChangeHistory entry. ✅ Working.
- **Edit:** EditAdPanel (slide-out), updates name, platform, budget, goal. ✅ Working.
- **Duplicate:** Navigates to builder with prefill data. ✅ Working.
- **Delete:** DeleteAdModal exists and is wired from EditAdPanel. ✅ Working.
- **AI Insight:** AIInsightPanel calls LLM with campaign metrics and returns suggestions. ✅ Working.
- **Budget modal:** BudgetModal accessible from MyAds (modal.type === 'budget' is defined but the trigger button is not present in the campaign card — only accessible from Insights page). ⚠️ Partial.

### My Ads Page
- Filtering by All/Active/Paused/In Review. ✅ Working.
- Sorting by Most Recent/Highest CTR/Highest Spend/Lowest CTR. ✅ Working.
- CSV export. ✅ Working.
- Metrics shown: Reach, Clicks, Spend, CTR for current day only. ✅ Working (by design).
- Budget progress bar per campaign. ✅ Working.

### Insights Page
- 4 insight cards in overview tab. ✅ Working.
- Attribution and Cohort Analysis tabs (advanced mode only). ✅ Working.
- BudgetModal accessible from BestPerformerInsightCard. ✅ Working.

### Settings Page
- **Profile:** First name, last name editable (email read-only). Save button. ✅ Working.
- **Mode:** Advanced Mode toggle (persisted to user). ✅ Working.
- **Data Mode:** Demo/Live radio selection (persisted to user). ✅ Working.
- **Testing Tools:** Reset Onboarding button (demo mode only). ✅ Working.
- **Notifications:** Weekly Digest, Budget Alerts, Performance Drops toggles (persisted to user). ✅ Working — preferences stored but no actual emails are sent.
- **Platform Connections:** Connect/disconnect Meta, Google, TikTok. Simulated OAuth only. ⚠️ Not functional for real API connections.
- **Billing:** Monthly spend cap, plan display. No real Stripe integration. ⚠️ Cosmetic only.
- **Team:** Invite team members (inviteUser called), manage roles, remove members. ✅ Working (invite sends real Base44 invite email).

### Notifications
- NotificationBell in topbar. Polls NotificationInbox every 30 seconds. ✅ Working.
- Mark as read functionality. ✅ Working.
- Unread count badge. ✅ Working.
- No automated notifications are created — inbox will be empty until manually populated or a backend notification job is built. ⚠️ Partially built.

---

## Section 5 — AI Integration

### Model
- Base44's `InvokeLLM` integration is used throughout. The underlying model is Base44's default (GPT-4o-mini class). No custom model selection is used in any prompt call.

### Prompts and Where They Are Called

#### 1. Ad Copy Generation — `components/wizard/Step2AICopy.jsx`
- **Trigger:** Auto-runs on step entry; manual re-run via "Regenerate" button (max 5/24h)
- **Input:** business_type, goal, product description, platform
- **Output:** 3 variants, each with headline, description, CTA, score (out of 50)
- **Response schema:** JSON with variants array
- **Rate limiting:** 5 regens per 24 hours tracked via `copy_regen_count` and `copy_regen_reset_at` on user entity

#### 2. Landing Page Health Check — `components/wizard/LandingPageChecker.jsx`
- **Trigger:** Manual — user clicks "Check page" button in Step 6
- **Input:** URL, ad headline
- **Output:** score (1–10), top_issue (string or null)
- **Response schema:** JSON `{ score, top_issue }`
- **Caching:** 60-second client-side cache per URL (useRef)

#### 3. AI Rule Suggestion — `pages/Rules.jsx`
- **Trigger:** User clicks "Suggest a rule for me" button
- **Input:** Generic prompt (no user context injected)
- **Output:** Rule object (name, metric, operator, threshold, action, action_value)
- **Response schema:** JSON rule object

#### 4. AI Insight on Campaign — `components/ads/AIInsightPanel.jsx`
- **Trigger:** User clicks "Insight" button on a campaign card
- **Input:** Campaign data + weekly metrics
- **Output:** AI analysis and recommendations

#### 5. AI Chat Assistant — `components/chat/ChatWidget.jsx` + `agents/passiv_assistant.json`
- **Trigger:** User sends a message in the chat widget
- **Model:** Base44 agent (`passiv_assistant`)
- **Context injected:** User name, mode, business type, goal, current page, active campaigns (name, platform, spend, goal), goals (metric, progress %), alerts, date
- **Agent tool access:** Read-only access to Campaign, AdMetric, Alert, Goal entities
- **Actions agent can trigger (via regex parsing of response):** PAUSE_CAMPAIGN, RESUME_CAMPAIGN, UPDATE_BUDGET, NAVIGATE, ESCALATE
- **Escalation:** Creates SupportTicket entity with conversation history and SLA based on billing plan

### What Was Instructed But Not Wired Up
- Weekly digest email generation via AI — not built
- AI-generated performance summaries pushed to NotificationInbox — not built
- Automated alert generation from metric anomalies via AI — not built
- AI copy generation using Claude Sonnet (higher quality model specified in agent instructions) — not used; default model used throughout

---

## Section 6 — Security — Current State

| Item | Status | Notes |
|---|---|---|
| RLS on all tables | ✅ Done | All 16 entities (Campaign, AdMetric, Alert, ChangeHistory, NotificationInbox, ABTest, AutomatedRule, PlatformConnection, Pixel, TeamMember, BillingSettings, ReportSchedule, Goal, SupportTicket, ChatSession, AssetLibrary) have `created_by` RLS applied |
| API keys in environment variables only | ✅ Done | All AI and integration calls go through Base44's backend SDK. No API keys exist in the client bundle. No `sk_`, `ANTHROPIC_`, or secret values exist in any source file |
| Rate limiting on endpoints | ⚠️ Partially done | Copy regen limit (5/24h) implemented client-side on user record. Auth rate limiting handled by Base44 platform. No rate limiting on campaign creation, landing page checker, or other endpoints |
| Mass assignment protection on user create | ✅ Done | Base44 platform controls signup — only email/password accepted. All other fields set server-side or via explicit `updateMe()` calls post-signup |
| XSS prevention — no dangerouslySetInnerHTML with user content | ✅ Done | No `dangerouslySetInnerHTML` exists anywhere in the codebase. All user-supplied content rendered via React JSX interpolation (auto-escaped) |
| Stripe webhook signature verification | N/A | Stripe not yet integrated |
| Stripe webhook idempotency | N/A | Stripe not yet integrated |
| Input sanitisation on all form fields | ⚠️ Partially done | Character limits enforced on ad copy fields (headline, description max chars). Description textarea capped at 200 chars. No server-side sanitisation — relies on Base44 platform input handling. No explicit HTML-stripping or injection prevention beyond React's default escaping |

---

## Section 7 — What Was Instructed But Is Not Built

### Platform Integrations
- **Real OAuth flow for Meta, Google, TikTok** — The platform connection step in onboarding and the Settings platform connection section are cosmetic. No real OAuth token exchange occurs. Clicking "Connect" updates a database record but does not authenticate with any external API.
- **Real data sync from Meta API** — Not built. AdMetric data only exists in demo mode (client-generated) or must be manually inserted.
- **Real data sync from Google Ads API** — Not built.
- **Real data sync from TikTok Ads API** — Not built.
- **Webhook receivers for platform events** — Not built.

### Notifications & Emails
- **Weekly digest email** — Notification preference toggle exists in Settings, ReportSchedule entity exists, but no scheduled job or email delivery pipeline sends the digest.
- **Budget alert emails** — Preference toggle exists but no automated check or email send is wired up.
- **Performance drop alerts** — Preference toggle exists but no metric monitoring job exists.
- **Automated push of alerts to NotificationInbox** — Alerts panel shows demo data or manually created records. No system creates alerts automatically from live metric analysis.

### Automated Rules Execution
- AutomatedRule records can be created and stored, but **no execution engine evaluates rules against live metric data**. Rules are UI-only — they never trigger any action automatically.

### Ad Creative Upload in Wizard
- The wizard has 7 steps and the Step 6 checklist checks for `data.creative`, but **there is no step in the wizard that collects a creative file**. The Assets page exists separately but is not integrated into the wizard flow. Creative upload is effectively disconnected from the ad creation process.

### Audiences Page
- The `/audiences` route renders a placeholder: "Audience management coming in a future build." No functionality exists.

### Help Centre Content
- The Help page renders 4 static cards with no working links, no content, no article system, no video player, no glossary, and no live chat integration. All 4 cards have `cursor-pointer` styling but no `onClick` handlers.

### GDPR Right to Deletion
- No data deletion flow exists for end users to request removal of their data.

### Security Headers
- No CSP, HSTS, or other security headers configured in the app.

### Real Pixel Verification
- The pixel setup flow exists and stores data, but actual pixel verification (checking if the pixel fires on a real URL) uses the same LLM-based estimation approach as the landing page checker — it does not make a real HTTP request to verify pixel installation.

### A/B Test Auto-Analysis
- Confidence levels and metric values on ABTest records are not auto-calculated from AdMetric data. The system stores these fields but does not compute statistical significance from actual metric rows.

### Report Email Delivery
- ScheduleModal saves a ReportSchedule record but no cron job or backend function generates and emails reports on schedule.

---

## Section 8 — Bugs and Issues

### Bug 1 — Creative field checked in wizard but never collected
- **Where:** `components/wizard/Step6Review.jsx`, checklist item
- **What:** `data.creative` is checked and shows "No creative — placeholder will be used" warning, but no step in the 7-step wizard collects a creative file. The user has no way to set `data.creative` from within the wizard.
- **Expected:** Either a creative upload step should exist in the wizard, or the creative should be selectable from the asset library in Step 5 or 6.

### Bug 2 — Chat widget header says "Add App Assistant" instead of "Passiv Assistant"
- **Where:** `components/chat/ChatWidget.jsx`, line with "Add App Assistant"
- **What:** The header text is a placeholder left from development. Should read "Passiv Assistant" or similar branded name.
- **Expected:** Correct branding in chat header.

### Bug 3 — Budget modal not accessible from MyAds campaign cards
- **Where:** `pages/MyAds.jsx`
- **What:** `modal.type === 'budget'` handler and `BudgetModal` are imported and defined, but there is no button in the campaign card that sets `modal` to `{ type: 'budget', campaign: c }`. The modal is unreachable from the My Ads page.
- **Expected:** A "Budget" or "Edit Budget" button should appear on each campaign card, or the budget field in EditAdPanel should be the entry point.

### Bug 4 — Step2AICopy does not pass updated data back to parent correctly
- **Where:** `components/wizard/Step2AICopy.jsx` — `onChange` calls pass partial objects (`{ variants, selectedVariant, mix }`) without spreading `data`, which could overwrite previously collected fields in the parent's `data` state.
- **What:** `onChange({ variants: newVariants, selectedVariant: top, mix: newMix })` replaces the entire data object with only these three keys if the parent uses this directly.
- **Expected:** `onChange({ ...data, variants: newVariants, selectedVariant: top, mix: newMix })`

### Bug 5 — Help page cards have no onClick handlers
- **Where:** `pages/Help.jsx`
- **What:** All 4 help cards have `cursor-pointer` but no `onClick`. Clicking them does nothing.
- **Expected:** Either remove cursor-pointer or wire up navigation/modal.

### Bug 6 — Onboarding Step 4 platform "connections" not persisted
- **Where:** `pages/Onboarding.jsx`, Step 4
- **What:** User can click "Connect" on platforms which toggles local state, but nothing is saved to the database or user record when moving to Step 5. The comment in code confirms: "Platform connection — placeholder, just move on".
- **Expected:** Either real OAuth or a clear message that connections happen in Settings.

### Bug 7 — Rules page uses `is_active` field but entity schema defines `enabled`
- **Where:** `pages/Rules.jsx` — `toggleActive` function uses `rule.is_active`, `handleSave` creates with `is_active: true`
- **What:** The AutomatedRule entity schema defines the field as `enabled` (boolean), but the Rules page reads and writes `is_active`. This means the toggle functionality does not update the correct field.
- **Expected:** Should use `enabled` consistently throughout, matching the entity schema.

### Bug 8 — ABTest page references `campaign_id_a` / `campaign_id_b` but entity uses `variant_a_id` / `variant_b_id`
- **Where:** `pages/ABTests.jsx`, `handleDeclareWinner` function
- **What:** `test.campaign_id_a` and `test.campaign_id_b` are referenced, but the ABTest entity schema uses `variant_a_id` and `variant_b_id`. The winner declaration will fail to find the correct campaign IDs.
- **Expected:** Should reference `test.variant_a_id` and `test.variant_b_id`.

### Bug 9 — Step6Review `onLaunched` prop mismatch with Builder.jsx
- **Where:** `pages/Builder.jsx` passes `onLaunched={(id) => { ... setStep(7) }}` but `components/wizard/Step6Review.jsx` calls `onNext` not `onLaunched`
- **What:** In Builder.jsx, Step 6 is given `onLaunched` prop. But in Step6Review, the launch completion calls `onNext(...)`. This means after launch, step 7 (Goal) is never triggered — the wizard stays on Step 6.
- **Expected:** Either Builder.jsx should pass `onNext` to Step6Review, or Step6Review should call `onLaunched`.

### Bug 10 — ChatWidget message filtering uses wrong string
- **Where:** `components/chat/ChatWidget.jsx`, `displayMessages` filter
- **What:** Messages are filtered by checking `m.content?.includes("User's message:")` (with curly apostrophe) to strip context prefix, but the context is prepended as `"User message: ${text}"` (plain "User message" without apostrophe). The strip will never match.
- **Expected:** Both strings should match — either `"User message:"` or `"User's message:"` consistently.

---

## Section 9 — Recommendations

### Priority 1 — Critical (fix before any real user touches the app)

1. **Fix the Step6Review → Step7Goal navigation bug (Bug 9).** After launching an ad, the wizard should advance to the Goal step. Currently it does not. Every user who creates an ad hits this and goal-setting is skipped entirely.

2. **Fix the AutomatedRule `enabled` vs `is_active` field mismatch (Bug 7).** Rules cannot be toggled on/off correctly. This affects every user who creates a rule.

3. **Fix the ABTest `variant_a_id` / `variant_b_id` field mismatch (Bug 8).** Declaring a winner in an A/B test will silently fail or produce wrong results.

4. **Add creative upload to the wizard (Bug 1).** Users have no way to attach a creative to their ad during the creation flow. The checklist warns them but offers no solution. This is a core ad creation requirement.

### Priority 2 — High (fix before launch)

5. **Build real OAuth for platform connections.** Without real Meta/Google/TikTok OAuth, the app cannot deliver its core value proposition (managing real ads). This is the most important feature gap.

6. **Build basic data sync from at least one platform (Meta recommended).** Even a polling-based sync of spend and impressions would make the live mode functional. Currently live mode shows nothing.

7. **Fix Step2AICopy `onChange` partial data overwrite (Bug 4).** This can silently discard earlier wizard data (ad name, description, goal) when AI copy is generated or a variant is selected. Test by checking that `data.name` and `data.goal` survive through step 2.

8. **Fix chat widget header label "Add App Assistant" → correct name (Bug 2).** This is a visible branding issue shown to every user who opens the chat.

9. **Wire up Budget modal from MyAds campaign cards (Bug 3).** The modal is built but unreachable. Budget editing is a common action.

10. **Fix ChatWidget context stripping bug (Bug 10).** The raw context prefix (user name, campaigns, alerts, date) is currently displayed to users in the chat window because the strip logic never matches.

### Priority 3 — Medium (fix in next iteration)

11. **Build automated rule execution engine.** A scheduled backend function should evaluate AutomatedRule records against the latest AdMetric data and execute actions. Without this, the Rules page is purely cosmetic.

12. **Build notification pipeline.** Notifications exist in the database but are never written automatically. At minimum, a daily job should write budget alerts and performance drops to NotificationInbox.

13. **Add real landing page verification.** The current LandingPageChecker sends a URL to an LLM and asks it to guess the health score. It does not actually fetch the URL. A real HTTP check would be far more accurate and trustworthy.

14. **Fix Help page cards to have real content or links (Bug 5).** Currently clicking any help card does nothing. This damages trust.

15. **Build the Audiences page.** It is in the nav and has a route but shows only a placeholder. Users navigating here see a dead end.

16. **Add GDPR right-to-deletion flow.** Users need a way to request account and data deletion before the app goes live with real users in regulated markets.

### Priority 4 — Low (nice to have)

17. **Auto-calculate A/B test confidence levels from AdMetric data.** Currently confidence must be manually entered. Auto-computation from metric variance would make the feature meaningful.

18. **Wire report scheduling to actual email delivery.** ReportSchedule records are created but no job sends the reports. Completing this closes the loop on the Reports feature.

19. **Improve AI rule suggestions to inject user context.** The "Suggest a rule for me" prompt is generic. Injecting the user's actual campaigns, metrics, and business type would produce much more relevant suggestions.

20. **Add security headers (CSP, HSTS, X-Frame-Options).** These should be configured before public launch to reduce attack surface.

21. **Onboarding Step 4 — show clear message about platform connections.** Currently the note in the UI says "wired in Build 02" — this is a development comment visible to users. Replace with a proper UX message explaining connections happen in Settings.

---

*End of audit. Generated 2026-05-16.*
