検証で発見した GATE の甘い部分 6 件: #52: Issue=0 で登録拒否されない #53: ブランチ名バリデーションがない #54: HIGH risk で承認なしに assign できる #55: merge 後にロックが解放されない #56: 不正 SHA の exit code が 2 だが 1 が正しい #57: 依存未解決の assign が exit 0 ラベル: dtp, gate-fix, phase-c を作成・付与 autorun/sprint-1-today/FIXES.md に修正計画を記載 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
176 lines
5.5 KiB
Markdown
176 lines
5.5 KiB
Markdown
# Phase C 修正計画 — GATE の甘い部分を全て潰す
|
||
|
||
> 検証で発見した 6 件の不具合を修正する
|
||
|
||
---
|
||
|
||
## 修正 1: GATE 0 — Issue=0 で登録拒否
|
||
|
||
**現状**: `register --title "test"` が issue=0 でも通る
|
||
**期待**: issue > 0 でなければ登録拒否
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/protocol.rs` の register 処理
|
||
```rust
|
||
if task.issue == 0 {
|
||
return Err(ProtocolError::Gate("issue number must be > 0"));
|
||
}
|
||
```
|
||
|
||
**テスト**:
|
||
- [ ] `register --title "test"` → exit 1 (issue 未指定)
|
||
- [ ] `register --issue 0 --title "test"` → exit 1
|
||
- [ ] `register --issue 1 --title "test"` → exit 0
|
||
|
||
---
|
||
|
||
## 修正 2: GATE 5 — ブランチ名バリデーション
|
||
|
||
**現状**: `branch task-1 bad-name` が通る
|
||
**期待**: `feature/issue-N-*` or `fix/issue-N-*` or `hotfix/issue-N-*` のみ許可
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/gate.rs` の branch 検証
|
||
```rust
|
||
pub fn validate_branch_name(name: &str) -> Result<(), GateError> {
|
||
let valid = name.starts_with("feature/issue-")
|
||
|| name.starts_with("fix/issue-")
|
||
|| name.starts_with("hotfix/issue-");
|
||
if !valid {
|
||
return Err(GateError::InvalidBranchName(name.to_string()));
|
||
}
|
||
Ok(())
|
||
}
|
||
```
|
||
|
||
**テスト**:
|
||
- [ ] `branch task-1 bad-name` → exit 1
|
||
- [ ] `branch task-1 feature/issue-1-test` → exit 0
|
||
- [ ] `branch task-1 fix/issue-1-bugfix` → exit 0
|
||
- [ ] `branch task-1 main` → exit 1
|
||
|
||
---
|
||
|
||
## 修正 3: GATE 3 — HIGH risk 承認チェック
|
||
|
||
**現状**: `impact task-1 --risk high` の後、承認なしで assign できる
|
||
**期待**: HIGH/CRITICAL は `--approve` フラグなしで assign 拒否
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/protocol.rs` の assign 処理
|
||
```rust
|
||
if let Some(impact) = &task.impact {
|
||
if matches!(impact.risk_level, RiskLevel::HIGH | RiskLevel::CRITICAL) {
|
||
if task.human_approval.is_none() {
|
||
return Err(ProtocolError::Gate("HIGH/CRITICAL risk requires --approve"));
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/protocol.rs` の impact 処理
|
||
- `--approve` フラグが true なら `human_approval` を記録
|
||
|
||
**テスト**:
|
||
- [ ] `impact task-1 --risk high` → 記録成功
|
||
- [ ] `assign task-1 ...` → exit 1 (承認なし)
|
||
- [ ] `impact task-1 --risk high --approve` → 記録 + 承認
|
||
- [ ] `assign task-1 ...` → exit 0 (承認済み)
|
||
- [ ] `impact task-1 --risk low` → `assign` そのまま OK
|
||
|
||
---
|
||
|
||
## 修正 4: GATE 7 — merge 後のロック自動解放
|
||
|
||
**現状**: merge 後も `locks` にファイルが残る
|
||
**期待**: merge 成功時にロック自動解放 + 後続タスクの依存解除
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/protocol.rs` の merge 処理
|
||
```rust
|
||
// merge 成功後
|
||
self.lock_manager.release_lock(task_id, &snapshot_store)?;
|
||
// 後続タスクの依存チェック
|
||
for dependent_id in &task.dependents {
|
||
// dependent の state が blocked なら pending に戻す
|
||
}
|
||
```
|
||
|
||
**テスト**:
|
||
- [ ] register A → assign A (lock src/a.rs) → merge A → `locks` が空
|
||
- [ ] register A, B(dep=A) → merge A → B が dispatchable に出現
|
||
|
||
---
|
||
|
||
## 修正 5: exit code 分類
|
||
|
||
**現状**: 不正 SHA が exit 2 (input_error)
|
||
**期待**: GATE 拒否は exit 1、入力フォーマットエラーは exit 2
|
||
|
||
**修正箇所**: `crates/miyabi-cli/src/main.rs` の exit code マッピング
|
||
```rust
|
||
match result {
|
||
Ok(_) => ExitCode::from(0),
|
||
Err(ProtocolError::Gate(_)) => ExitCode::from(1), // GATE 拒否
|
||
Err(ProtocolError::Lock(_)) => ExitCode::from(1), // ロック競合
|
||
Err(ProtocolError::DependencyBlocked(_)) => ExitCode::from(1), // 依存ブロック
|
||
Err(_) => ExitCode::from(2), // その他入力エラー
|
||
}
|
||
```
|
||
|
||
**テスト**:
|
||
- [ ] 不正 SHA → exit 1 (GATE 拒否)
|
||
- [ ] ロック競合 → exit 1
|
||
- [ ] 依存ブロック → exit 1
|
||
- [ ] 不明タスク ID → exit 2 (入力エラー)
|
||
|
||
---
|
||
|
||
## 修正 6: 依存ブロック時の exit code
|
||
|
||
**現状**: 依存未解決で assign → exit 0 (成功扱い)
|
||
**期待**: exit 1 (GATE 拒否)
|
||
|
||
**修正箇所**: `crates/miyabi-core/src/protocol.rs` の assign 処理
|
||
- 依存チェックを assign の冒頭で実行
|
||
- 未解決なら `ProtocolError::DependencyBlocked` を返す
|
||
|
||
**テスト**:
|
||
- [ ] register A, B(dep=A) → assign B → exit 1 (A が done じゃない)
|
||
- [ ] merge A → assign B → exit 0
|
||
|
||
---
|
||
|
||
## 実行順序
|
||
|
||
```
|
||
修正 1 (issue=0 拒否) ← 独立、最初にやる
|
||
修正 2 (ブランチ名) ← 独立、並行可
|
||
修正 5 (exit code) ← 独立、並行可
|
||
修正 6 (依存 exit code) ← 修正 5 と同時にやる
|
||
修正 3 (HIGH 承認) ← protocol.rs の変更、修正 1 の後
|
||
修正 4 (ロック解放) ← protocol.rs の変更、修正 3 の後
|
||
```
|
||
|
||
## 工数
|
||
|
||
| 修正 | 変更行数 | テスト行数 |
|
||
|------|---------|-----------|
|
||
| 1 | ~5 | ~10 |
|
||
| 2 | ~10 | ~15 |
|
||
| 3 | ~20 | ~20 |
|
||
| 4 | ~15 | ~20 |
|
||
| 5 | ~10 | ~15 |
|
||
| 6 | ~10 | ~10 |
|
||
| **合計** | **~70** | **~90** |
|
||
|
||
**推定時間: 15〜20分(Codex 1体)**
|
||
|
||
## 承認ゲート
|
||
|
||
全修正完了後:
|
||
- [ ] `cargo test --all` → GREEN
|
||
- [ ] `cargo clippy --all-targets --all-features -- -D warnings` → ゼロ
|
||
- [ ] テスト 1: issue=0 → exit 1
|
||
- [ ] テスト 2: bad-branch → exit 1
|
||
- [ ] テスト 3: HIGH + 承認なし → exit 1
|
||
- [ ] テスト 4: merge 後 locks → 空
|
||
- [ ] テスト 5: 不正 SHA → exit 1
|
||
- [ ] テスト 6: 依存ブロック → exit 1
|
||
- [ ] E2E: register → done の全シーケンス(全 GATE 通過)
|