CLAUDE.md から外出し。ブランチ運用・clasp 環境分離・デプロイ手順の詳細を集約する。

ブランチ運用

  • main — 本番ブランチ。直接 push 禁止。PR 経由でのみマージ
  • 作業ブランチは目的別プレフィックス + 短い説明
  • GitHub Actions (deploy.yml) は main push 時に自動デプロイ。docs/** *.md はトリガー対象外

ブランチ命名規則

プレフィックス用途
feat/新機能の追加feat/add-sync-tool
fix/バグ修正fix/cc-header-mismatch
refactor/リファクタリング(動作変更なし)refactor/modular-monolith
docs/ドキュメントのみの変更docs/update-file-paths
chore/設定・CI/CD・雑務chore/update-gitignore

コード変更時の順序

  1. git commit — ローカルにコミット
  2. npm run push:dev — 開発用 GAS にソース push
  3. GAS 上で動作確認 — UI / メニュー / ログから挙動を検証
  4. git push — 動作確認済みコードを GitHub に push
  5. PR 作成 → レビュー → main マージ

動作未確認のコードを GitHub に push しない。 prod デプロイは main マージ時の GitHub Actions で自動実行される。

GAS 環境分離 (Prod / Dev)

本番データの誤操作を防ぐため、GAS プロジェクトを本番用・開発用に分離している。

  • 環境判定: GAS スクリプトプロパティ ENV の値で決まる(dev → 開発、未設定 or prod → 本番)
  • 環境依存値の参照: Env モジュール (mas/000_infra/001_env.js) 経由で一元取得(ADR-0013 参照)
    • Env.name() / Env.isDev() / Env.isProd() — 環境判定
    • Env.spreadsheetId() — 対象スプレッドシート ID
    • Env.geminiApiKey() — Gemini API キー
    • Env.receiptFolderId() / Env.setReceiptFolderId() — 領収書フォルダ ID
  • clasp 環境切替: .clasp.dev.json / .clasp.prod.jsonclasp-switch.sh.clasp.json にコピーして切り替え

npm-scripts(デプロイ操作)

コマンド動作
npm run push:dev開発用 GAS にソースを push (Web アプリ URL は更新されない)
npm run push:prod本番用 GAS にソースを push (Web アプリ URL は更新されない)
npm run deploy:devpush:dev + Web アプリ deployment 更新 (共有 /exec URL に反映)
npm run deploy:prodpush:prod + Web アプリ deployment 更新 (共有 /exec URL に反映)
npm run open:dev開発用 GAS エディタを開く
npm run open:prod本番用 GAS エディタを開く
npm run status現在の .clasp.json がどちらの環境か確認

deploy:* は Web アプリとして共有されている /exec URL を最新コードで更新する。deployment ID は scripts/deploy.sh で dev/prod 別に管理。コード変更なしでも実行可能(バージョン番号のみ進む)。

PR rebase + force-with-lease push のオペレーション

PR ブランチを最新 main に追従させる際の rebase + force-with-lease push 手順。scripts/hooks/pre_bash_guard.sh--force / --force-with-lease を含む push を自動実行から技術的にブロックしているため、AI Agent から直接 push できない。docs/_internal/06_ops/failure_patterns.md #42 (PR #873 で実発生) の対策として明文化。

手順

  1. AI Agent 側: rebase + コンフリクト解消まで実施
    git checkout main && git pull origin main
    git checkout <feature-branch>
    git rebase main
    # コンフリクトがあれば解消 → git add → git rebase --continue
    
  2. AI Agent 側: ローカル検証を pass させる (例: docs 系)
    LOCAL=1 node scripts/docs-build.mjs        # → Built N pages
    node scripts/docs-nav-lint.mjs             # → 0 WARN / 0 FAIL
    node scripts/adr-lint-doc-consistency.mjs  # → 0 WARN / 0 FAIL (ADR 系)
    
  3. ユーザに ! prefix で手動実行を依頼:
    以下を ! prefix で実行してください:
    ! git push --force-with-lease origin <feature-branch>
    
    ! prefix で sandbox 外から実行することで pre_bash_guard hook を経由しない (CLAUDE.md L「不可逆禁止 + PreToolUse Hook」由来の意図的な設計)
  4. AI Agent 側: push 完了後、remote 反映を確認
    gh pr view <PR番号> --json mergeable,headRefOid
    # mergeable: "MERGEABLE" / headRefOid: ローカル HEAD と一致
    
    mergeable の確定待ちは node scripts/pr-merge-check.mjs --pre <PR番号> でも可 (UNKNOWN を抜けるまでポーリングする)。下記「PR merge 前後の定型照合」を参照。
  5. AI Agent 側: CI green 確認後、ユーザの「マージ」明示指示でのみ merge (自動マージ禁止規約)
    gh pr checks <PR番号> --watch   # CI green 待ち
    gh pr merge <PR番号> --squash --delete-branch
    

通常 push との違い

  • 通常の git push (force でない) は hook 対象外 → AI Agent から自動実行 OK
  • --force / --force-with-lease を含む push のみ手動依頼が必要
  • git push -u origin <branch> (新規ブランチの初回 push) は force ではないため自動実行 OK
  • gh pr merge 自体は hook 対象外だが、feedback_pr_merge_discipline でユーザ明示指示必須

よくあるケース: PR が CONFLICTING になった時

  1. PR のコンフリクト確認: node scripts/pr-merge-check.mjs --pre <N> で確定値を取得 (CONFLICTING は exit 1 で報告)
  2. 上記手順 1-2 で rebase + ローカル検証
  3. 手順 3 でユーザに force-with-lease push を依頼
  4. PR が MERGEABLE に戻ることを確認 (gh pr view <N> --json mergeable,headRefOid)
  5. CI 再走を待って merge

PR merge 前後の定型照合 (pr-merge-check)

PR を merge する前後で毎回確認する 2 点を scripts/pr-merge-check.mjs の 1 コマンドにまとめてある。詳しい仕様はスクリプト先頭のコメントが大元なので、ここでは使い方だけ示す。

merge 前: merge 可否の確定を待つ

node scripts/pr-merge-check.mjs --pre <PR番号>
  • mergeableUNKNOWN のあいだは GitHub が再計算中の暫定値。確定するまで最大 60 秒ポーリングして確定値を報告する
  • CONFLICTING のまま merge を待つと CI が発火しない。green に見えても未検証なので exit 1 で止める。解消手順は「main を取り込む → 生成物を再生成する → push する」
  • 判定が確定しても、CI green とユーザの「マージ」明示指示は別に確認する

merge 後: 取り込み漏れの照合

node scripts/pr-merge-check.mjs --post <PR番号> [期待SHA]
  • merge commit の第 2 親が PR の head と一致するかを照合する。push イベントの遅延で古い head が merge される取り込み漏れを検知できる (#1791 で実発生)
  • 期待SHA にローカルで push した控え (git rev-parse HEAD) を渡すと、検証済みのローカルがそのまま merge されたことまで確認できる
  • squash / rebase merge は第 2 親を持たないため照合できない。このリポの標準は merge commit 方式

clasp push 失敗の切り分け

clasp push (および npm run push:dev / deploy:dev) で push が走っていないと疑われる時の切り分け。npm run deploy:*push:* && deploy.sh&& 連結で、push が "Skipping push." と表示しつつ exit 0 で抜けると deploy.sh が無批判に走り「✅ Deploy complete」を出す ため、ログを tail -3 で切ると気づけない (failure_patterns.md #43)。tail -30 以上で出力全体を確認する。

ケース A: 認証切れ (invalid_grant / invalid_rapt)

  1. ユーザーに ! clasp login --creds creds.json の実行を依頼
  2. ブラウザ操作が必要なため、Claude Code は自律実行不可

ケース B: manifest invalid → silent skip (clasp 3.x の罠)

Skipping push. だけ表示されて他にエラーが見えない場合、mas/appsscript.json の enum 値が不正で manifest validation が落ちている可能性が高い。

  • webapp.access の有効値は UNKNOWN_ACCESS / DOMAIN / ANYONE / ANYONE_ANONYMOUS / MYSELF の 5 つのみ。ANYONE_WITH_GOOGLE_ACCOUNT 等の口語表現は受け付けられない
  • oauthScopes の部分宣言も自動検出を完全オフにする副作用あり (failure_patterns.md #26)
  • 切り分け: 直近で mas/appsscript.json を触っていないか git log で確認 → clasp push --force を試して silent skip しないか観察 → デプロイ後の HAR/console で新シンボル grep して bundle 同一性を verify

この症状が出たら最初に「修正が反映されていない」をコード問題と誤診せず deploy 状況を疑う (CLAUDE.md "Definition of Done" #1 / #5)。

テストランナー

  • 配置: mas/900_test/901_test_runner.js
  • prod デプロイ時は GitHub Actions で mas/900_test/ ディレクトリごと削除(本番に持ち込まない)
  • テストケース追加は 901_test_runner.js を直接編集
  • 実行は GAS エディタの関数選択 → 実行 (UI 操作)

docs コミットの軽微マーカー [skip-news](news 一覧との連動・ADR-0136)

docs サイトの「最新更新ページ (What's New)」一覧は、各ページの更新日時を「本文が最後に意味的に変わったコミット」から計算する。誤字直しのような軽微な修正で日時を動かしたくない場合は、コミットメッセージの 1 行目に [skip-news] を含める。

docs(adr): 参照先の表記揃え [skip-news]
fix(docs): 誤字修正 (受け取り→受取) [skip-news]
  • 効果: そのコミットは news 一覧の更新日時の計算から除外される(一覧の日時は前回の本文変更コミットのまま)。
  • 非強制: 付け忘れても一覧の日時が動くだけで、何も壊れない。lint・コミットフックの強制はない。
  • 適用範囲: news 一覧のみ。PR 単位の全変更を記録する「ドキュメント変更履歴 (doc_changelog)」には適用されない。git 履歴自体も不変。
  • 遡及しない: マーカーを付けた時点から効く。過去のコミットには効かない。
  • 実質変更に付けない: 読者に見せるべき変更(設計変更の記録・方針追記など)への誤付与は誤除外として月次集計の監視対象(月 2 件以上で ADR-0136 撤退条件 #1 発動)。
  • 自動キーワードはない: typo / style: / chore: 等の subject キーワードでは除外されない(ADR-0136 受理前精査で chore/style コミットの 94.1% が実質変更を含むと判明したため不採用)。
  • 言及にも効く点に注意: 判定は字面一致のため、実質変更のコミットで本機能に言及する場合は subject に角括弧つきの字面を書かない(「skip-news マーカー」のように書く)。本実装のコミットがこの誤爆を踏み、subject を修正した実例。
  • 判定パターンの正典は scripts/generate-doc-news.mjsMINOR_COMMIT_PATTERNS。変更する場合は本節を同一 PR で更新する。

関連ドキュメント

  • docs/test/02_integration_test.md — 統合テスト手順
  • docs/dev/dev_mas-096_clasp_push_protection.md — clasp push 保護
  • docs/dev/dev_mas-195_pre_push_hook.md — pre-push 自動チェック
  • docs/_internal/workspace_rules.md — 並行開発時のコンフリクト防止
  • docs/_internal/hooks_setup.md — Claude Code Hooks (LLM 行動の技術的強制ガード)