From 6bd698941cb1497556b2e15225ba0bd2a219ced4 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:02:19 +0900 Subject: [PATCH 1/5] =?UTF-8?q?planner=E3=81=8C=E5=8F=82=E7=85=A7=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=82=92=E7=A2=BA=E5=AE=9F=E3=81=AB=E8=AA=AD=E3=82=80?= =?UTF-8?q?=E3=82=88=E3=81=86=E5=88=B6=E7=B4=84=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit タスク指示書に参照資料が指定されている場合に、plannerが別ファイルで 代用してしまう問題への対策。instruction に参照資料の読み込みを必須の 最初のステップとして追加し、persona に情報の優先順位を明記した。 --- builtins/ja/instructions/plan.md | 16 +++++++++++----- builtins/ja/personas/planner.md | 12 ++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/builtins/ja/instructions/plan.md b/builtins/ja/instructions/plan.md index a1f6ea2b..3827f767 100644 --- a/builtins/ja/instructions/plan.md +++ b/builtins/ja/instructions/plan.md @@ -11,8 +11,14 @@ 小規模タスクの場合は設計セクションを省略してください。 **やること:** -1. タスクの要件を理解する -2. コードを調査して不明点を解決する -3. 影響範囲を特定する -4. ファイル構成・設計パターンを決定する(必要な場合) -5. 実装アプローチを決める +1. **参照資料の読み込み(必須・最初に実行)** + - タスク指示書の「参照資料」セクションに記載されたファイル・ディレクトリを **Read/Glob で実際に開いて内容を確認する** + - ディレクトリが指定されている場合は中身を列挙し、該当ファイルを特定してから読む + - 参照資料が存在しない・見つからない場合はその旨を報告し、推測で代用しない + - **指示書に明記されていない別ファイルを「参照資料の代わり」として使うことは禁止** +2. タスクの要件を理解する + - 参照資料の内容と現在の実装を突き合わせて差分を特定する +3. コードを調査して不明点を解決する +4. 影響範囲を特定する +5. ファイル構成・設計パターンを決定する(必要な場合) +6. 実装アプローチを決める diff --git a/builtins/ja/personas/planner.md b/builtins/ja/personas/planner.md index afecb15f..d6afbcd7 100644 --- a/builtins/ja/personas/planner.md +++ b/builtins/ja/personas/planner.md @@ -25,6 +25,17 @@ ## ドメイン知識 +### 情報の優先順位 + +タスク指示書に「参照資料」が指定されている場合、**そのファイルが唯一のソース・オブ・トゥルース**である。 +類似の情報を含む別ファイルが存在しても、指示書が指定したファイルを優先する。 + +| 優先度 | ソース | +|--------|--------| +| **最優先** | タスク指示書の「参照資料」で指定されたファイル | +| 次点 | 実際のソースコード(現在の実装) | +| 参考 | その他のドキュメント | + ### 情報の裏取り(ファクトチェック) 分析で使用する情報は必ずソース・オブ・トゥルースで裏取りする。 @@ -35,6 +46,7 @@ | 設定値・名前 | 実際の設定ファイル・定義ファイル | | API・コマンド | 実際の実装コード | | データ構造・型 | 型定義ファイル・スキーマ | +| デザイン仕様 | タスク指示書で指定された参照ファイル | ### 構造設計 From 479ee7ec25cd0bf760af55b35406e0a6af0cf797 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:37:07 +0900 Subject: [PATCH 2/5] =?UTF-8?q?provider=E3=81=94=E3=81=A8=E3=81=AB?= =?UTF-8?q?=E9=80=9A=E4=BF=A1=E3=82=92=E8=A8=B1=E5=8F=AF=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++++ builtins/en/pieces/backend-cqrs.yaml | 7 +++ builtins/en/pieces/backend.yaml | 7 +++ builtins/en/pieces/coding.yaml | 7 +++ builtins/en/pieces/compound-eye.yaml | 7 +++ builtins/en/pieces/default.yaml | 7 +++ builtins/en/pieces/e2e-test.yaml | 7 +++ builtins/en/pieces/expert-cqrs.yaml | 7 +++ builtins/en/pieces/expert.yaml | 7 +++ builtins/en/pieces/frontend.yaml | 7 +++ builtins/en/pieces/magi.yaml | 7 +++ builtins/en/pieces/minimal.yaml | 7 +++ builtins/en/pieces/passthrough.yaml | 7 +++ builtins/en/pieces/research.yaml | 7 +++ builtins/en/pieces/review-fix-minimal.yaml | 7 +++ builtins/en/pieces/review-only.yaml | 7 +++ builtins/en/pieces/structural-reform.yaml | 7 +++ builtins/en/pieces/unit-test.yaml | 7 +++ builtins/ja/pieces/backend-cqrs.yaml | 7 +++ builtins/ja/pieces/backend.yaml | 7 +++ builtins/ja/pieces/coding.yaml | 7 +++ builtins/ja/pieces/compound-eye.yaml | 7 +++ builtins/ja/pieces/default.yaml | 7 +++ builtins/ja/pieces/e2e-test.yaml | 7 +++ builtins/ja/pieces/expert-cqrs.yaml | 7 +++ builtins/ja/pieces/expert.yaml | 7 +++ builtins/ja/pieces/frontend.yaml | 7 +++ builtins/ja/pieces/magi.yaml | 7 +++ builtins/ja/pieces/minimal.yaml | 7 +++ builtins/ja/pieces/passthrough.yaml | 7 +++ builtins/ja/pieces/research.yaml | 7 +++ builtins/ja/pieces/review-fix-minimal.yaml | 7 +++ builtins/ja/pieces/review-only.yaml | 7 +++ builtins/ja/pieces/structural-reform.yaml | 7 +++ builtins/ja/pieces/unit-test.yaml | 7 +++ docs/README.ja.md | 12 +++++ e2e/fixtures/pieces/mock-cycle-detect.yaml | 7 +++ e2e/fixtures/pieces/mock-max-iter.yaml | 7 +++ e2e/fixtures/pieces/mock-no-match.yaml | 7 +++ e2e/fixtures/pieces/mock-single-step.yaml | 7 +++ e2e/fixtures/pieces/mock-slow-multi-step.yaml | 7 +++ e2e/fixtures/pieces/mock-two-step.yaml | 7 +++ e2e/fixtures/pieces/multi-step-parallel.yaml | 7 +++ e2e/fixtures/pieces/report-judge.yaml | 7 +++ e2e/fixtures/pieces/simple.yaml | 7 +++ e2e/fixtures/pieces/structured-output.yaml | 7 +++ src/__tests__/codex-structured-output.test.ts | 22 ++++++++- src/__tests__/models.test.ts | 48 +++++++++++++++++++ src/__tests__/opencode-types.test.ts | 12 +++++ .../provider-options-piece-parser.test.ts | 46 ++++++++++++++++++ src/agents/runner.ts | 1 + src/agents/types.ts | 5 ++ src/core/models/piece-types.ts | 22 +++++++++ src/core/models/schemas.ts | 18 +++++++ src/core/piece/engine/OptionsBuilder.ts | 1 + src/infra/codex/client.ts | 1 + src/infra/codex/types.ts | 2 + src/infra/config/loaders/pieceParser.ts | 41 +++++++++++++++- src/infra/opencode/client.ts | 4 +- src/infra/opencode/types.ts | 38 ++++++++++++--- src/infra/providers/codex.ts | 1 + src/infra/providers/opencode.ts | 1 + src/infra/providers/types.ts | 5 ++ 63 files changed, 589 insertions(+), 11 deletions(-) create mode 100644 src/__tests__/provider-options-piece-parser.test.ts diff --git a/README.md b/README.md index 08382009..21ef03e8 100644 --- a/README.md +++ b/README.md @@ -803,10 +803,22 @@ Special `next` values: `COMPLETE` (success), `ABORT` (failure) | `provider` | - | Override provider for this movement (`claude`, `codex`, or `opencode`) | | `model` | - | Override model for this movement | | `permission_mode` | - | Permission mode: `readonly`, `edit`, `full` (provider-independent) | +| `provider_options` | - | Provider-specific options (e.g. `codex.network_access`, `opencode.network_access`) | | `output_contracts` | - | Output contract definitions for report files | | `quality_gates` | - | AI directives for movement completion requirements | | `mcp_servers` | - | MCP (Model Context Protocol) server configuration (stdio/SSE/HTTP) | +Piece-level defaults can be set with `piece_config.provider_options`, and movement-level `provider_options` overrides them. + +```yaml +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true +``` + ## API Usage Example ```typescript diff --git a/builtins/en/pieces/backend-cqrs.yaml b/builtins/en/pieces/backend-cqrs.yaml index aa4a1129..e5c8d543 100644 --- a/builtins/en/pieces/backend-cqrs.yaml +++ b/builtins/en/pieces/backend-cqrs.yaml @@ -1,5 +1,12 @@ name: backend-cqrs description: CQRS+ES, Security, QA Expert Review +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/en/pieces/backend.yaml b/builtins/en/pieces/backend.yaml index d3d75227..8719355d 100644 --- a/builtins/en/pieces/backend.yaml +++ b/builtins/en/pieces/backend.yaml @@ -1,5 +1,12 @@ name: backend description: Backend, Security, QA Expert Review +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/en/pieces/coding.yaml b/builtins/en/pieces/coding.yaml index ba288923..642614a2 100644 --- a/builtins/en/pieces/coding.yaml +++ b/builtins/en/pieces/coding.yaml @@ -1,5 +1,12 @@ name: coding description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan movements: diff --git a/builtins/en/pieces/compound-eye.yaml b/builtins/en/pieces/compound-eye.yaml index 8c37938a..b1e65b61 100644 --- a/builtins/en/pieces/compound-eye.yaml +++ b/builtins/en/pieces/compound-eye.yaml @@ -1,5 +1,12 @@ name: compound-eye description: Multi-model review - send the same instruction to Claude and Codex simultaneously, synthesize both responses +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: evaluate movements: diff --git a/builtins/en/pieces/default.yaml b/builtins/en/pieces/default.yaml index 05afa4fd..270880f4 100644 --- a/builtins/en/pieces/default.yaml +++ b/builtins/en/pieces/default.yaml @@ -1,5 +1,12 @@ name: default description: Standard development piece with planning and specialized reviews +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan loop_monitors: diff --git a/builtins/en/pieces/e2e-test.yaml b/builtins/en/pieces/e2e-test.yaml index ae582e95..3de5d75e 100644 --- a/builtins/en/pieces/e2e-test.yaml +++ b/builtins/en/pieces/e2e-test.yaml @@ -1,5 +1,12 @@ name: e2e-test description: E2E test focused piece (E2E analysis → E2E implementation → review → fix) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan_test loop_monitors: diff --git a/builtins/en/pieces/expert-cqrs.yaml b/builtins/en/pieces/expert-cqrs.yaml index 041f5d96..bced2041 100644 --- a/builtins/en/pieces/expert-cqrs.yaml +++ b/builtins/en/pieces/expert-cqrs.yaml @@ -1,5 +1,12 @@ name: expert-cqrs description: CQRS+ES, Frontend, Security, QA Expert Review +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/en/pieces/expert.yaml b/builtins/en/pieces/expert.yaml index 65a006ee..0ea5818d 100644 --- a/builtins/en/pieces/expert.yaml +++ b/builtins/en/pieces/expert.yaml @@ -1,5 +1,12 @@ name: expert description: Architecture, Frontend, Security, QA Expert Review +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/en/pieces/frontend.yaml b/builtins/en/pieces/frontend.yaml index 31e3eb68..077c6c8e 100644 --- a/builtins/en/pieces/frontend.yaml +++ b/builtins/en/pieces/frontend.yaml @@ -1,5 +1,12 @@ name: frontend description: Frontend, Security, QA Expert Review +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/en/pieces/magi.yaml b/builtins/en/pieces/magi.yaml index 76631ed9..7bb7d800 100644 --- a/builtins/en/pieces/magi.yaml +++ b/builtins/en/pieces/magi.yaml @@ -1,5 +1,12 @@ name: magi description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 5 initial_movement: melchior movements: diff --git a/builtins/en/pieces/minimal.yaml b/builtins/en/pieces/minimal.yaml index 344e95d4..0512ef32 100644 --- a/builtins/en/pieces/minimal.yaml +++ b/builtins/en/pieces/minimal.yaml @@ -1,5 +1,12 @@ name: minimal description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: implement movements: diff --git a/builtins/en/pieces/passthrough.yaml b/builtins/en/pieces/passthrough.yaml index f4fbae57..2d730b75 100644 --- a/builtins/en/pieces/passthrough.yaml +++ b/builtins/en/pieces/passthrough.yaml @@ -1,5 +1,12 @@ name: passthrough description: Single-agent thin wrapper. Pass task directly to coder as-is. +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: execute movements: diff --git a/builtins/en/pieces/research.yaml b/builtins/en/pieces/research.yaml index 75ec5c00..0d9af43c 100644 --- a/builtins/en/pieces/research.yaml +++ b/builtins/en/pieces/research.yaml @@ -1,5 +1,12 @@ name: research description: Research piece - autonomously executes research without asking questions +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: plan movements: diff --git a/builtins/en/pieces/review-fix-minimal.yaml b/builtins/en/pieces/review-fix-minimal.yaml index 26051de5..5c3a9363 100644 --- a/builtins/en/pieces/review-fix-minimal.yaml +++ b/builtins/en/pieces/review-fix-minimal.yaml @@ -1,5 +1,12 @@ name: review-fix-minimal description: Review and fix piece for existing code (starts with review, no implementation) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: reviewers movements: diff --git a/builtins/en/pieces/review-only.yaml b/builtins/en/pieces/review-only.yaml index da4fed89..a665b86d 100644 --- a/builtins/en/pieces/review-only.yaml +++ b/builtins/en/pieces/review-only.yaml @@ -1,5 +1,12 @@ name: review-only description: Review-only piece - reviews code without making edits +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: plan movements: diff --git a/builtins/en/pieces/structural-reform.yaml b/builtins/en/pieces/structural-reform.yaml index 8a268f4f..11d57bae 100644 --- a/builtins/en/pieces/structural-reform.yaml +++ b/builtins/en/pieces/structural-reform.yaml @@ -1,5 +1,12 @@ name: structural-reform description: Full project review and structural reform - iterative codebase restructuring with staged file splits +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 50 initial_movement: review loop_monitors: diff --git a/builtins/en/pieces/unit-test.yaml b/builtins/en/pieces/unit-test.yaml index 819e8b42..b1037ee3 100644 --- a/builtins/en/pieces/unit-test.yaml +++ b/builtins/en/pieces/unit-test.yaml @@ -1,5 +1,12 @@ name: unit-test description: Unit test focused piece (test analysis → test implementation → review → fix) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan_test loop_monitors: diff --git a/builtins/ja/pieces/backend-cqrs.yaml b/builtins/ja/pieces/backend-cqrs.yaml index efd269b7..bf6b7ea1 100644 --- a/builtins/ja/pieces/backend-cqrs.yaml +++ b/builtins/ja/pieces/backend-cqrs.yaml @@ -1,5 +1,12 @@ name: backend-cqrs description: CQRS+ES・セキュリティ・QA専門家レビュー +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/ja/pieces/backend.yaml b/builtins/ja/pieces/backend.yaml index 1ac6ea67..fa7a3958 100644 --- a/builtins/ja/pieces/backend.yaml +++ b/builtins/ja/pieces/backend.yaml @@ -1,5 +1,12 @@ name: backend description: バックエンド・セキュリティ・QA専門家レビュー +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/ja/pieces/coding.yaml b/builtins/ja/pieces/coding.yaml index 990ff1b2..4a6ead8b 100644 --- a/builtins/ja/pieces/coding.yaml +++ b/builtins/ja/pieces/coding.yaml @@ -1,5 +1,12 @@ name: coding description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan movements: diff --git a/builtins/ja/pieces/compound-eye.yaml b/builtins/ja/pieces/compound-eye.yaml index 8a94f414..fd0f3002 100644 --- a/builtins/ja/pieces/compound-eye.yaml +++ b/builtins/ja/pieces/compound-eye.yaml @@ -1,5 +1,12 @@ name: compound-eye description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: evaluate diff --git a/builtins/ja/pieces/default.yaml b/builtins/ja/pieces/default.yaml index 258cdb0f..d281dc38 100644 --- a/builtins/ja/pieces/default.yaml +++ b/builtins/ja/pieces/default.yaml @@ -1,5 +1,12 @@ name: default description: Standard development piece with planning and specialized reviews +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan loop_monitors: diff --git a/builtins/ja/pieces/e2e-test.yaml b/builtins/ja/pieces/e2e-test.yaml index 6096e980..11a86737 100644 --- a/builtins/ja/pieces/e2e-test.yaml +++ b/builtins/ja/pieces/e2e-test.yaml @@ -1,5 +1,12 @@ name: e2e-test description: E2Eテスト追加に特化したピース(E2E分析→E2E実装→レビュー→修正) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan_test loop_monitors: diff --git a/builtins/ja/pieces/expert-cqrs.yaml b/builtins/ja/pieces/expert-cqrs.yaml index 8c2a2b67..f24448a1 100644 --- a/builtins/ja/pieces/expert-cqrs.yaml +++ b/builtins/ja/pieces/expert-cqrs.yaml @@ -1,5 +1,12 @@ name: expert-cqrs description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/ja/pieces/expert.yaml b/builtins/ja/pieces/expert.yaml index 9f36c606..8f5e5bf7 100644 --- a/builtins/ja/pieces/expert.yaml +++ b/builtins/ja/pieces/expert.yaml @@ -1,5 +1,12 @@ name: expert description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/ja/pieces/frontend.yaml b/builtins/ja/pieces/frontend.yaml index 6bcbcb29..19aa1f1b 100644 --- a/builtins/ja/pieces/frontend.yaml +++ b/builtins/ja/pieces/frontend.yaml @@ -1,5 +1,12 @@ name: frontend description: フロントエンド・セキュリティ・QA専門家レビュー +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 30 initial_movement: plan movements: diff --git a/builtins/ja/pieces/magi.yaml b/builtins/ja/pieces/magi.yaml index 83614cb1..f722c3d4 100644 --- a/builtins/ja/pieces/magi.yaml +++ b/builtins/ja/pieces/magi.yaml @@ -1,5 +1,12 @@ name: magi description: MAGI合議システム - 3つの観点から分析し多数決で判定 +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 5 initial_movement: melchior movements: diff --git a/builtins/ja/pieces/minimal.yaml b/builtins/ja/pieces/minimal.yaml index c190ac9f..ea902003 100644 --- a/builtins/ja/pieces/minimal.yaml +++ b/builtins/ja/pieces/minimal.yaml @@ -1,5 +1,12 @@ name: minimal description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: implement movements: diff --git a/builtins/ja/pieces/passthrough.yaml b/builtins/ja/pieces/passthrough.yaml index ac4cb8b1..b4d11eac 100644 --- a/builtins/ja/pieces/passthrough.yaml +++ b/builtins/ja/pieces/passthrough.yaml @@ -1,5 +1,12 @@ name: passthrough description: Single-agent thin wrapper. Pass task directly to coder as-is. +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: execute movements: diff --git a/builtins/ja/pieces/research.yaml b/builtins/ja/pieces/research.yaml index 78f25216..708601c5 100644 --- a/builtins/ja/pieces/research.yaml +++ b/builtins/ja/pieces/research.yaml @@ -1,5 +1,12 @@ name: research description: 調査ピース - 質問せずに自律的に調査を実行 +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: plan movements: diff --git a/builtins/ja/pieces/review-fix-minimal.yaml b/builtins/ja/pieces/review-fix-minimal.yaml index f9f33003..fa12b335 100644 --- a/builtins/ja/pieces/review-fix-minimal.yaml +++ b/builtins/ja/pieces/review-fix-minimal.yaml @@ -1,5 +1,12 @@ name: review-fix-minimal description: 既存コードのレビューと修正ピース(レビュー開始、実装なし) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: reviewers movements: diff --git a/builtins/ja/pieces/review-only.yaml b/builtins/ja/pieces/review-only.yaml index 6a8a0d93..c9eeb06f 100644 --- a/builtins/ja/pieces/review-only.yaml +++ b/builtins/ja/pieces/review-only.yaml @@ -1,5 +1,12 @@ name: review-only description: レビュー専用ピース - コードをレビューするだけで編集は行わない +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 initial_movement: plan movements: diff --git a/builtins/ja/pieces/structural-reform.yaml b/builtins/ja/pieces/structural-reform.yaml index 3cb04196..7903163c 100644 --- a/builtins/ja/pieces/structural-reform.yaml +++ b/builtins/ja/pieces/structural-reform.yaml @@ -1,5 +1,12 @@ name: structural-reform description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築 +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 50 initial_movement: review loop_monitors: diff --git a/builtins/ja/pieces/unit-test.yaml b/builtins/ja/pieces/unit-test.yaml index 3c5e94c6..89f408c6 100644 --- a/builtins/ja/pieces/unit-test.yaml +++ b/builtins/ja/pieces/unit-test.yaml @@ -1,5 +1,12 @@ name: unit-test description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正) +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: plan_test loop_monitors: diff --git a/docs/README.ja.md b/docs/README.ja.md index c67bb765..4ce8796f 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -803,10 +803,22 @@ rules: | `provider` | - | このムーブメントのプロバイダーを上書き(`claude`、`codex`、または`opencode`) | | `model` | - | このムーブメントのモデルを上書き | | `permission_mode` | - | パーミッションモード: `readonly`、`edit`、`full`(プロバイダー非依存) | +| `provider_options` | - | プロバイダー固有オプション(例: `codex.network_access`、`opencode.network_access`) | | `output_contracts` | - | レポートファイルの出力契約定義 | | `quality_gates` | - | ムーブメント完了要件のAIディレクティブ | | `mcp_servers` | - | MCP(Model Context Protocol)サーバー設定(stdio/SSE/HTTP) | +ピース全体のデフォルトは `piece_config.provider_options` で設定でき、ムーブメント側 `provider_options` で上書きできます。 + +```yaml +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true +``` + ## API使用例 ```typescript diff --git a/e2e/fixtures/pieces/mock-cycle-detect.yaml b/e2e/fixtures/pieces/mock-cycle-detect.yaml index c98ea488..addd846d 100644 --- a/e2e/fixtures/pieces/mock-cycle-detect.yaml +++ b/e2e/fixtures/pieces/mock-cycle-detect.yaml @@ -1,5 +1,12 @@ name: e2e-cycle-detect description: Piece with loop_monitors for cycle detection E2E testing +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 initial_movement: review diff --git a/e2e/fixtures/pieces/mock-max-iter.yaml b/e2e/fixtures/pieces/mock-max-iter.yaml index 1912fde9..632bbf90 100644 --- a/e2e/fixtures/pieces/mock-max-iter.yaml +++ b/e2e/fixtures/pieces/mock-max-iter.yaml @@ -1,5 +1,12 @@ name: e2e-mock-max-iter description: Piece with max_movements=2 that loops between two steps +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 2 diff --git a/e2e/fixtures/pieces/mock-no-match.yaml b/e2e/fixtures/pieces/mock-no-match.yaml index 493bd88b..142ee36b 100644 --- a/e2e/fixtures/pieces/mock-no-match.yaml +++ b/e2e/fixtures/pieces/mock-no-match.yaml @@ -1,5 +1,12 @@ name: e2e-mock-no-match description: Piece with a strict rule condition that will not match mock output +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 3 diff --git a/e2e/fixtures/pieces/mock-single-step.yaml b/e2e/fixtures/pieces/mock-single-step.yaml index 087869c7..b2703eed 100644 --- a/e2e/fixtures/pieces/mock-single-step.yaml +++ b/e2e/fixtures/pieces/mock-single-step.yaml @@ -1,5 +1,12 @@ name: e2e-mock-single description: Minimal mock-only piece for CLI E2E +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 3 diff --git a/e2e/fixtures/pieces/mock-slow-multi-step.yaml b/e2e/fixtures/pieces/mock-slow-multi-step.yaml index 8da8f3fc..09218268 100644 --- a/e2e/fixtures/pieces/mock-slow-multi-step.yaml +++ b/e2e/fixtures/pieces/mock-slow-multi-step.yaml @@ -1,5 +1,12 @@ name: e2e-mock-slow-multi-step description: Multi-step mock piece to keep tasks in-flight long enough for SIGINT E2E +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 20 diff --git a/e2e/fixtures/pieces/mock-two-step.yaml b/e2e/fixtures/pieces/mock-two-step.yaml index 8090cf46..73e565e4 100644 --- a/e2e/fixtures/pieces/mock-two-step.yaml +++ b/e2e/fixtures/pieces/mock-two-step.yaml @@ -1,5 +1,12 @@ name: e2e-mock-two-step description: Two-step sequential piece for E2E testing +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 5 diff --git a/e2e/fixtures/pieces/multi-step-parallel.yaml b/e2e/fixtures/pieces/multi-step-parallel.yaml index 2b25b3e1..236b354e 100644 --- a/e2e/fixtures/pieces/multi-step-parallel.yaml +++ b/e2e/fixtures/pieces/multi-step-parallel.yaml @@ -1,5 +1,12 @@ name: e2e-multi-step-parallel description: Multi-step piece with parallel sub-movements for E2E testing +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 10 diff --git a/e2e/fixtures/pieces/report-judge.yaml b/e2e/fixtures/pieces/report-judge.yaml index d8556098..b0a18b85 100644 --- a/e2e/fixtures/pieces/report-judge.yaml +++ b/e2e/fixtures/pieces/report-judge.yaml @@ -1,5 +1,12 @@ name: e2e-report-judge description: E2E piece that exercises report + judge phases +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 3 diff --git a/e2e/fixtures/pieces/simple.yaml b/e2e/fixtures/pieces/simple.yaml index c8f813e0..1afd38f0 100644 --- a/e2e/fixtures/pieces/simple.yaml +++ b/e2e/fixtures/pieces/simple.yaml @@ -1,5 +1,12 @@ name: e2e-simple description: Minimal E2E test piece +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 5 diff --git a/e2e/fixtures/pieces/structured-output.yaml b/e2e/fixtures/pieces/structured-output.yaml index fcb7280b..d4df485d 100644 --- a/e2e/fixtures/pieces/structured-output.yaml +++ b/e2e/fixtures/pieces/structured-output.yaml @@ -1,5 +1,12 @@ name: e2e-structured-output description: E2E piece to verify structured output rule matching +piece_config: + provider_options: + codex: + network_access: true + opencode: + network_access: true + max_movements: 5 diff --git a/src/__tests__/codex-structured-output.test.ts b/src/__tests__/codex-structured-output.test.ts index a262b146..a7a6a28a 100644 --- a/src/__tests__/codex-structured-output.test.ts +++ b/src/__tests__/codex-structured-output.test.ts @@ -14,11 +14,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; // ===== Codex SDK mock ===== let mockEvents: Array> = []; +let lastThreadOptions: Record | undefined; vi.mock('@openai/codex-sdk', () => { return { Codex: class MockCodex { - async startThread() { + async startThread(options?: Record) { + lastThreadOptions = options; return { id: 'thread-mock', runStreamed: async () => ({ @@ -44,6 +46,7 @@ describe('CodexClient — structuredOutput 抽出', () => { beforeEach(() => { vi.clearAllMocks(); mockEvents = []; + lastThreadOptions = undefined; }); it('outputSchema 指定時に agent_message の JSON テキストを structuredOutput として返す', async () => { @@ -149,4 +152,21 @@ describe('CodexClient — structuredOutput 抽出', () => { expect(result.structuredOutput).toEqual({ step: 1 }); }); + + it('provider_options.codex.network_access が ThreadOptions に反映される', async () => { + mockEvents = [ + { type: 'thread.started', thread_id: 'thread-1' }, + { type: 'turn.completed', usage: { input_tokens: 0, cached_input_tokens: 0, output_tokens: 0 } }, + ]; + + const client = new CodexClient(); + await client.call('coder', 'prompt', { + cwd: '/tmp', + networkAccess: true, + }); + + expect(lastThreadOptions).toMatchObject({ + networkAccessEnabled: true, + }); + }); }); diff --git a/src/__tests__/models.test.ts b/src/__tests__/models.test.ts index 0709d022..1d8b4238 100644 --- a/src/__tests__/models.test.ts +++ b/src/__tests__/models.test.ts @@ -105,6 +105,54 @@ describe('PieceConfigRawSchema', () => { expect(result.movements![0]?.permission_mode).toBe('edit'); }); + it('should parse movement with provider_options', () => { + const config = { + name: 'test-piece', + movements: [ + { + name: 'implement', + provider: 'codex', + provider_options: { + codex: { network_access: true }, + opencode: { network_access: false }, + }, + instruction: '{task}', + }, + ], + }; + + const result = PieceConfigRawSchema.parse(config); + expect(result.movements![0]?.provider_options).toEqual({ + codex: { network_access: true }, + opencode: { network_access: false }, + }); + }); + + it('should parse piece-level piece_config.provider_options', () => { + const config = { + name: 'test-piece', + piece_config: { + provider_options: { + codex: { network_access: true }, + }, + }, + movements: [ + { + name: 'implement', + provider: 'codex', + instruction: '{task}', + }, + ], + }; + + const result = PieceConfigRawSchema.parse(config); + expect(result.piece_config).toEqual({ + provider_options: { + codex: { network_access: true }, + }, + }); + }); + it('should allow omitting permission_mode', () => { const config = { name: 'test-piece', diff --git a/src/__tests__/opencode-types.test.ts b/src/__tests__/opencode-types.test.ts index f661940b..bf3f492e 100644 --- a/src/__tests__/opencode-types.test.ts +++ b/src/__tests__/opencode-types.test.ts @@ -84,4 +84,16 @@ describe('OpenCode permissions', () => { action: 'deny', }); }); + + it('should force allow web tools when networkAccess=true', () => { + const ruleset = buildOpenCodePermissionRuleset('readonly', true); + expect(ruleset.find((rule) => rule.permission === 'webfetch')?.action).toBe('allow'); + expect(ruleset.find((rule) => rule.permission === 'websearch')?.action).toBe('allow'); + }); + + it('should force deny web tools when networkAccess=false', () => { + const ruleset = buildOpenCodePermissionRuleset('full', false); + expect(ruleset.find((rule) => rule.permission === 'webfetch')?.action).toBe('deny'); + expect(ruleset.find((rule) => rule.permission === 'websearch')?.action).toBe('deny'); + }); }); diff --git a/src/__tests__/provider-options-piece-parser.test.ts b/src/__tests__/provider-options-piece-parser.test.ts new file mode 100644 index 00000000..45d7adc9 --- /dev/null +++ b/src/__tests__/provider-options-piece-parser.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; +import { normalizePieceConfig } from '../infra/config/loaders/pieceParser.js'; + +describe('normalizePieceConfig provider_options', () => { + it('piece-level global を movement に継承し、movement 側で上書きできる', () => { + const raw = { + name: 'provider-options', + piece_config: { + provider_options: { + codex: { network_access: true }, + opencode: { network_access: false }, + }, + }, + movements: [ + { + name: 'codex-default', + provider: 'codex', + instruction: '{task}', + }, + { + name: 'codex-override', + provider: 'codex', + provider_options: { + codex: { network_access: false }, + }, + instruction: '{task}', + }, + ], + }; + + const config = normalizePieceConfig(raw, process.cwd()); + + expect(config.providerOptions).toEqual({ + codex: { networkAccess: true }, + opencode: { networkAccess: false }, + }); + expect(config.movements[0]?.providerOptions).toEqual({ + codex: { networkAccess: true }, + opencode: { networkAccess: false }, + }); + expect(config.movements[1]?.providerOptions).toEqual({ + codex: { networkAccess: false }, + opencode: { networkAccess: false }, + }); + }); +}); diff --git a/src/agents/runner.ts b/src/agents/runner.ts index 5b126d0b..80952cd9 100644 --- a/src/agents/runner.ts +++ b/src/agents/runner.ts @@ -107,6 +107,7 @@ export class AgentRunner { maxTurns: options.maxTurns, model: AgentRunner.resolveModel(resolvedProvider, options, agentConfig), permissionMode: options.permissionMode, + providerOptions: options.providerOptions, onStream: options.onStream, onPermissionRequest: options.onPermissionRequest, onAskUserQuestion: options.onAskUserQuestion, diff --git a/src/agents/types.ts b/src/agents/types.ts index dad5d175..da7cea03 100644 --- a/src/agents/types.ts +++ b/src/agents/types.ts @@ -24,6 +24,11 @@ export interface RunAgentOptions { maxTurns?: number; /** Permission mode for tool execution (from piece step) */ permissionMode?: PermissionMode; + /** Provider-specific movement options */ + providerOptions?: { + codex?: { networkAccess?: boolean }; + opencode?: { networkAccess?: boolean }; + }; onStream?: StreamCallback; onPermissionRequest?: PermissionHandler; onAskUserQuestion?: AskUserQuestionHandler; diff --git a/src/core/models/piece-types.ts b/src/core/models/piece-types.ts index 49deeec5..5a02088b 100644 --- a/src/core/models/piece-types.ts +++ b/src/core/models/piece-types.ts @@ -80,6 +80,24 @@ export interface McpHttpServerConfig { /** MCP server configuration (union of all YAML-configurable transports) */ export type McpServerConfig = McpStdioServerConfig | McpSseServerConfig | McpHttpServerConfig; +/** Codex provider-specific options */ +export interface CodexProviderOptions { + /** Enable network access for Codex workspace-write sandbox */ + networkAccess?: boolean; +} + +/** OpenCode provider-specific options */ +export interface OpenCodeProviderOptions { + /** Enable/disable network tools (webfetch/websearch) */ + networkAccess?: boolean; +} + +/** Provider-specific movement options */ +export interface MovementProviderOptions { + codex?: CodexProviderOptions; + opencode?: OpenCodeProviderOptions; +} + /** Single movement in a piece */ export interface PieceMovement { name: string; @@ -103,6 +121,8 @@ export interface PieceMovement { model?: string; /** Permission mode for tool execution in this movement */ permissionMode?: PermissionMode; + /** Provider-specific movement options */ + providerOptions?: MovementProviderOptions; /** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */ edit?: boolean; instructionTemplate: string; @@ -201,6 +221,8 @@ export interface LoopMonitorConfig { export interface PieceConfig { name: string; description?: string; + /** Piece-level default provider options (used as movement defaults) */ + providerOptions?: MovementProviderOptions; /** Persona definitions — map of name to file path or inline content (raw, not content-resolved) */ personas?: Record; /** Resolved policy definitions — map of name to file content (resolved at parse time) */ diff --git a/src/core/models/schemas.ts b/src/core/models/schemas.ts index 0c0f812e..82a3dde2 100644 --- a/src/core/models/schemas.ts +++ b/src/core/models/schemas.ts @@ -59,6 +59,20 @@ export const StatusSchema = z.enum([ /** Permission mode schema for tool execution */ export const PermissionModeSchema = z.enum(['readonly', 'edit', 'full']); +/** Provider-specific movement options schema */ +export const MovementProviderOptionsSchema = z.object({ + codex: z.object({ + network_access: z.boolean().optional(), + }).optional(), + opencode: z.object({ + network_access: z.boolean().optional(), + }).optional(), +}).optional(); + +/** Piece-level provider options schema */ +export const PieceProviderOptionsSchema = z.object({ + provider_options: MovementProviderOptionsSchema, +}).optional(); /** * Output contract item schema (new structured format). @@ -204,6 +218,7 @@ export const ParallelSubMovementRawSchema = z.object({ provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(), model: z.string().optional(), permission_mode: PermissionModeSchema.optional(), + provider_options: MovementProviderOptionsSchema, edit: z.boolean().optional(), instruction: z.string().optional(), instruction_template: z.string().optional(), @@ -235,6 +250,8 @@ export const PieceMovementRawSchema = z.object({ model: z.string().optional(), /** Permission mode for tool execution in this movement */ permission_mode: PermissionModeSchema.optional(), + /** Provider-specific movement options */ + provider_options: MovementProviderOptionsSchema, /** Whether this movement is allowed to edit project files */ edit: z.boolean().optional(), instruction: z.string().optional(), @@ -295,6 +312,7 @@ export const InteractiveModeSchema = z.enum(INTERACTIVE_MODES); export const PieceConfigRawSchema = z.object({ name: z.string().min(1), description: z.string().optional(), + piece_config: PieceProviderOptionsSchema, /** Piece-level persona definitions — map of name to .md file path or inline content */ personas: z.record(z.string(), z.string()).optional(), /** Piece-level policy definitions — map of name to .md file path or inline content */ diff --git a/src/core/piece/engine/OptionsBuilder.ts b/src/core/piece/engine/OptionsBuilder.ts index 1b11ddc3..535cacff 100644 --- a/src/core/piece/engine/OptionsBuilder.ts +++ b/src/core/piece/engine/OptionsBuilder.ts @@ -38,6 +38,7 @@ export class OptionsBuilder { provider: resolved.provider, model: resolved.model, permissionMode: step.permissionMode, + providerOptions: step.providerOptions, language: this.getLanguage(), onStream: this.engineOptions.onStream, onPermissionRequest: this.engineOptions.onPermissionRequest, diff --git a/src/infra/codex/client.ts b/src/infra/codex/client.ts index 0a0e0458..ceb49c59 100644 --- a/src/infra/codex/client.ts +++ b/src/infra/codex/client.ts @@ -95,6 +95,7 @@ export class CodexClient { ...(options.model ? { model: options.model } : {}), workingDirectory: options.cwd, sandboxMode, + ...(options.networkAccess === undefined ? {} : { networkAccessEnabled: options.networkAccess }), }; let threadId = options.sessionId; diff --git a/src/infra/codex/types.ts b/src/infra/codex/types.ts index 097eed66..102c79e1 100644 --- a/src/infra/codex/types.ts +++ b/src/infra/codex/types.ts @@ -27,6 +27,8 @@ export interface CodexCallOptions { systemPrompt?: string; /** Permission mode for sandbox configuration */ permissionMode?: PermissionMode; + /** Enable network access for workspace-write sandbox */ + networkAccess?: boolean; /** Enable streaming mode with callback (best-effort) */ onStream?: StreamCallback; /** OpenAI API key (bypasses CLI auth) */ diff --git a/src/infra/config/loaders/pieceParser.ts b/src/infra/config/loaders/pieceParser.ts index 16168004..83df5dcd 100644 --- a/src/infra/config/loaders/pieceParser.ts +++ b/src/infra/config/loaders/pieceParser.ts @@ -24,6 +24,36 @@ import { type RawStep = z.output; +function normalizeProviderOptions( + raw: RawStep['provider_options'], +): PieceMovement['providerOptions'] { + if (!raw) return undefined; + + const codex = raw.codex?.network_access === undefined + ? undefined + : { networkAccess: raw.codex.network_access }; + const opencode = raw.opencode?.network_access === undefined + ? undefined + : { networkAccess: raw.opencode.network_access }; + + if (!codex && !opencode) return undefined; + return { ...(codex ? { codex } : {}), ...(opencode ? { opencode } : {}) }; +} + +function mergeProviderOptions( + base: PieceMovement['providerOptions'], + override: PieceMovement['providerOptions'], +): PieceMovement['providerOptions'] { + const codexNetworkAccess = override?.codex?.networkAccess ?? base?.codex?.networkAccess; + const opencodeNetworkAccess = override?.opencode?.networkAccess ?? base?.opencode?.networkAccess; + + const codex = codexNetworkAccess === undefined ? undefined : { networkAccess: codexNetworkAccess }; + const opencode = opencodeNetworkAccess === undefined ? undefined : { networkAccess: opencodeNetworkAccess }; + + if (!codex && !opencode) return undefined; + return { ...(codex ? { codex } : {}), ...(opencode ? { opencode } : {}) }; +} + /** Check if a raw output contract item is the object form (has 'name' property). */ function isOutputContractItem(raw: unknown): raw is { name: string; order?: string; format?: string } { return typeof raw === 'object' && raw !== null && !Array.isArray(raw) && 'name' in raw; @@ -209,6 +239,7 @@ function normalizeStepFromRaw( step: RawStep, pieceDir: string, sections: PieceSections, + inheritedProviderOptions?: PieceMovement['providerOptions'], context?: FacetResolutionContext, ): PieceMovement { const rules: PieceRule[] | undefined = step.rules?.map(normalizeRule); @@ -241,6 +272,7 @@ function normalizeStepFromRaw( provider: step.provider, model: step.model, permissionMode: step.permission_mode, + providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizeProviderOptions(step.provider_options)), edit: step.edit, instructionTemplate: (step.instruction_template ? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) @@ -254,7 +286,9 @@ function normalizeStepFromRaw( }; if (step.parallel && step.parallel.length > 0) { - result.parallel = step.parallel.map((sub: RawStep) => normalizeStepFromRaw(sub, pieceDir, sections, context)); + result.parallel = step.parallel.map((sub: RawStep) => + normalizeStepFromRaw(sub, pieceDir, sections, inheritedProviderOptions, context), + ); } const arpeggioConfig = normalizeArpeggio(step.arpeggio, pieceDir); @@ -327,8 +361,10 @@ export function normalizePieceConfig( resolvedReportFormats, }; + const pieceProviderOptions = normalizeProviderOptions(parsed.piece_config?.provider_options as RawStep['provider_options']); + const movements: PieceMovement[] = parsed.movements.map((step) => - normalizeStepFromRaw(step, pieceDir, sections, context), + normalizeStepFromRaw(step, pieceDir, sections, pieceProviderOptions, context), ); // Schema guarantees movements.min(1) @@ -337,6 +373,7 @@ export function normalizePieceConfig( return { name: parsed.name, description: parsed.description, + providerOptions: pieceProviderOptions, personas: parsed.personas, policies: resolvedPolicies, knowledge: resolvedKnowledge, diff --git a/src/infra/opencode/client.ts b/src/infra/opencode/client.ts index 0b049991..841221d8 100644 --- a/src/infra/opencode/client.ts +++ b/src/infra/opencode/client.ts @@ -311,7 +311,7 @@ export class OpenCodeClient { const parsedModel = parseProviderModel(options.model, 'OpenCode model'); const fullModel = `${parsedModel.providerID}/${parsedModel.modelID}`; const port = await getFreePort(); - const permission = buildOpenCodePermissionConfig(options.permissionMode); + const permission = buildOpenCodePermissionConfig(options.permissionMode, options.networkAccess); const config = { model: fullModel, small_model: fullModel, @@ -332,7 +332,7 @@ export class OpenCodeClient { ? { data: { id: options.sessionId } } : await client.session.create({ directory: options.cwd, - permission: buildOpenCodePermissionRuleset(options.permissionMode), + permission: buildOpenCodePermissionRuleset(options.permissionMode, options.networkAccess), }); const sessionId = sessionResult.data?.id; diff --git a/src/infra/opencode/types.ts b/src/infra/opencode/types.ts index d981247a..e407d6b4 100644 --- a/src/infra/opencode/types.ts +++ b/src/infra/opencode/types.ts @@ -100,14 +100,38 @@ function buildPermissionMap(mode?: PermissionMode): OpenCodePermissionMap { }; } -export function buildOpenCodePermissionConfig(mode?: PermissionMode): OpenCodePermissionAction | Record { - if (mode === 'readonly') return 'deny'; - if (mode === 'full') return 'allow'; - return buildPermissionMap(mode); +function applyNetworkAccessOverride( + map: OpenCodePermissionMap, + networkAccess?: boolean, +): OpenCodePermissionMap { + if (networkAccess === undefined) { + return map; + } + + const action: OpenCodePermissionAction = networkAccess ? 'allow' : 'deny'; + return { + ...map, + webfetch: action, + websearch: action, + }; +} + +export function buildOpenCodePermissionConfig( + mode?: PermissionMode, + networkAccess?: boolean, +): OpenCodePermissionAction | Record { + if (networkAccess === undefined) { + if (mode === 'readonly') return 'deny'; + if (mode === 'full') return 'allow'; + } + return applyNetworkAccessOverride(buildPermissionMap(mode), networkAccess); } -export function buildOpenCodePermissionRuleset(mode?: PermissionMode): Array<{ permission: string; pattern: string; action: OpenCodePermissionAction }> { - const permissionMap = buildPermissionMap(mode); +export function buildOpenCodePermissionRuleset( + mode?: PermissionMode, + networkAccess?: boolean, +): Array<{ permission: string; pattern: string; action: OpenCodePermissionAction }> { + const permissionMap = applyNetworkAccessOverride(buildPermissionMap(mode), networkAccess); return OPEN_CODE_PERMISSION_KEYS.map((permission) => ({ permission, pattern: '**', @@ -165,6 +189,8 @@ export interface OpenCodeCallOptions { allowedTools?: string[]; /** Permission mode for automatic permission handling */ permissionMode?: PermissionMode; + /** Override network access (webfetch/websearch) */ + networkAccess?: boolean; /** Enable streaming mode with callback (best-effort) */ onStream?: StreamCallback; onAskUserQuestion?: AskUserQuestionHandler; diff --git a/src/infra/providers/codex.ts b/src/infra/providers/codex.ts index 47a4de82..c124f94d 100644 --- a/src/infra/providers/codex.ts +++ b/src/infra/providers/codex.ts @@ -31,6 +31,7 @@ function toCodexOptions(options: ProviderCallOptions): CodexCallOptions { sessionId: options.sessionId, model: options.model, permissionMode: options.permissionMode, + networkAccess: options.providerOptions?.codex?.networkAccess, onStream: options.onStream, openaiApiKey: options.openaiApiKey ?? resolveOpenaiApiKey(), outputSchema: options.outputSchema, diff --git a/src/infra/providers/opencode.ts b/src/infra/providers/opencode.ts index 32431584..3c80878a 100644 --- a/src/infra/providers/opencode.ts +++ b/src/infra/providers/opencode.ts @@ -19,6 +19,7 @@ function toOpenCodeOptions(options: ProviderCallOptions): OpenCodeCallOptions { model: options.model, allowedTools: options.allowedTools, permissionMode: options.permissionMode, + networkAccess: options.providerOptions?.opencode?.networkAccess, onStream: options.onStream, onAskUserQuestion: options.onAskUserQuestion, opencodeApiKey: options.opencodeApiKey ?? resolveOpencodeApiKey(), diff --git a/src/infra/providers/types.ts b/src/infra/providers/types.ts index e9214b84..b47c2e82 100644 --- a/src/infra/providers/types.ts +++ b/src/infra/providers/types.ts @@ -30,6 +30,11 @@ export interface ProviderCallOptions { maxTurns?: number; /** Permission mode for tool execution (from piece step) */ permissionMode?: PermissionMode; + /** Provider-specific movement options */ + providerOptions?: { + codex?: { networkAccess?: boolean }; + opencode?: { networkAccess?: boolean }; + }; onStream?: StreamCallback; onPermissionRequest?: PermissionHandler; onAskUserQuestion?: AskUserQuestionHandler; From 3a7259cf0666ef7a7aaa7f2caecb960900b93339 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:33:29 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E3=82=BF=E3=82=B9=E3=82=AF=E6=8C=87?= =?UTF-8?q?=E7=A4=BA=E6=9B=B8=E3=82=B9=E3=82=B3=E3=83=BC=E3=83=97=E5=A4=96?= =?UTF-8?q?=E3=81=AE=E5=89=8A=E9=99=A4=E3=82=92=E9=98=B2=E6=AD=A2=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=AC=E3=83=BC=E3=83=89=E3=83=AC=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 実装者がステータス変更タスクでSaga・エンドポイントを丸ごと削除してしまい、 レビュアー・監督者もそれを承認してしまった問題への対策。 - planner: スコープ規律セクション追加、削除対象を「今回新たに未使用になったコード」に限定 - coder: 指示書に根拠がない大規模削除の報告義務を追加 - supervisor/expert-supervisor: 削除ファイルの指示書照合手順を追加、スコープクリープをREJECT対象に変更 --- builtins/en/personas/coder.md | 1 + builtins/en/personas/expert-supervisor.md | 17 +++++++++++++++++ builtins/en/personas/planner.md | 13 ++++++++++++- builtins/en/personas/supervisor.md | 17 ++++++++++++++++- builtins/ja/personas/coder.md | 1 + builtins/ja/personas/expert-supervisor.md | 17 +++++++++++++++++ builtins/ja/personas/planner.md | 13 ++++++++++++- builtins/ja/personas/supervisor.md | 15 +++++++++++++++ 8 files changed, 91 insertions(+), 3 deletions(-) diff --git a/builtins/en/personas/coder.md b/builtins/en/personas/coder.md index f2226201..0662f293 100644 --- a/builtins/en/personas/coder.md +++ b/builtins/en/personas/coder.md @@ -35,3 +35,4 @@ You are the implementer. Focus on implementation, not design decisions. - Adding backward compatibility or legacy support without being asked → Absolutely prohibited - Leaving replaced code/exports after refactoring → Prohibited (remove unless explicitly told to keep) - Layering workarounds that bypass safety mechanisms on top of a root cause fix → Prohibited +- Deleting existing features or structural changes not in the task order as a "side effect" → Prohibited (report even if included in the plan, when there's no basis in the task order for large-scale deletions) diff --git a/builtins/en/personas/expert-supervisor.md b/builtins/en/personas/expert-supervisor.md index 6f6c7a70..7a9666dd 100644 --- a/builtins/en/personas/expert-supervisor.md +++ b/builtins/en/personas/expert-supervisor.md @@ -50,6 +50,23 @@ Judge from a big-picture perspective to avoid "missing the forest for the trees. | Non-functional Requirements | Are performance, security, etc. met? | | Scope | Is there scope creep beyond requirements? | +### Scope Creep Detection (Deletions are Critical) + +File **deletions** and removal of existing features are the most dangerous form of scope creep. +Additions can be reverted, but restoring deleted flows is difficult. + +**Required steps:** +1. List all deleted files (D) and deleted classes/methods/endpoints from the diff +2. Cross-reference each deletion against the task order to find its justification +3. REJECT any deletion that has no basis in the task order + +**Typical scope creep patterns:** +- A "change statuses" task includes wholesale deletion of Sagas or endpoints +- A "UI fix" task includes structural changes to backend domain models +- A "display change" task rewrites business logic flows + +Even if reviewers approved a deletion as "sound design," REJECT it if it's outside the task order scope. + ### 3. Risk Assessment **Risk Matrix:** diff --git a/builtins/en/personas/planner.md b/builtins/en/personas/planner.md index fb858be7..94549801 100644 --- a/builtins/en/personas/planner.md +++ b/builtins/en/personas/planner.md @@ -86,11 +86,22 @@ Based on investigation and design, determine the implementation direction: - Points to be careful about - Spec constraints +## Scope Discipline + +Only plan work that is explicitly stated in the task order. Do not include implicit "improvements." + +**Deletion criteria:** +- **Code made newly unused by this task's changes** → OK to plan deletion (e.g., renamed old variable) +- **Existing features, flows, endpoints, Sagas, events** → Do NOT delete unless explicitly instructed in the task order + +"Change statuses to 5 values" means "rewrite enum values," NOT "delete flows that seem unnecessary." +Do not over-interpret the task order. Plan only what is written. + ## Design Principles **Backward Compatibility:** - Do not include backward compatibility code unless explicitly instructed -- Plan to delete things that are unused +- Delete code that was made newly unused by this task's changes **Don't Generate Unnecessary Code:** - Don't plan "just in case" code, future fields, or unused methods diff --git a/builtins/en/personas/supervisor.md b/builtins/en/personas/supervisor.md index 586d1d26..78c2b787 100644 --- a/builtins/en/personas/supervisor.md +++ b/builtins/en/personas/supervisor.md @@ -100,6 +100,21 @@ Check: **REJECT if spec violations are found.** Don't assume "probably correct"—actually read and cross-reference the specs. +### Scope Creep Detection (Deletions are Critical) + +File **deletions** and removal of existing features are the most dangerous form of scope creep. +Additions can be reverted, but restoring deleted flows is difficult. + +**Required steps:** +1. List all deleted files (D) and deleted classes/methods/endpoints from the diff +2. Cross-reference each deletion against the task order to find its justification +3. REJECT any deletion that has no basis in the task order + +**Typical scope creep patterns:** +- A "change statuses" task includes wholesale deletion of Sagas or endpoints +- A "UI fix" task includes structural changes to backend domain models +- A "display change" task rewrites business logic flows + ### 8. Piece Overall Review **Check all reports in the report directory and verify overall piece consistency.** @@ -115,7 +130,7 @@ Check: | Plan-implementation gap | REJECT - Request plan revision or implementation fix | | Unaddressed review feedback | REJECT - Point out specific unaddressed items | | Deviation from original purpose | REJECT - Request return to objective | -| Scope creep | Record only - Address in next task | +| Scope creep | REJECT - Deletions outside task order must be reverted | ### 9. Improvement Suggestion Check diff --git a/builtins/ja/personas/coder.md b/builtins/ja/personas/coder.md index ad81de35..afed754c 100644 --- a/builtins/ja/personas/coder.md +++ b/builtins/ja/personas/coder.md @@ -35,3 +35,4 @@ - 後方互換・Legacy 対応を勝手に追加する → 絶対禁止 - リファクタリングで置き換えたコード・エクスポートを残す → 禁止(明示的に残すよう指示されない限り削除する) - 根本原因を修正した上で安全機構を迂回するワークアラウンドを重ねる → 禁止 +- タスク指示書にない既存機能の削除・構造変更を「ついでに」行う → 禁止(計画に含まれていても、指示書に根拠がない大規模削除は報告する) diff --git a/builtins/ja/personas/expert-supervisor.md b/builtins/ja/personas/expert-supervisor.md index dfb72826..3d234bc7 100644 --- a/builtins/ja/personas/expert-supervisor.md +++ b/builtins/ja/personas/expert-supervisor.md @@ -43,6 +43,23 @@ | 非機能要件 | パフォーマンス、セキュリティ等は満たされているか | | スコープ | 要求以上のことをしていないか(スコープクリープ) | +### スコープクリープの検出(削除は最重要チェック) + +ファイルの**削除**と既存機能の**除去**はスコープクリープの最も危険な形態。 +追加は元に戻せるが、削除されたフローの復元は困難。 + +**必須手順:** +1. 変更差分から削除されたファイル(D)と削除されたクラス・メソッド・エンドポイントを列挙する +2. 各削除がタスク指示書のどの項目に対応するかを照合する +3. タスク指示書に根拠がない削除は REJECT する + +**典型的なスコープクリープ:** +- 「ステータス変更」タスクで Saga やエンドポイントが丸ごと削除されている +- 「UI修正」タスクでバックエンドのドメインモデルが構造変更されている +- 「表示変更」タスクでビジネスロジックのフローが書き換えられている + +レビュアーが「設計判断として妥当」と承認していても、タスク指示書のスコープ外であれば REJECT する。 + ### リスク評価 | 影響度\発生確率 | 低 | 中 | 高 | diff --git a/builtins/ja/personas/planner.md b/builtins/ja/personas/planner.md index d6afbcd7..c2469517 100644 --- a/builtins/ja/personas/planner.md +++ b/builtins/ja/personas/planner.md @@ -64,8 +64,19 @@ - 循環依存を作らない - 責務の分離(読み取りと書き込み、ビジネスロジックと IO) +### スコープ規律 + +タスク指示書に明記された作業のみを計画する。暗黙の「改善」を勝手に含めない。 + +**削除の判断基準:** +- **今回の変更で新たに未使用になったコード** → 削除を計画してよい(例: リネームした旧変数) +- **既存の機能・フロー・エンドポイント・Saga・イベント** → タスク指示書で明示的に指示されない限り削除しない + +「ステータスを5つに変更する」は「enum値を書き換える」であり、「不要になったフローを丸ごと削除する」ではない。 +タスク指示書の文言を拡大解釈しない。書かれていることだけを計画する。 + ### 計画の原則 - 後方互換コードは計画に含めない(明示的な指示がない限り不要) -- 使われていないものは削除する計画を立てる +- 今回の変更で新たに未使用になったコードは削除する計画を立てる - TODO コメントで済ませる計画は立てない。今やるか、やらないか diff --git a/builtins/ja/personas/supervisor.md b/builtins/ja/personas/supervisor.md index 5f2211ff..b9b22c71 100644 --- a/builtins/ja/personas/supervisor.md +++ b/builtins/ja/personas/supervisor.md @@ -102,6 +102,21 @@ 「機能的に無害」は免罪符ではない。修正コストがほぼゼロの指摘を「非ブロッキング」「次回タスク」に分類することは妥協である。レビュアーが発見し、数分以内に修正できる問題は今回のタスクで修正させる。 +### スコープクリープの検出(削除は最重要チェック) + +ファイルの**削除**と既存機能の**除去**はスコープクリープの最も危険な形態。 +追加は元に戻せるが、削除されたフローの復元は困難。 + +**必須手順:** +1. 変更差分から削除されたファイル(D)と削除されたクラス・メソッド・エンドポイントを列挙する +2. 各削除がタスク指示書のどの項目に対応するかを照合する +3. タスク指示書に根拠がない削除は REJECT する + +**典型的なスコープクリープ:** +- 「ステータス変更」タスクで Saga やエンドポイントが丸ごと削除されている +- 「UI修正」タスクでバックエンドのドメインモデルが構造変更されている +- 「表示変更」タスクでビジネスロジックのフローが書き換えられている + ### ピース全体の見直し レポートディレクトリ内の全レポートを確認し、ピース全体の整合性をチェックする。 From 5072d26e74ed244ae31500c21756f40a13392f48 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:20:45 +0900 Subject: [PATCH 4/5] remove slack notification on e2e --- e2e/helpers/isolated-env.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/helpers/isolated-env.ts b/e2e/helpers/isolated-env.ts index 8f1e24d3..4e762819 100644 --- a/e2e/helpers/isolated-env.ts +++ b/e2e/helpers/isolated-env.ts @@ -123,6 +123,7 @@ export function createIsolatedEnv(): IsolatedEnv { TAKT_CONFIG_DIR: taktDir, GIT_CONFIG_GLOBAL: gitConfigPath, TAKT_NO_TTY: '1', + TAKT_NOTIFY_WEBHOOK: undefined, }; return { From ac83dcace24fca5a2c7642407d519d83885511b9 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:23:14 +0900 Subject: [PATCH 5/5] Release v0.13.0 --- CHANGELOG.md | 5 ++++- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f9acd6..f3e26ef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). -## [0.13.0-alpha.1] - 2026-02-13 +## [0.13.0] - 2026-02-13 ### Added - **Team Leader ムーブメント**: ムーブメント内でチームリーダーエージェントがタスクを動的にサブタスク(Part)へ分解し、複数のパートエージェントを並列実行する新しいムーブメントタイプ — `team_leader` 設定(persona, maxParts, timeoutMs, partPersona, partEdit, partPermissionMode)をサポート (#244) - **構造化出力(Structured Output)**: エージェント呼び出しに JSON Schema ベースの構造化出力を導入 — タスク分解(decomposition)、ルール評価(evaluation)、ステータス判定(judgment)の3つのスキーマを `builtins/schemas/` に追加。Claude / Codex 両プロバイダーで対応 (#257) +- **`provider_options` ピースレベル設定**: ピース全体(`piece_config.provider_options`)および個別ムーブメントにプロバイダー固有オプション(`codex.network_access`、`opencode.network_access`)を設定可能に — 全ビルトインピースに Codex/OpenCode のネットワークアクセスを有効化 - **`backend` ビルトインピース**: バックエンド開発特化のピースを新規追加 — バックエンド、セキュリティ、QA の並列専門家レビュー対応 - **`backend-cqrs` ビルトインピース**: CQRS+ES 特化のバックエンド開発ピースを新規追加 — CQRS+ES、セキュリティ、QA の並列専門家レビュー対応 - **AbortSignal によるパートタイムアウト**: Team Leader のパート実行にタイムアウト制御と親シグナル連動の AbortSignal を追加 @@ -21,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **Phase 3 判定ロジックの刷新**: `JudgmentDetector` / `FallbackStrategy` を廃止し、構造化出力ベースの `status-judgment-phase.ts` に統合。判定の安定性と保守性を向上 (#257) - **Report フェーズのリトライ改善**: Report Phase(Phase 2)が失敗した場合、新規セッションで自動リトライするよう改善 (#245) - **Ctrl+C シャットダウンの統一**: `sigintHandler.ts` を廃止し、`ShutdownManager` に統合 — グレースフルシャットダウン → タイムアウト → 強制終了の3段階制御を全プロバイダーで共通化 (#237) +- **スコープ外削除の防止ガードレール**: coder ペルソナにタスク指示書の範囲外の削除・構造変更を禁止するルールを追加。planner ペルソナにスコープ規律と参照資料の優先順位を追加 - フロントエンドナレッジにデザイントークンとテーマスコープのガイダンスを追加 - アーキテクチャナレッジの改善(en/ja 両対応) @@ -39,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - AbortSignal のユニットテスト追加(abort-signal, claude-executor-abort-signal, claude-provider-abort-signal) - Report Phase リトライのユニットテスト追加(report-phase-retry) - パブリック API エクスポートのユニットテスト追加(public-api-exports) +- provider_options 関連のテスト追加(provider-options-piece-parser, models, opencode-types) - E2E テストの大幅拡充: cycle-detection, model-override, multi-step-sequential, pipeline-local-repo, report-file-output, run-sigint-graceful, session-log, structured-output, task-status-persistence - E2E テストヘルパーのリファクタリング(共通 setup 関数の抽出) - `judgment/` ディレクトリ(JudgmentDetector, FallbackStrategy)を削除 diff --git a/package-lock.json b/package-lock.json index 135f507c..fa413aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "takt", - "version": "0.13.0-alpha.1", + "version": "0.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "takt", - "version": "0.13.0-alpha.1", + "version": "0.13.0", "license": "MIT", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.37", diff --git a/package.json b/package.json index 794d349e..ce7fbf1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "takt", - "version": "0.13.0-alpha.1", + "version": "0.13.0", "description": "TAKT: TAKT Agent Koordination Topology - AI Agent Piece Orchestration", "main": "dist/index.js", "types": "dist/index.d.ts",