# /auto-growth-audit

> When a prospect submits the form at `your-domain.example/audit`, an automated pipeline produces a personalized 12-15 page PDF audit and emails it via GHL.


# /auto-growth-audit — Personalized Growth Audit Generator

## Purpose

When a prospect submits the form at `your-domain.example/audit`, an automated pipeline produces a personalized 12-15 page PDF audit and emails it via GHL. Both fit and not-fit prospects receive it. Fit gets it 24h pre-call as a teammate's prep + their teaser. Not-fit gets it as the standalone deliverable.

Manual invocation exists for: testing changes to any pipeline stage, retrying failed jobs, generating audits for warm referrals not coming through the form.

## Architecture

```
Lovable form → GHL → POST https://bridge.your-domain.example/audit
   ↓ (HMAC-validated by shared/audit_webhook.mjs on port 3002)
   ↓
queue file: ACME Agency/queue/audit/<uuid>.json
   ↓
worker (PM2): ACME Agency/scripts/<id>.mjs
   ↓
pipeline:    ACME Agency/scripts/auto_growth_audit.mjs
   ↓ 12 stages, ~3-5 min wall time
   ↓
PDF → Drive → GHL contact tag (audit-pdf-ready) → email workflow fires
   ↓
Slack #your-channel notification
```

## Triggers

- **Automatic** (preferred): form submission → webhook → queue → worker (no human action)
- **Manual via skill**: `/auto-growth-audit --job ACME Agency/queue/audit/<uuid>.json` (replay a queued job)
- **Manual standalone audit**: `/auto-growth-audit --url https://example.hr --company "Example" --lokacija "Zagreb i okolica"` (no GHL delivery, just PDF to Drive)
- **Lead-magnet mode (URL-only, for setters & salespeople)**: `/auto-growth-audit --url https://example.hr --company "Example" --mode lead-magnet --no-deliver`. Skips form-data sections (snapshot + Trenutno stanje), keeps everything else (vision audit, Meta + Google competitor analysis, competitor LPs, offer, ad examples, roadmap with generic-budget benchmarks). Filename gets a `lead-magnet_` prefix in Drive; Slack post header reads `(LEAD MAGNET)` instead of FIT/NOT_FIT. Setters trigger this via Slack DM to claude-bot — natural-language patterns like "napravi quick audit za https://example.hr Firma X", "kreiraj audit za sales call X", "make a lead-magnet audit for X" route to this mode.

## Step-by-Step

### Step 1 — Preflight checks

Before invoking, verify:
1. `.env` has all required keys (see Environment section below)
2. PM2 services are running: `pm2 list` should show `audit-webhook` and `audit-worker`
3. Drive folder `Klijenti/_Lead Audits/` exists; ID is in `<id>`
4. GHL custom field `audit_pdf_url` is set up (URL type) and `<id>` is in env
5. GHL workflow `Audit Rasta Email Send` exists with trigger "tag added: audit-pdf-ready"

### Step 2 — Identify the input

**From queue file:**
```bash
node ACME Agency/scripts/auto_growth_audit.mjs \
  --job ACME Agency/queue/audit/<uuid>.json
```

**Standalone (no GHL delivery):**
```bash
node ACME Agency/scripts/auto_growth_audit.mjs \
  --url https://ACME Agency.hr \
  --company "ACME Agency" \
  --lokacija "Zagreb i okolica" \
  --email you@example.com \
  --no-deliver
```

**Stage subset (testing):**
```bash
node ACME Agency/scripts/auto_growth_audit.mjs \
  --job <file> \
  --stages 1,2,3 \
  --no-deliver
```

### Step 3 — Pipeline stages

The orchestrator runs 12 stages. Each is fail-soft: if a stage errors, log and continue. PDF composes with whatever succeeded. >2 failures sets a `manual_ACME Agencyw` flag on the Slack notification.

