The end-to-end harness that turns Phase-0 scraped pool-service leads into
live *.pages.dev URLs ready for the outbound email stage.
Every agent, prompt, config, and gate documented before a single new
mock is built.
scored_llm.jsonl to https://<slug>.pages.devFive stages. Each stage is idempotent, resumable, and writes a status row to a shared ledger. Nothing is re-done if it's already done.
data/scored_llm.jsonl · data/scored_no_website.jsonlbatch-state.jsonl.scripts/batch_pick.py --niche pool_service --count 10place_ids to process · prospect dirs scaffoldedimages/manifest.json already exists with 4 valid files.scripts/batch_images.pygpt-image-2-medium via openai-codex provider (ChatGPT OAuth · $0 marginal)scripts/render_prospect.py <slug> (already built)prospects/<slug>/mock/index.html + preview screenshots + qa-log.jsonscripts/deploy_prospect.py <slug>npx wrangler@latest pages deployhttps://sigil-pool-<slug>.pages.devbatch-state.jsonl row · spot-check Pages URL · Telegram messageNo subagent zoo. Each agent is a process with a clear input/output contract and a cost.
image_generate tool, provider openai-codexdesign-brief.md brand register (Petri = warm Texas earth, Crystal White = clean blue/white, etc.)claude --print --model claude-opus-4-7 --permission-mode bypassPermissionsclaude --print, multimodal mode on preview screenshotsThis is the part Samar already approved on 5/5 calibration prospects. Reusing as-is, no changes.
white-space: nowrap OR <br>naturalWidth > 0 after networkidlepreview/{mobile,tablet,desktop}.pngneeds_manual_review/Proven path. Token saved, wrangler verified, 13 existing Pages projects already listed via the same credentials.
npx wrangler@latest pages project create sigil-pool-<slug> --production-branch main · errors silenced if it already exists
npx wrangler@latest pages deploy prospects/pool-<slug>/mock --project-name sigil-pool-<slug> --branch main --commit-dirty=true
https://sigil-pool-<slug>.pages.dev — stable across redeploys.
{slug, place_id, status: "ship_ready", url, vision_score, attempts, ts}
generally-engineers-predict-coffee.trycloudflare.com tunnel dies if the mini
reboots and changes URL anyway. Pages URLs are forever. Free tier covers everything we need.
| Field | Type | Example |
|---|---|---|
| slug | str | petri-pools |
| place_id | str | ChIJxxx... (Google) |
| niche | str | pool_service |
| status | enum | ship_ready · needs_manual_review · image_failed · render_failed · deploy_failed |
| vision_score | int | 8 |
| attempts | int | 2 |
| pages_url | str | https://sigil-pool-petri-pools.pages.dev |
| duration_s | int | 187 |
| cost_usd | float | 0.40 (images only) |
| ts | iso8601 | 2026-05-16T17:42:00Z |
batch-state.jsonl for that place_id. If status is ship_ready,
skip. If *_failed, allow re-attempt. If needs_manual_review, skip
(don't loop on it).
| File | Role | Status |
|---|---|---|
scripts/render_prospect.py | Stage 3 renderer + 3-gate QA | DONE |
scripts/build_prospect_dirs.py | Stage 1 scaffolder | DONE |
scripts/build_image_manifests.py | Stage 2 image gen | DONE |
data/scored_llm.jsonl | Stage 0 output | DONE |
DESIGN.md (9 sections, 10/10 contract, image rules) | Shared design constraint | DONE |
~/.hermes/.env CLOUDFLARE_API_TOKEN + ACCOUNT_ID | Stage 4 auth | DONE |
scripts/batch_state.py | Dedup ledger helpers (mark / is_done / pick_next) | NEW |
scripts/batch_pick.py | Stage 1: pick N from scored, exclude done | NEW |
scripts/deploy_prospect.py | Stage 4: wrangler pages create + deploy | NEW |
scripts/batch_run.py | Orchestrator: pick → image → render → deploy → ledger | NEW |
scripts/spot_check.py | Build comparison HTML for batch → Pages | NEW |
scripts/launch_tmux_batch.sh | tmux wrapper so SSH drops don't kill the run | NEW |
| cron job (Hermes) — "ping when batch done" | Notification mechanism that actually works | NEW |
| Resource | Per prospect | Per batch of 10 | Per 173-prospect full pool sweep |
|---|---|---|---|
| Image gen ($) | $0 (Codex OAuth) | $0 | $0 |
| Opus rendering ($) | $0 (Claude Max OAuth) | $0 | $0 |
| Vision QA ($) | $0 (Claude Max OAuth) | $0 | $0 |
| Cloudflare Pages ($) | $0 (free tier) | $0 | $0 |
| Wall-clock (concurrency=3) | 3-8 min | ~25 min | ~7 hours |
| Output | 1 .pages.dev URL | 10 URLs | 173 URLs |
needs_manual_review/ with the last_findings. Plan does NOT auto-retry across batches. You decide if/when to re-attempt them.bash scripts/launch_tmux_batch.sh --niche pool_service --count 10. Returns immediately with the tmux session name.
batch-state.jsonl. You can tmux attach -t lead-to-mock-batch any time.
spot_check.py picks 5 random ship_ready prospects, generates A/B HTML, deploys to https://sigil-batch-spot-check.pages.dev (overwrites prior).
place_ids seed batch-state.jsonl at first run with status=calibration_done.needs_manual_review/, Samar is pinged.--max-cost-usd halt. Per-call image generation is the only real spend; everything else is OAuth-subscription.| File | What it does | Est build time |
|---|---|---|
scripts/batch_state.py | Ledger helpers (read/write JSONL, dedup, status enum) | 15 min |
scripts/batch_pick.py | Filter scored JSONL, exclude done, write picks | 10 min |
scripts/deploy_prospect.py | Wrangler wrapper · create-if-missing + deploy + capture URL | 20 min |
scripts/batch_run.py | Orchestrator: pick → for each (image, render, deploy, ledger) with concurrency | 30 min |
scripts/spot_check.py | Pick 5 random ship_ready, build A/B HTML, deploy to Pages | 15 min |
scripts/launch_tmux_batch.sh + cron arming | tmux session + scheduled cron for completion ping | 10 min |
batch-state.jsonl with the 5 calibration prospects marked
calibration_done (so dedup blocks them), run batch_run.py --count 10,
and you wait for one Telegram message. Total wall-clock from go to ping: ~90 min build + ~25 min run.