prod-push について
DeepAPI の main ブランチ、CI、Vercel の手順で本番変更を出荷するためのスキルです。リリース担当者向けに、ユーザーの明示的な許可を求め、デプロイとヘルスチェックまで追跡します。
主な機能
- 01main ブランチ限定の push 手順
- 02worktree の変更を取り込む手順
- 03CI とデプロイの追跡
- 04本番環境の健全性確認
- 05インシデント復旧の案内
活用シーン
- 01許可を得た変更を本番へ出荷
- 02プライマリチェックアウトに worktree のコミットを反映
- 03CI やデプロイの失敗を調査
- 04インシデント中に復旧コミットを追跡
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davidondrej/ skills --skill prod-pushインストール後、Claude Code で/prod-pushを入力して読み込まれたか確認します。
詳細
Prod Push
ユーザーから明示的にプッシュするよう指示された場合にのみ実行してください。自分の判断でプッシュしてはいけません。
ここでのリリース方法
mainに直接プッシュします。PR もサイドブランチも使いません。- GitHub Actions の
CI(.github/workflows/ci.yml)では、必須チェックのverifyが常に実行されます。コード変更では型チェックと lint が実行され、生成物、ブラウザー、DB、Windows のチェックは関連するパスに変更がある場合にのみ実行されます。全テストスイートは毎晩実行されます。 - Vercel(プロジェクト
deep-api)が本番ビルドを管理し、mainへのすべてのプッシュでビルドします。ドキュメントのみのコミットも対象です(ADR 0161 で無視されたビルドルールは削除されています。再接続しないでください)。verifyが成功した場合にのみ、本番環境へ昇格します。本番環境はdeepapi.coで配信されます。 - 同じブランチで新しい CI が実行されると、進行中の実行はキャンセルされます(
cancel-in-progress: true)。
ワークツリーからの作業を反映する
エージェントはリンクされたワークツリー(cursor/*、agent/* ブランチ)で作業します。以下の手順はそこでの実行を拒否します。ワークツリーの作業をリリースするには、次の手順に従ってください。
- ワークツリー内で、自分のブランチにすべての変更をコミットします。ワークツリーから
mainにコミットしてはいけません。 - プライマリーチェックアウト(
~/code/DeepAPI)では、作業ツリーがクリーンでなければなりません(git status --porcelain --untracked-files=noの出力がない状態)。自分のものではない WIP で変更されている場合は、停止してユーザーに伝えてください。周囲の変更を退避するために stash してはいけません。 git pull --rebase origin mainを実行し、その後、自分のブランチを反映します。git merge <branch>(可能な場合は fast-forward)を実行するか、ブランチの履歴が複雑な場合はコミットを cherry-pick します。一度に反映するブランチは 1 つだけです。1 回の作業で 2 つのエージェントブランチを反映してはいけません。- ここハブで競合を解決し、関連するチェックを再実行してから、以下のループをステップ 2 から続けます。
- プッシュが反映されたことを確認した後も、ワークツリーとそのブランチは残してください。削除するかどうかを決めるのは、常にユーザーです。
ループ
変更が確実に反映されるまで繰り返します。
# 0. Fail closed before touching Git. Ship only from the primary main checkout:
# linked worktrees use their own branch's hook and may carry stale safety code.
[ "$(git branch --show-current)" = "main" ] || { echo "prod-push: not on main" >&2; exit 1; }
git_dir=$(git rev-parse --path-format=absolute --git-dir)
git_common_dir=$(git rev-parse --path-format=absolute --git-common-dir)
[ "$git_dir" = "$git_common_dir" ] || { echo "prod-push: linked worktree pushes are forbidden" >&2; exit 1; }
if git config --local --get-regexp '^user\.(name|email)$' >/dev/null; then
echo "prod-push: unexpected local Git identity override" >&2
exit 1
fi
if [ "$(git config user.name)" != "David Ondrej" ] || [ "$(git config user.email)" != "<email-address>" ]; then
echo "prod-push: unexpected effective Git identity" >&2
exit 1
fi
# If an incident is active and you are not the named recovery owner, STOP.
# Follow docs/runbooks/incident-response.md if you are the owner.
npm run incident:status
# 1. commit ONLY your files. WIP belongs in worktrees, never parked in this
# primary checkout — it must be clean between pushes. Never `git add -A`,
# `git add .`, or `commit -a`. If a file you touched also carries someone
# else's unfinished edits, copy it aside (outside the repo), strip their
# part, commit yours — and restore the copy only AFTER the push, because
# step 2 requires a clean tree.
git add <your files> && git commit
[ "$(git log -1 --format='%an <%ae>')" = "David Ondrej <<email-address>>" ] || { echo "prod-push: unexpected commit author" >&2; exit 1; }
# 2. sync + push (other agents also commit to main)
# The pull requires a CLEAN tree. --autostash is BANNED here: replaying
# dirty edits onto fresh upstream commits creates UU conflicts and orphaned
# autostash entries (this bit us on 2026-07-25). If tracked dirt remains
# that is not yours, stop and tell the user — never stash around it.
[ -z "$(git status --porcelain --untracked-files=no)" ] || { echo "prod-push: tree not clean; commit yours or stop" >&2; exit 1; }
# The pre-push hook typechecks the EXACT pushed commit in a clean worktree.
# Incident recovery pushes still run the full suite. Never use `--no-verify`.
git pull --rebase origin main
git push origin main
if git config --local --get-regexp '^user\.(name|email)$' >/dev/null; then
echo "prod-push: push introduced a local Git identity override; stop" >&2
exit 1
fi
SHA=$(git rev-parse HEAD)
# 3. Find the CI run immediately, then let GitHub stream it to completion
RUN_ID=""
for attempt in 1 2 3 4 5 6; do
RUN_ID=$(gh run list --workflow=ci.yml --branch=main --limit=10 \
--json headSha,databaseId --jq ".[] | select(.headSha==\"$SHA\") | .databaseId")
[ -n "$RUN_ID" ] && break
sleep 5
done
[ -n "$RUN_ID" ] || { echo "prod-push: CI run not found" >&2; exit 1; }
gh run watch "$RUN_ID" --exit-status
# 4. CI green -> confirm Vercel promoted this exact commit.
# EVERY main push deploys (ADR 0161), docs-only commits included — always
# verify the deployment; never skip this on a "docs-only" judgment call.
gh api "repos/davidondrej/DeepAPI/commits/$SHA/status" \
--jq '{state, vercel_logs: [.statuses[] | select(.context=="Vercel") | .target_url][0]}'
DEP=$(gh api "repos/davidondrej/DeepAPI/deployments?sha=$SHA&environment=Production&per_page=1" --jq '.[0].id')
gh api "repos/davidondrej/DeepAPI/deployments/$DEP/statuses" --jq '.[0].state'
# If promotion is still pending, poll every 10 seconds.
# 5. confirm prod is serving
curl -sS -m 15 https://deepapi.co/v1/health
完了の条件は、CI が成功し、本番環境へのデプロイが成功し、正確な SHA に対する GET /v1/health が正常であることです。その後、何をリリースしたかをユーザーに報告してください。
インシデント対応レーン
npm run incident:status で指定された担当者のみ、このレーンを使用できます。無関係な作業を混在させてはいけません。
# Exactly one minimal recovery commit. The hook verifies that exact commit.
DEEPAPI_INCIDENT_RECOVERY=1 DEEPAPI_INCIDENT_OWNER=<owner> git push origin main
SHA=$(git rev-parse HEAD)
通常のループを使って、正確な SHA の CI、本番環境へのデプロイ、ヘルスチェックを追跡してください。最初の SHA の結果が確定するまで、2 回目の復旧対応をプッシュしてはいけません。復旧が実証され、フリーズを解除できるかどうかを決めるのはユーザーのみです。ランブックに記載された解除コマンドを使い、.github/incident-freeze.json のみをコミットしてください。
失敗時の対応
CI が失敗した場合 — 失敗したステップと出力を正確に確認します。
gh run view <databaseId> --log-failed
ローカルで再現し(npm run verify、または失敗したステップのみ。例:npm run typecheck)、修正してコミットし、ループを再開します。正確なコミットについてはプッシュ前のログが正しい情報源です。手動実行では共有作業ツリーがチェックされるため、他のエージェントの WIP が含まれる場合があります。新しいプッシュでは $SHA が新しくなるため、そちらを追跡してください。
- CI は成功したが Vercel のステータスが
failure— Vercel のビルド自体が失敗しています。npm run buildで再現してください。Vercel 側の問題(環境変数、プラットフォームの制限など)の場合は停止し、ユーザーにvercel_logsのリンクを伝えてください。エージェントには意図的に Vercel CLI やダッシュボードへのアクセス権がありません。 - CI の
conclusion: cancelled— より新しいプッシュによって自分のプッシュが置き換えられました。自分のコミットが単独でデプロイされることはありません。新しい SHA に自分の変更が含まれていることを確認し(git merge-base --is-ancestor $SHA <newer-sha>)、代わりにその SHA を追跡してください。 - インシデント中に CI の
conclusion: cancelled— 停止してください。禁止されている新しいプッシュがmainに入りました。次の復旧対応を行う前に、ユーザーに伝え、その SHA を特定してください。 - ヘルスチェックが
{"ok":false}(HTTP 503)を返す — マイグレーションの不整合です。デプロイされたコードが、本番 DB にまだ適用されていないマイグレーションを要求しています。デプロイ自体は成功しています。エージェントが本番環境に書き込むことはできません。適用が必要なマイグレーションをユーザーに伝え、ループを停止してください。 - プッシュが拒否された(non-fast-forward) — その間に誰かがプッシュしています。
git pull --rebase origin mainを実行し、再度プッシュしてください。
厳守事項
- ユーザーから明示的な許可を得ずにプッシュしてはいけません。強制プッシュは禁止です。サイドブランチもプッシュしてはいけません。
- CI を通すために回避したり弱めたりしてはいけません。テストの削除やスキップ、
--no-verifyの使用、チェックが赤のままマージすることは禁止です。 npm run smoke:prodはユーザー専用です(実際に費用が発生します)。無料で実行できる本番環境の確認方法はGET /v1/healthです。- 変更に DB マイグレーションが含まれる場合、本番環境への適用はユーザーが手動で行います。適用されるまでヘルスチェックが赤のままになることを想定し、報告でその旨を伝えてください。