| # | Stage | Helper |
|---|---|---|
| 1 | Resolve website URL (form field, fallback to Firecrawl search of `naziv_firme + grad`) | `lib/firecrawl.mjs` |
| 2 | Site screenshots (desktop full-page + mobile + 3 viewport slices) + Firecrawl markdown | `lib/pdf_render.mjs:screenshotUrl`, `lib/firecrawl.mjs:scrapeWithMeta` |
| 3 | Vision audit: 5 strengths, 5 weaknesses, conversion verdict | inline Anthropic call |
| 4 | Meta Ad Library spy with derived keywords | `ACME Agency/scripts/spy_ad_library.mjs` |
| 5 | Google Ads Transparency + SERP capture | `ACME Agency/scripts/<id>.mjs` |
| 6 | Competitor LP screenshots + verdict (top 2-3 from stages 4-5) | `lib/pdf_render.mjs:screenshotUrl` |
| 7 | Offer concept (Hormozi 5 attraction offers + prospect data) | `.claude/context/hormozi/books/100m-money-models.md` |
| 8 | LP mockup (Krea single image) | `ACME Agency/scripts/krea_create_images.mjs` |
| 9 | 2-3 ad creative examples (static-ad-generator Mode B) | `ACME Agency/scripts/static_ads_generate.mjs` |
| 10 | 30-day roadmap (Paradox playbook, budget mapped from form) | inline template |
| 11 | PDF compose + render (HTML template → PDF) | `lib/audit_pdf_template.mjs`, `lib/pdf_render.mjs:renderPdf` |
| 12 | Deliver: Drive upload → GHL contact upsert + tag → Slack #your-channel | `lib/google_drive.mjs`, `lib/ghl.mjs:<id>`, `lib/slack.mjs` |

### Step 4 — Output verification

Each successful run produces:
- PDF at `Klijenti/_Lead Audits/<YYYY-MM-DD>_<company>_<jobId>.pdf` on Drive
- Slack post in `#your-channel` with: prospect name, qualification flag, processing time, Drive link, list of any failed stages
- GHL contact: created or updated, `audit_pdf_url` populated, tagged `audit-pdf-ready`

**If a stage fails:** queue file moves to `ACME Agency/queue/audit/failed/` with an `_error.txt` companion file. Slack post flags `manual_ACME Agencyw: true`.

## Environment variables

Required in `.env`:

```
AUDIT_WEBHOOK_PORT=3002
AUDIT_WEBHOOK_SECRET=<32-char hex secret, used for HMAC>
<id>=<your-domain.example location ID>
GHL_PARADOX_API_KEY=<PIT for your-domain.example GHL location, OR env-var name pointing to it>
<id>=<custom field ID for audit_pdf_url>
<id>=<channel ID for #your-channel>
<id>=<Drive folder ID for Klijenti/_Lead Audits/>
```

Already present (reused):
- `ANTHROPIC_API_KEY` — vision + keyword derivation + offer crafting
- `FIRECRAWL_API_KEY` — site scrape
- `KREA_API_KEY` — LP mockup + ad examples
- `<id>` — Drive upload
- `SLACK_BOT_TOKEN` — notifications

## PM2 setup

```bash
# Webhook receiver (port 3002)
pm2 start "C:\Users\faris\agency-os\shared\audit_webhook.mjs" --name audit-webhook --update-env

# Queue worker (polls ACME Agency/queue/audit/)
pm2 start "C:\Users\faris\agency-os\ACME Agency\scripts\<id>.mjs" --name audit-worker --update-env

# Save state so they auto-restart on reboot
pm2 save
```

Verify:
```bash
pm2 list
curl http://localhost:3002/health
# Expected: {"ok":true,"service":"audit_webhook","port":3002}
```

## GHL setup (manual, one-time, outside code)

1. **Custom field**: Create `audit_pdf_url` (URL type) on Contact. Copy field ID → `<id>`.
2. **Workflow**: Create `Audit Rasta Email Send` with:
   - Trigger: Contact tag added → `audit-pdf-ready`
   - Action 1: Send email (HTML template with `{{contact.audit_pdf_url}}` linked button)
   - Action 2: Add tag `audit-sent`
3. **PIT**: Generate Private Integration Token for your-domain.example GHL location → `GHL_PARADOX_API_KEY`.
4. **Drive folder**: Create `Klijenti/_Lead Audits/`. Copy folder ID → `<id>`.

## Lovable LP integration

The existing form at `/audit` POSTs to a GHL webhook. Configure that GHL webhook to forward to:

