diff --git a/CLAUDE.md b/CLAUDE.md index 82df6ce..8d8eceb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -162,14 +162,14 @@ Implemented in `src/core/piece/evaluation/RuleEvaluator.ts`. The matched method 3. User request (`{task}` — auto-injected unless placeholder present) 4. Previous response (auto-injected if `pass_previous_response: true`) 5. User inputs (auto-injected unless `{user_inputs}` placeholder present) - 6. `instruction_template` content + 6. `instruction` content 7. Status output rules (auto-injected for tag-based rules) - Localized for `en` and `ja` - Related: `ReportInstructionBuilder` (Phase 2), `StatusJudgmentBuilder` (Phase 3) **Agent Runner** (`src/agents/runner.ts`) - Resolves agent specs (name or path) to agent configurations -- Agent is optional — movements can execute with `instruction_template` only (no system prompt) +- Agent is optional — movements can execute with `instruction` only (no system prompt) - 5-layer resolution for provider/model: CLI `--provider` / `--model` → persona_providers → movement override → project `.takt/config.yaml` → global `~/.takt/config.yaml` - Custom personas via `~/.takt/personas/.md` or prompt files (.md) - Inline system prompts: If agent file doesn't exist, the agent string is used as inline system prompt @@ -299,7 +299,7 @@ loop_monitors: threshold: 3 # Cycles before triggering judge judge: persona: supervisor - instruction_template: "Evaluate if the fix loop is making progress..." + instruction: "Evaluate if the fix loop is making progress..." rules: - condition: "Progress is being made" next: fix @@ -342,7 +342,7 @@ movements: my-server: command: npx args: [-y, my-mcp-server] - instruction_template: | + instruction: | Custom instructions for this movement. {task}, {previous_response} are auto-injected if not present as placeholders. pass_previous_response: true # Default: true @@ -413,7 +413,7 @@ movements: part_edit: true # Edit permission for parts part_permission_mode: edit # Permission mode for parts part_allowed_tools: [Read, Glob, Grep, Edit, Write, Bash] - instruction_template: | + instruction: | Decompose this task into independent subtasks. rules: - condition: "All parts completed" @@ -549,7 +549,7 @@ Key rules: - Policy REJECT lists are what reviewers enforce. If a criterion is not in the policy REJECT list, reviewers will not catch it — even if knowledge explains the reasoning - Knowledge provides the WHY behind policy criteria. Knowledge alone does not trigger enforcement - Instructions are bound to a single piece movement. They reference procedures, not principles -- Piece YAML `instruction_template` is for movement-specific details (which reports to read, movement routing, output templates) +- Piece YAML `instruction` is for movement-specific details (which reports to read, movement routing, output templates) **Separation of concerns in piece engine:** - `PieceEngine` - Orchestration, state management, event emission diff --git a/builtins/en/pieces/backend-cqrs-mini.yaml b/builtins/en/pieces/backend-cqrs-mini.yaml index 0073532..c2cf579 100644 --- a/builtins/en/pieces/backend-cqrs-mini.yaml +++ b/builtins/en/pieces/backend-cqrs-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: fix_both diff --git a/builtins/en/pieces/backend-cqrs-review-fix.yaml b/builtins/en/pieces/backend-cqrs-review-fix.yaml index c7232b0..2e68b48 100644 --- a/builtins/en/pieces/backend-cqrs-review-fix.yaml +++ b/builtins/en/pieces/backend-cqrs-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/backend-cqrs.yaml b/builtins/en/pieces/backend-cqrs.yaml index 61031dd..4c02c46 100644 --- a/builtins/en/pieces/backend-cqrs.yaml +++ b/builtins/en/pieces/backend-cqrs.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/backend-mini.yaml b/builtins/en/pieces/backend-mini.yaml index c1b2d48..6487056 100644 --- a/builtins/en/pieces/backend-mini.yaml +++ b/builtins/en/pieces/backend-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: fix_both diff --git a/builtins/en/pieces/backend-review-fix.yaml b/builtins/en/pieces/backend-review-fix.yaml index 382c536..9b0f9de 100644 --- a/builtins/en/pieces/backend-review-fix.yaml +++ b/builtins/en/pieces/backend-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/backend.yaml b/builtins/en/pieces/backend.yaml index 490cf07..f72b893 100644 --- a/builtins/en/pieces/backend.yaml +++ b/builtins/en/pieces/backend.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/compound-eye.yaml b/builtins/en/pieces/compound-eye.yaml index 33ca603..db949dd 100644 --- a/builtins/en/pieces/compound-eye.yaml +++ b/builtins/en/pieces/compound-eye.yaml @@ -108,7 +108,7 @@ movements: rules: - condition: synthesis complete next: COMPLETE - instruction_template: | + instruction: | Two models (Claude / Codex) independently answered the same instruction. Synthesize their responses. diff --git a/builtins/en/pieces/default.yaml b/builtins/en/pieces/default.yaml index ce5da73..ef3cc82 100644 --- a/builtins/en/pieces/default.yaml +++ b/builtins/en/pieces/default.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The ai_review ↔ ai_fix loop has repeated {cycle_count} times. Review the reports from each cycle and determine whether this loop @@ -39,7 +39,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/dual-cqrs-mini.yaml b/builtins/en/pieces/dual-cqrs-mini.yaml index 6142468..39c4bcb 100644 --- a/builtins/en/pieces/dual-cqrs-mini.yaml +++ b/builtins/en/pieces/dual-cqrs-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: fix_both diff --git a/builtins/en/pieces/dual-cqrs-review-fix.yaml b/builtins/en/pieces/dual-cqrs-review-fix.yaml index b0c2820..3e98dc8 100644 --- a/builtins/en/pieces/dual-cqrs-review-fix.yaml +++ b/builtins/en/pieces/dual-cqrs-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/dual-cqrs.yaml b/builtins/en/pieces/dual-cqrs.yaml index a9af202..ff92413 100644 --- a/builtins/en/pieces/dual-cqrs.yaml +++ b/builtins/en/pieces/dual-cqrs.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: Healthy (making progress) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/dual-mini.yaml b/builtins/en/pieces/dual-mini.yaml index 8fc544d..78ae752 100644 --- a/builtins/en/pieces/dual-mini.yaml +++ b/builtins/en/pieces/dual-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: fix_both diff --git a/builtins/en/pieces/dual-review-fix.yaml b/builtins/en/pieces/dual-review-fix.yaml index 3d468d1..bed0afe 100644 --- a/builtins/en/pieces/dual-review-fix.yaml +++ b/builtins/en/pieces/dual-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/dual.yaml b/builtins/en/pieces/dual.yaml index 2959bae..e291a99 100644 --- a/builtins/en/pieces/dual.yaml +++ b/builtins/en/pieces/dual.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: Healthy (making progress) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/e2e-test.yaml b/builtins/en/pieces/e2e-test.yaml index 7648ab5..350b393 100644 --- a/builtins/en/pieces/e2e-test.yaml +++ b/builtins/en/pieces/e2e-test.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The ai_review ↔ ai_fix loop has repeated {cycle_count} times. Review the reports from each cycle and determine whether this loop diff --git a/builtins/en/pieces/frontend-mini.yaml b/builtins/en/pieces/frontend-mini.yaml index f4824cb..40ed9dd 100644 --- a/builtins/en/pieces/frontend-mini.yaml +++ b/builtins/en/pieces/frontend-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: fix_both diff --git a/builtins/en/pieces/frontend-review-fix.yaml b/builtins/en/pieces/frontend-review-fix.yaml index c42b36d..ebbff53 100644 --- a/builtins/en/pieces/frontend-review-fix.yaml +++ b/builtins/en/pieces/frontend-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/frontend.yaml b/builtins/en/pieces/frontend.yaml index 9a23b56..66bda37 100644 --- a/builtins/en/pieces/frontend.yaml +++ b/builtins/en/pieces/frontend.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: converging (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/magi.yaml b/builtins/en/pieces/magi.yaml index f13ece5..595e391 100644 --- a/builtins/en/pieces/magi.yaml +++ b/builtins/en/pieces/magi.yaml @@ -18,7 +18,7 @@ initial_movement: melchior movements: - name: melchior persona: melchior - instruction_template: | + instruction: | # MAGI System Initiated ## Matter for Deliberation @@ -48,7 +48,7 @@ movements: next: balthasar - name: balthasar persona: balthasar - instruction_template: | + instruction: | # MAGI System Continuing ## Matter for Deliberation @@ -82,7 +82,7 @@ movements: next: casper - name: casper persona: casper - instruction_template: | + instruction: | # MAGI System Final Deliberation ## Matter for Deliberation diff --git a/builtins/en/pieces/review-fix.yaml b/builtins/en/pieces/review-fix.yaml index ed91098..643c8ab 100644 --- a/builtins/en/pieces/review-fix.yaml +++ b/builtins/en/pieces/review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/review.yaml b/builtins/en/pieces/review.yaml index ff15cc8..133d43b 100644 --- a/builtins/en/pieces/review.yaml +++ b/builtins/en/pieces/review.yaml @@ -177,7 +177,7 @@ movements: rules: - condition: Review synthesis complete next: COMPLETE - instruction_template: | + instruction: | ## Review Results {previous_response} diff --git a/builtins/en/pieces/takt-default-review-fix.yaml b/builtins/en/pieces/takt-default-review-fix.yaml index 7c6b333..f14ec6a 100644 --- a/builtins/en/pieces/takt-default-review-fix.yaml +++ b/builtins/en/pieces/takt-default-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (progress being made) next: reviewers diff --git a/builtins/en/pieces/takt-default-team-leader.yaml b/builtins/en/pieces/takt-default-team-leader.yaml index de9bbbd..e9f06a3 100644 --- a/builtins/en/pieces/takt-default-team-leader.yaml +++ b/builtins/en/pieces/takt-default-team-leader.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: Healthy (making progress) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/takt-default.yaml b/builtins/en/pieces/takt-default.yaml index 8687c5e..39791bd 100644 --- a/builtins/en/pieces/takt-default.yaml +++ b/builtins/en/pieces/takt-default.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The ai_review ↔ ai_fix loop has repeated {cycle_count} times. Review the reports from each cycle and determine whether this loop @@ -39,7 +39,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: Healthy (findings decreasing, fixes applied) next: reviewers diff --git a/builtins/en/pieces/terraform.yaml b/builtins/en/pieces/terraform.yaml index 6523f03..6f34cb9 100644 --- a/builtins/en/pieces/terraform.yaml +++ b/builtins/en/pieces/terraform.yaml @@ -265,7 +265,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The review → fix cycle has repeated {cycle_count} times. Check the review report history in the Report Directory and assess convergence. @@ -282,7 +282,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The AI fix → review cycle has repeated {cycle_count} times. Check the review report history in the Report Directory and assess convergence. @@ -299,7 +299,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The supervisor fix → review cycle has repeated {cycle_count} times. Check the review report history in the Report Directory and assess convergence. diff --git a/builtins/en/pieces/unit-test.yaml b/builtins/en/pieces/unit-test.yaml index b6cb47a..45243eb 100644 --- a/builtins/en/pieces/unit-test.yaml +++ b/builtins/en/pieces/unit-test.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | The ai_review ↔ ai_fix loop has repeated {cycle_count} times. Review the reports from each cycle and determine whether this loop diff --git a/builtins/ja/pieces/backend-cqrs-mini.yaml b/builtins/ja/pieces/backend-cqrs-mini.yaml index 5498706..a1540a6 100644 --- a/builtins/ja/pieces/backend-cqrs-mini.yaml +++ b/builtins/ja/pieces/backend-cqrs-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: fix_both diff --git a/builtins/ja/pieces/backend-cqrs-review-fix.yaml b/builtins/ja/pieces/backend-cqrs-review-fix.yaml index 9c79494..d25e94b 100644 --- a/builtins/ja/pieces/backend-cqrs-review-fix.yaml +++ b/builtins/ja/pieces/backend-cqrs-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/backend-cqrs.yaml b/builtins/ja/pieces/backend-cqrs.yaml index 5f6587a..bc70fcf 100644 --- a/builtins/ja/pieces/backend-cqrs.yaml +++ b/builtins/ja/pieces/backend-cqrs.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/backend-mini.yaml b/builtins/ja/pieces/backend-mini.yaml index eb4170c..15599ed 100644 --- a/builtins/ja/pieces/backend-mini.yaml +++ b/builtins/ja/pieces/backend-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: fix_both diff --git a/builtins/ja/pieces/backend-review-fix.yaml b/builtins/ja/pieces/backend-review-fix.yaml index 31bdf18..6d14ca8 100644 --- a/builtins/ja/pieces/backend-review-fix.yaml +++ b/builtins/ja/pieces/backend-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/backend.yaml b/builtins/ja/pieces/backend.yaml index ab3c58b..f3d771f 100644 --- a/builtins/ja/pieces/backend.yaml +++ b/builtins/ja/pieces/backend.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/compound-eye.yaml b/builtins/ja/pieces/compound-eye.yaml index 245c8a7..6c0da93 100644 --- a/builtins/ja/pieces/compound-eye.yaml +++ b/builtins/ja/pieces/compound-eye.yaml @@ -106,7 +106,7 @@ movements: rules: - condition: 統合完了 next: COMPLETE - instruction_template: | + instruction: | 2つのモデル(Claude / Codex)が同じ指示に対して独立に回答しました。 両者の回答を統合してください。 diff --git a/builtins/ja/pieces/default.yaml b/builtins/ja/pieces/default.yaml index 71f0280..3457f6e 100644 --- a/builtins/ja/pieces/default.yaml +++ b/builtins/ja/pieces/default.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | ai_review と ai_fix のループが {cycle_count} 回繰り返されました。 各サイクルのレポートを確認し、このループが健全(進捗がある)か、 @@ -39,7 +39,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/dual-cqrs-mini.yaml b/builtins/ja/pieces/dual-cqrs-mini.yaml index 4ce2652..abffd2a 100644 --- a/builtins/ja/pieces/dual-cqrs-mini.yaml +++ b/builtins/ja/pieces/dual-cqrs-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: fix_both diff --git a/builtins/ja/pieces/dual-cqrs-review-fix.yaml b/builtins/ja/pieces/dual-cqrs-review-fix.yaml index c6b213f..9f2e887 100644 --- a/builtins/ja/pieces/dual-cqrs-review-fix.yaml +++ b/builtins/ja/pieces/dual-cqrs-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/dual-cqrs.yaml b/builtins/ja/pieces/dual-cqrs.yaml index ddae703..83149ff 100644 --- a/builtins/ja/pieces/dual-cqrs.yaml +++ b/builtins/ja/pieces/dual-cqrs.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: 健全(進捗あり) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/dual-mini.yaml b/builtins/ja/pieces/dual-mini.yaml index f81c7ff..d77386d 100644 --- a/builtins/ja/pieces/dual-mini.yaml +++ b/builtins/ja/pieces/dual-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: fix_both diff --git a/builtins/ja/pieces/dual-review-fix.yaml b/builtins/ja/pieces/dual-review-fix.yaml index 51b19b3..268e949 100644 --- a/builtins/ja/pieces/dual-review-fix.yaml +++ b/builtins/ja/pieces/dual-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/dual.yaml b/builtins/ja/pieces/dual.yaml index 1b5f7dc..38b0bdb 100644 --- a/builtins/ja/pieces/dual.yaml +++ b/builtins/ja/pieces/dual.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: 健全(進捗あり) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/e2e-test.yaml b/builtins/ja/pieces/e2e-test.yaml index e3f76ed..cda5dc1 100644 --- a/builtins/ja/pieces/e2e-test.yaml +++ b/builtins/ja/pieces/e2e-test.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | ai_review と ai_fix のループが {cycle_count} 回繰り返されました。 各サイクルのレポートを確認し、このループが健全(進捗がある)か、 diff --git a/builtins/ja/pieces/frontend-mini.yaml b/builtins/ja/pieces/frontend-mini.yaml index f7050f7..4554460 100644 --- a/builtins/ja/pieces/frontend-mini.yaml +++ b/builtins/ja/pieces/frontend-mini.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: fix_both diff --git a/builtins/ja/pieces/frontend-review-fix.yaml b/builtins/ja/pieces/frontend-review-fix.yaml index c45d73c..551264b 100644 --- a/builtins/ja/pieces/frontend-review-fix.yaml +++ b/builtins/ja/pieces/frontend-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/frontend.yaml b/builtins/ja/pieces/frontend.yaml index f96a251..7af1b0e 100644 --- a/builtins/ja/pieces/frontend.yaml +++ b/builtins/ja/pieces/frontend.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/magi.yaml b/builtins/ja/pieces/magi.yaml index e0717e5..d09558d 100644 --- a/builtins/ja/pieces/magi.yaml +++ b/builtins/ja/pieces/magi.yaml @@ -18,7 +18,7 @@ initial_movement: melchior movements: - name: melchior persona: melchior - instruction_template: | + instruction: | # MAGI System 起動 ## 審議事項 @@ -48,7 +48,7 @@ movements: next: balthasar - name: balthasar persona: balthasar - instruction_template: | + instruction: | # MAGI System 継続 ## 審議事項 @@ -82,7 +82,7 @@ movements: next: casper - name: casper persona: casper - instruction_template: | + instruction: | # MAGI System 最終審議 ## 審議事項 diff --git a/builtins/ja/pieces/review-fix.yaml b/builtins/ja/pieces/review-fix.yaml index 0d4d30d..f1c0249 100644 --- a/builtins/ja/pieces/review-fix.yaml +++ b/builtins/ja/pieces/review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/review.yaml b/builtins/ja/pieces/review.yaml index 9290200..b4f17ad 100644 --- a/builtins/ja/pieces/review.yaml +++ b/builtins/ja/pieces/review.yaml @@ -177,7 +177,7 @@ movements: rules: - condition: レビュー統合完了 next: COMPLETE - instruction_template: | + instruction: | ## レビュー結果 {previous_response} diff --git a/builtins/ja/pieces/takt-default-review-fix.yaml b/builtins/ja/pieces/takt-default-review-fix.yaml index 86d789c..d3b3388 100644 --- a/builtins/ja/pieces/takt-default-review-fix.yaml +++ b/builtins/ja/pieces/takt-default-review-fix.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(進捗あり) next: reviewers diff --git a/builtins/ja/pieces/takt-default-team-leader.yaml b/builtins/ja/pieces/takt-default-team-leader.yaml index f4cb45c..cb0ae06 100644 --- a/builtins/ja/pieces/takt-default-team-leader.yaml +++ b/builtins/ja/pieces/takt-default-team-leader.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-ai-fix + instruction: loop-monitor-ai-fix rules: - condition: 健全(進捗あり) next: ai_review @@ -27,7 +27,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/takt-default.yaml b/builtins/ja/pieces/takt-default.yaml index 4476b02..eebd5fb 100644 --- a/builtins/ja/pieces/takt-default.yaml +++ b/builtins/ja/pieces/takt-default.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | ai_review と ai_fix のループが {cycle_count} 回繰り返されました。 各サイクルのレポートを確認し、このループが健全(進捗がある)か、 @@ -39,7 +39,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: loop-monitor-reviewers-fix + instruction: loop-monitor-reviewers-fix rules: - condition: 健全(指摘数が減少、修正が反映されている) next: reviewers diff --git a/builtins/ja/pieces/terraform.yaml b/builtins/ja/pieces/terraform.yaml index 0f3c925..11e5445 100644 --- a/builtins/ja/pieces/terraform.yaml +++ b/builtins/ja/pieces/terraform.yaml @@ -265,7 +265,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | レビュー → 修正のサイクルが {cycle_count} 回繰り返されました。 Report Directory 内のレビューレポート履歴を確認し、収束状況を判断してください。 @@ -282,7 +282,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | AI修正 → レビューのサイクルが {cycle_count} 回繰り返されました。 Report Directory 内のレビューレポート履歴を確認し、収束状況を判断してください。 @@ -299,7 +299,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | 監督修正 → レビューのサイクルが {cycle_count} 回繰り返されました。 Report Directory 内のレビューレポート履歴を確認し、収束状況を判断してください。 diff --git a/builtins/ja/pieces/unit-test.yaml b/builtins/ja/pieces/unit-test.yaml index c7fb8a4..3520a2b 100644 --- a/builtins/ja/pieces/unit-test.yaml +++ b/builtins/ja/pieces/unit-test.yaml @@ -15,7 +15,7 @@ loop_monitors: threshold: 3 judge: persona: supervisor - instruction_template: | + instruction: | ai_review と ai_fix のループが {cycle_count} 回繰り返されました。 各サイクルのレポートを確認し、このループが健全(進捗がある)か、 diff --git a/builtins/skill/references/yaml-schema.md b/builtins/skill/references/yaml-schema.md index d9cadd1..61f9c40 100644 --- a/builtins/skill/references/yaml-schema.md +++ b/builtins/skill/references/yaml-schema.md @@ -47,22 +47,41 @@ movement 内では**キー名**で参照する(パスを直接書かない) - name: movement-name # movement 名(必須、一意) persona: coder # ペルソナキー(personas マップを参照、任意) policy: coding # ポリシーキー(policies マップを参照、任意) - policy: [coding, testing] # 複数指定も可(配列) - instruction: implement # 指示テンプレートキー(instructions マップを参照、任意) + instruction: implement # 指示(instructions マップのキー参照、またはインライン、任意) knowledge: architecture # ナレッジキー(knowledge マップを参照、任意) edit: true # ファイル編集可否(必須) required_permission_mode: edit # 必要最小権限: edit / readonly / full(任意) session: refresh # セッション管理(任意) pass_previous_response: true # 前の出力を渡すか(デフォルト: true) allowed_tools: [...] # 許可ツール一覧(任意、参考情報) - instruction_template: | # 指示テンプレート(参照解決またはインライン、任意) - 指示内容... output_contracts: [...] # 出力契約設定(任意) quality_gates: [...] # 品質ゲート(AIへの指示、任意) rules: [...] # 遷移ルール(必須) ``` -**`instruction` vs `instruction_template`**: どちらも同じ参照解決ルート(セクションマップ → パス → 3-layer facet → インライン)を使う。`instruction_template` はインライン文字列もそのまま使える。通常はどちらか一方を使用する。 +複数ポリシー指定(配列): + +```yaml +- name: movement-name + policy: [coding, testing] +``` + +参照形式: + +```yaml +- name: movement-name + instruction: implement +``` + +インライン形式: + +```yaml +- name: movement-name + instruction: | + 指示内容... +``` + +**`instruction`(正式) / `instruction_template`(deprecated)**: どちらも同じ参照解決ルート(セクションマップ → パス → 3-layer facet → インライン)を使う。`instruction_template` も互換のため受理されるが、新規定義では `instruction` を使う。 ### Parallel Movement @@ -185,7 +204,7 @@ quality_gates: ## テンプレート変数 -`instruction_template`(またはインストラクションファイル)内で使用可能な変数: +`instruction`(またはインストラクションファイル)内で使用可能な変数: | 変数 | 説明 | |-----|------| @@ -207,7 +226,7 @@ loop_monitors: threshold: 3 # 発動閾値(サイクル回数) judge: persona: supervisor # ペルソナキー参照 - instruction_template: | # 判定用指示 + instruction: | # 判定用指示 サイクルが {cycle_count} 回繰り返されました。 健全性を判断してください。 rules: diff --git a/src/__tests__/aggregate-evaluator.test.ts b/src/__tests__/aggregate-evaluator.test.ts index 9091d81..3509c59 100644 --- a/src/__tests__/aggregate-evaluator.test.ts +++ b/src/__tests__/aggregate-evaluator.test.ts @@ -35,7 +35,7 @@ function makeSubMovement(name: string, conditions: string[]): PieceMovement { return { name, personaDisplayName: name, - instructionTemplate: '', + instruction: '', passPreviousResponse: false, rules: conditions.map((c) => ({ condition: c })), }; @@ -48,7 +48,7 @@ function makeParentMovement( return { name: 'parent', personaDisplayName: 'parent', - instructionTemplate: '', + instruction: '', passPreviousResponse: false, parallel, rules, @@ -264,7 +264,7 @@ describe('AggregateEvaluator', () => { const step: PieceMovement = { name: 'test-movement', personaDisplayName: 'tester', - instructionTemplate: '', + instruction: '', passPreviousResponse: false, rules: [ { diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index bd4477d..9f618fa 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -77,7 +77,7 @@ describe('getBuiltinPiece', () => { const planMovement = piece!.movements.find((movement) => movement.name === 'plan'); expect(planMovement).toBeDefined(); - expect(planMovement!.instructionTemplate).not.toBe('plan'); + expect(planMovement!.instruction).not.toBe('plan'); }); it('should return null for non-existent piece names', () => { diff --git a/src/__tests__/engine-report.test.ts b/src/__tests__/engine-report.test.ts index ad55b12..40e8aec 100644 --- a/src/__tests__/engine-report.test.ts +++ b/src/__tests__/engine-report.test.ts @@ -50,7 +50,7 @@ function createMovement(overrides: Partial = {}): PieceMovement { name: 'test-movement', persona: 'coder', personaDisplayName: 'Coder', - instructionTemplate: '', + instruction: '', passPreviousResponse: false, ...overrides, }; diff --git a/src/__tests__/engine-team-leader.test.ts b/src/__tests__/engine-team-leader.test.ts index 0bbd5fb..6122fc1 100644 --- a/src/__tests__/engine-team-leader.test.ts +++ b/src/__tests__/engine-team-leader.test.ts @@ -32,7 +32,7 @@ function buildTeamLeaderConfig(): PieceConfig { maxMovements: 5, movements: [ makeMovement('implement', { - instructionTemplate: 'Task: {task}', + instruction: 'Task: {task}', teamLeader: { persona: '../personas/team-leader.md', maxParts: 3, diff --git a/src/__tests__/engine-test-helpers.ts b/src/__tests__/engine-test-helpers.ts index 6198357..c40c55a 100644 --- a/src/__tests__/engine-test-helpers.ts +++ b/src/__tests__/engine-test-helpers.ts @@ -41,7 +41,7 @@ export function makeMovement(name: string, overrides: Partial = { name, persona: `../personas/${name}.md`, personaDisplayName: name, - instructionTemplate: `Run ${name}`, + instruction: `Run ${name}`, passPreviousResponse: true, ...overrides, }; diff --git a/src/__tests__/engine-worktree-report.test.ts b/src/__tests__/engine-worktree-report.test.ts index 3cc2a8d..1f9c795 100644 --- a/src/__tests__/engine-worktree-report.test.ts +++ b/src/__tests__/engine-worktree-report.test.ts @@ -137,7 +137,7 @@ describe('PieceEngine: worktree reportDir resolution', () => { initialMovement: 'review', movements: [ makeMovement('review', { - instructionTemplate: 'Write report to {report_dir}', + instruction: 'Write report to {report_dir}', outputContracts: [{ name: '00-review.md', format: '00-review', useJudge: true }], rules: [ makeRule('approved', 'COMPLETE'), diff --git a/src/__tests__/facet-resolution.test.ts b/src/__tests__/facet-resolution.test.ts index 6e2acbe..6f98379 100644 --- a/src/__tests__/facet-resolution.test.ts +++ b/src/__tests__/facet-resolution.test.ts @@ -520,7 +520,6 @@ describe('normalizePieceConfig with layer resolution', () => { name: 'step1', persona: 'coder', instruction_template: 'implement', - instruction: '{task}', }, ], }; @@ -528,7 +527,7 @@ describe('normalizePieceConfig with layer resolution', () => { const context: FacetResolutionContext = { projectDir, lang: 'ja' }; const config = normalizePieceConfig(raw, pieceDir, context); - expect(config.movements[0]!.instructionTemplate).toBe('Mapped instruction template'); + expect(config.movements[0]!.instruction).toBe('Mapped instruction template'); }); it('should resolve instruction_template by name via layer resolution', () => { @@ -543,7 +542,6 @@ describe('normalizePieceConfig with layer resolution', () => { name: 'step1', persona: 'coder', instruction_template: 'implement', - instruction: '{task}', }, ], }; @@ -551,7 +549,7 @@ describe('normalizePieceConfig with layer resolution', () => { const context: FacetResolutionContext = { projectDir, lang: 'ja' }; const config = normalizePieceConfig(raw, pieceDir, context); - expect(config.movements[0]!.instructionTemplate).toBe('Project implement template'); + expect(config.movements[0]!.instruction).toBe('Project implement template'); }); it('should keep inline instruction_template when no facet is found', () => { @@ -564,7 +562,6 @@ Second line remains inline.`; name: 'step1', persona: 'coder', instruction_template: inlineTemplate, - instruction: '{task}', }, ], }; @@ -572,7 +569,7 @@ Second line remains inline.`; const context: FacetResolutionContext = { projectDir, lang: 'ja' }; const config = normalizePieceConfig(raw, pieceDir, context); - expect(config.movements[0]!.instructionTemplate).toBe(inlineTemplate); + expect(config.movements[0]!.instruction).toBe(inlineTemplate); }); it('should resolve loop monitor judge instruction_template via layer resolution', () => { @@ -612,6 +609,6 @@ Second line remains inline.`; const context: FacetResolutionContext = { projectDir, lang: 'ja' }; const config = normalizePieceConfig(raw, pieceDir, context); - expect(config.loopMonitors?.[0]?.judge.instructionTemplate).toBe('Project judge template'); + expect(config.loopMonitors?.[0]?.judge.instruction).toBe('Project judge template'); }); }); diff --git a/src/__tests__/instructionBuilder.test.ts b/src/__tests__/instructionBuilder.test.ts index 2e036fd..cc9d770 100644 --- a/src/__tests__/instructionBuilder.test.ts +++ b/src/__tests__/instructionBuilder.test.ts @@ -32,7 +32,7 @@ function createMinimalStep(template: string): PieceMovement { name: 'test-step', persona: 'test-agent', personaDisplayName: 'Test Agent', - instructionTemplate: template, + instruction: template, passPreviousResponse: false, }; } diff --git a/src/__tests__/it-error-recovery.test.ts b/src/__tests__/it-error-recovery.test.ts index 75199ba..a211cb8 100644 --- a/src/__tests__/it-error-recovery.test.ts +++ b/src/__tests__/it-error-recovery.test.ts @@ -63,7 +63,7 @@ function makeMovement(name: string, agentPath: string, rules: PieceRule[]): Piec persona: `./personas/${name}.md`, personaDisplayName: name, personaPath: agentPath, - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: true, rules, }; diff --git a/src/__tests__/it-instruction-builder.test.ts b/src/__tests__/it-instruction-builder.test.ts index e972a3d..c5a1d87 100644 --- a/src/__tests__/it-instruction-builder.test.ts +++ b/src/__tests__/it-instruction-builder.test.ts @@ -40,7 +40,7 @@ function makeMovement(overrides: Partial = {}): PieceMovement { name: 'test-step', persona: 'test-agent', personaDisplayName: 'test-step', - instructionTemplate: 'Do the work.', + instruction: 'Do the work.', passPreviousResponse: false, rules: [ makeRule('Done', 'COMPLETE'), @@ -66,7 +66,7 @@ function makeContext(overrides: Partial = {}): InstructionCo describe('Instruction Builder IT: task auto-injection', () => { it('should auto-inject task as "User Request" section when template has no {task}', () => { - const step = makeMovement({ instructionTemplate: 'Do the work.' }); + const step = makeMovement({ instruction: 'Do the work.' }); const ctx = makeContext({ task: 'Build the login page' }); const result = buildInstruction(step, ctx); @@ -76,7 +76,7 @@ describe('Instruction Builder IT: task auto-injection', () => { }); it('should NOT auto-inject task section when template contains {task}', () => { - const step = makeMovement({ instructionTemplate: 'Here is the task: {task}' }); + const step = makeMovement({ instruction: 'Here is the task: {task}' }); const ctx = makeContext({ task: 'Build the login page' }); const result = buildInstruction(step, ctx); @@ -93,7 +93,7 @@ describe('Instruction Builder IT: previous_response auto-injection', () => { it('should auto-inject previous response when passPreviousResponse is true', () => { const step = makeMovement({ passPreviousResponse: true, - instructionTemplate: 'Continue the work.', + instruction: 'Continue the work.', }); const previousOutput: AgentResponse = { persona: 'previous-agent', @@ -112,7 +112,7 @@ describe('Instruction Builder IT: previous_response auto-injection', () => { it('should NOT inject previous response when passPreviousResponse is false', () => { const step = makeMovement({ passPreviousResponse: false, - instructionTemplate: 'Do fresh work.', + instruction: 'Do fresh work.', }); const previousOutput: AgentResponse = { persona: 'previous-agent', @@ -131,7 +131,7 @@ describe('Instruction Builder IT: previous_response auto-injection', () => { it('should NOT auto-inject when template contains {previous_response}', () => { const step = makeMovement({ passPreviousResponse: true, - instructionTemplate: '## Context\n{previous_response}\n\nDo work.', + instruction: '## Context\n{previous_response}\n\nDo work.', }); const previousOutput: AgentResponse = { persona: 'prev', status: 'done', content: 'Prior work done.', timestamp: new Date(), @@ -161,7 +161,7 @@ describe('Instruction Builder IT: user_inputs auto-injection', () => { }); it('should NOT auto-inject when template contains {user_inputs}', () => { - const step = makeMovement({ instructionTemplate: 'Inputs: {user_inputs}' }); + const step = makeMovement({ instruction: 'Inputs: {user_inputs}' }); const ctx = makeContext({ userInputs: ['Input A'] }); const result = buildInstruction(step, ctx); @@ -175,7 +175,7 @@ describe('Instruction Builder IT: user_inputs auto-injection', () => { describe('Instruction Builder IT: iteration variables', () => { it('should replace {iteration}, {max_movements}, {movement_iteration} in template', () => { const step = makeMovement({ - instructionTemplate: 'Iter: {iteration}/{max_movements}, movement iter: {movement_iteration}', + instruction: 'Iter: {iteration}/{max_movements}, movement iter: {movement_iteration}', }); const ctx = makeContext({ iteration: 5, maxMovements: 30, movementIteration: 2 }); @@ -198,7 +198,7 @@ describe('Instruction Builder IT: iteration variables', () => { describe('Instruction Builder IT: report_dir expansion', () => { it('should replace {report_dir} in template', () => { const step = makeMovement({ - instructionTemplate: 'Read the plan from {report_dir}/00-plan.md', + instruction: 'Read the plan from {report_dir}/00-plan.md', }); const ctx = makeContext({ reportDir: '/tmp/test-project/.takt/runs/20250126-task/reports' }); @@ -209,7 +209,7 @@ describe('Instruction Builder IT: report_dir expansion', () => { it('should replace {report:filename} with full path', () => { const step = makeMovement({ - instructionTemplate: 'Read {report:00-plan.md} for the plan.', + instruction: 'Read {report:00-plan.md} for the plan.', }); const ctx = makeContext({ reportDir: '/tmp/reports' }); @@ -388,7 +388,7 @@ describe('Instruction Builder IT: template injection prevention', () => { it('should escape curly braces in previous response content', () => { const step = makeMovement({ passPreviousResponse: true, - instructionTemplate: 'Continue.', + instruction: 'Continue.', }); const ctx = makeContext({ previousOutput: { diff --git a/src/__tests__/it-notification-sound.test.ts b/src/__tests__/it-notification-sound.test.ts index 459773e..df23647 100644 --- a/src/__tests__/it-notification-sound.test.ts +++ b/src/__tests__/it-notification-sound.test.ts @@ -224,7 +224,7 @@ function makeConfig(): PieceConfig { name: 'step1', persona: '../agents/coder.md', personaDisplayName: 'coder', - instructionTemplate: 'Do something', + instruction: 'Do something', passPreviousResponse: true, rules: [ { condition: 'done', next: 'COMPLETE' }, diff --git a/src/__tests__/it-piece-execution.test.ts b/src/__tests__/it-piece-execution.test.ts index 912fa8b..36e3b08 100644 --- a/src/__tests__/it-piece-execution.test.ts +++ b/src/__tests__/it-piece-execution.test.ts @@ -66,7 +66,7 @@ function makeMovement(name: string, agentPath: string, rules: PieceRule[]): Piec persona: `./personas/${name}.md`, personaDisplayName: name, personaPath: agentPath, - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: true, rules, }; diff --git a/src/__tests__/it-piece-loader.test.ts b/src/__tests__/it-piece-loader.test.ts index a0d1bb0..1c64aaf 100644 --- a/src/__tests__/it-piece-loader.test.ts +++ b/src/__tests__/it-piece-loader.test.ts @@ -92,8 +92,8 @@ describe('Piece Loader IT: builtin piece loading', () => { expect(planMovement).toBeDefined(); expect(implementMovement).toBeDefined(); - expect(planMovement!.instructionTemplate).toContain('missing E2E tests'); - expect(implementMovement!.instructionTemplate).toContain('npm run test:e2e:mock'); + expect(planMovement!.instruction).toContain('missing E2E tests'); + expect(implementMovement!.instruction).toContain('npm run test:e2e:mock'); }); it('should load e2e-test as a builtin piece in ja locale', () => { @@ -110,8 +110,8 @@ describe('Piece Loader IT: builtin piece loading', () => { expect(planMovement).toBeDefined(); expect(implementMovement).toBeDefined(); - expect(planMovement!.instructionTemplate).toContain('E2Eテスト'); - expect(implementMovement!.instructionTemplate).toContain('npm run test:e2e:mock'); + expect(planMovement!.instruction).toContain('E2Eテスト'); + expect(implementMovement!.instruction).toContain('npm run test:e2e:mock'); }); }); @@ -156,6 +156,49 @@ movements: expect(config!.movements.length).toBe(1); expect(config!.movements[0]!.name).toBe('start'); }); + + it('should propagate canonical instruction field through loader for movement and loop monitor judge', () => { + // Given: project-local piece that uses instruction on both movement and loop monitor judge + const piecesDir = join(testDir, '.takt', 'pieces'); + mkdirSync(piecesDir, { recursive: true }); + + writeFileSync(join(piecesDir, 'instruction-canonical.yaml'), ` +name: instruction-canonical +max_movements: 8 +initial_movement: step1 + +movements: + - name: step1 + instruction: "Step 1 instruction" + rules: + - condition: next + next: step2 + - name: step2 + instruction: "Step 2 instruction" + rules: + - condition: done + next: COMPLETE + +loop_monitors: + - cycle: [step1, step2] + threshold: 2 + judge: + instruction: "Judge instruction" + rules: + - condition: continue + next: step2 +`); + + // When: loading the piece through the integration entry point + const config = loadPiece('instruction-canonical', testDir); + + // Then: canonical instruction is available on normalized movement/judge models + expect(config).not.toBeNull(); + const step1 = config!.movements[0] as unknown as Record; + const judge = config!.loopMonitors?.[0]?.judge as unknown as Record; + expect(step1.instruction).toBe('Step 1 instruction'); + expect(judge.instruction).toBe('Judge instruction'); + }); }); describe('Piece Loader IT: agent path resolution', () => { diff --git a/src/__tests__/it-rule-evaluation.test.ts b/src/__tests__/it-rule-evaluation.test.ts index 57ec9cc..a0da237 100644 --- a/src/__tests__/it-rule-evaluation.test.ts +++ b/src/__tests__/it-rule-evaluation.test.ts @@ -50,7 +50,7 @@ function makeMovement( name, persona: 'test-agent', personaDisplayName: name, - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: true, rules, parallel, @@ -401,7 +401,7 @@ describe('Rule Evaluation IT: movements without rules', () => { name: 'step', persona: 'agent', personaDisplayName: 'step', - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: true, }; diff --git a/src/__tests__/it-sigint-interrupt.test.ts b/src/__tests__/it-sigint-interrupt.test.ts index 114b733..c8b75d9 100644 --- a/src/__tests__/it-sigint-interrupt.test.ts +++ b/src/__tests__/it-sigint-interrupt.test.ts @@ -224,7 +224,7 @@ describe('executePiece: SIGINT handler integration', () => { name: 'step1', persona: '../agents/coder.md', personaDisplayName: 'coder', - instructionTemplate: 'Do something', + instruction: 'Do something', passPreviousResponse: true, rules: [ { condition: 'done', next: 'COMPLETE' }, diff --git a/src/__tests__/it-three-phase-execution.test.ts b/src/__tests__/it-three-phase-execution.test.ts index 82338a4..5ae29da 100644 --- a/src/__tests__/it-three-phase-execution.test.ts +++ b/src/__tests__/it-three-phase-execution.test.ts @@ -93,7 +93,7 @@ function makeMovement( persona: './agents/agent.md', personaDisplayName: name, personaPath: agentPath, - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: true, rules, outputContracts: options.outputContracts, diff --git a/src/__tests__/knowledge.test.ts b/src/__tests__/knowledge.test.ts index baa8839..43cf15e 100644 --- a/src/__tests__/knowledge.test.ts +++ b/src/__tests__/knowledge.test.ts @@ -317,11 +317,11 @@ describe('normalizePieceConfig knowledge resolution', () => { // --- Test helpers for InstructionBuilder --- -function createMinimalStep(instructionTemplate: string): PieceMovement { +function createMinimalStep(instruction: string): PieceMovement { return { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate, + instruction, passPreviousResponse: false, }; } diff --git a/src/__tests__/options-builder.test.ts b/src/__tests__/options-builder.test.ts index 478b47d..d940b79 100644 --- a/src/__tests__/options-builder.test.ts +++ b/src/__tests__/options-builder.test.ts @@ -7,7 +7,7 @@ function createMovement(overrides: Partial = {}): PieceMovement { return { name: 'reviewers', personaDisplayName: 'Reviewers', - instructionTemplate: 'review', + instruction: 'review', passPreviousResponse: false, ...overrides, }; diff --git a/src/__tests__/parallel-and-loader.test.ts b/src/__tests__/parallel-and-loader.test.ts index 4c484e7..3d72341 100644 --- a/src/__tests__/parallel-and-loader.test.ts +++ b/src/__tests__/parallel-and-loader.test.ts @@ -8,7 +8,12 @@ */ import { describe, it, expect } from 'vitest'; -import { PieceConfigRawSchema, ParallelSubMovementRawSchema, PieceMovementRawSchema } from '../core/models/index.js'; +import { + PieceConfigRawSchema, + ParallelSubMovementRawSchema, + PieceMovementRawSchema, + LoopMonitorJudgeSchema, +} from '../core/models/index.js'; describe('ParallelSubMovementRawSchema', () => { it('should validate a valid parallel sub-movement', () => { @@ -32,6 +37,39 @@ describe('ParallelSubMovementRawSchema', () => { expect(result.success).toBe(true); }); + it('should accept a sub-movement with instruction field', () => { + // Given: a parallel sub-movement that uses the new canonical field + const raw = { + name: 'no-agent-step', + instruction: 'Do something', + }; + + // When: validating the sub-movement schema + const result = ParallelSubMovementRawSchema.safeParse(raw); + + // Then: it is accepted + expect(result.success).toBe(true); + }); + + it('should accept a sub-movement when instruction and instruction_template are both provided', () => { + // Given: both canonical and deprecated fields are present during migration + const raw = { + name: 'dual-field-sub-step', + instruction: 'Canonical instruction', + instruction_template: 'Legacy instruction', + }; + + // When: validating the sub-movement schema + const result = ParallelSubMovementRawSchema.safeParse(raw); + + // Then: schema keeps backward compatibility and accepts both fields + expect(result.success).toBe(true); + if (result.success) { + expect((result.data as unknown as Record).instruction).toBe('Canonical instruction'); + expect((result.data as unknown as Record).instruction_template).toBe('Legacy instruction'); + } + }); + it('should accept optional fields', () => { const raw = { name: 'full-sub-step', @@ -144,6 +182,39 @@ describe('PieceMovementRawSchema with parallel', () => { expect(result.success).toBe(true); }); + it('should accept a movement with instruction only', () => { + // Given: a movement that only uses instruction + const raw = { + name: 'orphan-step', + instruction: 'Do something', + }; + + // When: validating the movement schema + const result = PieceMovementRawSchema.safeParse(raw); + + // Then: it is accepted + expect(result.success).toBe(true); + }); + + it('should accept a movement when instruction and instruction_template are both provided', () => { + // Given: movement includes both canonical and deprecated instruction fields + const raw = { + name: 'orphan-step', + instruction: 'Canonical movement instruction', + instruction_template: 'Legacy movement instruction', + }; + + // When: validating the movement schema + const result = PieceMovementRawSchema.safeParse(raw); + + // Then: schema accepts both fields for deprecation window + expect(result.success).toBe(true); + if (result.success) { + expect((result.data as unknown as Record).instruction).toBe('Canonical movement instruction'); + expect((result.data as unknown as Record).instruction_template).toBe('Legacy movement instruction'); + } + }); + it('should accept a movement with persona (no parallel)', () => { const raw = { name: 'normal-step', @@ -182,6 +253,46 @@ describe('PieceMovementRawSchema with parallel', () => { }); }); +describe('LoopMonitorJudgeSchema', () => { + it('should accept judge configuration with instruction field', () => { + // Given: a loop monitor judge with canonical instruction + const raw = { + persona: 'reviewer', + instruction: 'Judge loop health', + rules: [{ condition: 'continue', next: 'ai_fix' }], + }; + + // When: validating judge schema + const result = LoopMonitorJudgeSchema.safeParse(raw); + + // Then: it is accepted + expect(result.success).toBe(true); + if (result.success) { + expect((result.data as unknown as Record).instruction).toBe('Judge loop health'); + } + }); + + it('should accept judge configuration during deprecation window when both fields exist', () => { + // Given: judge config with both new and deprecated fields + const raw = { + persona: 'reviewer', + instruction: 'Judge loop health', + instruction_template: 'legacy judge instruction', + rules: [{ condition: 'continue', next: 'ai_fix' }], + }; + + // When: validating judge schema + const result = LoopMonitorJudgeSchema.safeParse(raw); + + // Then: it is accepted for backward compatibility + expect(result.success).toBe(true); + if (result.success) { + expect((result.data as unknown as Record).instruction).toBe('Judge loop health'); + expect((result.data as unknown as Record).instruction_template).toBe('legacy judge instruction'); + } + }); +}); + describe('PieceConfigRawSchema with parallel movements', () => { it('should validate a piece with parallel movement', () => { const raw = { diff --git a/src/__tests__/phase-runner-report-history.test.ts b/src/__tests__/phase-runner-report-history.test.ts index 377af91..92e0fba 100644 --- a/src/__tests__/phase-runner-report-history.test.ts +++ b/src/__tests__/phase-runner-report-history.test.ts @@ -17,7 +17,7 @@ function createStep(fileName: string): PieceMovement { return { name: 'reviewers', personaDisplayName: 'Reviewers', - instructionTemplate: 'review', + instruction: 'review', passPreviousResponse: false, outputContracts: [{ name: fileName }], }; diff --git a/src/__tests__/pieceExecution-ask-user-question.test.ts b/src/__tests__/pieceExecution-ask-user-question.test.ts index 0ef0ed8..2f68cb7 100644 --- a/src/__tests__/pieceExecution-ask-user-question.test.ts +++ b/src/__tests__/pieceExecution-ask-user-question.test.ts @@ -49,7 +49,7 @@ const { MockPieceEngine } = vi.hoisted(() => { return { status: 'aborted', iteration: 1 }; } if (firstStep) { - this.emit('movement:start', firstStep, 1, firstStep.instructionTemplate, { provider: undefined, model: undefined }); + this.emit('movement:start', firstStep, 1, firstStep.instruction, { provider: undefined, model: undefined }); } this.emit('piece:complete', { status: 'completed', iteration: 1 }); return { status: 'completed', iteration: 1 }; @@ -171,7 +171,7 @@ function makeConfig(): PieceConfig { name: 'implement', persona: '../agents/coder.md', personaDisplayName: 'coder', - instructionTemplate: 'Implement task', + instruction: 'Implement task', passPreviousResponse: true, rules: [{ condition: 'done', next: 'COMPLETE' }], }, diff --git a/src/__tests__/pieceExecution-debug-prompts.test.ts b/src/__tests__/pieceExecution-debug-prompts.test.ts index 6e7bf12..95a5384 100644 --- a/src/__tests__/pieceExecution-debug-prompts.test.ts +++ b/src/__tests__/pieceExecution-debug-prompts.test.ts @@ -236,7 +236,7 @@ describe('executePiece debug prompts logging', () => { name: 'implement', persona: '../agents/coder.md', personaDisplayName: 'coder', - instructionTemplate: 'Implement task', + instruction: 'Implement task', passPreviousResponse: true, rules: [{ condition: 'done', next: 'COMPLETE' }], }, diff --git a/src/__tests__/pieceExecution-session-loading.test.ts b/src/__tests__/pieceExecution-session-loading.test.ts index 98dd9a8..e93de57 100644 --- a/src/__tests__/pieceExecution-session-loading.test.ts +++ b/src/__tests__/pieceExecution-session-loading.test.ts @@ -77,7 +77,7 @@ const { const firstStep = this.config.movements[0]; if (firstStep) { const providerInfo = resolveProviderInfo(firstStep, this.receivedOptions); - this.emit('movement:start', firstStep, 1, firstStep.instructionTemplate, providerInfo); + this.emit('movement:start', firstStep, 1, firstStep.instruction, providerInfo); this.emit('movement:complete', firstStep, { persona: firstStep.personaDisplayName, status: 'done', @@ -85,7 +85,7 @@ const { timestamp: new Date('2026-03-04T00:00:00.000Z'), sessionId: 'movement-session', providerUsage: mockMovementResponse.providerUsage, - }, firstStep.instructionTemplate); + }, firstStep.instruction); } this.emit('piece:complete', { status: 'completed', iteration: 1 }); return { status: 'completed', iteration: 1 }; @@ -230,7 +230,7 @@ function makeConfig(): PieceConfig { name: 'implement', persona: '../agents/coder.md', personaDisplayName: 'coder', - instructionTemplate: 'Implement task', + instruction: 'Implement task', passPreviousResponse: true, rules: [{ condition: 'done', next: 'COMPLETE' }], }, diff --git a/src/__tests__/policy-persona.test.ts b/src/__tests__/policy-persona.test.ts index f841b2c..52c684d 100644 --- a/src/__tests__/policy-persona.test.ts +++ b/src/__tests__/policy-persona.test.ts @@ -9,7 +9,7 @@ * - File-based policy content loading via resolveContentPath */ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; @@ -371,7 +371,7 @@ describe('InstructionBuilder policy injection', () => { const step = { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate: 'Do the thing.', + instruction: 'Do the thing.', passPreviousResponse: false, policyContents: ['# Coding Policy\n\nWrite clean code.'], }; @@ -390,7 +390,7 @@ describe('InstructionBuilder policy injection', () => { const step = { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate: 'Do the thing.', + instruction: 'Do the thing.', passPreviousResponse: false, policyContents: ['# Coding Policy\n\nWrite clean code.'], }; @@ -408,7 +408,7 @@ describe('InstructionBuilder policy injection', () => { const step = { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate: 'Do the thing.', + instruction: 'Do the thing.', passPreviousResponse: false, }; @@ -423,7 +423,7 @@ describe('InstructionBuilder policy injection', () => { const step = { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate: 'Do the thing.', + instruction: 'Do the thing.', passPreviousResponse: false, policyContents: ['Policy A content.', 'Policy B content.'], }; @@ -441,7 +441,7 @@ describe('InstructionBuilder policy injection', () => { const step = { name: 'test-step', personaDisplayName: 'coder', - instructionTemplate: 'Do the thing.', + instruction: 'Do the thing.', passPreviousResponse: false, policyContents: ['Step policy.'], }; @@ -545,7 +545,22 @@ describe('section reference resolution', () => { }; const config = normalizePieceConfig(raw, testDir); - expect(config.movements[0]!.instructionTemplate).toBe('Implement the feature.'); + expect(config.movements[0]!.instruction).toBe('Implement the feature.'); + }); + + it('should expose normalized movement instruction on instruction field', () => { + const raw = { + name: 'test-piece', + movements: [{ + name: 'impl', + persona: 'coder', + instruction: 'Canonical movement instruction', + }], + }; + + const config = normalizePieceConfig(raw, testDir); + const movement = config.movements[0] as unknown as Record; + expect(movement.instruction).toBe('Canonical movement instruction'); }); it('should resolve output contract from report_formats section by name', () => { @@ -586,7 +601,7 @@ describe('section reference resolution', () => { expect(config.movements[0]!.persona).toBe('nonexistent'); }); - it('should prefer instruction_template over instruction section reference', () => { + it('should prefer instruction over instruction_template when both are provided', () => { const raw = { name: 'test-piece', instructions: { implement: './instructions/implement.md' }, @@ -599,7 +614,144 @@ describe('section reference resolution', () => { }; const config = normalizePieceConfig(raw, testDir); - expect(config.movements[0]!.instructionTemplate).toBe('Inline template takes priority.'); + expect(config.movements[0]!.instruction).toBe('Implement the feature.'); + }); + + it('should emit deprecation warning when movement uses instruction_template', () => { + // Given: deprecated instruction_template is used on a movement + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + try { + const raw = { + name: 'test-piece', + movements: [{ + name: 'impl', + persona: 'coder', + instruction_template: 'Legacy movement instruction', + }], + }; + + // When: normalizing piece config + normalizePieceConfig(raw, testDir); + + // Then: deprecation warning is emitted + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('instruction_template')); + } finally { + warnSpy.mockRestore(); + } + }); + + it('should emit deprecation warning when loop monitor judge uses instruction_template', () => { + // Given: deprecated instruction_template is used on loop monitor judge + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + try { + const raw = { + name: 'test-piece', + movements: [ + { + name: 'step1', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'next', next: 'step2' }], + }, + { + name: 'step2', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'done', next: 'COMPLETE' }], + }, + ], + loop_monitors: [ + { + cycle: ['step1', 'step2'], + threshold: 2, + judge: { + persona: 'coder', + instruction_template: 'Legacy judge instruction', + rules: [{ condition: 'continue', next: 'step2' }], + }, + }, + ], + }; + + // When: normalizing piece config + normalizePieceConfig(raw, testDir); + + // Then: deprecation warning is emitted + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('instruction_template')); + } finally { + warnSpy.mockRestore(); + } + }); + + it('should prefer loop monitor judge instruction over instruction_template when both are provided', () => { + const raw = { + name: 'test-piece', + instructions: { judge_template: './instructions/implement.md' }, + movements: [ + { + name: 'step1', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'next', next: 'step2' }], + }, + { + name: 'step2', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'done', next: 'COMPLETE' }], + }, + ], + loop_monitors: [ + { + cycle: ['step1', 'step2'], + threshold: 2, + judge: { + persona: 'coder', + instruction: 'judge_template', + instruction_template: 'Legacy judge template', + rules: [{ condition: 'continue', next: 'step2' }], + }, + }, + ], + }; + + const config = normalizePieceConfig(raw, testDir); + expect(config.loopMonitors?.[0]?.judge.instruction).toBe('Implement the feature.'); + }); + + it('should expose normalized loop monitor judge instruction on instruction field', () => { + const raw = { + name: 'test-piece', + movements: [ + { + name: 'step1', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'next', next: 'step2' }], + }, + { + name: 'step2', + persona: 'coder', + instruction: '{task}', + rules: [{ condition: 'done', next: 'COMPLETE' }], + }, + ], + loop_monitors: [ + { + cycle: ['step1', 'step2'], + threshold: 2, + judge: { + persona: 'coder', + instruction: 'Canonical judge instruction', + rules: [{ condition: 'continue', next: 'step2' }], + }, + }, + ], + }; + + const config = normalizePieceConfig(raw, testDir); + const judge = config.loopMonitors?.[0]?.judge as unknown as Record; + expect(judge.instruction).toBe('Canonical judge instruction'); }); it('should store resolved sections on PieceConfig', () => { @@ -653,7 +805,7 @@ describe('section reference resolution', () => { const parallel = config.movements[0]!.parallel!; expect(parallel[0]!.persona).toBe('./personas/coder.md'); expect(parallel[0]!.policyContents).toEqual(['# Coding Policy\nWrite clean code.']); - expect(parallel[0]!.instructionTemplate).toBe('Implement the feature.'); + expect(parallel[0]!.instruction).toBe('Implement the feature.'); expect(parallel[1]!.policyContents).toEqual([ '# Coding Policy\nWrite clean code.', '# Testing Policy\nTest everything.', diff --git a/src/__tests__/report-phase-retry.test.ts b/src/__tests__/report-phase-retry.test.ts index 74934a5..a46b7f7 100644 --- a/src/__tests__/report-phase-retry.test.ts +++ b/src/__tests__/report-phase-retry.test.ts @@ -17,7 +17,7 @@ function createStep(fileName: string): PieceMovement { name: 'implement', persona: 'coder', personaDisplayName: 'Coder', - instructionTemplate: 'Implement task', + instruction: 'Implement task', passPreviousResponse: false, outputContracts: [{ name: fileName }], }; diff --git a/src/__tests__/session-key.test.ts b/src/__tests__/session-key.test.ts index be4ccc1..13e63ab 100644 --- a/src/__tests__/session-key.test.ts +++ b/src/__tests__/session-key.test.ts @@ -11,7 +11,7 @@ function createMovement(overrides: Partial = {}): PieceMovement { name: 'test-movement', personaDisplayName: 'test', edit: false, - instructionTemplate: '', + instruction: '', passPreviousResponse: true, ...overrides, }; diff --git a/src/__tests__/status-judgment-phase.test.ts b/src/__tests__/status-judgment-phase.test.ts index 6fb91ba..6648206 100644 --- a/src/__tests__/status-judgment-phase.test.ts +++ b/src/__tests__/status-judgment-phase.test.ts @@ -43,7 +43,7 @@ describe('runStatusJudgmentPhase', () => { name: 'review', persona: 'reviewer', personaDisplayName: 'reviewer', - instructionTemplate: 'Review', + instruction: 'Review', passPreviousResponse: true, rules: [ { condition: 'needs_fix', next: 'fix' }, @@ -103,7 +103,7 @@ describe('runStatusJudgmentPhase', () => { name: 'review', persona: 'reviewer', personaDisplayName: 'reviewer', - instructionTemplate: 'Review', + instruction: 'Review', passPreviousResponse: true, rules: [ { condition: 'needs_fix', next: 'fix' }, diff --git a/src/__tests__/team-leader-common.test.ts b/src/__tests__/team-leader-common.test.ts index ab196c5..106714b 100644 --- a/src/__tests__/team-leader-common.test.ts +++ b/src/__tests__/team-leader-common.test.ts @@ -9,7 +9,7 @@ describe('createPartMovement', () => { name: 'implement', persona: 'coder', personaDisplayName: 'Coder', - instructionTemplate: 'do work', + instruction: 'do work', passPreviousResponse: false, providerOptions: { claude: { diff --git a/src/__tests__/test-helpers.ts b/src/__tests__/test-helpers.ts index e6118df..1392fda 100644 --- a/src/__tests__/test-helpers.ts +++ b/src/__tests__/test-helpers.ts @@ -16,7 +16,7 @@ export function makeMovement(overrides: Partial = {}): PieceMovem return { name: 'test-movement', personaDisplayName: 'tester', - instructionTemplate: '', + instruction: '', passPreviousResponse: false, ...overrides, }; diff --git a/src/__tests__/transitions.test.ts b/src/__tests__/transitions.test.ts index 4bf3244..a856367 100644 --- a/src/__tests__/transitions.test.ts +++ b/src/__tests__/transitions.test.ts @@ -12,7 +12,7 @@ function createMovementWithRules(rules: { condition: string; next: string }[]): name: 'test-step', persona: 'test-agent', personaDisplayName: 'Test Agent', - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: false, rules: rules.map((r) => ({ condition: r.condition, @@ -47,7 +47,7 @@ describe('determineNextMovementByRules', () => { name: 'test-step', persona: 'test-agent', personaDisplayName: 'Test Agent', - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: false, }; @@ -68,7 +68,7 @@ describe('determineNextMovementByRules', () => { name: 'sub-step', persona: 'test-agent', personaDisplayName: 'Test Agent', - instructionTemplate: '{task}', + instruction: '{task}', passPreviousResponse: false, rules: [ { condition: 'approved' }, diff --git a/src/__tests__/worktree-exceeded-requeue.test.ts b/src/__tests__/worktree-exceeded-requeue.test.ts index 4fa4571..6871eb5 100644 --- a/src/__tests__/worktree-exceeded-requeue.test.ts +++ b/src/__tests__/worktree-exceeded-requeue.test.ts @@ -126,7 +126,7 @@ function buildTestPieceConfig(): PieceConfig { name: 'plan', persona: '../personas/plan.md', personaDisplayName: 'plan', - instructionTemplate: 'Run plan', + instruction: 'Run plan', passPreviousResponse: true, rules: [], }, diff --git a/src/__tests__/yaml-schema-reference.test.ts b/src/__tests__/yaml-schema-reference.test.ts new file mode 100644 index 0000000..8fd14b1 --- /dev/null +++ b/src/__tests__/yaml-schema-reference.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +describe('yaml-schema reference', () => { + it('normal movement examples should not duplicate instruction keys in one movement block', () => { + const schemaPath = join(process.cwd(), 'builtins', 'skill', 'references', 'yaml-schema.md'); + const schemaText = readFileSync(schemaPath, 'utf-8'); + const normalSectionMatch = schemaText.match(/### 通常 Movement([\s\S]*?)### Parallel Movement/); + + expect(normalSectionMatch).not.toBeNull(); + const normalSection = normalSectionMatch![1]; + const yamlBlocks = [...normalSection.matchAll(/```yaml\n([\s\S]*?)```/g)].map((m) => m[1]); + + expect(yamlBlocks.length).toBeGreaterThan(0); + + const hasDuplicatedInstruction = yamlBlocks.some((block) => { + const instructionKeys = block + .split('\n') + .filter((line) => /^ {2}instruction:\s/.test(line)); + return instructionKeys.length > 1; + }); + + expect(hasDuplicatedInstruction).toBe(false); + }); + + it('normal movement examples should not duplicate policy keys in one movement block', () => { + const schemaPath = join(process.cwd(), 'builtins', 'skill', 'references', 'yaml-schema.md'); + const schemaText = readFileSync(schemaPath, 'utf-8'); + const normalSectionMatch = schemaText.match(/### 通常 Movement([\s\S]*?)### Parallel Movement/); + + expect(normalSectionMatch).not.toBeNull(); + const normalSection = normalSectionMatch![1]; + const yamlBlocks = [...normalSection.matchAll(/```yaml\n([\s\S]*?)```/g)].map((m) => m[1]); + + expect(yamlBlocks.length).toBeGreaterThan(0); + + const hasDuplicatedPolicy = yamlBlocks.some((block) => { + const policyKeys = block + .split('\n') + .filter((line) => /^ {2}policy:\s/.test(line)); + return policyKeys.length > 1; + }); + + expect(hasDuplicatedPolicy).toBe(false); + }); +}); diff --git a/src/core/models/piece-types.ts b/src/core/models/piece-types.ts index 98680c4..ba86b47 100644 --- a/src/core/models/piece-types.ts +++ b/src/core/models/piece-types.ts @@ -42,7 +42,7 @@ export interface OutputContractItem { format: string; /** Whether this report is used as input for status judgment phase (default: true) */ useJudge?: boolean; - /** Instruction prepended before instruction_template (e.g., output destination) */ + /** Instruction prepended before movement instruction (e.g., output destination) */ order?: string; } @@ -143,7 +143,7 @@ export interface PieceMovement { providerOptions?: MovementProviderOptions; /** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */ edit?: boolean; - instructionTemplate: string; + instruction: string; /** Rules for movement routing */ rules?: PieceRule[]; /** Output contracts for this movement (report definitions) */ @@ -219,8 +219,8 @@ export interface LoopMonitorJudge { persona?: string; /** Resolved absolute path to persona prompt file (set by loader) */ personaPath?: string; - /** Custom instruction template for the judge (uses default if omitted) */ - instructionTemplate?: string; + /** Custom instruction for the judge (uses default if omitted) */ + instruction?: string; /** Rules for the judge's decision */ rules: LoopMonitorRule[]; } diff --git a/src/core/models/schemas.ts b/src/core/models/schemas.ts index 22d4802..842ab9c 100644 --- a/src/core/models/schemas.ts +++ b/src/core/models/schemas.ts @@ -162,11 +162,11 @@ export const PieceProviderOptionsSchema = z.object({ export const OutputContractItemSchema = z.object({ /** Report file name */ name: z.string().min(1), - /** Instruction appended after instruction_template (e.g., output format) */ + /** Instruction appended after movement instruction (e.g., output format) */ format: z.string().min(1), /** Whether this report is used as input for status judgment phase */ use_judge: z.boolean().optional().default(true), - /** Instruction prepended before instruction_template (e.g., output destination) */ + /** Instruction prepended before movement instruction (e.g., output destination) */ order: z.string().optional(), }); @@ -376,7 +376,9 @@ export const LoopMonitorRuleSchema = z.object({ export const LoopMonitorJudgeSchema = z.object({ /** Persona reference — key name from piece-level personas map, or file path */ persona: z.string().optional(), - /** Custom instruction template for the judge */ + /** Custom judge instruction */ + instruction: z.string().optional(), + /** Deprecated alias */ instruction_template: z.string().optional(), /** Rules for the judge's decision */ rules: z.array(LoopMonitorRuleSchema).min(1), diff --git a/src/core/piece/engine/PieceEngine.ts b/src/core/piece/engine/PieceEngine.ts index 5c11b00..f452ce9 100644 --- a/src/core/piece/engine/PieceEngine.ts +++ b/src/core/piece/engine/PieceEngine.ts @@ -462,10 +462,10 @@ export class PieceEngine extends EventEmitter { } /** - * Build the default instruction template for a loop monitor judge. - * Used when the monitor config does not specify a custom instruction_template. + * Build the default instruction for a loop monitor judge. + * Used when the monitor config does not specify a custom instruction. */ - private buildDefaultJudgeInstructionTemplate( + private buildDefaultJudgeInstruction( monitor: LoopMonitorConfig, cycleCount: number, language: string, @@ -513,11 +513,11 @@ export class PieceEngine extends EventEmitter { cycleCount: number, ): Promise { const language = this.options.language ?? 'en'; - const instructionTemplate = monitor.judge.instructionTemplate - ?? this.buildDefaultJudgeInstructionTemplate(monitor, cycleCount, language); + const instruction = monitor.judge.instruction + ?? this.buildDefaultJudgeInstruction(monitor, cycleCount, language); - // Replace {cycle_count} in custom templates - const processedTemplate = instructionTemplate.replace(/\{cycle_count\}/g, String(cycleCount)); + // Replace {cycle_count} in custom instructions + const processedInstruction = instruction.replace(/\{cycle_count\}/g, String(cycleCount)); // Build a synthetic PieceMovement for the judge const judgeMovement: PieceMovement = { @@ -531,7 +531,7 @@ export class PieceEngine extends EventEmitter { allowedTools: ['Read', 'Glob', 'Grep'], }, }, - instructionTemplate: processedTemplate, + instruction: processedInstruction, rules: monitor.judge.rules.map((r) => ({ condition: r.condition, next: r.next, @@ -553,7 +553,7 @@ export class PieceEngine extends EventEmitter { this.emit('movement:start', judgeMovement, this.state.iteration, prebuiltInstruction, this.optionsBuilder.resolveStepProviderModel(judgeMovement)); - const { response, instruction } = await this.movementExecutor.runNormalMovement( + const { response, instruction: executedInstruction } = await this.movementExecutor.runNormalMovement( judgeMovement, this.state, this.task, @@ -562,7 +562,7 @@ export class PieceEngine extends EventEmitter { prebuiltInstruction, ); this.emitCollectedReports(); - this.emit('movement:complete', judgeMovement, response, instruction); + this.emit('movement:complete', judgeMovement, response, executedInstruction); // Resolve next movement from the judge's rules const nextMovement = this.resolveNextMovementFromDone(judgeMovement, response); diff --git a/src/core/piece/engine/team-leader-common.ts b/src/core/piece/engine/team-leader-common.ts index b34b756..a5352bb 100644 --- a/src/core/piece/engine/team-leader-common.ts +++ b/src/core/piece/engine/team-leader-common.ts @@ -45,7 +45,7 @@ export function createPartMovement(step: PieceMovement, part: PartDefinition): P model: step.model, requiredPermissionMode: step.teamLeader.partPermissionMode ?? step.requiredPermissionMode, edit: step.teamLeader.partEdit ?? step.edit, - instructionTemplate: part.instruction, + instruction: part.instruction, passPreviousResponse: false, }; } diff --git a/src/core/piece/instruction/InstructionBuilder.ts b/src/core/piece/instruction/InstructionBuilder.ts index 2d9d5e6..e44873a 100644 --- a/src/core/piece/instruction/InstructionBuilder.ts +++ b/src/core/piece/instruction/InstructionBuilder.ts @@ -89,7 +89,7 @@ export class InstructionBuilder { } // Skip auto-injection for sections whose placeholders exist in the template - const tmpl = this.step.instructionTemplate; + const tmpl = this.step.instruction; const hasTaskPlaceholder = tmpl.includes('{task}'); const hasPreviousResponsePlaceholder = tmpl.includes('{previous_response}'); const hasUserInputsPlaceholder = tmpl.includes('{user_inputs}'); @@ -120,9 +120,9 @@ export class InstructionBuilder { ? escapeTemplateChars(this.context.userInputs.join('\n')) : ''; - // Instructions (instruction_template processed) + // Instructions (movement instruction with placeholder processing) const instructions = replaceTemplatePlaceholders( - this.step.instructionTemplate, + tmpl, this.step, { ...this.context, diff --git a/src/infra/config/loaders/pieceParser.ts b/src/infra/config/loaders/pieceParser.ts index 1aecbe7..a7eb4bd 100644 --- a/src/infra/config/loaders/pieceParser.ts +++ b/src/infra/config/loaders/pieceParser.ts @@ -272,6 +272,12 @@ function normalizeStepFromRaw( const expandedInstruction = step.instruction ? resolveRefToContent(step.instruction, sections.resolvedInstructions, pieceDir, 'instructions', context) : undefined; + if (step.instruction_template !== undefined) { + console.warn(`Movement "${step.name}" uses deprecated field "instruction_template". Use "instruction" instead.`); + } + const expandedLegacyInstruction = step.instruction_template + ? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) + : undefined; const result: PieceMovement = { name: step.name, @@ -286,9 +292,7 @@ function normalizeStepFromRaw( requiredPermissionMode: step.required_permission_mode, providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizedProvider.providerOptions), edit: step.edit, - instructionTemplate: (step.instruction_template - ? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) - : undefined) || expandedInstruction || '{task}', + instruction: expandedInstruction || expandedLegacyInstruction || '{task}', rules, outputContracts: normalizeOutputContracts(step.output_contracts, pieceDir, sections.resolvedReportFormats, context), qualityGates: applyQualityGateOverrides( @@ -335,19 +339,25 @@ function normalizeStepFromRaw( /** Normalize a raw loop monitor judge from YAML into internal format. */ function normalizeLoopMonitorJudge( - raw: { persona?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> }, + raw: { persona?: string; instruction?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> }, pieceDir: string, sections: PieceSections, context?: FacetResolutionContext, ): LoopMonitorJudge { const { personaSpec, personaPath } = resolvePersona(raw.persona, sections, pieceDir, context); + if (raw.instruction_template !== undefined) { + console.warn('loop_monitors judge uses deprecated field "instruction_template". Use "instruction" instead.'); + } + const resolvedInstruction = raw.instruction + ? resolveRefToContent(raw.instruction, sections.resolvedInstructions, pieceDir, 'instructions', context) + : raw.instruction_template + ? resolveRefToContent(raw.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) + : undefined; return { persona: personaSpec, personaPath, - instructionTemplate: raw.instruction_template - ? resolveRefToContent(raw.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) - : undefined, + instruction: resolvedInstruction, rules: raw.rules.map((r) => ({ condition: r.condition, next: r.next })), }; } @@ -356,7 +366,7 @@ function normalizeLoopMonitorJudge( * Normalize raw loop monitors from YAML into internal format. */ function normalizeLoopMonitors( - raw: Array<{ cycle: string[]; threshold: number; judge: { persona?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> } }> | undefined, + raw: Array<{ cycle: string[]; threshold: number; judge: { persona?: string; instruction?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> } }> | undefined, pieceDir: string, sections: PieceSections, context?: FacetResolutionContext, diff --git a/src/infra/config/loaders/pieceResolver.ts b/src/infra/config/loaders/pieceResolver.ts index fab98ee..0628ee5 100644 --- a/src/infra/config/loaders/pieceResolver.ts +++ b/src/infra/config/loaders/pieceResolver.ts @@ -229,7 +229,7 @@ function buildMovementPreviews(piece: PieceConfig, maxCount: number): MovementPr name: movement.name, personaDisplayName: movement.personaDisplayName, personaContent: readMovementPersona(movement), - instructionContent: movement.instructionTemplate, + instructionContent: movement.instruction, allowedTools: movement.providerOptions?.claude?.allowedTools ?? [], canEdit: movement.edit === true, });