Skip to content
Back to skills

Ship To Prod

ASecurity

Ship delta-agents to production as one procedure — confirm target branch once, pre-flight via scripts/check.sh, heredoc commit, push, background-watch the "Deploy to ECS" run, ground-truth-verify per-service ECS image tags against the pushed SHA, smoke-test the gateway /health endpoint, and report. Use when the user says "ship it", "ship to prod", "deploy to prod", "push and deploy", or "commit, push and watch the deploy". Replaces the hand-rolled 200+-command commit/push/poll ritual with one...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
toolsrustgobashnoderailsdockerawsgitapidatabase

Works with

  • api

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add zalogarcia/zalo-claude-code-setup --skill ship-to-prod --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ship To Prod?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ship To Prod
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zalogarcia-ship-to-prod/badge)](https://www.skillsdirectory.com/skills/zalogarcia-ship-to-prod)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ship-to-prod
description: Ship delta-agents to production as one procedure — confirm target branch once, pre-flight via scripts/check.sh, heredoc commit, push, background-watch the "Deploy to ECS" run, ground-truth-verify per-service ECS image tags against the pushed SHA, smoke-test the gateway /health endpoint, and report. Use when the user says "ship it", "ship to prod", "deploy to prod", "push and deploy", or "commit, push and watch the deploy". Replaces the hand-rolled 200+-command commit/push/poll ritual with one background watch and a deterministic verification chain.
---

Ship the delta-agents repo (`/Users/zalo/dev/delta-agents`) to production ECS in one deterministic pass. Every constant below is grounded in `.github/workflows/deploy.yml` and `docs/RUNBOOK.md` — do not substitute names from memory.

## Grounded constants (from deploy.yml)

- **Workflow name:** `Deploy to ECS` — triggers ONLY on `push` to `main`. Pushing any other branch deploys nothing.
- **ECS cluster:** `delta-agents` (region `us-east-1`)
- **Services / containers / task-def families:** `gateway`, `worker`, `embedding-worker`, `insights-worker`, `url-watch-worker`, `wa-bailey`, `voice-bridge` (SEVEN, verified against deploy.yml 2026-09-13; a six-service list silently skips voice-bridge) — task-def families are `delta-agents-{service}` (see `.aws/task-definitions/*.json`)
- **Image tag = full commit SHA:** `340829666011.dkr.ecr.us-east-1.amazonaws.com/delta-agents/{service}:{sha}`
- **Path-filtered deploys:** the `detect-changes` job (dorny/paths-filter) only deploys services whose paths changed. `packages/**` fans out to six of the seven: voice-bridge's filter is deliberately narrower (its own source plus the `shared`, `contracts`, `workflow-schema` and `form-schema` packages), so a `packages/core` or `packages/providers` push leaves voice-bridge on an older tag. `apps/widget/**`, `apps/form/**` and `apps/voice-orb/**` rebuild the gateway (it serves those bundles). An `--allow-empty` commit deploys NOTHING.
- **Ordering:** worker deploys before gateway (`deploy-gateway` needs `deploy-worker`). Full run takes ~10-12 min. A `post-deploy-smoke` job runs last.
- **Health path:** `/health` in `apps/gateway/src/index.ts` returns `{"status":"ok","service":"gateway"}`. (`/ready` is the sticky readiness gate; RUNBOOK's `/healthz` is the ALB-level alias — the source-of-truth path is `/health`.)

## When to invoke

- The user approves shipping a finished change to production
- After an `/autopilot`/feature branch has been merged to `main` locally and needs to go live
- The user asks to "watch the deploy" for a push that is about to happen

Skip for: dashboard-only preview builds, local dev, anything not destined for `main`.

## Step 0 — Pre-flight (ask ONCE, then proceed)

1. **Branch permission — one question, one time.** Per `~/.claude/rules/git-safety.md`, never push without explicit permission. If the user's invocation already named the target ("ship to prod", "push to main"), that IS the permission — state `Target: push to main → Deploy to ECS` in your first reply and proceed. Otherwise ask exactly once ("Confirm: commit and push to `main`, which triggers the production ECS deploy?") and wait. Do NOT re-ask at the push step.
2. **Verify branch and tree state:**
   ```bash
   git branch --show-current   # must be main — the workflow only fires on main
   git status                  # staged set matches intent; no secrets; no stray files
   ```
   If not on `main`, stop and surface it — merging to `main` is a separate decision, not something this skill does silently.
3. **Run the repo's own check chain** (typecheck → build → test, failure-region output built in):
   ```bash
   scripts/check.sh
   ```
   Exit code non-zero → STOP and fix; do not commit a red tree. (In a repo without `scripts/check.sh`, fall back to the `typecheck-and-build` skill.)

## Step 1 — Commit (heredoc pattern)

Invoke the `commit-with-heredoc` skill: stage specific files (never `git add .`), review `git diff --cached`, then commit with the `$(cat <<'EOF' … EOF)` pattern including the Co-Authored-By trailer (the exact line from the session's attribution reminder when there is one, otherwise the running model's name).

**Gotcha — the commit may time out but LAND.** The gitleaks-guard pre-commit hook can eat the Bash tool budget so the call reports a timeout while git finished underneath. Before any retry:

```bash
git log -1 --format="%H %s"
```

If the commit is there, do NOT retry — a blind retry duplicates the commit. Capture the SHA now:

```bash
SHA=$(git rev-parse HEAD)
```

## Step 2 — Push

Only after the Step 0 confirmation:

```bash
git push origin main
```

Run it FOREGROUND (never background a mutating git op — the credential helper needs the TTY) and read the result in the same turn.

## Step 3 — Find and watch the deploy run (background, never poll)

A push to `main` triggers MULTIPLE workflow runs (lint guardrails go green in ~30s and will fool a bare `gh run list --limit 1`). Select the deploy run explicitly and confirm its `headSha` matches `$SHA`:

```bash
gh run list --workflow "Deploy to ECS" --limit 1 \
  --json databaseId,status,conclusion,headSha \
  --jq '.[0] | "\(.databaseId) \(.status) \(.conclusion // "in_progress") \(.headSha[0:8])"'
```

(If the run hasn't registered yet, re-run after ~10s.) Then watch it **as a background task** — pass `run_in_background: true` on the Bash call so the ~10-12 min roll never foreground-polls the main context:

```bash
RUN_ID=$(gh run list --workflow "Deploy to ECS" --limit 1 --json databaseId --jq '.[0].databaseId')
gh run watch "$RUN_ID" --exit-status
```

Do other work or wait for the background completion callback. When it returns, check the conclusion explicitly — `gh run watch --exit-status` has been observed exiting 0 while the run concluded `failure`:

```bash
gh run view "$RUN_ID" --json conclusion,url --jq '"\(.conclusion) \(.url)"'
```

`conclusion` must be `success`. On `failure`, open the failed job logs (`gh run view "$RUN_ID" --log-failed`) and route via RUNBOOK §"Deploy pipeline". Known transient: `auth.docker.io/token: 504 Gateway Timeout` on `FROM node:22-slim` is a Docker Hub outage, not your code — re-run the failed jobs once it recovers.

## Step 4 — Ground-truth verification (the deploy-clobber check)

**A green run is NOT proof your SHA is live.** A manually re-run OLDER deploy can overwrite a newer commit's services (hit live 2026-06-08). Ground truth is the image tag on each service's active task definition:

```bash
for svc in gateway worker embedding-worker insights-worker url-watch-worker wa-bailey voice-bridge; do
  td=$(aws ecs describe-services --cluster delta-agents --services "$svc" \
    --query 'services[0].taskDefinition' --output text)
  img=$(aws ecs describe-task-definition --task-definition "$td" \
    --query 'taskDefinition.containerDefinitions[0].image' --output text)
  echo "$svc ${img##*:}"
done
```

(The `zalo-admin` IAM user is configured locally — `aws sts get-caller-identity` confirms.)

Interpretation — compare each tag to `$SHA`:

- Services whose watched paths changed in this push → tag MUST equal `$SHA`. Mismatch = clobber or failed job; fix by re-running the NEWER run in full (`gh run rerun "$RUN_ID"` — not `--failed`, since "succeeded" jobs get skipped) so concurrency queues it last, then re-verify.
- Services whose paths did NOT change → an older tag is EXPECTED (paths-filter skipped them). Do not flag these.

## Step 5 — Smoke test

```bash
curl -sS -o /tmp/ship-health.json -w "HTTP %{http_code}\n" https://api.operatorbase.app/health
cat /tmp/ship-health.json
```

Expect `HTTP 200` and `{"status":"ok","service":"gateway"}`. Non-200 → the deploy is NOT done regardless of green CI; go to RUNBOOK "First 5 minutes".

## Step 6 — Report

One block, all evidence fresh from this session:

```
Shipped: {SHA} ({subject line})
Run: {conclusion} — {run URL}
Image tags: gateway={tag} worker={tag} embedding-worker={tag} insights-worker={tag} url-watch-worker={tag} wa-bailey={tag} voice-bridge={tag}
Smoke: GET https://api.operatorbase.app/health → HTTP {code}
```

## Anti-patterns

- ❌ Foreground `gh run watch` or a sleep/curl polling loop in main context — background the watch
- ❌ `gh run list --limit 1` without `--workflow "Deploy to ECS"` — grabs the 30s lint run and declares victory early
- ❌ Trusting `gh run watch` exit 0 or a green badge as "deployed" — verify per-service image tags (Step 4)
- ❌ Retrying a "timed out" commit without `git log -1` first — duplicates the commit
- ❌ `--allow-empty` commit to force a redeploy — `detect-changes` is path-gated and skips every build; use `gh run rerun` on the newest run instead
- ❌ Re-asking for push permission at every step — Step 0 asks once; after that, proceed

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…