How the “Ghost” plan was built

A cold agent — given only our public guide and the Ghost source — reconstructed the team’s product plan through the API, single-agent, in one clean run. Shown as it ran; the only touch is cosmetic: we standardized the project names.

The finished plan, live: productbrain.com/shared/ox_sUXVXaRONuPJTS51Ff (the transcript below ends on the staging link the run minted; this is the same plan on the public host).

Your ProductBrain API key is in $PB_API_KEY and the base URL in $PB_API_URL. Use them via those env vars; never print, echo, or hard-code the key. If $PB_API_KEY is NOT set, STOP and tell me — do NOT search the filesystem, env files, or credential stores for credentials. 1. Read the guide at https://productbrain.com/docs/llm-guide.md — it is the full contract for reading and writing a ProductBrain plan. 2. Clone https://github.com/TryGhost/Ghost (a shallow clone is fine) and study it enough to understand what the product does and what the team is actually building and shipping. 3. Create a project named "Ghost" and build a ProductBrain plan for it through the API. Build the plan the product genuinely warrants — don't hold back to keep it small, and don't pad it to look thorough. 4. When the plan is in, mint a share link (POST /share) and give me the URL as the deliverable.
bash if [ -z "$PB_API_KEY" ]; then echo "PB_API_KEY_NOT_SET"; else echo "PB_API_KEY is set"; fi; if [ -z "$PB_API_URL" ]; then echo "PB_API_URL_NOT_SET"; else echo " …

Both env vars are set. Let me read the guide and start the clone in parallel.

bash git clone --depth 1 https://github.com/TryGhost/Ghost.git 2>&1 | tail -5

I'll fetch the guide now while the clone runs.

fetch https://staging.productbrain.com/docs/llm-guide.md

I have the full contract and the clone finished. Let me study Ghost.

bash cd /work/Ghost && echo "=== TOP LEVEL ===" && ls -1 && echo "=== README (first 120 lines) ===" && head -120 README.md 2>/dev/null

Let me dig into the architecture and what's actively being built.

