Skip to content

Commit 97151b2

Browse files
committed
feat(workflow): add self-healing loop and stabilize completion checks
1 parent 0c5b701 commit 97151b2

20 files changed

Lines changed: 2140 additions & 154 deletions

‎docs/guide/workflow.md‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ steps:
9999
| `max_steps` | number | no | max worker steps |
100100
| `max_command_time` | DurationString | no | per-command timeout |
101101
| `completion_check` | object | no | completion check definition (see below) |
102+
| `convergence` | object | no | optional convergence control for completion-check loops (stall detection, scope guard, fail-fast) |
102103
| `max_iterations` | number | no | max completion-check iterations (default: 1) |
103104
| `on_iterations_exhausted` | enum | no | behavior on hitting iteration limit: `abort` / `continue` |
104105
| `on_failure` | enum | no | behavior on failure: `retry` / `continue` / `abort` (default: `abort`) |
@@ -336,7 +337,47 @@ Step start (iteration 1)
336337
| `instructions` | string | yes | check instructions |
337338
| `capabilities` | enum[] | yes | checker capabilities (usually `[READ]`) |
338339
| `timeout` | DurationString | no | timeout per check |
339-
| `decision_file` | string | no | optional decision file path (workspace-relative). If set, Roboppi reads it after the check runs and maps `PASS/COMPLETE` -> complete, `FAIL/INCOMPLETE` -> incomplete. |
340+
| `decision_file` | string | no | optional decision file path (workspace-relative). If set, Roboppi reads it after the check runs. Supported formats: structured JSON `{"decision":"complete"|"incomplete","check_id":"...","reasons":[...],"fingerprints":[...]}` (recommended) and legacy `PASS/COMPLETE` / `FAIL/INCOMPLETE` text. When `decision_file` is set, the runner provides `AGENTCORE_COMPLETION_CHECK_ID` in the check environment so the checker can include it as `check_id`. |
341+
342+
### convergence fields (opt-in)
343+
344+
The Convergence Controller helps prevent non-terminating `INCOMPLETE` loops by:
345+
346+
- escalating “stalled” iterations (same `fingerprints` repeating)
347+
- optionally enforcing `allowed_paths` / diff budget
348+
- fail-fast with a diagnosis artifact under `context/<step>/_convergence/`
349+
350+
| Field | Type | Required | Description |
351+
|-----------|-----|------|------|
352+
| `enabled` | boolean | no | enable convergence control (default: false) |
353+
| `stall_threshold` | number | no | repeats before escalating stage (default: 2) |
354+
| `max_stage` | number | no | max stage; reaching it triggers fail-fast unless disabled (default: 3) |
355+
| `fail_on_max_stage` | boolean | no | fail-fast when escalating to `max_stage` (default: true) |
356+
| `stages` | object[] | no | per-stage instruction overlays (`stage`, `append_instructions`) |
357+
| `allowed_paths` | string[] | no | restrict changed files to these patterns (prefix or glob) |
358+
| `ignored_paths` | string[] | no | ignore patterns for scope/budget checks |
359+
| `diff_base_ref` | string | no | git base ref used for diff/scope checks (default: `HEAD`) |
360+
| `diff_base_ref_file` | string | no | file containing git base ref (first line) |
361+
| `max_changed_files` | number | no | diff budget: max changed files (tracked + untracked) |
362+
363+
Example:
364+
365+
```yaml
366+
steps:
367+
implement:
368+
# ...
369+
completion_check:
370+
decision_file: .agentcore-loop/review.verdict
371+
# ...
372+
max_iterations: 10
373+
convergence:
374+
enabled: true
375+
stall_threshold: 2
376+
max_stage: 3
377+
diff_base_ref_file: .agentcore-loop/review.base_ref
378+
allowed_paths: ["src/**", "test/**"]
379+
max_changed_files: 200
380+
```
340381

