Simplified from 3 instances → 2: the agent layer merged into the backend (one instance can't juggle three lanes). Each lane OWNS a directory. Coordinate only through the frozen API contract below. Commit + push small and often; pull/rebase before every push. No keys in git — backend secrets in
backend/.env, frontend infrontend/.env.local(both git-ignored).
Owns: all of backend/
- Vision (
deepgaze_runner.py): DeepGaze IIE heatmap +attention_in_boxscore + scanpath, with the dimension-fix helpers and a demo-safe fallback saliency. The money-shot truth engine. - Edit/judge (
gemini.py):edit_image(localized prompt + align + integrity),detect_target, LLM-as-judge. - Agents (
agents.py): the Director loop (Insider, Scout, Eye, Retoucher) + laterbranch.py(beam search + Judge). - Memory (
pinecone_store.py): seed ~20 competitor ads + RAG for Scout. LangSmith:attention_scorefeedback chart. - API (
main.py):POST /predict,/edit,/agents(+/health). - See
tasks/workplan-backend.mdfor the chunked to-do.
Owns: all of frontend/
- Vite + React + React Flow: upload → heatmap overlay → before/after wipe → animated score counter → agent-pipeline canvas → scanpath nodes → Web Speech voice. Clerk auth (optional, must not block boot).
- Builds against
frontend/src/mock.jsonfirst; flipsUSE_MOCK=falseto hit live endpoints via the Vite proxy — never blocked on backend. - See
tasks/workplan-C.mdfor the full frontend plan.
POST /predict — multipart image, optional target (JSON normalized [x,y,w,h])
{ "width": 1024, "height": 768, "attention_score": 0.34,
"heatmap_png": "data:image/png;base64,...",
"target_box": [0.1,0.6,0.3,0.2],
"scanpath": [{"x":0.12,"y":0.08,"order":1}],
"distractors": [{"region":[0.5,0.1,0.2,0.2],"share":0.38,"desc":"upper-left region"}] }POST /edit — multipart image, directive (string)
{ "variant_png": "data:image/png;base64,...", "edit_description": "...", "width": 1024, "height": 768 }POST /agents — multipart image, brand (synchronous; returns full result when the loop finishes)
{ "baseline_score": 0.12, "final_score": 0.41, "delta": 0.29,
"brand_brief": {...}, "competitive_insights": {"tactics":[...]},
"heatmap_before": "data:...", "heatmap_after": "data:...",
"variant_png": "data:...", "rationale": "…",
"iterations": [ {"agent":"Scout","status":"done","summary":"…"} ],
"tree": [ {"id":0,"parent":null,"depth":0,"score":0.12,"status":"root","directive":"original"},
{"id":1,"parent":0,"depth":1,"score":0.31,"status":"best","directive":"dim the face…"},
{"id":2,"parent":0,"depth":1,"score":0.09,"status":"dead","directive":"…"} ] }
tree= branch-search nodes for the canvas variant view.status∈ root|alive|dead|pruned|best (dead = didn't beat its parent). Optional — frontend ignores it if not rendering the tree yet./agentsis synchronous → the canvas replaysiterations[]client-side (timed reveal), no streaming. Source of truth:backend/schemas.py+frontend/src/mock.json(keep in sync).
backend/.env(git-ignored, real keys) ← copy ofbackend/.env.example. Loaded byconfig.py.frontend/.env.local(git-ignored) ← copy offrontend/.env.example. HoldsVITE_CLERK_PUBLISHABLE_KEY(friend adds) +VITE_API_BASE.
- Backend ships
/predict(real heatmap+score) → unblocks the spine + frontend's real wiring. (/predict,/edit,/agentsalready return contract-shaped data on a fallback saliency, so the frontend can flip to live now.) - Backend swaps the fallback for real DeepGaze, then real Insider/Scout (Pinecone), then branching.
- Frontend builds the whole UI on
mock.json; flips each panel to live as endpoints firm up.