"Wait for PR merge, run deploy command, poll health, run smoke tests, then auto-chain /canary. Configured by .agentic-workflow/deploy.json."
Install
npx skillscat add jthorare/agentic-workflow/landanddeploy Install via the SkillsCat registry.
Land and Deploy — Merge, Deploy, Smoke, Chain Canary
Bridges /shipRelease and /canary. Waits for PR merge, runs the user-defined deploy command, polls health, runs smoke tests, then auto-chains /canary.
Before anything else: read $HOME/.agentic-workflow/toolkit/skills/_preamble.md and follow it (skill index, provider capability map, bootstrap check, session context). Run its Session Close section when this skill finishes.
Overview
Reads .agentic-workflow/deploy.json for deploy command, health URL, smoke tests, and timeout. On invocation, resolves the target PR (arg or current branch), optionally polls for merge, executes deploy, verifies health, runs smoke tests, writes a release record, and auto-invokes /canary on success. Skipped or --no-deploy callers from /shipRelease bypass this step entirely.
Inputs
- PR number (positional arg). If omitted, detect from current branch via
gh pr view --json number,state,mergedAt. --wait/--no-waitflag:- Default
--waitwhen invoked from/shipReleaseauto-chain (PR may not yet be merged at chain time) - Default
--no-waitwhen invoked standalone (user is at the terminal, expects fast feedback)
- Default
--setupflag: run interactive wizard to write.agentic-workflow/deploy.json, then exit.--skip-docsflag (optional). Propagates from/shipReleaseand forwards to/canaryon auto-chain, suppressing the eventual/syncDocsinvocation downstream. This skill does not call/syncDocsdirectly — it only forwards the flag.--chained-from-shipflag (optional). Set automatically by/shipReleasewhen chaining. Activates graceful-degrade ifdeploy.jsonis missing, and instructs this skill to consume~/.agentic-workflow/$REPO_SLUG/releases/.pending-ship.json(written by shipRelease Step 7) and fold its ship-phase metadata into a## Ship Phasesubsection at the top ofdeploy.md. A CLI flag propagates reliably across the skill-invocation boundary; env vars don't.- Config file:
.agentic-workflow/deploy.jsonin project root.
Config schema (.agentic-workflow/deploy.json)
{
"command": "npm run deploy:prod",
"healthUrl": "https://example.com/health",
"smokeTests": ["curl -fsS https://example.com/api/ping"],
"timeout": 600,
"journey": { "baseUrl": "https://example.com", "args": "--lenses functional,error-state" }
}command— shell command to run for deploy. Streamed to stdout.healthUrl— URL polled after deploy; expects 200 OK.smokeTests— array of shell commands. Each must exit 0.timeout— seconds. Used for both deploy command and health polling.journey— optional. When present, the deployed app is driven post-smoke via/verify-appagainstbaseUrl(see Step 12).
--setup Wizard
When invoked with --setup, Ask the user (one question per call):
- Deploy command (e.g.,
npm run deploy:prod) - Health URL (e.g.,
https://example.com/health) - Smoke test commands (comma-separated; can be empty)
- Timeout in seconds (default 600)
Write .agentic-workflow/deploy.json (create the dir if missing). Exit without deploying.
Steps (normal mode)
If
--setup: run wizard above and exit.Resolve target PR:
- Arg given → use it.
- Else:
gh pr view --json number,state,mergedAton current branch. If no PR exists, error: "no PR on this branch; create one with /shipRelease first".
Determine wait mode (
--waitvs--no-wait) per defaults above unless explicitly overridden.If PR not merged:
--wait: pollgh pr view --json mergedAtevery 30 s, max 30 min. Print a progress line every 5 min (waiting for merge: {elapsed}/{max}), and write a resumable marker~/.agentic-workflow/$REPO_SLUG/releases/.pending-deploy.json({pr, branch, started_at}) so a re-invocation resumes the same target instead of starting over. Stop on merge; delete the marker.--no-wait: error: "PR #N not merged; pass--waitto poll or merge manually first".
Load
.agentic-workflow/deploy.json.- If present: continue normally.
- If missing AND invoked with
--chained-from-ship: print a one-line note "no deploy.json — skipping deploy step (run/landAndDeploy --setupto enable)" and exit gracefully with success, so first-time users aren't blocked. - If missing AND standalone: error and suggest
--setup.
Refuse if config is checked into git. Run
git ls-files --error-unmatch .agentic-workflow/deploy.json 2>/dev/null— if it returns 0, error out: "deploy.json is committed; remove and run/landAndDeploy --setup. The skill executescommandandsmokeTests[]from this file as shell, so a tracked copy is an RCE foot-gun." Recommend adding.agentic-workflow/deploy.jsonto.gitignore. This refusal is the safety gate that justifies the broad Bash permission.Compute release-id (CD7):
<ISO-date>-<short-sha>where<short-sha>is the merge commit SHA. All release files live inreleases/<ISO-date>-<short-sha>/.Consume the ship handoff + capture the pre-deploy baseline:
- If
--chained-from-ship: read$AW_DIR/releases/.pending-ship.json, hold its metadata for the## Ship Phasesubsection, then delete it. - If
.agentic-workflow/canary.jsonexists: sampleerrorRateUrl,latencyUrl(p95), and onelogSourcerun now, before deploying, and persist toreleases/<release-id>/baseline.json({error_rate, p95, log_anomaly_count, sampled_at})./canaryreads this so a deploy-introduced regression can never become its own baseline.
- If
Run
deploy.command, stream output. Capture exit code.- On non-zero exit: write
releases/<release-id>/deploy.mdwith FAILED status, suggest/rootCause, exit.
- On non-zero exit: write
Poll
deploy.healthUrlwith exponential backoff (1s, 2s, 4s, … capped at 60s between probes; max totaldeploy.timeoutseconds). Stop on 200 OK.- On timeout: mark deploy DEGRADED in the release record. Continue to smoke tests anyway.
Run each
deploy.smokeTests[]command sequentially. Capture each exit code and stdout.- Any non-zero → mark deploy DEGRADED.
If
deploy.journeyis configured: drive the deployed app — Invoke skillverify-appwith args--base-url <journey.baseUrl> --yes <journey.args>. A FAIL verdict → mark deploy DEGRADED and record the evidence-pack path indeploy.md.Write
~/.agentic-workflow/$REPO_SLUG/releases/<release-id>/deploy.md. When invoked with--chained-from-ship, include a## Ship Phasesubsection at the top from the.pending-ship.jsonmetadata. The merge-SHA-based release-id ensures this file lives in the same subdir ascanary.md, so the full release (ship + deploy + canary + docs-sync) lives under one folder.# Deploy — <release-id> **PR:** #<num> **Merge SHA:** <full-sha> **Deployed:** <ISO date> **Verdict:** SUCCESS | DEGRADED | FAILED ## Ship Phase <!-- Only when --chained-from-ship: folded from releases/.pending-ship.json (then deleted). --> - **Branch:** <branch> → <base> - **Tip SHA (pre-merge):** <short-sha> - **Test result:** passed (<N> tests) - **Coverage:** <percentage>% (or "not available") - **PR URL:** <url> ## Deploy command `<deploy.command>` — exit <code> in <duration>s ## Health `<deploy.healthUrl>` — first OK at <Ns>, took <total>s ## Smoke tests | Test | Exit | Duration | |---|---|---| | <cmd> | 0 | <s> | ## Output excerpts <last 50 lines of deploy command output, smoke test stdout snippets>Branch on verdict:
- SUCCESS: auto-Invoke skill
canary, passing the release-id. If this skill received--skip-docs(directly or propagated from/shipRelease), forward--skip-docsto/canaryso it suppresses its own/syncDocsauto-chain on HEALTHY. - DEGRADED: do NOT silently stop — Ask the user: "Deploy is DEGRADED ({reason}). Proceed to /canary monitoring, roll back, or stop?" Options:
Proceed to canary(invoke/canarywith the release-id),Roll back(print the rollback path —gh pr revertor repo-specific — and stop),Stop(record the choice in deploy.md and exit). - FAILED: already handled in step 9.
- SUCCESS: auto-Invoke skill
Outputs
~/.agentic-workflow/$REPO_SLUG/releases/<release-id>/deploy.md~/.agentic-workflow/$REPO_SLUG/releases/<release-id>/baseline.json(pre-deploy metrics for/canary)- Consumes + deletes
releases/.pending-ship.json;deploy.mdis read by/canary(baseline window) and globbed by/weeklyRetro .agentic-workflow/deploy.json(only on--setup)
Next steps
/canary— auto-chained on SUCCESS/rootCause— if deploy or smoke fails/shipRelease --no-deploy— for next time if you want to skip the auto-chain (e.g., release-branch workflow)