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.