Landing Page Copy (AIDA) + Edit
MIT↓ 0 downloadsA mixed-provider copy pipeline: a Claude Code writer turns your product brief into full AIDA-structured landing page copy, then a Codex editor cuts fluff, makes claims concrete, and tightens headlines on the same warm branch, looping until it ships.
Topology
Disclosures
Everything below runs on your machine or inside the sandbox when you use this workflow. Mismatches between these declarations and the actual code block publishing.
Host hooks
Commands executed on YOUR host machine by Sandcastle lifecycle hooks.
None declared.
Sandbox hooks
Commands executed inside the sandbox container.
None declared.
Network access
None. Both agents operate only on the local repository inside the sandbox; the copy is written from BRIEF.md with no web fetching.
Shell expansion
No shell-expansion blocks in prompt files.
Files
Diff vs the stock Sandcastle 0.12.0 template Dockerfile — green lines were added by the author, red lines were removed from stock.
+# Sandbox image for the Landing Page Copy (AIDA) + Edit workflow.+# Installs both agent CLIs — Claude Code (writer) and Codex (editor).FROM node:22-bookworm# System dependencies.RUN apt-get update && apt-get install -y --no-install-recommends \git \curl \jq \ca-certificates \&& rm -rf /var/lib/apt/lists/*-# Claude Code CLI (the agent runtime).-RUN npm install -g @anthropic-ai/claude-code+# Agent CLIs: Claude Code (writer) and Codex (editor).+RUN npm install -g @anthropic-ai/claude-code @openai/codex# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to# the host user via --build-arg to avoid permission errors on bind mounts.+# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it+# (the stock Sandcastle template pattern) — groupadd/useradd would collide with+# the existing IDs on a default build.ARG AGENT_UID=1000ARG AGENT_GID=1000-RUN groupadd --gid ${AGENT_GID} agent \- && useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agent+RUN groupmod -o -g ${AGENT_GID} node \+ && usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node-USER agent-WORKDIR /workspace+USER ${AGENT_UID}:${AGENT_GID}+WORKDIR /home/agent++# Sandcastle bind-mounts the worktree and sets the working directory at+# container start; the container just needs to stay alive until then.+ENTRYPOINT ["sleep", "infinity"]
Show full Dockerfile (highlighted)
# Sandbox image for the Landing Page Copy (AIDA) + Edit workflow.
# Installs both agent CLIs — Claude Code (writer) and Codex (editor).
FROM node:22-bookworm
# System dependencies.
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
jq \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Agent CLIs: Claude Code (writer) and Codex (editor).
RUN npm install -g @anthropic-ai/claude-code @openai/codex
# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to
# the host user via --build-arg to avoid permission errors on bind mounts.
# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it
# (the stock Sandcastle template pattern) — groupadd/useradd would collide with
# the existing IDs on a default build.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
RUN groupmod -o -g ${AGENT_GID} node \
&& usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node
USER ${AGENT_UID}:${AGENT_GID}
WORKDIR /home/agent
# Sandcastle bind-mounts the worktree and sets the working directory at
# container start; the container just needs to stay alive until then.
ENTRYPOINT ["sleep", "infinity"]
Edit the landing copy until it converts
You are a senior conversion copy editor working on the same branch the writer
just finished. Your job is to make the copy concrete, tight, and persuasive
— editing content/landing.md in place, not rewriting it into a different page.
First, read both files
Read BRIEF.md (the source of truth for product facts) and content/landing.md
(the draft). Judge the draft against the brief and against how a skeptical
buyer actually reads.
Editing checklist
Revise content/landing.md directly, committing focused changes:
- Cut fluff. Remove hedges, filler, and any sentence that doesn't move the reader toward the CTA. Tighten wordy lines. Aim to lose 10–20% of the words without losing meaning.
- Make it concrete. Replace every vague claim ("saves time", "powerful",
"easy to use") with a specific outcome, number, or example. Where a number is
needed but not in the brief, do NOT invent one — flag it
<!-- proof needed: ... -->and rephrase to something defensible. - Sharpen the headline. The hero headline must state one specific value proposition and read in under a second. If the draft's is generic, rewrite it and keep two alternatives in a comment.
- Fix weak verbs and passive voice. "We generate the report", not "the report is generated". Kill "very", "really", "almost".
- Strengthen CTAs. Action + what they get. No "Submit" / "Sign up".
- Check AIDA flow. Attention → Interest → Desire → Action should build in order, each section advancing one idea. Merge or reorder if the logic skips.
- Match the brief. Every claim must be supported by
BRIEF.md. Delete or flag anything that isn't. Never fabricate testimonials, stats, or logos. - Kill buzzwords and exclamation points wherever they survived the draft.
When to stop
Keep editing across iterations until the page is genuinely tight, specific, and
on-brief. When you would be happy to ship it as-is, output the exact line
<promise>SHIP_IT</promise> and stop. Do not emit that line while any
<!-- proof needed --> placeholder is doing load-bearing work in the hero or
final CTA — resolve or safely reframe those first.
# Auth for the Claude Code writer (required).
# Run `claude setup-token` on your host to generate a token.
CLAUDE_CODE_OAUTH_TOKEN=
# Auth for the Codex editor (required).
OPENAI_API_KEY=
import { createSandbox, claudeCode, codex } from "@ai-hero/sandcastle";
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
// A two-agent landing-page copy pipeline on ONE warm sandbox branch.
// 1. write (Claude Opus) reads BRIEF.md and writes full AIDA-structured
// landing copy to content/landing.md.
// 2. edit (Codex) revises the same file for concreteness and punch, looping
// until it is satisfied and emits <promise>SHIP_IT</promise>.
// Both agents share the same container and branch, so the editor works on the
// exact file the writer committed.
await using sandbox = await createSandbox({
branch: "content/landing",
sandbox: docker({ imageName: "sandcastle:landing-copy-aida" }),
});
// 1. Write the first full draft with Claude Code (Opus).
const draft = await sandbox.run({
name: "write",
agent: claudeCode("claude-opus-4-8", { effort: "high" }),
promptFile: ".sandcastle/write-prompt.md",
maxIterations: 1,
});
console.log(`Writer produced ${draft.commits.length} commit(s).`);
// 2. Edit and tighten with Codex on the same branch, looping until it ships.
const edit = await sandbox.run({
name: "edit",
agent: codex("gpt-5.4", { effort: "high" }),
promptFile: ".sandcastle/edit-prompt.md",
maxIterations: 4,
completionSignal: "<promise>SHIP_IT</promise>",
});
console.log(`Editor finished with signal: ${edit.completionSignal ?? "none"}`);
Write the landing page copy (AIDA)
You are an expert conversion copywriter. Write the full copy for one landing page, structured by the AIDA framework (Attention, Interest, Desire, Action).
First, read the brief
Read BRIEF.md in the repository root. It contains the product facts: what it
is, who it's for, the problem it solves, what makes it different, proof points,
and the one action you want visitors to take. If BRIEF.md is missing or thin,
work from what's there and clearly mark any assumption you had to make with an
inline <!-- assumption: ... --> comment so a human can correct it. Never
invent statistics, customer names, or testimonials — if a proof point isn't in
the brief, leave a <!-- proof needed: ... --> placeholder instead.
What to produce
Write the complete copy to content/landing.md and commit it. Use this
structure, with a short comment before each block naming which AIDA stage it
serves:
- Attention — Hero. A headline that states the single most important value proposition (specific, not generic), a 1–2 sentence subheadline that adds concreteness, and the primary CTA button text. Provide the recommended headline plus two alternatives as an HTML comment underneath.
- Interest — Problem. 2–4 short paragraphs (or a tight list) that show you understand the reader's situation in their own words. Name the pain; don't lecture. A rhetorical question is welcome here.
- Interest — How it works. 3–4 steps that reduce perceived complexity.
- Desire — Benefits. 3–5 benefits. Lead each with the outcome, not the feature ("Cut weekly reporting from 4 hours to 15 minutes", not "Automated reports"). Tie every feature to what it means for the reader.
- Desire — Proof. Where the brief gives real numbers, logos, or quotes, use them. Otherwise leave clearly labelled placeholders.
- Desire — Objections. 3–5 FAQ-style objection/answers, or a short comparison, plus any guarantee/risk-reversal from the brief.
- Action — Final CTA. Recap the core value in one line, repeat the CTA, and restate the risk reversal.
Writing rules
- Clarity over cleverness. Benefits over features. Specific over vague.
- Active voice, confident tone, short sentences. Use the reader's language, not company jargon.
- No buzzwords ("streamline", "seamless", "innovative", "revolutionary"), no exclamation points, no filler.
- Strong CTAs:
[Action] + [what they get](e.g. "Start my free trial"), never "Submit" / "Sign up" / "Learn more".
Commit the finished copy to content/landing.md. A separate editor agent will
tighten it next — write it well, but don't self-edit into blandness.
README
Landing Page Copy (AIDA) + Edit
Hand it a product brief; get back landing page copy that's structured to convert and edited until it's tight. A Claude Code writer drafts the full page using the classic AIDA framework, then a Codex editor tears into it — cutting fluff, demanding specifics, and sharpening the headline — on the same warm branch, looping until it's ready to ship.
What it does
One prompt asking an AI to "write landing page copy" gives you generic mush. This workflow splits writing from editing, the way a real copy team does:
- Write (Claude Opus) reads your
BRIEF.mdand produces the complete page, labelled by AIDA stage — hero (headline, subhead, CTA), the problem, how it works, benefits framed as outcomes, proof, objection handling, and a final CTA. It offers headline alternatives and refuses to fabricate stats or testimonials, flagging missing proof instead. Committed tocontent/landing.md. - Edit (Codex) revises that file in place: cutting 10–20% of the words,
replacing every vague claim with something concrete, fixing passive voice and
weak CTAs, checking the AIDA flow builds in order, and verifying every claim
traces back to the brief. It loops until it's satisfied, then emits
<promise>SHIP_IT</promise>.
How it works
main.ts opens one warm Docker sandbox with createSandbox() on the
content/landing branch. The Claude writer runs once; then the Codex editor
runs on the same container and branch for up to four iterations, stopping early
when it signals SHIP_IT. Because both agents share the branch, the editor is
working on the exact file the writer committed. The topology is
write → edit → (loop) → ship.
Requirements
Add a BRIEF.md at your repo root with the product facts: what it is, who it's
for, the problem it solves, what makes it different, any real proof points, and
the one action you want visitors to take. Set both CLAUDE_CODE_OAUTH_TOKEN
(run claude setup-token) and OPENAI_API_KEY in .sandcastle/.env. Build the
image once with npx @ai-hero/sandcastle docker build-image, then run it with
npx tsx .sandcastle/main.ts. The finished copy is at content/landing.md.