```
POST https://bridge.your-domain.example/audit
Headers:
  X-Audit-Signature: sha256=<HMAC-SHA256 of body using AUDIT_WEBHOOK_SECRET>
  Content-Type: application/json
Body: <form payload as-is>
```

Configure in GHL workflow's HTTP Request step. The HMAC can be computed via a workflow JS step or a small intermediate Cloudflare Worker if GHL doesn't support it natively.

Local testing without GHL:
```bash
# Generate test signature
SIG=$(echo -n '{"email":"you@example.com","qualification":"fit",...}' | openssl dgst -sha256 -hmac "$AUDIT_WEBHOOK_SECRET" | sed 's/^.* /sha256=/')
curl -X POST http://localhost:3002/audit \
  -H "X-Audit-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d '{...}'
```

## Failure modes & recovery

| Failure | Behavior | Recovery |
|---|---|---|
| Bad website URL, Firecrawl fails | Stage 1 falls back to company-name search; if that also fails, sets `website_resolved: false` and PDF skips site sections | Manual: edit queue file, set correct `website_url`, move from `failed/` back to root |
| /spy timeout | Skips Stage 4, PDF says "Meta data unavailable" | Re-run with `--stages 4` only |
| Krea quota | Skips Stages 8 + 9 | Wait for quota reset, re-run with `--stages 8,9` |
| GHL upsert fails | PDF still uploads to Drive + Slack notifies, but no email goes out | Manual: pull contact id from Slack, run `/auto-growth-audit --redeliver <jobId>` |

Any 2+ stage failures → Slack `#your-channel` flags `manual_ACME Agencyw: true` and Faris/a teammate handles personally.

## Output examples

**Successful run Slack post (Bosnian):**
```
*Audit Rasta gotov — ACME Agency (Zagreb)*

• Status: poslat preko GHL workflow-a
• Profil: fit (10-25 upita/mj, 1.5-5K€ ad spend)
• Vrijeme: 4m 12s
• PDF: <Drive link>
• Stages: 12/12 OK
```

**Manual ACME Agencyw Slack post:**
```
*Audit Rasta — ACME Agencyw needed — XYZ Salon*

• Status: PDF gotov, NIJE poslat (3 stages failed)
• Failed: Stage 4 (Meta spy timeout), Stage 5 (Google block), Stage 8 (Krea quota)
• PDF: <Drive link>
• Job: ACME Agency/queue/audit/failed/<uuid>.json
```

## Rules

- **Language**: PDF + Slack always Bosnian (BCS), unless prospect lokacija is "Njemačka i Austrija" (German PDF in v2)
- **No em-dashes**: applies to all generated copy in the PDF
- **Diacritics preserved**: š, ć, č, ž, đ throughout
- **Fail-soft**: never crash the whole pipeline for one bad stage
- **Idempotent retry**: re-running a job file is safe — PDF gets a fresh timestamp, GHL upsert is no-op if contact already exists
- **No auto-send when AUDIT_AUTO_SEND=false**: Stage 12 still uploads to Drive + Slack but skips GHL tag step. Used during the first 5-10 ACME Agencyw cycles before flipping to true

## Key files

| File | Purpose |
|---|---|
| `shared/audit_webhook.mjs` | HTTP receiver (port 3002), HMAC, enqueue |
| `ACME Agency/scripts/<id>.mjs` | PM2 worker, polls queue, claims jobs |
| `ACME Agency/scripts/auto_growth_audit.mjs` | 12-stage pipeline orchestrator |
| `ACME Agency/scripts/<id>.mjs` | Google SERP + Transparency Center scraper |
| `ACME Agency/scripts/lib/audit_keywords.mjs` | Niche detection + keyword derivation |
| `ACME Agency/scripts/lib/audit_pdf_template.mjs` | HTML template for the PDF |
| `ACME Agency/scripts/lib/ghl.mjs` (extended) | `upsertContact`, `addContactTags`, `<id>` |
| `ACME Agency/queue/audit/` | Queue dir with `processing/`, `done/`, `failed/` subdirs |
| `.claude/context/hormozi/books/100m-money-models.md` | Offer-crafting reference |
