Phase 2a 設計書: Dify → GitHub PR 自動生成(最小動作版)
TL;DR(このページは何か・専門語ゼロ): これはすでに役目を終えた古い構成の記録です。かつて「だれかが判断のたたき台を投稿すると、AI が二段階で中身を確かめ、合格したものだけ自動で変更提案を作る」仕組みを、ある外部サービス上で組もうとした初期の設計メモを残しています。実際にはこの方式は作る前に取りやめになり、別の土台に作り直しました。いまから新しく開発する人がこのページを手本にする必要はなく、当時どう考えていたかをたどるための履歴として置いてあります。
⚠️ Status: Retired (2026-05-08, ADR-0019)
本設計書は ADR〔Architecture Decision Record=技術判断の記録〕-0019 (Decision Pipeline 刷新 — LangGraph TS + adr-kit 採用 / Dify〔ディファイ=当時使った外部のワークフロー構築サービス〕 退役) により退役。Phase 2a は未着手のまま終了し、後継として LangGraph〔ランググラフ=判断審査の流れを組む実装基盤〕 TS + Hono on Cloudflare Workers + LiteLLM Gateway 構成へ移行する。
- 退役理由・代替案検討:
docs/adr/0019-drp-migration.md- 後継アーキテクチャの実装手順:
langgraph_migration/main_workspace_checklist.md- 退役対応表(既存 6 本の処遇まとめ):
langgraph_migration/migration_overview.md履歴として保持(Phase 1 投資の Triage v0.2 / Scoring v0.1 プロンプトは LangGraph node に手作業移植予定)。本ファイルの設計内容は LangGraph 構成に置き換わるため、新規開発の参照には使用しないこと。
Status: Retired (2026-05-08) Phase: 2a(GitHub 連携最小動作版) — 未着手のまま退役 前提: Phase 1 完了(Triage v0.2 / Scoring v0.1 確定) スコープ外: Gate〔ゲート=審査の関門。各段で起案を通すか差し戻すかを判定する〕 1 問診(Phase 2b)/ Gate 2 過去整合性(Phase 2c)/ Gate 3 並列レビュー(Phase 3)
1. ゴール
業務委託者が Dify に意思決定ドラフトを投稿すると、
- Gate 0(Triage〔トリアージ=そもそも記録に残す価値があるかの選別〕)で ADR 要否・Mode〔モード=起案の重要度区分。合格に必要な点数の高さが変わる〕 判定
- Gate 4(Scoring)で 50 点満点採点(40 点未満は差戻し)
- 合格起案のみ GitHub Actions に Webhook 送信
- GitHub 側で
docs/adr/NNNN-slug.mdを含むブランチ+ PR を自動作成 - オーナー(代表取締役)が PR レビュー → マージで
Status: Accepted確定
を エンドツーエンドで動かすことが目標。Gate 1/2/3 はスキップし、Triage/Scoring の 2 ゲート構成のみで運用する。
2. アーキテクチャ全体図
[業務委託者]
│ ブラウザ
▼
┌───────────────────────────────────────┐
│ Dify Workflow │
│ [開始] → user_input │
│ ↓ │
│ [Gate 0: Triage] → JSON │
│ ├─ is_adr_worthy=false → 終了 │
│ ↓ │
│ [Gate 4: Scoring] → JSON │
│ ├─ pass=false → 起案者に差戻し │
│ ↓ │
│ [HTTP リクエスト] │
│ POST → GitHub repository_dispatch │
└───────────────────────────────────────┘
│ HTTPS POST + GitHub App JWT
▼
┌───────────────────────────────────────┐
│ GitHub Actions │
│ .github/workflows/decision-pr.yml │
│ on: repository_dispatch │
│ types: [decision_submission] │
│ ├─ payload schema 検証 │
│ ├─ 連番採番(ls docs/adr/) │
│ ├─ ブランチ作成 │
│ ├─ ADR ファイル作成 │
│ ├─ コミット & push │
│ └─ PR 作成(タイトル/本文/ラベル) │
└───────────────────────────────────────┘
│
▼
[オーナー(代表取締役)]
PR レビュー → マージ
3. 確定した設計判断
| # | 論点 | 決定 | 根拠 |
|---|---|---|---|
| 1 | スコープ範囲 | C: 段階的(Phase 2a → 2b → 2c → Phase 3) | Gate 1/2 の必要性は実運用で見極める |
| 2 | 認証方式 | GitHub App | 中長期運用での権限委譲・退職時継続性 |
| 3 | ADR 命名 | 連番 + slug(NNN_slug.md、3桁 + アンダースコア) | 既存 ADR (001〜009) との一貫性を優先 |
| 4 | PR タイトル | [ADR-NNN] 要旨 (Mode) | 一覧で Mode が即視認可能 |
| 5 | PR 本文 | 起案入力原文 + Scoring サマリ + ADR リンク | トレーサビリティ最優先 |
| 6 | ラベル | adr / mode:* / status:* / auto-generated(4 系統) | フィルタ性最大化 |
| 7 | 起案者身元 | Dify 認証 + payload に名前同梱 | 最シンプル、運用負荷小 |
4. Dify Webhook Payload Schema
4.1 スキーマ定義(v1.0)
{
"schema_version": "1.0",
"submitter": {
"name": "string", // 業務委託者の表示名(PR 本文に表示)
"dify_user_id": "string" // Dify 内部 user id(追跡用)
},
"user_input": "string", // 起案テキスト原文(Markdown 可)
"triage": {
"is_adr_worthy": true,
"mode": "Light | Standard | Critical",
"title_summary": "string", // 30 字以内(Triage v0.2 制約)
"reason": "string" // 60 字以内
},
"scoring": {
"scores": {
"1_problem_definition": { "score": 0, "comment": "string" }, // 0-5
"2_alternatives": { "score": 0, "comment": "string" },
"3_decision_criteria": { "score": 0, "comment": "string" },
"4_past_adr_consistency": { "score": 0, "comment": "string" },
"5_impact_scope": { "score": 0, "comment": "string" },
"6_operational_pitfalls": { "score": 0, "comment": "string" },
"7_rollback_strategy": { "score": 0, "comment": "string" },
"8_cost_estimate": { "score": 0, "comment": "string" },
"9_completion_criteria": { "score": 0, "comment": "string" },
"10_long_term_impact": { "score": 0, "comment": "string" }
},
"total": 0, // 0-50(10 項目 × 5 点)
"mode": "Light | Standard | Critical",
"threshold": 40, // Light=35 / Standard=40 / Critical=45
"pass": true, // total >= threshold
"weakest_items": ["string"],
"feedback_for_user": "string",
"score_summary_md": "string" // PR 本文に貼る整形済み Markdown
},
"adr_draft": {
"slug": "string", // snake_case、英数字 + アンダースコア、20 字以内
"body_md": "string" // ADR テンプレートに沿った本文
},
"submitted_at": "string" // ISO 8601
}
4.2 schema_version の運用ルール
- メジャー番号変更(1.0 → 2.0)= 破壊的変更。Actions 側で旧バージョン拒否
- マイナー番号変更(1.0 → 1.1)= 後方互換あり。新フィールド追加など
- Actions 側は
schema_versionの major 部分が一致しない payload を 400 で拒否
4.3 必須/オプションフィールド
| フィールド | 必須 | 欠落時の挙動 |
|---|---|---|
schema_version | ✅ | 400 拒否 |
submitter.name | ✅ | 400 拒否 |
triage.is_adr_worthy=true | ✅ | false の場合は Dify 側で Webhook 送信しない |
scoring.pass=true | ✅ | false の場合は Dify 側で Webhook 送信しない |
adr_draft.slug | ✅ | 400 拒否(slug 採番ロジックは Dify 側責務) |
submitted_at | ⚪️ | 欠落時は Actions 受信時刻を記録 |
5. GitHub Actions ワークフロー
5.1 ファイル配置
.github/
└── workflows/
└── decision-pipeline-pr.yml
5.2 トリガー設計
repository_dispatch を採用。Dify から event_type: "decision_submission" で呼び出す。
on:
repository_dispatch:
types: [decision_submission]
理由:
workflow_dispatchは inputs サイズ制限(10 入力まで)で payload を運べないrepository_dispatchは payload をclient_payloadとして丸ごと受け取れる- Webhook URL:
POST /repos/BizLinkPartners/bizlp-gas-accounting/dispatches
5.3 ステップ一覧
- Checkout main
- Payload 検証 —
schema_versionmajor / 必須フィールド /pass=trueの三段チェック - 連番採番 —
ls docs/adr/ | grep -E '^[0-9]{3}_' | sort -r | head -1で次番号算出(3桁ゼロパディング) - ブランチ作成 —
claude/decision-NNN-slug - ADR ファイル作成 —
docs/adr/NNN_slug.mdに payload を流し込み - コミット & push — Bot identity で(GitHub App の
bot[bot]ユーザ) - PR 作成 — タイトル/本文/ラベル/Reviewer を一括設定
- 失敗時の通知 — Slack/Discord webhook で
#dev-alertsに通知(オプション、Phase 2a 末で検討)
5.4 連番採番の同時実行衝突対策
- Phase 2a: 業務委託者は 1 人ずつ起案する運用とし、衝突対策はしない
- Phase 2b 以降: optimistic concurrency で実装(push 失敗時に番号取り直してリトライ)
- 候補1: GitHub Actions の concurrency group で直列化
- 候補2: ADR ファイル名にハッシュサフィックスを付与
- 設計判断は Phase 2b で行う
6. GitHub App セットアップ仕様
6.1 App 名
bizlp-decision-pipeline-bot
6.2 権限スコープ(最小権限の原則)
| Permission | レベル | 用途 |
|---|---|---|
| Contents | Read & Write | ブランチ作成 + ADR ファイル作成 + コミット |
| Pull requests | Write | PR 作成 + ラベル付与 + Reviewer 指定 |
| Metadata | Read | デフォルト(必須) |
| Issues | – | 不要 |
| Workflows | – | 不要(自分のワークフロー定義は触らない) |
6.3 インストール先
BizLinkPartners/bizlp-gas-accounting のみ。Org 全体には install しない。
6.4 認証フロー
Dify ← App ID + Private Key + Installation ID
↓ JWT 生成(10 分有効)
↓ POST /app/installations/{id}/access_tokens
↓ Installation Access Token (1 時間有効)
↓ POST /repos/.../dispatches
↓
GitHub
Dify 側で App Private Key を扱う必要があるため、
- Phase 2a 暫定: Dify の HTTP リクエストノードで JWT を生成(Dify が JWT 署名できるか要検証)
- Phase 2a 暫定の代替案: Cloudflare Worker を中継 — Dify → Worker(JWT 生成 → GitHub 認証)→ repository_dispatch
推奨は Cloudflare Worker 中継。理由:
- Dify でプライベートキーを保持・署名生成する責務を持たせるとセキュリティ要件が増える
- Worker なら GitHub App の private key を Cloudflare の secret に格納
- Worker URL に Dify からの payload を HMAC で検証して中継できる
ただし Phase 2a の MVP 性を重視するなら、PAT (Personal Access Token) で先に動かして検証 → Phase 2a 完了前に GitHub App + Worker に置き換える、という二段構えも可。
6.5 Phase 2a の認証実装方針(暫定)
| ステップ | 認証方式 | 期間 |
|---|---|---|
| Step 1: 動作検証 | PAT(オーナー個人) | 1〜2 週間 |
| Step 2: Worker 中継 + GitHub App | Cloudflare Worker + GitHub App | Step 1 動作確認後 |
Step 2 完了をもって Phase 2a 完了とする。
7. ADR テンプレート設計
7.1 ファイル配置
docs/
└── adr/
├── README.md # ADR 運用ガイド(新規)
├── _template.md # テンプレート(新規)
├── 001_ssot_invoice.md # 既存
├── ...
├── 009_separation_strategy.md # 既存
└── 010_xxx.md # Phase 2a 以降に Decision Pipeline で生成
7.2 テンプレート構造
# ADR-NNN: タイトル要旨
- **Status**: Proposed | Accepted | Superseded by ADR-XXX | Rejected
- **Mode**: Light | Standard | Critical
- **起案者**: 名前
- **起案日**: YYYY-MM-DD
- **承認日**: YYYY-MM-DD(Accepted 時のみ)
## Context(背景)
何を解決しようとしているか。なぜ今この決定が必要か。
## Decision(決定)
採用する方針を 1 段落で。
## Alternatives Considered(検討した代替案)
- 案 A: ...(不採用理由: ...)
- 案 B: ...(不採用理由: ...)
## Consequences(影響)
- 正の影響: ...
- 負の影響: ...
- リスク: ...
## Rollback Plan(撤退条件)
どうなったら元に戻すか。
## References
- 関連 ADR: ADR-XXXX
- 関連 PR/Issue: #NNN
Light Mode の場合は Alternatives Considered と Rollback Plan を省略可とする(Triage v0.2 の Light 定義に準拠)。
7.3 docs/adr/README.md の役割
- ADR 運用ガイド(命名規則 / Status 遷移 / 連番採番)
- ADR インデックス(自動生成 or 手動メンテ)
- Phase 2a 時点では手動メンテ。自動生成は Phase 2c 以降の課題
8. PR タイトル / 本文 / ラベルの仕様
8.1 タイトル
[ADR-NNN] {triage.title_summary} ({triage.mode})
例: [ADR-0010] 領収書 OCR を Claude へ移行 (Standard)
8.2 本文テンプレート
> Decision Pipeline (Dify) 経由で自動生成された PR です。
## 起案者
**{submitter.name}** (Dify: {submitter.dify_user_id})
{submitted_at}
## Triage
- ADR 要否: ✅ 要
- Mode: {triage.mode}
- 理由: {triage.reason}
## Scoring
{scoring.score_summary_md}
## ADR 本文
[`docs/adr/{NNN}_{slug}.md`](../blob/{branch}/docs/adr/{NNN}_{slug}.md) を参照
## 起案入力原文
<details><summary>展開</summary>
{user_input}
</details>
---
このPRをマージすると ADR が `Status: Accepted` として確定します。
差し戻す場合は本 PR を Close してください(`status:rejected` ラベルが自動付与)。
8.3 ラベル設計
| ラベル | 付与タイミング | 説明 |
|---|---|---|
adr | PR 作成時 | ADR 関連 PR の総称タグ |
mode:light / mode:standard / mode:critical | PR 作成時 | Triage の mode に対応 |
status:proposed | PR 作成時 | デフォルト |
status:accepted | PR マージ時 | GitHub Actions の on: pull_request で自動付け替え |
status:rejected | PR Close 時 | GitHub Actions で自動付け替え |
status:superseded | 後続 ADR の supersedes フィールド経由 | Phase 2c で実装 |
auto-generated | PR 作成時 | 人手 PR と区別 |
8.4 Reviewer 指定
team-reviewers ではなく reviewers で個人指定する。
Reviewers: [t-saitoh-bizlp]
CODEOWNERS の使用は Phase 2b で再検討(業務委託者の範囲が広がった場合)。
9. 起案者の身元保証
9.1 Dify 認証
業務委託者は Dify アカウントでログイン(Dify の SSO 機能で Google 認証を採用)。
9.2 Payload への組み込み
Dify Workflow の開始ノードで、以下の組み込み変数を使用:
{{user.name}} → submitter.name
{{user.id}} → submitter.dify_user_id
これらを Webhook payload に同梱して GitHub に送る。
9.3 PR 上の表示
PR 本文の「起案者」セクションに submitter.name をそのまま表示。Phase 2a では GitHub Account とのマッピングは行わない(Phase 2b で検討)。
10. 失敗ケースと運用設計
10.1 Dify 側の失敗
| ケース | 挙動 |
|---|---|
| LLM API レート制限 | Dify の自動リトライに任せる(最大 3 回) |
| GitHub Webhook 送信失敗 | Dify ログに記録、起案者に「再送信してください」を表示 |
| Triage で is_adr_worthy=false | フィードバック表示して終了(Webhook 送信しない) |
| Scoring で pass=false | スコア詳細をフィードバック表示して終了 |
10.2 GitHub Actions 側の失敗
| ケース | 挙動 |
|---|---|
| Payload schema 不正 | core.setFailed でジョブ失敗、起案者には通知されない(運用で検出) |
| 連番採番衝突 | Phase 2a では発生しない前提(運用で 1 人ずつ起案) |
| Slug 重複 | Actions ジョブ失敗、Slack 通知(Phase 2a 末) |
| ブランチ作成失敗 | Actions ジョブ失敗 |
| PR 作成失敗 | ブランチは作成されているが PR がない状態に。手動で PR を立てる |
10.3 失敗時のオブザーバビリティ
- GitHub Actions ジョブ失敗ログを Slack
#dev-alertsチャンネルに送る(Phase 2a 末で実装) - Dify ログは Dify 標準のログビューワで確認
11. Phase 2a 完了基準
以下のすべてを満たした時点で Phase 2a 完了とする:
- ✅ Dify Workflow に Triage / Scoring / Webhook 送信ノードが配線済み
- ✅ GitHub App
bizlp-decision-pipeline-botが作成・install 済み - ✅ Cloudflare Worker(または同等の中継)が動作
- ✅
.github/workflows/decision-pipeline-pr.ymlが main にマージ済み - ✅
docs/adr/README.mddocs/adr/_template.mdが main に配置済み - ✅ TC-07 入力(高品質起案)を Dify に流すと、自動で PR が立つ
- ✅ TC-06 入力(雑なドラフト)を流すと、Webhook 送信されず Dify 側で差戻し表示される
- ✅ Phase 2a の operator_guide.md が公開され、業務委託者にアカウント発行可能な状態
12. Phase 2a 実装タスク一覧
実装担当: main スペース(GAS 実装担当)/ ドキュメント担当: sub スペース。
| # | タスク | 担当 | 依存 |
|---|---|---|---|
| 1 | docs/adr/README.md docs/adr/_template.md 作成 | sub | – |
| 2 | docs/_internal/05_how-to/operator_guide.md 作成 | sub | – |
| 3 | docs/_internal/decision_pipeline/dify_workflow_spec.md 作成 | sub | – |
| 4 | prompts/06_adr_body.md prompts/07_slug.md 新規プロンプト作成 | sub | 3 |
| 5 | GitHub App bizlp-decision-pipeline-bot 作成・install | オーナー | – |
| 6 | Cloudflare Worker 実装(HMAC 検証 + JWT 生成 + repository_dispatch 中継) | main | 5 |
| 7 | .github/workflows/decision-pipeline-pr.yml 実装 | main | 1, 6 |
| 8 | Dify Workflow に N2〜N13 ノード追加(仕様: dify_workflow_spec.md) | オーナー | 4, 6 |
| 9 | TC-07 / TC-06 / TC-01 でエンドツーエンド動作確認 | オーナー | 1〜8 |
| 10 | Phase 2a 振り返り / Phase 2b 着手判定 | オーナー | 9 |
完了済みタスクは ✅ 列でマーク。タスク #4 は本 PR とは別途、追加作業が必要。
13. 残課題(Phase 2a → 2b 候補)
- 並行起案時の連番衝突対策
- ADR インデックス自動生成
- Slack/Discord 通知の実装
- Dify から GitHub Account へのマッピング
- CODEOWNERS 導入
- Status: Superseded の自動付与(後続 ADR が supersedes フィールドを持つ場合)
変更履歴
| 日時 | 変更内容 |
|---|---|
| 2026-05-07 | 初版作成(Phase 1 完了直後の設計セッションで決定事項を確定) |
| 2026-05-07 | ADR 命名を NNN_slug.md に統一(既存規則に整合) |
| 2026-05-07 | Scoring schema を 10 項目 × 5 点に修正(実プロンプトとの不整合解消)/§12 にタスク #3, #4 追加 |