Deploy SOP · canon · locked 2026-05-24

Five gates. One command shape.

The canonical sequence for shipping articulate-ai.work. Written by Dicky after two SEV-1 failures inside one deploy. Reviewed adversarially by Kendall. Older variants in deploy-log.md headers and various personas' notes are superseded.

Owner: Dicky (execution) · Adversarial gate: Kendall · Preflight: Scotty · Target: Vercel project articulate-ai on team Articulate-AI · domain articulate-ai.work

The one-liner

This is the only command shape Dicky hands Anthony. Everything else is a fail.

(cd ~/ClaudeWork/Articulate/website && git status -s && git push origin main && vercel --prod --yes)
If git status -s returns anything — STOP. That's a dirty tree. Don't deploy until commits are clean. See the dirty-tree trap below.

The five gates — every deploy passes all five

Gate 1 · Canon read

Read the existing pattern before writing

Before writing or editing a file under ~/ClaudeWork/Articulate/website/, read the existing pattern. Reference posts: blog/slaying-the-memoryless-dragon.html (canonical post template), index.html (top-level template), team/index.html (microsite template).

Refused: building a parallel publish pipeline (e.g. deploy-2026-05-24/public/) instead of using website/. FAIL-DEPLOY-PARALLEL-INFRA, 2026-05-24.

Gate 2 · Kendall

Adversarial review — default NO

Routine deploy (blog post, copy fix, nav patch): Kendall's gate is short — does the diff match the brief, and is there a rollback path. Two questions, answered in one paragraph. Don't theatre it.

Structural deploy (template change, vercel.json rewrites, new section): full charter pass — five alternatives, atomic rollback per step, named prior art, honest probability, Anthony-hours declared.

Refused: single-option plans, "should work," missing rollback. Override phrase: "skip Kendall, ship it." — logged to Kendall's decisions.md.

Gate 3 · Clean tree

git status -s must be empty

(cd ~/ClaudeWork/Articulate/website && git status -s)

Empty output = clean. Anything else = STOP. If there are uncommitted changes not part of this deploy, commit them by concern OR stash, then re-check. Never deploy on top of unrelated dirty work — last week's local edits get swept into prod.

Refused: "I'll deploy and clean up the tree later." FAIL-DEPLOY-SEQUENCE-WHOLE step 5, 2026-05-24.

Gate 4 · cwd guard

The cd lives inside the command, not next to it

(cd ~/ClaudeWork/Articulate/website && vercel --prod --yes)

Never hand Anthony a bare vercel --prod --yes and a separate cd instruction. The two get split when pasted, the shell state resets between his pastes (Last login: on every fresh shell), and the bare vercel runs from ~. Vercel will prompt "You are deploying your home directory. Continue?" and Anthony in a hurry will type yes.

Refused: any command Dicky hands Anthony that doesn't include the cd inline. FAIL-DEPLOY-CMD-NO-CWD, 2026-05-24, SEV 1.

Gate 5 · Post-deploy verification

Curl every page or it didn't ship

for u in \ https://articulate-ai.work/ \ https://articulate-ai.work/blog \ https://articulate-ai.work/team \ https://articulate-ai.work/blog/<changed-slug>; do code=$(curl -s -o /dev/null -w "%{http_code}" "$u") echo "$code $u" done

Every line must be 200. A 308 is acceptable only on root-vanity URLs that rewrite to a longer canonical path. 404, 500, 502 = revert.

Refused: Dicky calling a deploy "shipped" without running the curl sweep. FAIL-2026-05-16-plex-URL-not-tested, FAIL-2026-05-18-broken-postgraduate-assistant-link.

Brief format — at the top of every deploy report

Per the rule locked 2026-05-20 in HOME.md:

✅ Live now: - [page-1-path](https://articulate-ai.work/page-1-path) - [page-2-path](https://articulate-ai.work/page-2-path)

URLs are clickable Markdown hyperlinks at the TOP of the brief. No "click through to verify," no URL in the footer, no plain-text URLs to copy-paste. If the deploy is still in flight when the brief is written: "URL pending — will fill in once deploy returns" and update the moment it lands.

Rollback

(cd ~/ClaudeWork/Articulate/website && git revert HEAD --no-edit && git push origin main && vercel --prod --yes)

Vercel's git-integration is connected but does not auto-deploy on push as of 2026-05-24 — confirmed by 20 consecutive deploys with gitDirty=1 from local CLI. The deploy step is mandatory; revert-and-push alone is not enough.

For instant rollback to a known-good deploy, use the Vercel dashboard → Deployments → "Promote to Production" on the previous green build. Faster than a revert + redeploy when the bad ship is breaking the site.

Why Anthony runs the actual deploy command

Two sandbox constraints:

  1. No GitHub credentials. git push fails with "could not read Username for 'https://github.com'". Anthony's laptop uses macOS Keychain auth.
  2. Vercel API not on allowlist. The Cowork proxy 403s api.vercel.com, so vercel --prod --yes fails from the sandbox. Allowlist is locked at api.elevenlabs.io, *.cloudfront.net, higgsfield.ai, api.openai.com.

Both are addressable in future sessions (Vercel PAT to credentials.md, allowlist expansion request, or wire the Vercel git-integration dashboard toggle to auto-deploy on push). Until then: Dicky writes the cwd-guarded one-liner; Anthony pastes; Dicky verifies via curl.

Failure modes — known, named, with forcing functions

IDPatternForcing function
FAIL-DEPLOY-CMD-NO-CWD
(2026-05-24, SEV 1)
Bare vercel --prod --yes with separate cd instructionGate 4 — cwd guard inline, every time
FAIL-DEPLOY-PARALLEL-INFRA
(2026-05-24)
Built deploy-2026-05-24/public/ parallel pipeline instead of website/Gate 1 — read canon first
FAIL-DEPLOY-SEQUENCE-WHOLE step 5
(2026-05-24)
Dirty-tree deploy swept unrelated edits to prodGate 3 — git status -s empty before push
FAIL-2026-05-18-postgraduate-assistantDeploy shipped with broken root-vanity URL, only Anthony caught itGate 5 — curl sweep before "shipped"
FAIL-2026-05-16-plex-URL-not-tested"Done" claimed without HTTP probeGate 5 — same

Sign-off — this SOP itself

Dicky
Written
Authored after FAIL-DEPLOY-CMD-NO-CWD + FAIL-DEPLOY-SEQUENCE-WHOLE. Five gates codified. Single command shape adopted as canon.
Kendall
Reviewed
Default-NO posture cleared. Five-gate pass: (1) canon-read named, (2) Kendall gate folded in, (3) clean-tree non-negotiable, (4) cwd guard in command shape, (5) curl verification mandatory. Rollback path stated. Anthony-hours declared (zero — Dicky writes; Anthony pastes one line; verify is automated). ACCEPT.
Scotty
Read
Preflight handoff intact — Scotty owns engine readiness, Dicky takes it from there. No conflict with mini-stack discipline.