Deployment Plan Generator (Persimmon)
Purpose
Produce a short, actionable deployment plan for a specific milestone on a Persimmon client project. The doc is the written agreement — before/during/after the push — between Persimmon and the client. It lives in the client repo, gets signed off, and is the reference if anything goes sideways.
Not a full scope document. Not a sales artifact. Keep it under 80 lines of body content. If it's longer, you're planning too much in one cutover.
When to Use
- Shipping a named milestone to production (e.g. "Indexer MVP", "Auth hardening", "RAG go-live").
- Cutting a release that the client must sign off on before it goes live.
- Any deployment that touches a destructive migration, a custom domain, a new env var, or a new third-party integration.
- Do not use for routine patch deploys (typo fix, copy change, dependency bump) — those don't need a plan.
Output
- File:
docs/deployment-plan-YYYY-MM-DD.md in the client project repo.
- Format: Markdown. Eight fixed sections. One title line. One metadata table.
- Length: ≤ 80 lines of body content (excluding frontmatter/signature block).
Stack Assumptions
This skill assumes the Persimmon default stack — read /Users/renatoprado/Documents/Projects/persimmon-claude-skills/CLAUDE.md first and adapt if the client's stack differs.
- Hosting: Railway (app + DB + bucket). Push to
main → auto-build → live.
- DB: PostgreSQL on Railway, schema via Prisma (
prisma db push or migrate deploy — confirm per project).
- CI: GitHub Actions runs lint / typecheck / prisma validate / build on every push.
- Rollback: Railway keeps previous deploy builds; rollback = click previous build → "Redeploy" in dashboard, or
railway redeploy <deploymentId> via CLI.
- DNS: Custom domain already cut over via Railway → DNS; no DNS work during deploy unless the plan explicitly says so.
Section Template (Copy Exactly)
# Deployment Plan — {Milestone Name}
| | |
|---|---|
| **Project** | {client-project-repo} |
| **Milestone** | {short name} |
| **Target date** | YYYY-MM-DD |
| **Prepared by** | Persimmon Automation Labs |
| **Client contact** | {Name, role} |
| **Status** | Draft / Approved / Shipped |
## 1. Overview
One paragraph. What is shipping, why now, what the client will see after this goes live. No jargon — the client has to understand the value in 30 seconds.
## 2. Scope
**In:**
- {Feature or fix 1 — one line each}
- {Feature or fix 2}
- {...}
**Out (explicitly not in this deploy):**
- {Thing the client might assume is in — name it}
- {Deferred items, next milestone}
## 3. Pre-deployment checklist
- [ ] DB migration reviewed and applied to `{project}_dev` without error
- [ ] New env vars set in Railway `Production` environment: `{VAR_1}`, `{VAR_2}`
- [ ] Custom domain / DNS already live (or N/A)
- [ ] CI green on the commit being deployed (lint, typecheck, prisma validate, build)
- [ ] `review-skills` dimensions pass (security, accessibility, performance) — or waivers documented below
- [ ] Backup of production DB taken at `{location}` (required if migration is destructive)
- [ ] Client notified of maintenance window (if downtime expected)
## 4. Deployment steps
1. Merge `{branch}` → `main` (or push `main` directly if already merged).
2. Railway auto-deploys `{service-name}`. Build time typically {N} min.
3. Watch Railway build log until status = **SUCCESS**. If `prisma db push` is in the build step, confirm it completed without drift warnings.
4. Smoke-test production (§7 checklist) within 5 minutes of deploy success.
5. Verify custom domain `{https://custom.domain}` serves the new build (hard refresh, check build ID in network tab or `/api/health` if available).
6. Notify client with link + 1-line summary of what shipped.
## 5. Rollback plan
- **Previous Railway deployment ID**: `{deploymentId}` — redeploy via dashboard or `railway redeploy {deploymentId}`.
- **DB rollback**:
- If migration is additive only → no DB rollback needed; previous build works against new schema.
- If migration is destructive (drop column, drop table, rename, enum value removed) → **restore DB from backup taken in §3** before redeploying previous build. Restore path: `{backup-location}` → `pg_restore --clean --if-exists --no-owner --dbname=$DATABASE_URL {backup-file}`.
- **Data backup location**: `{s3-bucket-path or local-path}`.
- **Rollback authority**: Persimmon can rollback unilaterally within the first 60 min if smoke tests fail. After that, client sign-off required.
## 6. Post-deployment verification
Smoke test checklist — all must pass within 10 min of deploy:
- [ ] Landing route loads over HTTPS on custom domain
- [ ] Login works (test account: `{test-user-email}`)
- [ ] Core user flow end-to-end: `{the one path the client cares about most}`
- [ ] No 5xx errors in Railway logs for 10 minutes post-deploy
- [ ] New feature from §2 actually visible/usable
- [ ] Existing critical flow from previous milestone still works (regression check)
## 7. Sign-off
| Role | Name | Signature | Date |
|---|---|---|---|
| Client approver | | | |
| Persimmon lead | | | |
Status legend: **Draft** (pre-review) → **Approved** (client signed, ready to ship) → **Shipped** (deployed, smoke tests green).
Writing Rules
- One milestone per plan. If you're shipping two unrelated features, write two plans.
- Scope section is binding. Everything in "In" goes out with this deploy. Everything in "Out" does not. If something surfaces mid-deploy that isn't in "In", it waits for the next plan.
- Rollback must be real. If you can't describe how to roll back, the plan isn't ready. Destructive migrations require a named backup file before deploy.
- Checklists are checkboxes, not prose. The client (and future-you) should be able to scan and tick.
- Never include secrets. Env var names only. Values live in Railway.
- Assume Railway + push-to-deploy. If the project is self-hosted or uses a different CD, note it in §4 and adapt. Do not pretend it's Railway if it isn't.
- Skip sections only when truly N/A. If there's no DB migration, §3 still lists the migration line as
N/A — no schema changes. Don't delete the line; the client reads structure.
Example — Filled Plan for Piccino
Realistic example for the Piccino milestone "Indexer MVP" — page-by-page PDF classification with ProcessDocument persistence.
# Deployment Plan — Indexer MVP
| | |
|---|---|
| **Project** | piccino-legal |
| **Milestone** | Indexer MVP (page classification + ProcessDocument persistence) |
| **Target date** | 2026-04-17 |
| **Prepared by** | Persimmon Automation Labs |
| **Client contact** | João Piccino, sócio |
| **Status** | Draft |
## 1. Overview
Este deploy liga a primeira etapa do pipeline do Piccino: após o upload, cada página do PDF é classificada automaticamente pelo Claude (petição inicial, sentença, intimação, demonstrativo de débito, etc.) e persistida em `ProcessDocument`. O painel passa a mostrar o status `INDEXED` e a lista de peças identificadas por processo.
## 2. Scope
**In:**
- Job de indexação em background por processo (status `UPLOADED → INDEXING → INDEXED`)
- Classificação página-a-página via Claude Sonnet com cache de system prompt
- Persistência em `ProcessDocument` (tipo, páginas inicial/final, resumo curto)
- Tela `/processes/[id]` passa a listar as peças classificadas
- Botão "Reindexar" (admin only) — apaga `ProcessDocument` do processo e reenfileira
**Out (explicitly not in this deploy):**
- Análise de parâmetros (prescrição, nulidade) — próximo milestone
- Geração de peça — milestone posterior
- RAG sobre doutrina do escritório — ainda não
- SSE / push de status — por enquanto polling a cada 5s
## 3. Pre-deployment checklist
- [ ] Migration `ProcessDocument` model + enum `DocumentType` aplicada em `piccino_dev` sem erro
- [ ] Env vars em Railway Production: `ANTHROPIC_API_KEY` (já existe, verificar), `INDEXER_CONCURRENCY=2` (novo)
- [ ] Custom domain `sistema.piccino.com.br` já live — N/A (sem mudança)
- [ ] CI verde no commit de deploy (lint, typecheck, prisma validate, build)
- [ ] review-skills: security + performance passam; accessibility com waiver (tela de lista ainda sem labels ARIA — backlog)
- [ ] Backup de `piccino_prod` em `tigris://piccino-backups/2026-04-17-pre-indexer.dump` (migration é aditiva, mas tabela nova é grande — backup por precaução)
- [ ] João avisado: sem janela de manutenção, deploy em horário comercial ok
## 4. Deployment steps
1. Merge PR `feat/indexer-mvp` → `main`.
2. Railway auto-deploys `piccino-web`. Build típico: 3 min.
3. Acompanhar log até status **SUCCESS**. Confirmar `prisma db push` aplicou `ProcessDocument` + enum sem drift.
4. Smoke test (§7) em 5 min.
5. Abrir `https://sistema.piccino.com.br`, fazer login, ver painel.
6. Avisar João: "Indexer no ar — pode subir um PDF de teste e acompanhar o status em /processes".
## 5. Rollback plan
- **Previous Railway deployment ID**: `d-a7f3e2` (deploy anterior, auth hardening) — redeploy via dashboard.
- **DB rollback**: migration é aditiva (nova tabela + novo enum + nova coluna `Process.indexedAt`). Não precisa rollback de schema — build anterior ignora colunas novas. Se der ruim de verdade, restaurar `tigris://piccino-backups/2026-04-17-pre-indexer.dump` via `pg_restore --clean --if-exists --no-owner --dbname=$DATABASE_URL`.
- **Data backup location**: `tigris://piccino-backups/2026-04-17-pre-indexer.dump`.
- **Rollback authority**: Persimmon rollback unilateral em 60 min se smoke falhar.
## 6. Post-deployment verification
- [ ] `https://sistema.piccino.com.br` carrega em HTTPS
- [ ] Login de João funciona
- [ ] Upload de PDF de teste (3 páginas) → status vai `UPLOADED → INDEXING → INDEXED` em < 2 min
- [ ] `/processes/[id]` mostra peças classificadas com tipo + páginas
- [ ] Sem 5xx no log do Railway por 10 min pós-deploy
- [ ] Botão "Reindexar" funciona (admin) e reexecuta sem duplicar registros
- [ ] Upload antigo (pré-deploy) continua abrindo sem erro
## 7. Sign-off
| Role | Name | Signature | Date |
|---|---|---|---|
| Client approver | João Piccino | | |
| Persimmon lead | Renato Prado | | |
Delivery Steps
- Confirm the milestone. If the user hasn't given a name, ask. Don't guess.
- Read the client CLAUDE.md to pull stack specifics, domain, service name, migration strategy.
- Check the repo for:
- Current Railway deployment ID (ask user or check
railway status if MCP available).
- Existing migrations in
prisma/migrations/ — is this one destructive?
- Env vars in
.env.example — anything new needing Railway config?
- Generate the doc at
docs/deployment-plan-YYYY-MM-DD.md. YYYY-MM-DD = target deploy date (not today).
- Mark status
Draft. Only the human flips to Approved / Shipped.
- Verify:
- Body ≤ 80 lines.
- All 7 sections present.
- Rollback plan is concrete (has IDs, backup paths — or an explicit "N/A because additive only").
- No secrets.
- Report file path + one-line summary. Do not commit unless the user asks.
Anti-patterns
- Writing a plan as a sales doc (hero copy, value props) — this is an ops doc.
- Skipping §5 rollback because "it's a small change". Small changes break production too.
- Listing env var values instead of names.
- Describing deploy as "push and pray". If §4 has fewer than 4 concrete steps, you're being sloppy.
- One plan covering three milestones. Split it.
- Forgetting the regression line in §6. Every deploy can break something that already worked.
1---2name: meta-deployment-plan3description: Generate a lean, client-facing deployment plan document for a Persimmon Automation Labs milestone. Use when shipping a feature to production, cutting a release, pushing a milestone live, or preparing sign-off for a client go-live. Trigger keywords — deployment plan, ship plan, go-live, release doc, production cutover, milestone launch, cutover checklist, rollout doc.4---56# Deployment Plan Generator (Persimmon)78## Purpose910Produce a short, actionable deployment plan for a specific milestone on a Persimmon client project. The doc is the written agreement — before/during/after the push — between Persimmon and the client. It lives in the client repo, gets signed off, and is the reference if anything goes sideways.1112**Not** a full scope document. Not a sales artifact. **Keep it under 80 lines of body content.** If it's longer, you're planning too much in one cutover.1314---1516## When to Use1718- Shipping a named milestone to production (e.g. "Indexer MVP", "Auth hardening", "RAG go-live").19- Cutting a release that the client must sign off on before it goes live.20- Any deployment that touches a **destructive migration**, a **custom domain**, a **new env var**, or a **new third-party integration**.21- **Do not** use for routine patch deploys (typo fix, copy change, dependency bump) — those don't need a plan.2223---2425## Output2627- **File**: `docs/deployment-plan-YYYY-MM-DD.md` in the client project repo.28- **Format**: Markdown. Eight fixed sections. One title line. One metadata table.29- **Length**: ≤ 80 lines of body content (excluding frontmatter/signature block).3031---3233## Stack Assumptions3435This skill assumes the Persimmon default stack — read `/Users/renatoprado/Documents/Projects/persimmon-claude-skills/CLAUDE.md` first and adapt if the client's stack differs.3637- **Hosting**: Railway (app + DB + bucket). Push to `main` → auto-build → live.38- **DB**: PostgreSQL on Railway, schema via Prisma (`prisma db push` or `migrate deploy` — confirm per project).39- **CI**: GitHub Actions runs lint / typecheck / prisma validate / build on every push.40- **Rollback**: Railway keeps previous deploy builds; rollback = click previous build → "Redeploy" in dashboard, or `railway redeploy <deploymentId>` via CLI.41- **DNS**: Custom domain already cut over via Railway → DNS; no DNS work during deploy unless the plan explicitly says so.4243---4445## Section Template (Copy Exactly)4647```markdown48# Deployment Plan — {Milestone Name}4950| | |51|---|---|52| **Project** | {client-project-repo} |53| **Milestone** | {short name} |54| **Target date** | YYYY-MM-DD |55| **Prepared by** | Persimmon Automation Labs |56| **Client contact** | {Name, role} |57| **Status** | Draft / Approved / Shipped |5859## 1. Overview6061One paragraph. What is shipping, why now, what the client will see after this goes live. No jargon — the client has to understand the value in 30 seconds.6263## 2. Scope6465**In:**66- {Feature or fix 1 — one line each}67- {Feature or fix 2}68- {...}6970**Out (explicitly not in this deploy):**71- {Thing the client might assume is in — name it}72- {Deferred items, next milestone}7374## 3. Pre-deployment checklist7576- [ ] DB migration reviewed and applied to `{project}_dev` without error77- [ ] New env vars set in Railway `Production` environment: `{VAR_1}`, `{VAR_2}`78- [ ] Custom domain / DNS already live (or N/A)79- [ ] CI green on the commit being deployed (lint, typecheck, prisma validate, build)80- [ ] `review-skills` dimensions pass (security, accessibility, performance) — or waivers documented below81- [ ] Backup of production DB taken at `{location}` (required if migration is destructive)82- [ ] Client notified of maintenance window (if downtime expected)8384## 4. Deployment steps85861. Merge `{branch}` → `main` (or push `main` directly if already merged).872. Railway auto-deploys `{service-name}`. Build time typically {N} min.883. Watch Railway build log until status = **SUCCESS**. If `prisma db push` is in the build step, confirm it completed without drift warnings.894. Smoke-test production (§7 checklist) within 5 minutes of deploy success.905. Verify custom domain `{https://custom.domain}` serves the new build (hard refresh, check build ID in network tab or `/api/health` if available).916. Notify client with link + 1-line summary of what shipped.9293## 5. Rollback plan9495- **Previous Railway deployment ID**: `{deploymentId}` — redeploy via dashboard or `railway redeploy {deploymentId}`.96- **DB rollback**:97 - If migration is additive only → no DB rollback needed; previous build works against new schema.98 - If migration is destructive (drop column, drop table, rename, enum value removed) → **restore DB from backup taken in §3** before redeploying previous build. Restore path: `{backup-location}` → `pg_restore --clean --if-exists --no-owner --dbname=$DATABASE_URL {backup-file}`.99- **Data backup location**: `{s3-bucket-path or local-path}`.100- **Rollback authority**: Persimmon can rollback unilaterally within the first 60 min if smoke tests fail. After that, client sign-off required.101102## 6. Post-deployment verification103104Smoke test checklist — all must pass within 10 min of deploy:105106- [ ] Landing route loads over HTTPS on custom domain107- [ ] Login works (test account: `{test-user-email}`)108- [ ] Core user flow end-to-end: `{the one path the client cares about most}`109- [ ] No 5xx errors in Railway logs for 10 minutes post-deploy110- [ ] New feature from §2 actually visible/usable111- [ ] Existing critical flow from previous milestone still works (regression check)112113## 7. Sign-off114115| Role | Name | Signature | Date |116|---|---|---|---|117| Client approver | | | |118| Persimmon lead | | | |119120Status legend: **Draft** (pre-review) → **Approved** (client signed, ready to ship) → **Shipped** (deployed, smoke tests green).121```122123---124125## Writing Rules1261271. **One milestone per plan.** If you're shipping two unrelated features, write two plans.1282. **Scope section is binding.** Everything in "In" goes out with this deploy. Everything in "Out" does not. If something surfaces mid-deploy that isn't in "In", it waits for the next plan.1293. **Rollback must be real.** If you can't describe how to roll back, the plan isn't ready. Destructive migrations require a named backup file *before* deploy.1304. **Checklists are checkboxes, not prose.** The client (and future-you) should be able to scan and tick.1315. **Never include secrets.** Env var *names* only. Values live in Railway.1326. **Assume Railway + push-to-deploy.** If the project is self-hosted or uses a different CD, note it in §4 and adapt. Do not pretend it's Railway if it isn't.1337. **Skip sections only when truly N/A.** If there's no DB migration, §3 still lists the migration line as `N/A — no schema changes`. Don't delete the line; the client reads structure.134135---136137## Example — Filled Plan for Piccino138139Realistic example for the Piccino milestone "Indexer MVP" — page-by-page PDF classification with `ProcessDocument` persistence.140141```markdown142# Deployment Plan — Indexer MVP143144| | |145|---|---|146| **Project** | piccino-legal |147| **Milestone** | Indexer MVP (page classification + ProcessDocument persistence) |148| **Target date** | 2026-04-17 |149| **Prepared by** | Persimmon Automation Labs |150| **Client contact** | João Piccino, sócio |151| **Status** | Draft |152153## 1. Overview154155Este deploy liga a primeira etapa do pipeline do Piccino: após o upload, cada página do PDF é classificada automaticamente pelo Claude (petição inicial, sentença, intimação, demonstrativo de débito, etc.) e persistida em `ProcessDocument`. O painel passa a mostrar o status `INDEXED` e a lista de peças identificadas por processo.156157## 2. Scope158159**In:**160- Job de indexação em background por processo (status `UPLOADED → INDEXING → INDEXED`)161- Classificação página-a-página via Claude Sonnet com cache de system prompt162- Persistência em `ProcessDocument` (tipo, páginas inicial/final, resumo curto)163- Tela `/processes/[id]` passa a listar as peças classificadas164- Botão "Reindexar" (admin only) — apaga `ProcessDocument` do processo e reenfileira165166**Out (explicitly not in this deploy):**167- Análise de parâmetros (prescrição, nulidade) — próximo milestone168- Geração de peça — milestone posterior169- RAG sobre doutrina do escritório — ainda não170- SSE / push de status — por enquanto polling a cada 5s171172## 3. Pre-deployment checklist173174- [ ] Migration `ProcessDocument` model + enum `DocumentType` aplicada em `piccino_dev` sem erro175- [ ] Env vars em Railway Production: `ANTHROPIC_API_KEY` (já existe, verificar), `INDEXER_CONCURRENCY=2` (novo)176- [ ] Custom domain `sistema.piccino.com.br` já live — N/A (sem mudança)177- [ ] CI verde no commit de deploy (lint, typecheck, prisma validate, build)178- [ ] review-skills: security + performance passam; accessibility com waiver (tela de lista ainda sem labels ARIA — backlog)179- [ ] Backup de `piccino_prod` em `tigris://piccino-backups/2026-04-17-pre-indexer.dump` (migration é aditiva, mas tabela nova é grande — backup por precaução)180- [ ] João avisado: sem janela de manutenção, deploy em horário comercial ok181182## 4. Deployment steps1831841. Merge PR `feat/indexer-mvp` → `main`.1852. Railway auto-deploys `piccino-web`. Build típico: 3 min.1863. Acompanhar log até status **SUCCESS**. Confirmar `prisma db push` aplicou `ProcessDocument` + enum sem drift.1874. Smoke test (§7) em 5 min.1885. Abrir `https://sistema.piccino.com.br`, fazer login, ver painel.1896. Avisar João: "Indexer no ar — pode subir um PDF de teste e acompanhar o status em /processes".190191## 5. Rollback plan192193- **Previous Railway deployment ID**: `d-a7f3e2` (deploy anterior, auth hardening) — redeploy via dashboard.194- **DB rollback**: migration é aditiva (nova tabela + novo enum + nova coluna `Process.indexedAt`). Não precisa rollback de schema — build anterior ignora colunas novas. Se der ruim de verdade, restaurar `tigris://piccino-backups/2026-04-17-pre-indexer.dump` via `pg_restore --clean --if-exists --no-owner --dbname=$DATABASE_URL`.195- **Data backup location**: `tigris://piccino-backups/2026-04-17-pre-indexer.dump`.196- **Rollback authority**: Persimmon rollback unilateral em 60 min se smoke falhar.197198## 6. Post-deployment verification199200- [ ] `https://sistema.piccino.com.br` carrega em HTTPS201- [ ] Login de João funciona202- [ ] Upload de PDF de teste (3 páginas) → status vai `UPLOADED → INDEXING → INDEXED` em < 2 min203- [ ] `/processes/[id]` mostra peças classificadas com tipo + páginas204- [ ] Sem 5xx no log do Railway por 10 min pós-deploy205- [ ] Botão "Reindexar" funciona (admin) e reexecuta sem duplicar registros206- [ ] Upload antigo (pré-deploy) continua abrindo sem erro207208## 7. Sign-off209210| Role | Name | Signature | Date |211|---|---|---|---|212| Client approver | João Piccino | | |213| Persimmon lead | Renato Prado | | |214```215216---217218## Delivery Steps2192201. **Confirm the milestone.** If the user hasn't given a name, ask. Don't guess.2212. **Read the client CLAUDE.md** to pull stack specifics, domain, service name, migration strategy.2223. **Check the repo** for:223 - Current Railway deployment ID (ask user or check `railway status` if MCP available).224 - Existing migrations in `prisma/migrations/` — is this one destructive?225 - Env vars in `.env.example` — anything new needing Railway config?2264. **Generate the doc** at `docs/deployment-plan-YYYY-MM-DD.md`. YYYY-MM-DD = target deploy date (not today).2275. **Mark status `Draft`**. Only the human flips to `Approved` / `Shipped`.2286. **Verify**:229 - Body ≤ 80 lines.230 - All 7 sections present.231 - Rollback plan is concrete (has IDs, backup paths — or an explicit "N/A because additive only").232 - No secrets.2337. **Report** file path + one-line summary. Do not commit unless the user asks.234235---236237## Anti-patterns238239- Writing a plan as a sales doc (hero copy, value props) — this is an ops doc.240- Skipping §5 rollback because "it's a small change". Small changes break production too.241- Listing env var *values* instead of names.242- Describing deploy as "push and pray". If §4 has fewer than 4 concrete steps, you're being sloppy.243- One plan covering three milestones. Split it.244- Forgetting the regression line in §6. Every deploy can break something that already worked.