@geekbeer/minion 3.17.0 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -240,6 +240,42 @@ Changes via the config API take effect immediately (no restart required).
240
240
 
241
241
  Allowed keys: `LLM_COMMAND`, `REFLECTION_TIME`
242
242
 
243
+ ### LLM Plugins (opt-in)
244
+
245
+ プラグイン方式の LLM 設定。`primary` を設定すると有効化される。未設定の場合は従来の `LLM_COMMAND` 経路で動作する。設定は `~/minion/llm/config.json` にファイルとして保存される (env var に依存しないため quote 破損バグの影響を受けない)。
246
+
247
+ | Method | Endpoint | Description |
248
+ |--------|----------|-------------|
249
+ | GET | `/api/llm/plugins` | List available plugins with capabilities & auth status |
250
+ | GET | `/api/llm/config` | Get current primary/enabled selection |
251
+ | PUT | `/api/llm/config` | Update primary/enabled. Body: `{primary?, enabled?}` |
252
+
253
+ **GET /api/llm/plugins response**:
254
+ ```json
255
+ {
256
+ "success": true,
257
+ "plugins": [
258
+ {
259
+ "name": "claude",
260
+ "displayName": "Claude Code",
261
+ "capabilities": {
262
+ "toolUse": true,
263
+ "streaming": true,
264
+ "vision": true,
265
+ "imageGeneration": false,
266
+ "sessionResume": true
267
+ },
268
+ "authenticated": true,
269
+ "builtin": true,
270
+ "enabled": true,
271
+ "primary": true
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ **PUT /api/llm/config** body: `{ "primary": "claude", "enabled": ["claude", "gemini", "codex"] }`
278
+
243
279
  ### Permissions
244
280
 
245
281
  CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション管理。
@@ -853,6 +889,409 @@ POST `/api/minion/execution` body:
853
889
  }
854
890
  ```
855
891
 
