Taking a local prototype company to a hardened, monitored, cost-governed production deployment: the 10-step checklist covering every module's production concerns in sequence.
The complete process of moving a Paperclip company from local development to production, without data loss or agent downtime.
□ All agents tested with real tasks (≥10 invocations each)
□ System prompts reviewed and locked in version control
□ Budget limits set conservatively (start at 50% of expected monthly)
□ Approval policies configured and tested
□ SQL seed scripts run on a staging database successfully
□ .env file documented (not committed) with all required variables
# 1. Create production database
createdb paperclip_prod
# 2. Run schema
psql $PROD_DATABASE_URL < db/schema.sql
# 3. Run seed scripts in order
psql $PROD_DATABASE_URL < db/seed.sql
psql $PROD_DATABASE_URL < db/your_company_seed.sql
# 4. Verify
psql $PROD_DATABASE_URL -c "SELECT name FROM companies;"
psql $PROD_DATABASE_URL -c "SELECT name, status FROM agents;"
psql $PROD_DATABASE_URL -c "SELECT COUNT(*) FROM issues WHERE status = 'open';"
□ Docker Compose stack started and healthy
□ Nginx TLS configured and cert valid
□ All environment variables set (use the checklist from pcl-64)
□ Health endpoint returns 200: GET /health/ready
□ Heartbeat scheduler confirmed running in logs
# Manually trigger first heartbeat
curl -X POST https://your-domain.com/api/heartbeat/run \
-H "Authorization: Bearer $JWT_TOKEN" \
-d '{"company_id": "'$COMPANY_ID'"}'
# Watch in real time
curl https://your-domain.com/api/heartbeat/runs/$RUN_ID
□ Heartbeat completing without errors
□ Issues being resolved (not stuck in 'open')
□ Cost per run within expected range
□ No agent auto-paused
□ No unexpected approval requests
If you ran agents in sandbox (PGlite) and want to keep historical data:
# Export from PGlite
sqlite3 .paperclip/sandbox.db ".dump" > sandbox_dump.sql
# Note: PGlite schema may differ from PostgreSQL — use the export as reference only
# Re-create issues in production with:
psql $PROD_DATABASE_URL < historical_issues_seed.sql
pg_dump of the source databaseDATABASE_URL in the new deployment# If the production deployment fails after migration:
# 1. Point DATABASE_URL back to the last known good database
# 2. Roll back Docker images to the previous tag
docker compose down
docker compose up -d --no-build # uses the previous pulled images