bash echo "=== CONTEXT-MAP.md ===" && cat CONTEXT-MAP.md 2>/dev/null | head -200
bash echo "=== apps ===" && ls -1 apps/ && echo "" && echo "=== ghost dir ===" && ls -1 ghost/ && echo "" && echo "=== CONTEXT.md files (contexts) ===" && find apps …
bash echo "=== git log last 60 commits ===" && git log --oneline -60 2>/dev/null && echo "" && echo "=== commit date range ===" && git log -1 --format="%ci" 2>/dev/n …
bash cd /work/Ghost echo "=== ADRs ===" && ls -1 adr/ 2>/dev/null echo "" echo "=== docs ===" && ls -1 docs/ 2>/dev/null echo "" echo "=== package.json name/desc === …
bash cd /work/Ghost echo "=== apps descriptions (package.json name+description) ===" for d in apps/*/; do name=$(grep -m1 '"name"' "$d/package.json" 2>/dev/null | se …
bash cd /work/Ghost echo "=== portal CONTEXT ===" && cat apps/portal/CONTEXT.md 2>/dev/null | head -60 echo "" echo "=== README files for key apps ===" for a in acti …
bash cd /work/Ghost echo "=== ghost/core/core structure ===" && ls -1 ghost/core/core/ 2>/dev/null echo "" echo "=== server services (products/features) ===" && ls - …
bash cd /work/Ghost echo "=== activitypub views (features) ===" && ls apps/activitypub/src/views 2>/dev/null && echo "--- routes ---" && grep -oE 'path="[^"]*"|<Rout …
bash cd /work/Ghost echo "=== stats views ===" && ls apps/stats/src/views apps/stats/src/views/Stats 2>/dev/null echo "" echo "=== posts views ===" && ls apps/posts/ …
bash git fetch --depth 200 origin 2>&1 | tail -2 && echo "---" && git log --oneline -100 2>/dev/null | head -100

I now have a strong understanding of Ghost. Let me capture the strategic picture and verify a couple of remaining feature areas, then start on the API.

bash cd /work/Ghost echo "=== admin-x-settings views/panels ===" && ls apps/admin-x-settings/src/components 2>/dev/null | head; ls apps/admin-x-settings/src/componen …

I have a thorough understanding. Let me set up an API helper and create the project.

bash cat > /tmp/pb.sh <<'EOF' # ProductBrain curl helper — never prints the key pb() { local method="$1"; local path="$2"; local body="$3" if [ -n "$body" ]; then cu …

The API works. I'll create the "Ghost" project.

bash source /tmp/pb.sh pb POST "/mutate" '{"action":"addProject","project":{"name":"Ghost","id":"ghost"}}' echo ""

Project `ghost` created. Now I'll build the plan programmatically in Python for reliable ID mapping. Let me check the environment.

bash python3 --version 2>&1; python3 -c "import urllib.request, json, os; print('stdlib ok')"

Let me test the mutate response shape with one goal before building the full tree.

bash source /tmp/pb.sh pb POST "/mutate" '{"action":"add","projectId":"ghost","node":{"type":"goal","data":{"label":"__probe__ delete me","description":"probe"}}}' e …
bash source /tmp/pb.sh pb POST "/mutate" '{"action":"delete","projectId":"ghost","nodeId":"goal-1"}' echo "" echo "=== batch probe ===" pb POST "/mutate" '{"action": …

Batch works and returns IDs in order. Let me clean up the probes and build the full plan with a Python orchestrator.

bash source /tmp/pb.sh pb POST "/mutate" '{"action":"delete","projectId":"ghost","nodeId":"goal-2"}' >/dev/null && echo "deleted goal-2" pb POST "/mutate" '{"action" …

Clean slate. Now I'll write the full plan orchestrator.

write /tmp/build_plan.py
bash cd /work && python3 /tmp/build_plan.py

Plan built: 6 goals, 24 needs, 36 approaches, 44 jobs, 4 tasks. Let me verify the tree integrity.

bash source /tmp/pb.sh echo "=== node counts / iterations ===" pb GET "/nodes?projectId=ghost" | python3 -c "import sys,json; d=json.load(sys.stdin); m=d['_meta']; p …

No orphans, all required fields present, hierarchy intact. Let me confirm the tree traverses correctly, then mint the share link.

bash source /tmp/pb.sh echo "=== tree down from goal-5 (Grow audience) depth check ===" pb GET "/tree?projectId=ghost&nodeId=goal-5&direction=down" | python3 -c " im …

Share link minted. Let me confirm the tree endpoint returns the built hierarchy (my parser missed the shape) so the deliverable is verified.

bash source /tmp/pb.sh pb GET "/tree?projectId=ghost&nodeId=goal-6&direction=down" | python3 -c " import sys,json d=json.load(sys.stdin) print('top-level keys:', lis …

The tree returns a flat list with `depth`/`parentId` — the Revenue goal correctly nests Need → Approach with all fields intact. The plan is verified and the share link is live. ## Done — deliverable **Share link (read-only, expires 2026-08-07):** ``` https://staging.productbrain.com/shared/M1znSLX5KevnAODIQdlhK ``` ### What I did 1. **Read the contract** at `llm-guide.md` — Goal → Need → Approach → Job hierarchy plus standalone Tasks and themed Phases, with the field/enum rules and the `/mutate` batch + `/share` endpoints. 2. **Studied Ghost** (shallow clone + ~200 commits of history, app/service structure, context docs). It's the professional open-source publishing platform: a writing/publishing CMS whose active bets are the **Social Web / ActivityPub** fediverse integration, **first-party web + content analytics** (Tinybird/Stats), **membership growth** (gifts→GA, offers, comp/Stripe fixes), the **Ember→React admin migration** (shade design system, Rolldown/Vite), the **Mobiledoc→Lexical** editor move, and **security** (2FA, device verification). 3. **Built the plan** in project `ghost` (name "Ghost") — **114 nodes**: - **6 goals** — publish professionally, grow audience, earn a living, insight to grow, stay independent/owned, modern platform. - **24 needs**, **36 approaches** (each with a success `measure`, Kano tier, t-shirt `size`, and lifecycle `approachStatus` — `resolved` for shipped foundations, `validation`/`development` for live bets). - **44 jobs** — verifiable checkpoints attached only to the approaches actively in flight, **34 marked `done`** to match what's already merged (e.g. Mobiledoc removal, Social Web handle prefs, gift-links GA, Top Content tabs, Rolldown/Vite 8, 2FA). - **4 tasks** as genuine loose captures (AI drafting, native reader app, RSS automations, theme Lexical audit). - **6 themed phases**: Social web GA · First-party analytics · Membership growth · Lexical editor · Admin on React · Trust & security. Verification confirmed zero orphans, every approach carries its `measure`, and the tree traverses correctly. The API key was used only via `$PB_API_KEY` and never printed.

Same prompt each time — only the repo and the project name change. ← all three · back to the demo