892
+ ### Pending Revisions (PM のみ)
893
+
894
+ レビューで `revision_requested` になったステップを検知する。ミニオンの `revision-watcher` デーモンが30秒ごとにポーリングし、LLM で差し戻し先ステップを判断した上で `/api/minion/revision-reset` を呼び出す。
895
+
896
+ | Method | Endpoint | Description |
897
+ |--------|----------|-------------|
898
+ | GET | `/api/minion/pending-revisions` | `revision_requested` 状態のステップ一覧(PMのプロジェクトのみ) |
899
+ | POST | `/api/minion/revision-reset` | 差し戻し対象ステップを `pending` に戻す |
900
+
901
+ GET Response:
902
+ ```json
903
+ {
904
+ "revisions": [
905
+ {
906
+ "execution_id": "uuid",
907
+ "workflow_name": "daily-check",
908
+ "revision_step_index": 2,
909
+ "review_comment": "string (レビュアーのフィードバック)",
910
+ "pipeline": [
911
+ {
912
+ "step_index": 0,
913
+ "skill_version_id": "uuid",
914
+ "skill_name": "string|null",
915
+ "assigned_role": "pm|engineer|accountant"
916
+ }
917
+ ]
918
+ }
919
+ ]
920
+ }
921
+ ```
922
+
923
+ POST `/api/minion/revision-reset` body:
924
+ ```json
925
+ {
926
+ "execution_id": "uuid",
927
+ "target_step_index": 0,
928
+ "revision_step_index": 2,
929
+ "revision_feedback": "string (省略可、target step の review_comment に保存)"
930
+ }
931
+ ```
932
+
933
+ - `target_step_index` は差し戻し先(やり直し起点)のインデックス
934
+ - `revision_step_index` は差し戻しを要求されたレビューステップのインデックス(`target_step_index <= revision_step_index`)
935
+ - target から revision までのステップが `pending` に戻り、`revision_feedback` が再実行時のコンテキストとして注入される
936
+
937
+ Response:
938
+ ```json
939
+ {
940
+ "success": true,
941
+ "reset_count": 3,
942
+ "target_step_index": 0,
943
+ "revision_step_index": 2
944
+ }
945
+ ```
946
+
947
+ ---
948
+
949
+ ## DAG Workflows (HQ, ノード/エッジ方式)
950
+
951
+ DAG ワークフローは従来の線形パイプラインを拡張した有向非巡回グラフ方式のワークフローです。HQダッシュボードの DAG エディタで作成・編集できるほか、ミニオンAPI経由でも JSON ベースで編集可能(PMロールのみ)。ミニオンはプルベースのポーリングで pending ノードを検出・実行します。
952
+
953
+ ### 編集エンドポイント (PMロール限定)
954
+
955
+ | Method | Endpoint | Description |
956
+ |--------|----------|-------------|
957
+ | POST | `/api/minion/dag-workflows` | 新規 DAG ワークフローを作成(ドラフトとして保存) |
958
+ | PUT | `/api/minion/dag-workflows/:id` | ドラフト graph / メタデータを更新 |
959
+ | POST | `/api/minion/dag-workflows/:id/publish` | ドラフトをバリデーションし新バージョンとして公開 |
960
+
961
+ いずれも Bearer 認証 + プロジェクトメンバーシップが `role='pm'` のミニオンに限定。非PMは 403。PUT / POST はドラフト保存時にセマンティックバリデーションを行わない(`graph` が object で `nodes` / `edges` 配列を持つことの構造チェックのみ)。パブリッシュ時には `validateDagGraph` によるフル検証(ノードID重複・エッジの参照整合性・サイクル検出・必須フィールド等)が走る。
962
+
963
+ **推奨ワークフロー**: create → put で graph を整える → publish、または既存ワークフローに対して put → publish。
964
+
965
+ POST `/api/minion/dag-workflows` body:
966
+ ```json
967
+ {
968
+ "project_id": "uuid",
969
+ "name": "my-dag",
970
+ "graph": { "nodes": [], "edges": [] },
971
+ "content": "Markdown description (optional)",
972
+ "change_summary": "initial draft (optional)"
973
+ }
974
+ ```
975
+
976
+ - `name` は `/^[a-z0-9-]+$/`
977
+ - `graph` は省略可。省略時はドラフト無しで作成される
978
+ - レスポンスは作成された DAG ワークフロー全体(`current_version_id` は null のまま、ドラフトに保存)
979
+
980
+ PUT `/api/minion/dag-workflows/:id` body(全フィールド optional、省略したものは変更しない):
981
+ ```json
982
+ {
983
+ "graph": { "nodes": [...], "edges": [...] },
984
+ "content": "...",
985
+ "change_summary": "Updated fan-out branch",
986
+ "name": "renamed-dag",
987
+ "is_active": true,
988
+ "maturity": "preview|stable|..."
989
+ }
990
+ ```
991
+
992
+ - `graph` を渡すと draft_graph が上書きされる(構造チェックのみ、セマンティックチェック無し)
993
+ - `content` / `change_summary` は `graph` と併せて draft スロットに保存される
994
+
995
+ POST `/api/minion/dag-workflows/:id/publish` (body なし):
996
+ - 現在の draft_graph を `validateDagGraph` でフル検証
997
+ - OK なら新バージョン行を `dag_workflow_versions` に追加し、`current_version_id` を更新、ドラフトをクリア
998
+ - NG なら 400 で `{ error, details: [...] }` を返却
999
+
1000
+ ### ミニオン CLI ラッパー
1001
+
1002
+ 以下の `hq` サブコマンドが上記エンドポイントを呼び出す。いずれもリクエスト送信前にローカルで JSON 構文検証を行う。
1003
+
1004
+ ```bash
1005
+ hq create dag-workflow <body.json> # POST /api/minion/dag-workflows
1006
+ hq put dag-workflow <id> <body.json> # PUT /api/minion/dag-workflows/:id
1007
+ hq publish dag-workflow <id> # POST /api/minion/dag-workflows/:id/publish
1008
+ hq fetch dag-workflow <id> # GET /api/minion/dag-workflows/:id
1009
+ hq fetch dag-execution <id> # GET /api/minion/dag-executions/:id
1010
+ ```
1011
+
1012
+ ### DAG Read Endpoints (チャットコンテキスト用)
1013
+
1014
+ ユーザーがHQダッシュボードでDAGエディタ/実行詳細を閲覧中にチャットした場合、チャットコンテキストに `context.type = 'dag-workflow'` または `'dag-execution'` が注入される。以下の `hq` CLI コマンドで詳細を取得できる(内部的に `/api/minion/dag-workflows/:id` / `/api/minion/dag-executions/:id` を叩く)。
1015
+
1016
+ | コマンド | 対応エンドポイント | 用途 |
1017
+ |---------|-------------------|------|
1018
+ | `hq fetch dag-workflow <id>` | GET `/api/minion/dag-workflows/:id` | DAG ワークフロー定義(published graph + draft)取得 |
1019
+ | `hq fetch dag-execution <id>` | GET `/api/minion/dag-executions/:id` | DAG 実行詳細(graph_snapshot + node_executions)取得 |
1020
+
1021
+ いずれもミニオンのプロジェクトメンバーシップでスコープされる。ミニオンが参加していないプロジェクトのDAGは 404。
1022
+
1023
+ GET `/api/minion/dag-workflows/:id` Response:
1024
+ ```json
1025
+ {
1026
+ "id": "uuid",
1027
+ "project_id": "uuid",
1028
+ "name": "my-dag",
1029
+ "is_active": true,
1030
+ "maturity": "draft|stable|...",
1031
+ "current_version_id": "uuid",
1032
+ "draft_graph": { "nodes": [...], "edges": [...] } ,
1033
+ "draft_content": "markdown|null",
1034
+ "draft_change_summary": "string|null",
1035
+ "draft_updated_at": "ISO|null",
1036
+ "current_version": {
1037
+ "id": "uuid",
1038
+ "version": 3,
1039
+ "graph": { "nodes": [...], "edges": [...] },
1040
+ "content": "markdown",
1041
+ "change_summary": "string",
1042
+ "created_at": "ISO"
1043
+ },
1044
+ "my_role": "pm|engineer|accountant|null"
1045
+ }
1046
+ ```
1047
+
1048
+ GET `/api/minion/dag-executions/:id` Response:
1049
+ ```json
1050
+ {
1051
+ "id": "uuid",
1052
+ "dag_workflow_id": "uuid",
1053
+ "dag_workflow_name": "my-dag",
1054
+ "dag_workflow_version_id": "uuid",
1055
+ "dag_workflow_version": 3,
1056
+ "project_id": "uuid",
1057
+ "status": "pending|running|completed|failed",
1058
+ "graph_snapshot": { "nodes": [...], "edges": [...] },
1059
+ "started_at": "ISO|null",
1060
+ "completed_at": "ISO|null",
1061
+ "created_at": "ISO",
1062
+ "node_executions": [
1063
+ {
1064
+ "id": "uuid",
1065
+ "node_id": "n2",
1066
+ "scope_path": "",
1067
+ "status": "pending|waiting|running|completed|failed|skipped",
1068
+ "outcome": "success|failure|null",
1069
+ "input_data": {},
1070
+ "output_data": {},
1071
+ "output_summary": "string|null",
1072
+ "requires_review": false,
1073
+ "review_status": "review_pending|approved|rejected|revision_requested|null",
1074
+ "revision_count": 0,
1075
+ "started_at": "ISO|null",
1076
+ "completed_at": "ISO|null"
1077
+ }
1078
+ ]
1079
+ }
1080
+ ```
1081
+
1082
+ ### DAG Runtime Endpoints (ミニオン向け)
1083
+
1084
+ | Method | Endpoint | Description |
1085
+ |--------|----------|-------------|
1086
+ | GET | `/api/dag/minion/pending-nodes` | 自分が実行すべき pending ノード一覧(ロール一致・依存解決済み) |
1087
+ | POST | `/api/dag/minion/claim-node` | ノードを楽観ロックで取得(排他実行) |
1088
+ | POST | `/api/dag/minion/node-complete` | ノード完了を報告し、下流ノードへカスケード |
1089
+
1090
+ ミニオンの `dag-step-poller` デーモンが30秒ごとに `pending-nodes` を叩き、最大2並列で claim → skill/transform 実行 → node-complete の流れを回す。`skill`・`transform` 以外のノード(start/end/review/fan_out/join/conditional)はHQ内部のカスケードエンジンが処理するため、ミニオンには返されない。
1091
+
1092
+ #### GET /api/dag/minion/pending-nodes
1093
+
1094
+ Response:
1095
+ ```json
1096
+ {
1097
+ "nodes": [
1098
+ {
1099
+ "node_execution_id": "uuid",
1100
+ "execution_id": "uuid",
1101
+ "dag_workflow_name": "my-dag",
1102
+ "node_id": "node-abc",
1103
+ "scope_path": "",
1104
+ "node_type": "skill",
1105
+ "skill_version_id": "uuid|null",
1106
+ "skill_name": "skill-1|null",
1107
+ "assigned_role": "pm|engineer|accountant",
1108
+ "input_data": { "...": "..." },
1109
+ "revision_feedback": "string|null",
1110
+ "transform_instruction": "string|null"
1111
+ }
1112
+ ]
1113
+ }
1114
+ ```
1115
+
1116
+ 返却条件:
1117
+ - `assigned_role` がミニオンのプロジェクトロールと一致
1118
+ - ノードの `status` が `pending`
1119
+ - `node_type` が `skill` または `transform`
1120
+ - スコープ内の全依存ノードが `completed`(`requires_review` の場合は `approved` も必要)
1121
+
1122
+ `scope_path` は fan-out 内のインスタンスを示す(ルートは空文字列、1段のfan-outでは `fan_out_A:0`、ネストでは `fan_out_A:2/fan_out_B:1`)。依存解決は同じ scope_path 内で閉じる。
1123
+
1124
+ #### POST /api/dag/minion/claim-node
1125
+
1126
+ Body:
1127
+ ```json
1128
+ { "node_execution_id": "uuid" }
1129
+ ```
1130
+
1131
+ Response (成功 / 201):
1132
+ ```json
1133
+ { "success": true, "node_execution_id": "uuid" }
1134
+ ```
1135
+
1136
+ Response (競合 / 409): 他ミニオンが既に claim 済み、または状態が pending でない場合。
1137
+ ```json
1138
+ { "error": "Node already claimed or not pending" }
1139
+ ```
1140
+
1141
+ 409 を受け取った場合は `pending-nodes` を取り直して次のノードに進むこと。
1142
+
1143
+ #### POST /api/dag/minion/node-complete
1144
+
1145
+ Body:
1146
+ ```json
1147
+ {
1148
+ "node_execution_id": "uuid",
1149
+ "status": "completed|failed",
1150
+ "output_data": { "...": "..." },
1151
+ "output_summary": "string (省略可、レポート/サマリー)"
1152
+ }
1153
+ ```
1154
+
1155
+ - `status: completed` で `requires_review` なノードはサーバ側で `review_status=review_pending` になりカスケードは停止(レビュー承認まで下流は生成されない)
1156
+ - `status: failed` でもカスケードは走る(fan-out join が `on_failure=ignore|collect` で集約できるため)
1157
+ - `output_data` は下流ノードの `input_data` に伝播する。**スキル実行時はスキル本文の「## Output Data」セクションの JSON コードブロックを抽出して `output_data` に載せる規約**(ミニオンの `dag-node-executor` がこの抽出を行う)
1158
+
1159
+ Response:
1160
+ ```json
1161
+ { "success": true }
1162
+ ```
1163
+
1164
+ レビュー待ち状態の場合:
1165
+ ```json
1166
+ { "success": true, "review_pending": true }
1167
+ ```
1168
+
1169
+ ### DAG Graph Structure
1170
+
1171
+ DAG ワークフローの graph は以下の構造で保存される(`dag_workflow_versions.graph` / `dag_executions.graph_snapshot`):
1172
+
1173
+ ```json
1174
+ {
1175
+ "nodes": [
1176
+ { "id": "n1", "type": "start", "label": "Start", "position": { "x": 0, "y": 0 } },
1177
+ {
1178
+ "id": "n2",
1179
+ "type": "skill",
1180
+ "label": "Fetch data",
1181
+ "skill_version_id": "uuid",
1182
+ "assigned_role": "engineer"
1183
+ },
1184
+ {
1185
+ "id": "n3",
1186
+ "type": "fan_out",
1187
+ "label": "Per item",
1188
+ "fan_out_source": ".items",
1189
+ "template": { "nodes": [ ... ], "edges": [ ... ] },
1190
+ "join_node": "n4",
1191
+ "max_concurrency": 3
1192
+ },
1193
+ {
1194
+ "id": "n4",
1195
+ "type": "join",
1196
+ "label": "Collect",
1197
+ "fan_out_node": "n3",
1198
+ "join_mode": "all",
1199
+ "on_failure": "collect"
1200
+ },
1201
+ { "id": "n5", "type": "end", "label": "End" }
1202
+ ],
1203
+ "edges": [
1204
+ { "id": "e1", "source": "n1", "target": "n2", "kind": "normal" },
1205
+ { "id": "e2", "source": "n2", "target": "n3" },
1206
+ { "id": "e3", "source": "n3", "target": "n4" },
1207
+ { "id": "e4", "source": "n4", "target": "n5" }
1208
+ ]
1209
+ }
1210
+ ```
1211
+
1212
+ #### Node Types
1213
+
1214
+ | `type` | 役割 | ミニオン実行 |
1215
+ |--------|------|---------------|
1216
+ | `start` | エントリポイント | ❌ (内部) |
1217
+ | `end` | 終端 | ❌ (内部) |
1218
+ | `skill` | スキル実行。`skill_version_id` と `assigned_role` が必須 | ✅ |
1219
+ | `transform` | LLM によるデータ変換。`transform_instruction` が必須 | ✅ |
1220
+ | `review` | レビューゲート。`approved` / `revision_requested` で分岐 | ❌ (内部) |
1221
+ | `fan_out` | 配列入力をテンプレートsub-graphに展開して並列実行 | ❌ (内部) |
1222
+ | `join` | fan_out の結果を集約 | ❌ (内部) |
1223
+ | `conditional` | 条件分岐(`llm` / `regex` / `jq`) | ❌ (内部) |
1224
+
1225
+ #### DagNode 主要フィールド
1226
+
1227
+ | Field | Type | 説明 |
1228
+ |-------|------|------|
1229
+ | `id` | string | グラフ内ユニークなノードID |
1230
+ | `type` | DagNodeType | 上記ノード種別 |
1231
+ | `label` | string | 表示名 |
1232
+ | `skill_version_id` | string? | skill ノードで使用するスキルバージョンUUID |
1233
+ | `assigned_role` | `pm`\|`engineer`\|`accountant` | 実行ロール |
1234
+ | `fan_out_source` | string? | fan_out: 入力から配列を取り出すドット記法(例 `.items`) |
1235
+ | `template` | DagGraph? | fan_out: 各要素ごとに展開するsub-graph |
1236
+ | `join_node` | string? | fan_out: 対応する join ノードID |
1237
+ | `max_concurrency` | number? | fan_out: 並列インスタンス上限 |
1238
+ | `fan_out_node` | string? | join: 対応する fan_out ノードID |
1239
+ | `join_mode` | `all`\|`any`\|`majority` | join: 完了判定 |
1240
+ | `on_failure` | `fail_all`\|`ignore`\|`collect` | join: 失敗時の挙動 |
1241
+ | `condition_type` | `llm`\|`regex`\|`jq` | conditional: 条件評価方式 |
1242
+ | `condition_expression` | string | conditional: 条件式 |
1243
+ | `branches` | Record<string,string> | conditional: 条件出力→遷移先ノードID |
1244
+ | `default_branch` | string? | conditional: マッチしなかった場合の遷移先 |
1245
+ | `transform_instruction` | string | transform: LLMへの変換指示 |
1246
+ | `review` | object | review: レビュー設定 |
1247
+
1248
+ #### DagEdge
1249
+
1250
+ | Field | Type | 説明 |
1251
+ |-------|------|------|
1252
+ | `id` | string | エッジID |
1253
+ | `source` | string | 起点ノードID |
1254
+ | `target` | string | 終点ノードID |
1255
+ | `kind` | `normal`\|`approved`\|`revision` | `normal`=通常、`approved`=review承認時、`revision`=review差し戻し時(サイクル検出では無視される) |
1256
+ | `condition_label` | string? | 表示ラベル |
1257
+
1258
+ ### DAG Execution API (読み取り)
1259
+
1260
+ | Method | Endpoint | Auth | 用途 |
1261
+ |--------|----------|------|------|
1262
+ | GET | `/api/dag/executions/:execId` | セッション | 実行詳細 (graph_snapshot + node_executions) 取得。UI用、ミニオンは通常使わない |
1263
+
1264
+ Response:
1265
+ ```json
1266
+ {
1267
+ "id": "uuid",
1268
+ "dag_workflow_id": "uuid",
1269
+ "dag_workflow_name": "my-dag",
1270
+ "dag_workflow_version_id": "uuid",
1271
+ "status": "pending|running|completed|failed",
1272
+ "graph_snapshot": { "nodes": [...], "edges": [...] },
1273
+ "started_at": "ISO",
1274
+ "completed_at": "ISO|null",
1275
+ "node_executions": [
1276
+ {
1277
+ "id": "uuid",
1278
+ "node_id": "n2",
1279
+ "scope_path": "",
1280
+ "status": "pending|waiting|running|completed|failed|skipped",
1281
+ "outcome": "success|failure|null",
1282
+ "input_data": {},
1283
+ "output_data": {},
1284
+ "output_summary": "string|null",
1285
+ "requires_review": false,
1286
+ "review_status": "review_pending|approved|rejected|revision_requested|null",
1287
+ "revision_count": 0,
1288
+ "started_at": "ISO|null",
1289
+ "completed_at": "ISO|null"
1290
+ }
1291
+ ]
1292
+ }
1293
+ ```
1294
+
856
1295
  ### Issue Reporting (GitHub Issue 起票)