341382
---
342383

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# BASE_BRANCH 指定依存によるブランチドリフトと誤変更リスク
2+
3+
ステータス: 提案
4+
5+
## 問題
6+
7+
controller loop 実行時に `BASE_BRANCH` の手動指定が前提になっており、指定漏れ・指定ミスがあると意図しないブランチ(例: `main`)を基点に workflow が進むことがある。
8+
9+
その結果:
10+
11+
- 実装対象と無関係な差分や削除が発生して見える
12+
- 実行を停止してやり直す運用が発生する
13+
- 「どのブランチを基点に実行されているか」の認知負荷が高い
14+
15+
## 観測された症状
16+
17+
- 実行開始時の想定ブランチと、実際に bootstrap/implement が触るブランチがずれるケースがある
18+
- `BASE_BRANCH` の再指定や run script の再編集を何度も行う必要がある
19+
- ログだけでは「有効な base branch」が即時に分かりづらい
20+
21+
## 根本原因
22+
23+
1. base branch の解決元が分散している(script/env/workflow 既定値)
24+
2. 実行開始時の「現在ブランチ」がデフォルトとして扱われていない
25+
3. 実行中にブランチずれが起きた場合の fail-fast ガードが弱い
26+
4. branch 解決結果の可観測性(起動ログ/メタデータ出力)が不足している
27+
5. `BASE_BRANCH` の意味(差分基点/作業ブランチ親)が曖昧で、実装と運用の解釈が揺れる
28+
29+
## 用語 / 仕様の前提
30+
31+
この issue では、branch drift を「起動時点で想定していた repo/branch/commit と、実行時点のそれが食い違うこと」と定義する。
32+
33+
- `startup_toplevel`: 起動時点の `git rev-parse --show-toplevel` の結果
34+
- `startup_branch`: 起動時点のブランチ名(detached HEAD の場合は `HEAD` になり得る)
35+
- `startup_head_sha`: 起動時点の `HEAD` の commit SHA
36+
- `effective_base_branch`: base branch の解決結果(起動時に一度だけ確定)
37+
- `effective_base_branch_source`: `cli|env|current` のどれで確定したか
38+
- `effective_base_sha`: 起動時点での `effective_base_branch` の commit SHA(再現性のため固定)
39+
- `expected_work_branch`: 実際に編集/コマンド実行を行うブランチ
40+
- `expected_current_branch`: step 実行直前にいるべきブランチ(work branch 遷移を明示するための補助)
41+
- `protected_branches`: 直接変更を原則禁止するブランチ群(例: `main`, `master`, `release/*`)
42+
- `protected_branches_source`: `default|env|cli` のどれで `protected_branches` が確定したか
43+
- `allow_protected_branch`: `protected_branches` ガードを一時的に無効化する明示 override(危険操作のため opt-in)
44+
45+
また `BASE_BRANCH` は、少なくとも以下 2 つの責務を持ち得るため、実装時に明示する。
46+
47+
1. `create_branch=true` の場合の「新規作成ブランチの親」
48+
2. 差分表示/比較/安全ガードの「基点」
49+
50+
(将来的に責務分離が必要になった場合は、別パラメータ化を検討するが本 issue のスコープ外とする。)
51+
52+
## 目標
53+
54+
1. デフォルト動作を安全側に寄せる(起動時の現在ブランチを基点にする)
55+
2. 明示 override(`BASE_BRANCH`)は維持する
56+
3. 実行中のブランチドリフトを早期検知して停止する
57+
4. 有効な branch 設定をログと成果物で追跡可能にする
58+
5. 解決結果の「由来」と「基点 commit(SHA)」も残し、後追いで一意に再現できる
59+
60+
## 解決方針
61+
62+
## 1) BASE_BRANCH の解決順序を明確化
63+
64+
解決優先順位を固定する。
65+
66+
1. 明示指定(CLI オプションまたは `BASE_BRANCH`)
67+
2. 未指定時は `git rev-parse --abbrev-ref HEAD`(起動時の現在ブランチ)
68+
69+
追加で以下を必須にする。
70+
71+
- 解決結果とともに `effective_base_branch_source` を出力/永続化する(`cli|env|current`)
72+
- 起動時点の `effective_base_sha` を取得して context に保存する(branch 名だけに依存しない)
73+
- detached HEAD の場合は、未指定時の `current` 解決を禁止して fail-fast する(通常ブランチ checkout を要求)
74+
- `BASE_BRANCH` が明示指定され、かつ `startup_branch` と異なる場合は warning を出す(「差分基点は X、作業開始ブランチは Y」)
75+
76+
## 2) Branch Lock を導入
77+
78+
- 実行開始時に `effective_base_branch` を確定し context に保存する
79+
- bootstrap / implement など各 step 前に現在ブランチを検証する
80+
- 不一致時は即 `FAILED`(理由と復旧手順付き)
81+
82+
検証対象を branch 名だけに限定しない。
83+
84+
- 起動時に `startup_toplevel` / `startup_branch` / `startup_head_sha` を採取し保存する
85+
- step 実行前フックで `startup_toplevel` と一致する repo 上で走っているかを検証する(worktree 取り違い検知)
86+
- `expected_work_branch`(もしくは step ごとの `expected_current_branch`)を context に持ち、step 前に一致を検証する
87+
- `create_branch=true` で正当にブランチ遷移する場合は、遷移点で `expected_*` を更新し、許容される遷移を明示する
88+
89+
## 3) ブランチ作成ポリシーを明示
90+
91+
- `create_branch` の既定値と挙動を workflow ごとに明記する
92+
- `create_branch=false` の場合、既存ブランチ上でのみ実行
93+
- `create_branch=true` の場合、作成先・親ブランチを起動時に明示ログ出力
94+
95+
安全ガードを追加する。
96+
97+
- `create_branch=false` かつ `expected_work_branch` が `protected_branches` に一致する場合はデフォルトで停止する(明示 override がある場合のみ許可)
98+
- `create_branch=true` の場合、親ブランチ名だけでなく `effective_base_sha` から作成したことをログ/成果物で保証する
99+
100+
`protected_branches` の既定と override 手段をここで固定する。
101+
102+
- 既定値: `protected_branches = ["main", "master", "release/*"]`
103+
- 目的: main/master 直変更事故を防ぎつつ、release 系ブランチの誤変更も抑止する
104+
- 判定対象: `effective_base_branch` ではなく **`expected_work_branch`**(実際に変更を加えるブランチ)
105+
- パターン仕様: `*` を含む場合は glob として扱う(例: `release/*`)。それ以外は完全一致。
106+
- 設定(リスト上書き):
107+
- CLI: `--protected-branches <csv>`(例: `main,master,release/*`)
108+
- env: `AGENTCORE_PROTECTED_BRANCHES=<csv>`
109+
- 優先順位: CLI > env > default
110+
- 一時 override(ガード無効化; 危険操作なので明示が必要):
111+
- CLI: `--allow-protected-branch`
112+
- env: `AGENTCORE_ALLOW_PROTECTED_BRANCH=1`
113+
- override 有効時も、起動ログ/context に `allow_protected_branch: true` を残し、強い warning を出す
114+
115+
## 4) 可観測性の強化
116+
117+
起動直後に最低限以下を出力する。
118+
119+
- `startup_branch`
120+
- `startup_head_sha`
121+
- `startup_toplevel`
122+
- `effective_base_branch`
123+
- `effective_base_branch_source`
124+
- `effective_base_sha`
125+
- `create_branch`
126+
- `expected_work_branch`(必要に応じて)
127+
- `protected_branches` / `protected_branches_source`
128+
- `allow_protected_branch`(必要に応じて)
129+
130+
あわせて `context/_workflow.json` などにも同情報を残し、後追い調査を容易にする。
131+
132+
## 実装計画
133+
134+
1. run 起動時の branch 解決処理を共通化
135+
2. `effective_base_branch` を workflow context に永続化
136+
3. step 実行前の branch 検証フックを追加
137+
4. mismatch 時のエラーメッセージを定型化
138+
5. run script / docs を「現在ブランチ既定」前提に更新
139+
6. `effective_base_branch_source` / `effective_base_sha` / `startup_head_sha` / `startup_toplevel` の schema を追加し、成果物に含める
140+
7. `protected_branches` resolver(既定 + `AGENTCORE_PROTECTED_BRANCHES` + `--protected-branches`)を追加
141+
8. `protected_branches` ガードと明示 override(`AGENTCORE_ALLOW_PROTECTED_BRANCH` / `--allow-protected-branch`)を追加し、ログに理由を残す
142+
143+
## 受け入れ条件
144+
145+
1. `BASE_BRANCH` 未指定で起動した場合、起動時の現在ブランチが自動採用される
146+
2. 実行中に branch が想定とずれたら、実装 step に入る前に停止する
147+
3. 起動ログと context から有効 branch 設定(branch 名/由来/SHA)を一意に確認できる
148+
4. `BASE_BRANCH` 明示指定時は override される(必要なら warning を表示)
149+
5. `create_branch=false` で保護ブランチに対して実行しようとすると、明示 override(`AGENTCORE_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch`)がない限り停止する
150+
6. `AGENTCORE_PROTECTED_BRANCHES` / `--protected-branches` 未指定時、`protected_branches` は既定の `main,master,release/*` で確定し、`protected_branches_source=default` になる
151+
7. `AGENTCORE_PROTECTED_BRANCHES` または `--protected-branches` 指定時、`protected_branches` が上書きされ、`protected_branches_source` が `env|cli` として記録される
152+
8. `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch` 指定時、`allow_protected_branch=true` がログ/context に記録される
153+
154+
## 非目標
155+
156+
- 自動 rebase/merge 戦略の導入
157+
- 既存 workflow 全面刷新
158+
159+
## 既知リスクと緩和
160+
161+
1. Detached HEAD で起動された場合の扱い
162+
- 緩和: 起動時に明示エラーとして停止し、通常ブランチ checkout を要求する
163+
164+
2. 複数 worktree 利用時の誤認識
165+
- 緩和: `git rev-parse --show-toplevel` と branch 情報をセットでログに残す
166+
167+
3. 既存ユーザーの運用差分
168+
- 緩和: 互換モード(明示 `BASE_BRANCH` 優先)を維持しつつ段階移行する
169+
170+
4. 保護ブランチへの直接変更事故
171+
- 緩和: `protected_branches` ガードをデフォルト有効にし、明示 override がない限り fail-fast する
172+
173+
5. `release/*` 既定が運用をブロックする可能性
174+
- 緩和: `AGENTCORE_PROTECTED_BRANCHES` / `--protected-branches` で `main,master` のみに絞れる。緊急時は `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` / `--allow-protected-branch` を明示し warning 付きで通す

0 commit comments

Comments
 (0)