857
1296
 
858
1297
  | Method | Endpoint | Description |
@@ -144,6 +144,226 @@ curl -s -X POST "http://localhost:8080/api/workflows/push/<name>" \
144
144
  -H "Authorization: Bearer $API_TOKEN"
145
145
  ```
146
146
 
147
+ > **Note**: `/api/workflows/push|fetch` は線形パイプライン形式(`pipeline_skill_names`)の旧式ワークフロー専用です。DAGワークフロー(ノード/エッジ形式)はHQダッシュボードの DAG エディタで作成・編集します。次節を参照。
148
+
149
+ ---
150
+
151
+ ## DAG ワークフロー (ノード/エッジ形式)
152
+
153
+ DAG ワークフローは有向非巡回グラフでスキル間の依存関係を表現する新方式のワークフローです。fan-out による並列展開、join による集約、conditional による分岐、transform による LLM データ変換、review によるゲーティングをサポートします。
154
+
155
+ **HQダッシュボードの DAG エディタ(プロジェクト画面 → 「DAG (beta)」タブ)で GUI 編集**できるほか、**ミニオンからは JSON ベースで編集可能**(PMロールのみ)。ミニオンは実行ランタイムも担当し、`dag-step-poller` デーモンが pending ノードを自動で処理します。
156
+
157
+ ### ミニオンによる DAG ワークフロー編集 (PM のみ)
158
+
159
+ ミニオンは `hq` CLI で DAG ワークフローの graph JSON を直接編集できる。
160
+
161
+ #### 1. 新規作成
162
+
163
+ ```bash
164
+ # body.json を用意
165
+ cat > /tmp/dag-create.json <<'EOF'
166
+ {
167
+ "project_id": "<project-uuid>",
168
+ "name": "my-dag",
169
+ "content": "Description of this DAG",
170
+ "change_summary": "initial draft",
171
+ "graph": {
172
+ "nodes": [
173
+ { "id": "start", "type": "start", "label": "Start", "position": { "x": 0, "y": 0 } },
174
+ { "id": "end", "type": "end", "label": "End", "position": { "x": 300, "y": 0 } }
175
+ ],
176
+ "edges": [
177
+ { "id": "e1", "source": "start", "target": "end" }
178
+ ]
179
+ }
180
+ }
181
+ EOF
182
+
183
+ # 作成(ドラフトとして保存される、まだ v1 にはならない)
184
+ hq create dag-workflow /tmp/dag-create.json
185
+ ```
186
+
187
+ - `name` は `/^[a-z0-9-]+$/`
188
+ - `graph` は省略可。省略時は空で作成してから put で埋めていく
189
+ - レスポンスの `id` を控えて後続の put / publish に使う
190
+
191
+ #### 2. ドラフト更新
192
+
193
+ ```bash
194
+ # body.json を用意(フィールドは全て optional)
195
+ cat > /tmp/dag-update.json <<'EOF'
196
+ {
197
+ "graph": {
198
+ "nodes": [
199
+ { "id": "start", "type": "start", "label": "Start" },
200
+ { "id": "fetch", "type": "skill", "label": "Fetch",
201
+ "skill_version_id": "<skill-version-uuid>", "assigned_role": "engineer" },
202
+ { "id": "end", "type": "end", "label": "End" }
203
+ ],
204
+ "edges": [
205
+ { "id": "e1", "source": "start", "target": "fetch" },
206
+ { "id": "e2", "source": "fetch", "target": "end" }
207
+ ]
208
+ },
209
+ "content": "Description updated",
210
+ "change_summary": "add fetch skill node"
211
+ }
212
+ EOF
213
+
214
+ hq put dag-workflow <dag-workflow-id> /tmp/dag-update.json
215
+ ```
216
+
217
+ - ドラフト保存時はセマンティックバリデーション無し(構造チェックのみ)。未完成状態でも保存できる。
218
+ - `hq` CLI は送信前にローカルで JSON 構文を検証する。壊れていれば API を叩かずに `Error: invalid JSON syntax in ...` で停止する。
219
+
220
+ #### 3. 公開(バリデーション付き)
221
+
222
+ ```bash
223
+ hq publish dag-workflow <dag-workflow-id>
224
+ ```
225
+
226
+ - 現在のドラフトを `validateDagGraph` でフル検証(ノードID重複・エッジ参照整合性・サイクル・必須フィールド等)
227
+ - OK なら新バージョンが `dag_workflow_versions` に追加され、`current_version_id` が更新される
228
+ - NG なら 400 で `{ error, details: [...] }` が返る。エラー詳細を見てドラフトを修正し再試行する
229
+ - 公開後、`dag-step-poller` による実行対象になる
230
+
231
+ #### 4. 既存ワークフローの取得と編集のラウンドトリップ
232
+
233
+ ```bash
234
+ # 現在の state をダウンロード
235
+ hq fetch dag-workflow <dag-workflow-id> > /tmp/current.json
236
+
237
+ # jq で graph 部分だけ抜き出して編集用ボディを作る
238
+ jq '{ graph: (.draft_graph // .current_version.graph), content: (.draft_content // .current_version.content), change_summary: "…" }' \
239
+ /tmp/current.json > /tmp/dag-update.json
240
+
241
+ # 編集(エディタや AI で graph を変更)
242
+ # ...
243
+
244
+ # 更新 → 公開
245
+ hq put dag-workflow <dag-workflow-id> /tmp/dag-update.json
246
+ hq publish dag-workflow <dag-workflow-id>
247
+ ```
248
+
249
+ #### 権限と注意事項
250
+
251
+ - 書き込み(create / put / publish)は **PMロールのみ**。Engineer / accountant では 403 が返る。読み取り(fetch)は全メンバー可。
252
+ - ドラフト保存は軽量(構造チェックのみ)。インクリメンタルに graph を組み立てていくのに適している。
253
+ - セマンティックエラーは publish 時にまとめて検出されるため、途中保存と最終公開を使い分けること。
254
+
255
+ ### 実行フロー(ランタイム側、参考)
256
+
257
+ ```
258
+ HQ側:
259
+ ユーザーが DAG 実行を開始
260
+ → dag_executions レコード作成
261
+ → 初期ノード (start の下流) が status=pending で生成
262
+
263
+ ミニオン側 (dag-step-poller, 30秒ごと):
264
+ 1. GET /api/dag/minion/pending-nodes
265
+ → 自分のロールに一致し、依存が解決済みの pending ノードを取得
266
+ 2. POST /api/dag/minion/claim-node
267
+ → 楽観ロックでノードを取得(409 の場合は他ミニオンが先に処理中)
268
+ 3. スキル実行 (skill ノード) または変換実行 (transform ノード)
269
+ 4. POST /api/dag/minion/node-complete
270
+ → output_data と output_summary を報告
271
+
272
+ HQ側 (カスケードエンジン):
273
+ - completed → 下流ノードを pending で生成 (依存解決後)
274
+ - fan_out ノード → template を N 個展開、各インスタンスに scope_path 付与
275
+ - join ノード → fan-out 全インスタンス完了待機・集約
276
+ - conditional ノード → 条件評価で分岐先を決定
277
+ - review ノード → review_pending で停止、レビュー承認で再開
278
+ ```
279
+
280
+ ### スキルの output_data 出力規約
281
+
282
+ DAGのスキルノードでは、下流ノードに構造化データを渡すため、スキル本文に「## Output Data」セクションを設けて JSON コードブロックで出力を記述すること。ミニオンの `dag-node-executor` がこのブロックを抽出して `output_data` に載せる。
283
+
284
+ 例(スキル実行結果の末尾):
285
+
286
+ ````markdown
287
+ ## 実行サマリー
288
+
289
+ 検索結果を3件取得しました。
290
+
291
+ ## Output Data
292
+
293
+ ```json
294
+ {
295
+ "items": [
296
+ { "id": "a1", "title": "Item A" },
297
+ { "id": "a2", "title": "Item B" },
298
+ { "id": "a3", "title": "Item C" }
299
+ ],
300
+ "count": 3
301
+ }
302
+ ```
303
+ ````
304
+
305
+ - 「## Output Data」セクションが無い、または JSON パースに失敗した場合は `{ _raw: "<出力全文>" }` にフォールバック
306
+ - 失敗時 (`status: failed`) は `output_data` は空オブジェクト `{}` として扱われる
307
+
308
+ ### Fan-out / Join の動作
309
+
310
+ fan-out ノードは入力から配列を取り出し、template sub-graph を配列要素数だけ並列展開する。
311
+
312
+ ```
313
+ 入力 input_data = { "items": [A, B, C] }
314
+ fan_out_source = ".items"
315
+
316
+ 3 つのインスタンスを生成:
317
+ scope_path="<fan_out_id>:0", input=A, 対応する template nodes が pending で出現
318
+ scope_path="<fan_out_id>:1", input=B, ...
319
+ scope_path="<fan_out_id>:2", input=C, ...
320
+
321
+ 各インスタンスの template 実行完了後、対応する join ノードが集約
322
+ join_mode=all: 全成功で完了
323
+ join_mode=any: いずれか成功で完了
324
+ join_mode=majority: 過半数成功で完了
325
+ on_failure=collect: 失敗も含め結果を集める
326
+ on_failure=ignore: 失敗を除外
327
+ on_failure=fail_all: 1つでも失敗すれば join も失敗
328
+
329
+ 集約結果が join の output_data として下流に渡る
330
+ ```
331
+
332
+ ミニオンから見ると、fan-out 内の skill/transform ノードも通常どおり `pending-nodes` に返ってくる(`scope_path` が空でない点だけが違い)。
333
+
334
+ ### Transform ノード
335
+
336
+ Transform ノードは LLM を使って input_data を output_data に変換する軽量ノード。`transform_instruction` に自然言語で変換指示を書く。ミニオン側では `transform_instruction` を本文とする一時的なスキルを組み立てて実行する。
337
+
338
+ ```
339
+ transform_instruction: "items 配列から title が 'Item B' のエントリを除外し、残りを返してください"
340
+ input_data: { "items": [{ "title": "Item A" }, { "title": "Item B" }, { "title": "Item C" }] }
341
+ ↓ (LLM)
342
+ output_data: { "items": [{ "title": "Item A" }, { "title": "Item C" }] }
343
+ ```
344
+
345
+ ### Review ノードとリビジョン
346
+
347
+ Review ノードはレビューゲート。`review_status=review_pending` で下流カスケードが停止する。レビュアーが:
348
+ - `approved` にすると `approved` 種別のエッジで下流に進む
349
+ - `revision_requested` にすると `revision` 種別のエッジで差し戻し先に戻る
350
+
351
+ レガシー線形パイプラインの差し戻しは `revision-watcher` デーモン + `/api/minion/pending-revisions` + `/api/minion/revision-reset` で処理される。DAGの差し戻しはサーバ側カスケードで自動処理。
352
+
353
+ ### デバッグ
354
+
355
+ ```bash
356
+ # 自分が取得できる pending DAG ノードを確認
357
+ curl -s "$HQ_URL/api/dag/minion/pending-nodes" \
358
+ -H "Authorization: Bearer $API_TOKEN" | jq
359
+
360
+ # DAG 実行の詳細(UI用だがデバッグにも使える、セッション認証が必要なため通常はHQダッシュボード側から)
361
+ # ミニオンから実行詳細を直接取得する手段は現時点では無い。pending-nodes で見える範囲で判断する
362
+
363
+ # dag-step-poller のログを確認
364
+ tail -f ~/.minion/logs/agent.log | grep '\[DAG'
365
+ ```
366
+
147
367
  ---
148
368
 
149
369
  ## プロジェクトコンテキストの操作