takt: github-issue-212-max-iteration-max-movement-ostinato (#217)

This commit is contained in:
nrs 2026-02-10 23:43:29 +09:00 committed by GitHub
parent 0214f7f5e6
commit de6b5b5c2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
115 changed files with 266 additions and 266 deletions

View File

@ -218,7 +218,7 @@ Builtin resources are embedded in the npm package (`builtins/`). User files in `
```yaml ```yaml
name: piece-name name: piece-name
description: Optional description description: Optional description
max_iterations: 10 max_movements: 10
initial_step: plan # First step to execute initial_step: plan # First step to execute
steps: steps:
@ -291,7 +291,7 @@ Key points about parallel steps:
|----------|-------------| |----------|-------------|
| `{task}` | Original user request (auto-injected if not in template) | | `{task}` | Original user request (auto-injected if not in template) |
| `{iteration}` | Piece-wide iteration count | | `{iteration}` | Piece-wide iteration count |
| `{max_iterations}` | Maximum iterations allowed | | `{max_movements}` | Maximum movements allowed |
| `{step_iteration}` | Per-step iteration count | | `{step_iteration}` | Per-step iteration count |
| `{previous_response}` | Previous step output (auto-injected if not in template) | | `{previous_response}` | Previous step output (auto-injected if not in template) |
| `{user_inputs}` | Accumulated user inputs (auto-injected if not in template) | | `{user_inputs}` | Accumulated user inputs (auto-injected if not in template) |

View File

@ -335,7 +335,7 @@ TAKT uses YAML-based piece definitions and rule-based routing. Builtin pieces ar
```yaml ```yaml
name: default name: default
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
# Section maps — key: file path (relative to this YAML) # Section maps — key: file path (relative to this YAML)
@ -709,7 +709,7 @@ takt eject default
# ~/.takt/pieces/my-piece.yaml # ~/.takt/pieces/my-piece.yaml
name: my-piece name: my-piece
description: Custom piece description: Custom piece
max_iterations: 5 max_movements: 5
initial_movement: analyze initial_movement: analyze
personas: personas:
@ -759,7 +759,7 @@ Variables available in `instruction_template`:
|----------|-------------| |----------|-------------|
| `{task}` | Original user request (auto-injected if not in template) | | `{task}` | Original user request (auto-injected if not in template) |
| `{iteration}` | Piece-wide turn count (total steps executed) | | `{iteration}` | Piece-wide turn count (total steps executed) |
| `{max_iterations}` | Maximum iteration count | | `{max_movements}` | Maximum iteration count |
| `{movement_iteration}` | Per-movement iteration count (times this movement has been executed) | | `{movement_iteration}` | Per-movement iteration count (times this movement has been executed) |
| `{previous_response}` | Output from previous movement (auto-injected if not in template) | | `{previous_response}` | Output from previous movement (auto-injected if not in template) |
| `{user_inputs}` | Additional user inputs during piece (auto-injected if not in template) | | `{user_inputs}` | Additional user inputs during piece (auto-injected if not in template) |

View File

@ -1,6 +1,6 @@
name: coding name: coding
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
max_iterations: 20 max_movements: 20
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: compound-eye name: compound-eye
description: Multi-model review - send the same instruction to Claude and Codex simultaneously, synthesize both responses description: Multi-model review - send the same instruction to Claude and Codex simultaneously, synthesize both responses
max_iterations: 10 max_movements: 10
initial_movement: evaluate initial_movement: evaluate
movements: movements:
- name: evaluate - name: evaluate

View File

@ -1,6 +1,6 @@
name: default name: default
description: Standard development piece with planning and specialized reviews description: Standard development piece with planning and specialized reviews
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -1,6 +1,6 @@
name: e2e-test name: e2e-test
description: E2E test focused piece (E2E analysis → E2E implementation → review → fix) description: E2E test focused piece (E2E analysis → E2E implementation → review → fix)
max_iterations: 20 max_movements: 20
initial_movement: plan_test initial_movement: plan_test
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -1,6 +1,6 @@
name: expert-cqrs name: expert-cqrs
description: CQRS+ES, Frontend, Security, QA Expert Review description: CQRS+ES, Frontend, Security, QA Expert Review
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: expert name: expert
description: Architecture, Frontend, Security, QA Expert Review description: Architecture, Frontend, Security, QA Expert Review
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: frontend name: frontend
description: Frontend, Security, QA Expert Review description: Frontend, Security, QA Expert Review
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: magi name: magi
description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority
max_iterations: 5 max_movements: 5
initial_movement: melchior initial_movement: melchior
movements: movements:
- name: melchior - name: melchior

View File

@ -1,6 +1,6 @@
name: minimal name: minimal
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
max_iterations: 20 max_movements: 20
initial_movement: implement initial_movement: implement
movements: movements:
- name: implement - name: implement

View File

@ -1,6 +1,6 @@
name: passthrough name: passthrough
description: Single-agent thin wrapper. Pass task directly to coder as-is. description: Single-agent thin wrapper. Pass task directly to coder as-is.
max_iterations: 10 max_movements: 10
initial_movement: execute initial_movement: execute
movements: movements:
- name: execute - name: execute

View File

@ -1,6 +1,6 @@
name: research name: research
description: Research piece - autonomously executes research without asking questions description: Research piece - autonomously executes research without asking questions
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan
@ -13,7 +13,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: plan - Movement: plan
@ -48,7 +48,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: dig - Movement: dig
@ -88,7 +88,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: supervise (research quality evaluation) - Movement: supervise (research quality evaluation)

View File

@ -1,6 +1,6 @@
name: review-fix-minimal name: review-fix-minimal
description: Review and fix piece for existing code (starts with review, no implementation) description: Review and fix piece for existing code (starts with review, no implementation)
max_iterations: 20 max_movements: 20
initial_movement: reviewers initial_movement: reviewers
movements: movements:
- name: implement - name: implement

View File

@ -1,6 +1,6 @@
name: review-only name: review-only
description: Review-only piece - reviews code without making edits description: Review-only piece - reviews code without making edits
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: structural-reform name: structural-reform
description: Full project review and structural reform - iterative codebase restructuring with staged file splits description: Full project review and structural reform - iterative codebase restructuring with staged file splits
max_iterations: 50 max_movements: 50
initial_movement: review initial_movement: review
loop_monitors: loop_monitors:
- cycle: - cycle:
@ -44,7 +44,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: review (full project review) - Movement: review (full project review)
@ -126,7 +126,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: plan_reform (reform plan creation) - Movement: plan_reform (reform plan creation)
@ -323,7 +323,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: verify (build and test verification) - Movement: verify (build and test verification)
@ -378,7 +378,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## Piece Status ## Piece Status
- Iteration: {iteration}/{max_iterations} (piece-wide) - Iteration: {iteration}/{max_movements} (piece-wide)
- Movement Iteration: {movement_iteration} (times this movement has run) - Movement Iteration: {movement_iteration} (times this movement has run)
- Movement: next_target (progress check and next target selection) - Movement: next_target (progress check and next target selection)

View File

@ -1,6 +1,6 @@
name: unit-test name: unit-test
description: Unit test focused piece (test analysis → test implementation → review → fix) description: Unit test focused piece (test analysis → test implementation → review → fix)
max_iterations: 20 max_movements: 20
initial_movement: plan_test initial_movement: plan_test
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -82,7 +82,7 @@ InstructionBuilder が instruction_template 内の `{変数名}` を展開する
| 変数 | 内容 | | 変数 | 内容 |
|------|------| |------|------|
| `{iteration}` | ピース全体のイテレーション数 | | `{iteration}` | ピース全体のイテレーション数 |
| `{max_iterations}` | 最大イテレーション数 | | `{max_movements}` | 最大イテレーション数 |
| `{movement_iteration}` | ムーブメント単位のイテレーション数 | | `{movement_iteration}` | ムーブメント単位のイテレーション数 |
| `{report_dir}` | レポートディレクトリ名(`.takt/runs/{slug}/reports` | | `{report_dir}` | レポートディレクトリ名(`.takt/runs/{slug}/reports` |
| `{report:filename}` | 指定レポートの内容展開(ファイルが存在する場合) | | `{report:filename}` | 指定レポートの内容展開(ファイルが存在する場合) |

View File

@ -1,6 +1,6 @@
name: coding name: coding
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
max_iterations: 20 max_movements: 20
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: compound-eye name: compound-eye
description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する
max_iterations: 10 max_movements: 10
initial_movement: evaluate initial_movement: evaluate
movements: movements:

View File

@ -1,6 +1,6 @@
name: default name: default
description: Standard development piece with planning and specialized reviews description: Standard development piece with planning and specialized reviews
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -1,6 +1,6 @@
name: e2e-test name: e2e-test
description: E2Eテスト追加に特化したピースE2E分析→E2E実装→レビュー→修正 description: E2Eテスト追加に特化したピースE2E分析→E2E実装→レビュー→修正
max_iterations: 20 max_movements: 20
initial_movement: plan_test initial_movement: plan_test
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -1,6 +1,6 @@
name: expert-cqrs name: expert-cqrs
description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: expert name: expert
description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: frontend name: frontend
description: フロントエンド・セキュリティ・QA専門家レビュー description: フロントエンド・セキュリティ・QA専門家レビュー
max_iterations: 30 max_movements: 30
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: magi name: magi
description: MAGI合議システム - 3つの観点から分析し多数決で判定 description: MAGI合議システム - 3つの観点から分析し多数決で判定
max_iterations: 5 max_movements: 5
initial_movement: melchior initial_movement: melchior
movements: movements:
- name: melchior - name: melchior

View File

@ -1,6 +1,6 @@
name: minimal name: minimal
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
max_iterations: 20 max_movements: 20
initial_movement: implement initial_movement: implement
movements: movements:
- name: implement - name: implement

View File

@ -1,6 +1,6 @@
name: passthrough name: passthrough
description: Single-agent thin wrapper. Pass task directly to coder as-is. description: Single-agent thin wrapper. Pass task directly to coder as-is.
max_iterations: 10 max_movements: 10
initial_movement: execute initial_movement: execute
movements: movements:
- name: execute - name: execute

View File

@ -1,6 +1,6 @@
name: research name: research
description: 調査ピース - 質問せずに自律的に調査を実行 description: 調査ピース - 質問せずに自律的に調査を実行
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan
@ -13,7 +13,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピース状況 ## ピース状況
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数) - ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: plan - ムーブメント: plan
@ -48,7 +48,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピース状況 ## ピース状況
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数) - ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: dig - ムーブメント: dig
@ -88,7 +88,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピース状況 ## ピース状況
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数) - ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: supervise (調査品質評価) - ムーブメント: supervise (調査品質評価)

View File

@ -1,6 +1,6 @@
name: review-fix-minimal name: review-fix-minimal
description: 既存コードのレビューと修正ピース(レビュー開始、実装なし) description: 既存コードのレビューと修正ピース(レビュー開始、実装なし)
max_iterations: 20 max_movements: 20
initial_movement: reviewers initial_movement: reviewers
movements: movements:
- name: implement - name: implement

View File

@ -1,6 +1,6 @@
name: review-only name: review-only
description: レビュー専用ピース - コードをレビューするだけで編集は行わない description: レビュー専用ピース - コードをレビューするだけで編集は行わない
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:
- name: plan - name: plan

View File

@ -1,6 +1,6 @@
name: structural-reform name: structural-reform
description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築 description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築
max_iterations: 50 max_movements: 50
initial_movement: review initial_movement: review
loop_monitors: loop_monitors:
- cycle: - cycle:
@ -44,7 +44,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピースステータス ## ピースステータス
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数) - ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: reviewプロジェクト全体レビュー - ムーブメント: reviewプロジェクト全体レビュー
@ -126,7 +126,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピースステータス ## ピースステータス
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数) - ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: plan_reform改革計画策定 - ムーブメント: plan_reform改革計画策定
@ -323,7 +323,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピースステータス ## ピースステータス
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数) - ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: verifyビルド・テスト検証 - ムーブメント: verifyビルド・テスト検証
@ -378,7 +378,7 @@ movements:
- WebFetch - WebFetch
instruction_template: | instruction_template: |
## ピースステータス ## ピースステータス
- イテレーション: {iteration}/{max_iterations}(ピース全体) - イテレーション: {iteration}/{max_movements}(ピース全体)
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数) - ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
- ムーブメント: next_target進捗確認と次ターゲット選択 - ムーブメント: next_target進捗確認と次ターゲット選択

View File

@ -1,6 +1,6 @@
name: unit-test name: unit-test
description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正) description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正)
max_iterations: 20 max_movements: 20
initial_movement: plan_test initial_movement: plan_test
loop_monitors: loop_monitors:
- cycle: - cycle:

View File

@ -83,7 +83,7 @@ $ARGUMENTS を以下のように解析する:
3. 見つからない場合: 上記2ディレクトリを Glob で列挙し、AskUserQuestion で選択させる 3. 見つからない場合: 上記2ディレクトリを Glob で列挙し、AskUserQuestion で選択させる
YAMLから以下を抽出する→ references/yaml-schema.md 参照): YAMLから以下を抽出する→ references/yaml-schema.md 参照):
- `name`, `max_iterations`, `initial_movement`, `movements` 配列 - `name`, `max_movements`, `initial_movement`, `movements` 配列
- セクションマップ: `personas`, `policies`, `instructions`, `output_contracts`, `knowledge` - セクションマップ: `personas`, `policies`, `instructions`, `output_contracts`, `knowledge`
### 手順 2: セクションリソースの事前読み込み ### 手順 2: セクションリソースの事前読み込み
@ -130,7 +130,7 @@ TeamCreate tool を呼ぶ:
### 手順 5: チームメイト起動 ### 手順 5: チームメイト起動
**iteration が max_iterations を超えていたら → 手順 8ABORT: イテレーション上限)に進む。** **iteration が max_movements を超えていたら → 手順 8ABORT: イテレーション上限)に進む。**
current_movement のプロンプトを構築する(→ references/engine.md のプロンプト構築を参照)。 current_movement のプロンプトを構築する(→ references/engine.md のプロンプト構築を参照)。

View File

@ -133,7 +133,7 @@ movement の `instruction:` キーから指示テンプレートファイルを
- ワーキングディレクトリ: {cwd} - ワーキングディレクトリ: {cwd}
- ピース: {piece_name} - ピース: {piece_name}
- Movement: {movement_name} - Movement: {movement_name}
- イテレーション: {iteration} / {max_iterations} - イテレーション: {iteration} / {max_movements}
- Movement イテレーション: {movement_iteration} 回目 - Movement イテレーション: {movement_iteration} 回目
``` ```
@ -146,7 +146,7 @@ movement の `instruction:` キーから指示テンプレートファイルを
| `{task}` | ユーザーが入力したタスク内容 | | `{task}` | ユーザーが入力したタスク内容 |
| `{previous_response}` | 前の movement のチームメイト出力 | | `{previous_response}` | 前の movement のチームメイト出力 |
| `{iteration}` | ピース全体のイテレーション数1始まり | | `{iteration}` | ピース全体のイテレーション数1始まり |
| `{max_iterations}` | ピースの max_iterations 値 | | `{max_movements}` | ピースの max_movements 値 |
| `{movement_iteration}` | この movement が実行された回数1始まり | | `{movement_iteration}` | この movement が実行された回数1始まり |
| `{report_dir}` | レポートディレクトリパス(`.takt/runs/{slug}/reports` | | `{report_dir}` | レポートディレクトリパス(`.takt/runs/{slug}/reports` |
| `{report:ファイル名}` | 指定レポートファイルの内容Read で取得) | | `{report:ファイル名}` | 指定レポートファイルの内容Read で取得) |
@ -317,7 +317,7 @@ parallel のサブステップにも同様にタグ出力指示を注入する
### 基本ルール ### 基本ルール
- 同じ movement が連続3回以上実行されたら警告を表示する - 同じ movement が連続3回以上実行されたら警告を表示する
- `max_iterations` に到達したら強制終了ABORTする - `max_movements` に到達したら強制終了ABORTする
### カウンター管理 ### カウンター管理

View File

@ -7,7 +7,7 @@
```yaml ```yaml
name: piece-name # ピース名(必須) name: piece-name # ピース名(必須)
description: 説明テキスト # ピースの説明(任意) description: 説明テキスト # ピースの説明(任意)
max_iterations: 10 # 最大イテレーション数(必須) max_movements: 10 # 最大イテレーション数(必須)
initial_movement: plan # 最初に実行する movement 名(必須) initial_movement: plan # 最初に実行する movement 名(必須)
# セクションマップ(キー → ファイルパスの対応表) # セクションマップ(キー → ファイルパスの対応表)
@ -192,7 +192,7 @@ quality_gates:
| `{task}` | ユーザーのタスク入力template に含まれない場合は自動追加) | | `{task}` | ユーザーのタスク入力template に含まれない場合は自動追加) |
| `{previous_response}` | 前の movement の出力pass_previous_response: true 時、自動追加) | | `{previous_response}` | 前の movement の出力pass_previous_response: true 時、自動追加) |
| `{iteration}` | ピース全体のイテレーション数 | | `{iteration}` | ピース全体のイテレーション数 |
| `{max_iterations}` | 最大イテレーション数 | | `{max_movements}` | 最大イテレーション数 |
| `{movement_iteration}` | この movement の実行回数 | | `{movement_iteration}` | この movement の実行回数 |
| `{report_dir}` | レポートディレクトリ名 | | `{report_dir}` | レポートディレクトリ名 |
| `{report:ファイル名}` | 指定レポートファイルの内容を展開 | | `{report:ファイル名}` | 指定レポートファイルの内容を展開 |

View File

@ -335,7 +335,7 @@ TAKTはYAMLベースのピース定義とルールベースルーティングを
```yaml ```yaml
name: default name: default
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
# セクションマップ — キー: ファイルパスこのYAMLからの相対パス # セクションマップ — キー: ファイルパスこのYAMLからの相対パス
@ -709,7 +709,7 @@ takt eject default
# ~/.takt/pieces/my-piece.yaml # ~/.takt/pieces/my-piece.yaml
name: my-piece name: my-piece
description: カスタムピース description: カスタムピース
max_iterations: 5 max_movements: 5
initial_movement: analyze initial_movement: analyze
personas: personas:
@ -759,7 +759,7 @@ personas:
|------|------| |------|------|
| `{task}` | 元のユーザーリクエスト(テンプレートになければ自動注入) | | `{task}` | 元のユーザーリクエスト(テンプレートになければ自動注入) |
| `{iteration}` | ピース全体のターン数(実行された全ムーブメント数) | | `{iteration}` | ピース全体のターン数(実行された全ムーブメント数) |
| `{max_iterations}` | 最大イテレーション数 | | `{max_movements}` | 最大イテレーション数 |
| `{movement_iteration}` | ムーブメントごとのイテレーション数(このムーブメントが実行された回数) | | `{movement_iteration}` | ムーブメントごとのイテレーション数(このムーブメントが実行された回数) |
| `{previous_response}` | 前のムーブメントの出力(テンプレートになければ自動注入) | | `{previous_response}` | 前のムーブメントの出力(テンプレートになければ自動注入) |
| `{user_inputs}` | ピース中の追加ユーザー入力(テンプレートになければ自動注入) | | `{user_inputs}` | ピース中の追加ユーザー入力(テンプレートになければ自動注入) |

View File

@ -498,7 +498,7 @@ TAKTのデータフローは以下の7つの主要なレイヤーで構成され
while (state.status === 'running') { while (state.status === 'running') {
// 1. Abort & Iteration チェック // 1. Abort & Iteration チェック
if (abortRequested) { ... } if (abortRequested) { ... }
if (iteration >= maxIterations) { ... } if (iteration >= maxMovements) { ... }
// 2. ステップ取得 // 2. ステップ取得
const step = getStep(state.currentStep); const step = getStep(state.currentStep);
@ -646,7 +646,7 @@ const match = await detectMatchedRule(step, response.content, tagContent, {...})
- `{previous_response}`: 前ステップの出力 - `{previous_response}`: 前ステップの出力
- `{user_inputs}`: 追加ユーザー入力 - `{user_inputs}`: 追加ユーザー入力
- `{iteration}`: ピース全体のイテレーション - `{iteration}`: ピース全体のイテレーション
- `{max_iterations}`: 最大イテレーション - `{max_movements}`: 最大イテレーション
- `{step_iteration}`: ステップのイテレーション - `{step_iteration}`: ステップのイテレーション
- `{report_dir}`: レポートディレクトリ - `{report_dir}`: レポートディレクトリ
@ -824,7 +824,7 @@ new PieceEngine(pieceConfig, cwd, task, {
1. **コンテキスト収集**: 1. **コンテキスト収集**:
- `task`: 元のユーザーリクエスト - `task`: 元のユーザーリクエスト
- `iteration`, `maxIterations`: イテレーション情報 - `iteration`, `maxMovements`: イテレーション情報
- `stepIteration`: ステップごとの実行回数 - `stepIteration`: ステップごとの実行回数
- `cwd`, `projectCwd`: ディレクトリ情報 - `cwd`, `projectCwd`: ディレクトリ情報
- `userInputs`: blocked時の追加入力 - `userInputs`: blocked時の追加入力

View File

@ -331,7 +331,7 @@ Faceted Promptingの中核メカニズムは**宣言的な合成**である。
```yaml ```yaml
name: my-workflow name: my-workflow
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:

View File

@ -331,7 +331,7 @@ Key properties:
```yaml ```yaml
name: my-workflow name: my-workflow
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:

View File

@ -25,7 +25,7 @@ A piece is a YAML file that defines a sequence of steps executed by AI agents. E
```yaml ```yaml
name: my-piece name: my-piece
description: Optional description description: Optional description
max_iterations: 10 max_movements: 10
initial_step: first-step # Optional, defaults to first step initial_step: first-step # Optional, defaults to first step
steps: steps:
@ -55,7 +55,7 @@ steps:
|----------|-------------| |----------|-------------|
| `{task}` | Original user request (auto-injected if not in template) | | `{task}` | Original user request (auto-injected if not in template) |
| `{iteration}` | Piece-wide turn count (total steps executed) | | `{iteration}` | Piece-wide turn count (total steps executed) |
| `{max_iterations}` | Maximum iterations allowed | | `{max_movements}` | Maximum movements allowed |
| `{step_iteration}` | Per-step iteration count (how many times THIS step has run) | | `{step_iteration}` | Per-step iteration count (how many times THIS step has run) |
| `{previous_response}` | Previous step's output (auto-injected if not in template) | | `{previous_response}` | Previous step's output (auto-injected if not in template) |
| `{user_inputs}` | Additional user inputs during piece (auto-injected if not in template) | | `{user_inputs}` | Additional user inputs during piece (auto-injected if not in template) |
@ -170,7 +170,7 @@ report:
```yaml ```yaml
name: simple-impl name: simple-impl
max_iterations: 5 max_movements: 5
steps: steps:
- name: implement - name: implement
@ -191,7 +191,7 @@ steps:
```yaml ```yaml
name: with-review name: with-review
max_iterations: 10 max_movements: 10
steps: steps:
- name: implement - name: implement

View File

@ -1,7 +1,7 @@
name: e2e-mock-max-iter name: e2e-mock-max-iter
description: Piece with max_iterations=2 that loops between two steps description: Piece with max_movements=2 that loops between two steps
max_iterations: 2 max_movements: 2
initial_movement: step-a initial_movement: step-a

View File

@ -1,7 +1,7 @@
name: e2e-mock-no-match name: e2e-mock-no-match
description: Piece with a strict rule condition that will not match mock output description: Piece with a strict rule condition that will not match mock output
max_iterations: 3 max_movements: 3
movements: movements:
- name: execute - name: execute

View File

@ -1,7 +1,7 @@
name: e2e-mock-single name: e2e-mock-single
description: Minimal mock-only piece for CLI E2E description: Minimal mock-only piece for CLI E2E
max_iterations: 3 max_movements: 3
movements: movements:
- name: execute - name: execute

View File

@ -1,7 +1,7 @@
name: e2e-mock-slow-multi-step name: e2e-mock-slow-multi-step
description: Multi-step mock piece to keep tasks in-flight long enough for SIGINT E2E description: Multi-step mock piece to keep tasks in-flight long enough for SIGINT E2E
max_iterations: 20 max_movements: 20
initial_movement: step-1 initial_movement: step-1

View File

@ -1,7 +1,7 @@
name: e2e-mock-two-step name: e2e-mock-two-step
description: Two-step sequential piece for E2E testing description: Two-step sequential piece for E2E testing
max_iterations: 5 max_movements: 5
initial_movement: step-1 initial_movement: step-1

View File

@ -1,7 +1,7 @@
name: e2e-multi-step-parallel name: e2e-multi-step-parallel
description: Multi-step piece with parallel sub-movements for E2E testing description: Multi-step piece with parallel sub-movements for E2E testing
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan

View File

@ -1,7 +1,7 @@
name: e2e-report-judge name: e2e-report-judge
description: E2E piece that exercises report + judge phases description: E2E piece that exercises report + judge phases
max_iterations: 3 max_movements: 3
movements: movements:
- name: execute - name: execute

View File

@ -1,7 +1,7 @@
name: e2e-simple name: e2e-simple
description: Minimal E2E test piece description: Minimal E2E test piece
max_iterations: 5 max_movements: 5
movements: movements:
- name: execute - name: execute

View File

@ -69,15 +69,15 @@ describe('E2E: Piece error handling (mock)', () => {
expect(combined).toMatch(/failed|aborted|error/i); expect(combined).toMatch(/failed|aborted|error/i);
}, 240_000); }, 240_000);
it('should abort when max_iterations is reached', () => { it('should abort when max_movements is reached', () => {
// Given: a piece with max_iterations=2 that loops between step-a and step-b // Given: a piece with max_movements=2 that loops between step-a and step-b
const piecePath = resolve(__dirname, '../fixtures/pieces/mock-max-iter.yaml'); const piecePath = resolve(__dirname, '../fixtures/pieces/mock-max-iter.yaml');
const scenarioPath = resolve(__dirname, '../fixtures/scenarios/max-iter-loop.json'); const scenarioPath = resolve(__dirname, '../fixtures/scenarios/max-iter-loop.json');
// When: executing the piece // When: executing the piece
const result = runTakt({ const result = runTakt({
args: [ args: [
'--task', 'Test max iterations', '--task', 'Test max movements',
'--piece', piecePath, '--piece', piecePath,
'--create-worktree', 'no', '--create-worktree', 'no',
'--provider', 'mock', '--provider', 'mock',
@ -93,7 +93,7 @@ describe('E2E: Piece error handling (mock)', () => {
// Then: piece aborts due to iteration limit // Then: piece aborts due to iteration limit
expect(result.exitCode).not.toBe(0); expect(result.exitCode).not.toBe(0);
const combined = result.stdout + result.stderr; const combined = result.stdout + result.stderr;
expect(combined).toMatch(/Max iterations|iteration|aborted/i); expect(combined).toMatch(/Max movements|iteration|aborted/i);
}, 240_000); }, 240_000);
it('should pass previous response between sequential steps', () => { it('should pass previous response between sequential steps', () => {

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "takt", "name": "takt",
"version": "0.11.0", "version": "0.11.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "takt", "name": "takt",
"version": "0.11.0", "version": "0.11.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.37", "@anthropic-ai/claude-agent-sdk": "^0.2.37",

View File

@ -22,7 +22,7 @@ describe('StreamDisplay', () => {
describe('progress info display', () => { describe('progress info display', () => {
const progressInfo: ProgressInfo = { const progressInfo: ProgressInfo = {
iteration: 3, iteration: 3,
maxIterations: 10, maxMovements: 10,
movementIndex: 1, movementIndex: 1,
totalMovements: 4, totalMovements: 4,
}; };
@ -253,7 +253,7 @@ describe('StreamDisplay', () => {
it('should format progress as (iteration/max) step index/total', () => { it('should format progress as (iteration/max) step index/total', () => {
const progressInfo: ProgressInfo = { const progressInfo: ProgressInfo = {
iteration: 5, iteration: 5,
maxIterations: 20, maxMovements: 20,
movementIndex: 2, movementIndex: 2,
totalMovements: 6, totalMovements: 6,
}; };
@ -267,7 +267,7 @@ describe('StreamDisplay', () => {
it('should convert 0-indexed movementIndex to 1-indexed display', () => { it('should convert 0-indexed movementIndex to 1-indexed display', () => {
const progressInfo: ProgressInfo = { const progressInfo: ProgressInfo = {
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIndex: 0, // First movement (0-indexed) movementIndex: 0, // First movement (0-indexed)
totalMovements: 4, totalMovements: 4,
}; };

View File

@ -188,7 +188,7 @@ describe('loadAllPieces', () => {
const samplePiece = ` const samplePiece = `
name: test-piece name: test-piece
description: Test piece description: Test piece
max_iterations: 10 max_movements: 10
movements: movements:
- name: step1 - name: step1
persona: coder persona: coder

View File

@ -65,7 +65,7 @@ describe('PieceEngine: Abort (SIGINT)', () => {
function makeSimpleConfig(): PieceConfig { function makeSimpleConfig(): PieceConfig {
return { return {
name: 'test', name: 'test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'step1', initialMovement: 'step1',
movements: [ movements: [
makeMovement('step1', { makeMovement('step1', {

View File

@ -54,7 +54,7 @@ describe('PieceEngine agent overrides', () => {
name: 'override-test', name: 'override-test',
movements: [movement], movements: [movement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -83,7 +83,7 @@ describe('PieceEngine agent overrides', () => {
name: 'override-fallback', name: 'override-fallback',
movements: [movement], movements: [movement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -114,7 +114,7 @@ describe('PieceEngine agent overrides', () => {
name: 'movement-defaults', name: 'movement-defaults',
movements: [movement], movements: [movement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([

View File

@ -75,7 +75,7 @@ function buildArpeggioPieceConfig(arpeggioConfig: ArpeggioMovementConfig, tmpDir
return { return {
name: 'test-arpeggio', name: 'test-arpeggio',
description: 'Test arpeggio piece', description: 'Test arpeggio piece',
maxIterations: 10, maxMovements: 10,
initialMovement: 'process', initialMovement: 'process',
movements: [ movements: [
{ {

View File

@ -117,7 +117,7 @@ describe('PieceEngine Integration: Error Handling', () => {
describe('Loop detection', () => { describe('Loop detection', () => {
it('should abort when loop detected with action: abort', async () => { it('should abort when loop detected with action: abort', async () => {
const config = buildDefaultPieceConfig({ const config = buildDefaultPieceConfig({
maxIterations: 100, maxMovements: 100,
loopDetection: { maxConsecutiveSameStep: 3, action: 'abort' }, loopDetection: { maxConsecutiveSameStep: 3, action: 'abort' },
initialMovement: 'loop-step', initialMovement: 'loop-step',
movements: [ movements: [
@ -156,7 +156,7 @@ describe('PieceEngine Integration: Error Handling', () => {
// ===================================================== // =====================================================
describe('Iteration limit', () => { describe('Iteration limit', () => {
it('should abort when max iterations reached without onIterationLimit callback', async () => { it('should abort when max iterations reached without onIterationLimit callback', async () => {
const config = buildDefaultPieceConfig({ maxIterations: 2 }); const config = buildDefaultPieceConfig({ maxMovements: 2 });
const engine = new PieceEngine(config, tmpDir, 'test task', { projectCwd: tmpDir }); const engine = new PieceEngine(config, tmpDir, 'test task', { projectCwd: tmpDir });
mockRunAgentSequence([ mockRunAgentSequence([
@ -182,11 +182,11 @@ describe('PieceEngine Integration: Error Handling', () => {
expect(limitFn).toHaveBeenCalledWith(2, 2); expect(limitFn).toHaveBeenCalledWith(2, 2);
expect(abortFn).toHaveBeenCalledOnce(); expect(abortFn).toHaveBeenCalledOnce();
const reason = abortFn.mock.calls[0]![1] as string; const reason = abortFn.mock.calls[0]![1] as string;
expect(reason).toContain('Max iterations'); expect(reason).toContain('Max movements');
}); });
it('should extend iterations when onIterationLimit provides additional iterations', async () => { it('should extend iterations when onIterationLimit provides additional iterations', async () => {
const config = buildDefaultPieceConfig({ maxIterations: 2 }); const config = buildDefaultPieceConfig({ maxMovements: 2 });
const onIterationLimit = vi.fn().mockResolvedValueOnce(10); const onIterationLimit = vi.fn().mockResolvedValueOnce(10);

View File

@ -388,7 +388,7 @@ describe('PieceEngine Integration: Happy Path', () => {
it('should pass instruction to movement:start for normal movements', async () => { it('should pass instruction to movement:start for normal movements', async () => {
const simpleConfig: PieceConfig = { const simpleConfig: PieceConfig = {
name: 'test', name: 'test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', { makeMovement('plan', {
@ -456,7 +456,7 @@ describe('PieceEngine Integration: Happy Path', () => {
}); });
it('should emit iteration:limit when max iterations reached', async () => { it('should emit iteration:limit when max iterations reached', async () => {
const config = buildDefaultPieceConfig({ maxIterations: 1 }); const config = buildDefaultPieceConfig({ maxMovements: 1 });
engine = new PieceEngine(config, tmpDir, 'test task', { projectCwd: tmpDir }); engine = new PieceEngine(config, tmpDir, 'test task', { projectCwd: tmpDir });
mockRunAgentSequence([ mockRunAgentSequence([
@ -518,7 +518,7 @@ describe('PieceEngine Integration: Happy Path', () => {
it('should emit phase:start and phase:complete events for Phase 1', async () => { it('should emit phase:start and phase:complete events for Phase 1', async () => {
const simpleConfig: PieceConfig = { const simpleConfig: PieceConfig = {
name: 'test', name: 'test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', { makeMovement('plan', {
@ -609,7 +609,7 @@ describe('PieceEngine Integration: Happy Path', () => {
it('should throw when rule references nonexistent movement', () => { it('should throw when rule references nonexistent movement', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'test', name: 'test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'step1', initialMovement: 'step1',
movements: [ movements: [
makeMovement('step1', { makeMovement('step1', {

View File

@ -60,7 +60,7 @@ function buildConfigWithLoopMonitor(
return { return {
name: 'test-loop-monitor', name: 'test-loop-monitor',
description: 'Test piece with loop monitors', description: 'Test piece with loop monitors',
maxIterations: 30, maxMovements: 30,
initialMovement: 'implement', initialMovement: 'implement',
loopMonitors: [ loopMonitors: [
{ {

View File

@ -54,7 +54,7 @@ function buildParallelOnlyConfig(): PieceConfig {
return { return {
name: 'test-parallel-failure', name: 'test-parallel-failure',
description: 'Test parallel failure handling', description: 'Test parallel failure handling',
maxIterations: 10, maxMovements: 10,
initialMovement: 'reviewers', initialMovement: 'reviewers',
movements: [ movements: [
makeMovement('reviewers', { makeMovement('reviewers', {

View File

@ -55,7 +55,7 @@ describe('PieceEngine persona_providers override', () => {
name: 'persona-provider-test', name: 'persona-provider-test',
movements: [movement], movements: [movement],
initialMovement: 'implement', initialMovement: 'implement',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -84,7 +84,7 @@ describe('PieceEngine persona_providers override', () => {
name: 'persona-provider-nomatch', name: 'persona-provider-nomatch',
movements: [movement], movements: [movement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -114,7 +114,7 @@ describe('PieceEngine persona_providers override', () => {
name: 'movement-over-persona', name: 'movement-over-persona',
movements: [movement], movements: [movement],
initialMovement: 'implement', initialMovement: 'implement',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -143,7 +143,7 @@ describe('PieceEngine persona_providers override', () => {
name: 'no-persona-providers', name: 'no-persona-providers',
movements: [movement], movements: [movement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 1, maxMovements: 1,
}; };
mockRunAgentSequence([ mockRunAgentSequence([
@ -175,7 +175,7 @@ describe('PieceEngine persona_providers override', () => {
name: 'multi-persona-providers', name: 'multi-persona-providers',
movements: [planMovement, implementMovement], movements: [planMovement, implementMovement],
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 3, maxMovements: 3,
}; };
mockRunAgentSequence([ mockRunAgentSequence([

View File

@ -70,7 +70,7 @@ export function buildDefaultPieceConfig(overrides: Partial<PieceConfig> = {}): P
return { return {
name: 'test-default', name: 'test-default',
description: 'Test piece', description: 'Test piece',
maxIterations: 30, maxMovements: 30,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', { makeMovement('plan', {

View File

@ -64,7 +64,7 @@ function buildSimpleConfig(): PieceConfig {
return { return {
name: 'worktree-test', name: 'worktree-test',
description: 'Test piece for worktree', description: 'Test piece for worktree',
maxIterations: 10, maxMovements: 10,
initialMovement: 'review', initialMovement: 'review',
movements: [ movements: [
makeMovement('review', { makeMovement('review', {
@ -133,7 +133,7 @@ describe('PieceEngine: worktree reportDir resolution', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'worktree-test', name: 'worktree-test',
description: 'Test', description: 'Test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'review', initialMovement: 'review',
movements: [ movements: [
makeMovement('review', { makeMovement('review', {
@ -247,7 +247,7 @@ describe('PieceEngine: worktree reportDir resolution', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'snapshot-test', name: 'snapshot-test',
description: 'Test', description: 'Test',
maxIterations: 10, maxMovements: 10,
initialMovement: 'implement', initialMovement: 'implement',
movements: [ movements: [
makeMovement('implement', { makeMovement('implement', {

View File

@ -26,7 +26,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
return { return {
task: 'test task', task: 'test task',
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIteration: 1, movementIteration: 1,
cwd: '/tmp/test', cwd: '/tmp/test',
projectCwd: '/tmp/project', projectCwd: '/tmp/project',
@ -78,10 +78,10 @@ describe('replaceTemplatePlaceholders', () => {
expect(result).toBe('fix bug in code'); expect(result).toBe('fix bug in code');
}); });
it('should replace {iteration} and {max_iterations}', () => { it('should replace {iteration} and {max_movements}', () => {
const step = makeMovement(); const step = makeMovement();
const ctx = makeContext({ iteration: 3, maxIterations: 20 }); const ctx = makeContext({ iteration: 3, maxMovements: 20 });
const template = 'Iteration {iteration}/{max_iterations}'; const template = 'Iteration {iteration}/{max_movements}';
const result = replaceTemplatePlaceholders(template, step, ctx); const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Iteration 3/20'); expect(result).toBe('Iteration 3/20');
@ -186,11 +186,11 @@ describe('replaceTemplatePlaceholders', () => {
const ctx = makeContext({ const ctx = makeContext({
task: 'test task', task: 'test task',
iteration: 2, iteration: 2,
maxIterations: 5, maxMovements: 5,
movementIteration: 1, movementIteration: 1,
reportDir: '/reports', reportDir: '/reports',
}); });
const template = '{task} - iter {iteration}/{max_iterations} - mv {movement_iteration} - dir {report_dir}'; const template = '{task} - iter {iteration}/{max_movements} - mv {movement_iteration} - dir {report_dir}';
const result = replaceTemplatePlaceholders(template, step, ctx); const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('test task - iter 2/5 - mv 1 - dir /reports'); expect(result).toBe('test task - iter 2/5 - mv 1 - dir /reports');

View File

@ -37,7 +37,7 @@ describe('getLabel', () => {
it('replaces {variableName} placeholders with provided values', () => { it('replaces {variableName} placeholders with provided values', () => {
const result = getLabel('piece.iterationLimit.maxReached', undefined, { const result = getLabel('piece.iterationLimit.maxReached', undefined, {
currentIteration: '5', currentIteration: '5',
maxIterations: '10', maxMovements: '10',
}); });
expect(result).toContain('(5/10)'); expect(result).toContain('(5/10)');
}); });

View File

@ -27,7 +27,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
return { return {
task: 'test task', task: 'test task',
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIteration: 1, movementIteration: 1,
cwd: '/tmp/test', cwd: '/tmp/test',
projectCwd: '/tmp/project', projectCwd: '/tmp/project',

View File

@ -41,7 +41,7 @@ function createMinimalContext(overrides: Partial<InstructionContext> = {}): Inst
return { return {
task: 'Test task', task: 'Test task',
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIteration: 1, movementIteration: 1,
cwd: '/project', cwd: '/project',
projectCwd: '/project', projectCwd: '/project',
@ -458,7 +458,7 @@ describe('instruction-builder', () => {
step.name = 'implement'; step.name = 'implement';
const context = createMinimalContext({ const context = createMinimalContext({
iteration: 3, iteration: 3,
maxIterations: 20, maxMovements: 20,
movementIteration: 2, movementIteration: 2,
language: 'en', language: 'en',
}); });
@ -1035,9 +1035,9 @@ describe('instruction-builder', () => {
expect(result).toContain('Build the app'); expect(result).toContain('Build the app');
}); });
it('should replace {iteration} and {max_iterations}', () => { it('should replace {iteration} and {max_movements}', () => {
const step = createMinimalStep('Step {iteration}/{max_iterations}'); const step = createMinimalStep('Step {iteration}/{max_movements}');
const context = createMinimalContext({ iteration: 3, maxIterations: 20 }); const context = createMinimalContext({ iteration: 3, maxMovements: 20 });
const result = buildInstruction(step, context); const result = buildInstruction(step, context);

View File

@ -99,11 +99,11 @@ function buildEngineOptions(projectCwd: string) {
}; };
} }
function buildPiece(agentPaths: Record<string, string>, maxIterations: number): PieceConfig { function buildPiece(agentPaths: Record<string, string>, maxMovements: number): PieceConfig {
return { return {
name: 'it-error', name: 'it-error',
description: 'IT error recovery piece', description: 'IT error recovery piece',
maxIterations, maxMovements,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', agentPaths.plan, [ makeMovement('plan', agentPaths.plan, [

View File

@ -57,7 +57,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
return { return {
task: 'Test task description', task: 'Test task description',
iteration: 3, iteration: 3,
maxIterations: 30, maxMovements: 30,
movementIteration: 2, movementIteration: 2,
cwd: '/tmp/test-project', cwd: '/tmp/test-project',
projectCwd: '/tmp/test-project', projectCwd: '/tmp/test-project',
@ -176,11 +176,11 @@ describe('Instruction Builder IT: user_inputs auto-injection', () => {
}); });
describe('Instruction Builder IT: iteration variables', () => { describe('Instruction Builder IT: iteration variables', () => {
it('should replace {iteration}, {max_iterations}, {movement_iteration} in template', () => { it('should replace {iteration}, {max_movements}, {movement_iteration} in template', () => {
const step = makeMovement({ const step = makeMovement({
instructionTemplate: 'Iter: {iteration}/{max_iterations}, movement iter: {movement_iteration}', instructionTemplate: 'Iter: {iteration}/{max_movements}, movement iter: {movement_iteration}',
}); });
const ctx = makeContext({ iteration: 5, maxIterations: 30, movementIteration: 2 }); const ctx = makeContext({ iteration: 5, maxMovements: 30, movementIteration: 2 });
const result = buildInstruction(step, ctx); const result = buildInstruction(step, ctx);
@ -189,7 +189,7 @@ describe('Instruction Builder IT: iteration variables', () => {
it('should include iteration in Piece Context section', () => { it('should include iteration in Piece Context section', () => {
const step = makeMovement(); const step = makeMovement();
const ctx = makeContext({ iteration: 7, maxIterations: 20, movementIteration: 3 }); const ctx = makeContext({ iteration: 7, maxMovements: 20, movementIteration: 3 });
const result = buildInstruction(step, ctx); const result = buildInstruction(step, ctx);

View File

@ -74,7 +74,7 @@ const {
if (this.onIterationLimit) { if (this.onIterationLimit) {
await this.onIterationLimit({ await this.onIterationLimit({
currentIteration: 10, currentIteration: 10,
maxIterations: 10, maxMovements: 10,
currentMovement: 'step1', currentMovement: 'step1',
}); });
} }
@ -201,7 +201,7 @@ import type { PieceConfig } from '../core/models/index.js';
function makeConfig(): PieceConfig { function makeConfig(): PieceConfig {
return { return {
name: 'test-notify', name: 'test-notify',
maxIterations: 10, maxMovements: 10,
initialMovement: 'step1', initialMovement: 'step1',
movements: [ movements: [
{ {

View File

@ -105,7 +105,7 @@ function buildSimplePiece(agentPaths: Record<string, string>): PieceConfig {
return { return {
name: 'it-simple', name: 'it-simple',
description: 'IT simple piece', description: 'IT simple piece',
maxIterations: 15, maxMovements: 15,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', agentPaths.planner, [ makeMovement('plan', agentPaths.planner, [
@ -128,7 +128,7 @@ function buildLoopPiece(agentPaths: Record<string, string>): PieceConfig {
return { return {
name: 'it-loop', name: 'it-loop',
description: 'IT piece with fix loop', description: 'IT piece with fix loop',
maxIterations: 20, maxMovements: 20,
initialMovement: 'plan', initialMovement: 'plan',
movements: [ movements: [
makeMovement('plan', agentPaths.planner, [ makeMovement('plan', agentPaths.planner, [
@ -286,7 +286,7 @@ describe('Piece Engine IT: Max Iterations', () => {
rmSync(testDir, { recursive: true, force: true }); rmSync(testDir, { recursive: true, force: true });
}); });
it('should abort when maxIterations exceeded in infinite loop', async () => { it('should abort when maxMovements exceeded in infinite loop', async () => {
// Create an infinite loop: plan always goes to implement, implement always goes back to plan // Create an infinite loop: plan always goes to implement, implement always goes back to plan
const infiniteScenario = Array.from({ length: 10 }, (_, i) => ({ const infiniteScenario = Array.from({ length: 10 }, (_, i) => ({
status: 'done' as const, status: 'done' as const,
@ -295,7 +295,7 @@ describe('Piece Engine IT: Max Iterations', () => {
setMockScenario(infiniteScenario); setMockScenario(infiniteScenario);
const config = buildSimplePiece(agentPaths); const config = buildSimplePiece(agentPaths);
config.maxIterations = 5; config.maxMovements = 5;
const engine = new PieceEngine(config, testDir, 'Looping task', { const engine = new PieceEngine(config, testDir, 'Looping task', {
...buildEngineOptions(testDir), ...buildEngineOptions(testDir),

View File

@ -58,7 +58,7 @@ describe('Piece Loader IT: builtin piece loading', () => {
expect(config!.name).toBe(name); expect(config!.name).toBe(name);
expect(config!.movements.length).toBeGreaterThan(0); expect(config!.movements.length).toBeGreaterThan(0);
expect(config!.initialMovement).toBeDefined(); expect(config!.initialMovement).toBeDefined();
expect(config!.maxIterations).toBeGreaterThan(0); expect(config!.maxMovements).toBeGreaterThan(0);
}); });
} }
@ -123,7 +123,7 @@ describe('Piece Loader IT: project-local piece override', () => {
writeFileSync(join(piecesDir, 'custom-wf.yaml'), ` writeFileSync(join(piecesDir, 'custom-wf.yaml'), `
name: custom-wf name: custom-wf
description: Custom project piece description: Custom project piece
max_iterations: 5 max_movements: 5
initial_movement: start initial_movement: start
movements: movements:
@ -250,11 +250,11 @@ describe('Piece Loader IT: piece config validation', () => {
rmSync(testDir, { recursive: true, force: true }); rmSync(testDir, { recursive: true, force: true });
}); });
it('should set max_iterations from YAML', () => { it('should set max_movements from YAML', () => {
const config = loadPiece('minimal', testDir); const config = loadPiece('minimal', testDir);
expect(config).not.toBeNull(); expect(config).not.toBeNull();
expect(typeof config!.maxIterations).toBe('number'); expect(typeof config!.maxMovements).toBe('number');
expect(config!.maxIterations).toBeGreaterThan(0); expect(config!.maxMovements).toBeGreaterThan(0);
}); });
it('should set initial_movement from YAML', () => { it('should set initial_movement from YAML', () => {
@ -397,7 +397,7 @@ describe('Piece Loader IT: quality_gates loading', () => {
writeFileSync(join(piecesDir, 'with-gates.yaml'), ` writeFileSync(join(piecesDir, 'with-gates.yaml'), `
name: with-gates name: with-gates
description: Piece with quality gates description: Piece with quality gates
max_iterations: 5 max_movements: 5
initial_movement: implement initial_movement: implement
movements: movements:
@ -434,7 +434,7 @@ movements:
writeFileSync(join(piecesDir, 'no-gates.yaml'), ` writeFileSync(join(piecesDir, 'no-gates.yaml'), `
name: no-gates name: no-gates
description: Piece without quality gates description: Piece without quality gates
max_iterations: 5 max_movements: 5
initial_movement: implement initial_movement: implement
movements: movements:
@ -461,7 +461,7 @@ movements:
writeFileSync(join(piecesDir, 'empty-gates.yaml'), ` writeFileSync(join(piecesDir, 'empty-gates.yaml'), `
name: empty-gates name: empty-gates
description: Piece with empty quality gates description: Piece with empty quality gates
max_iterations: 5 max_movements: 5
initial_movement: implement initial_movement: implement
movements: movements:
@ -501,7 +501,7 @@ describe('Piece Loader IT: mcp_servers parsing', () => {
writeFileSync(join(piecesDir, 'with-mcp.yaml'), ` writeFileSync(join(piecesDir, 'with-mcp.yaml'), `
name: with-mcp name: with-mcp
description: Piece with MCP servers description: Piece with MCP servers
max_iterations: 5 max_movements: 5
initial_movement: e2e-test initial_movement: e2e-test
movements: movements:
@ -541,7 +541,7 @@ movements:
writeFileSync(join(piecesDir, 'no-mcp.yaml'), ` writeFileSync(join(piecesDir, 'no-mcp.yaml'), `
name: no-mcp name: no-mcp
description: Piece without MCP servers description: Piece without MCP servers
max_iterations: 5 max_movements: 5
initial_movement: implement initial_movement: implement
movements: movements:
@ -568,7 +568,7 @@ movements:
writeFileSync(join(piecesDir, 'multi-mcp.yaml'), ` writeFileSync(join(piecesDir, 'multi-mcp.yaml'), `
name: multi-mcp name: multi-mcp
description: Piece with multiple MCP servers description: Piece with multiple MCP servers
max_iterations: 5 max_movements: 5
initial_movement: test initial_movement: test
movements: movements:
@ -625,7 +625,7 @@ describe('Piece Loader IT: structural-reform piece', () => {
expect(config).not.toBeNull(); expect(config).not.toBeNull();
expect(config!.name).toBe('structural-reform'); expect(config!.name).toBe('structural-reform');
expect(config!.movements.length).toBe(7); expect(config!.movements.length).toBe(7);
expect(config!.maxIterations).toBe(50); expect(config!.maxMovements).toBe(50);
expect(config!.initialMovement).toBe('review'); expect(config!.initialMovement).toBe('review');
}); });

View File

@ -171,7 +171,7 @@ function createTestPieceDir(): { dir: string; piecePath: string } {
const pieceYaml = ` const pieceYaml = `
name: it-pipeline name: it-pipeline
description: Pipeline test piece description: Pipeline test piece
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:

View File

@ -152,7 +152,7 @@ function createTestPieceDir(): { dir: string; piecePath: string } {
const pieceYaml = ` const pieceYaml = `
name: it-simple name: it-simple
description: Integration test piece description: Integration test piece
max_iterations: 10 max_movements: 10
initial_movement: plan initial_movement: plan
movements: movements:

View File

@ -196,7 +196,7 @@ describe('executePiece: SIGINT handler integration', () => {
function makeConfig(): PieceConfig { function makeConfig(): PieceConfig {
return { return {
name: 'test-sigint', name: 'test-sigint',
maxIterations: 10, maxMovements: 10,
initialMovement: 'step1', initialMovement: 'step1',
movements: [ movements: [
{ {

View File

@ -133,7 +133,7 @@ describe('Three-Phase Execution IT: phase1 only (no report, no tag rules)', () =
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-phase1-only', name: 'it-phase1-only',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step', initialMovement: 'step',
movements: [ movements: [
makeMovement('step', agentPath, [ makeMovement('step', agentPath, [
@ -185,7 +185,7 @@ describe('Three-Phase Execution IT: phase1 + phase2 (report defined)', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-phase1-2', name: 'it-phase1-2',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step', initialMovement: 'step',
movements: [ movements: [
makeMovement('step', agentPath, [ makeMovement('step', agentPath, [
@ -215,7 +215,7 @@ describe('Three-Phase Execution IT: phase1 + phase2 (report defined)', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-phase1-2-multi', name: 'it-phase1-2-multi',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step', initialMovement: 'step',
movements: [ movements: [
makeMovement('step', agentPath, [ makeMovement('step', agentPath, [
@ -266,7 +266,7 @@ describe('Three-Phase Execution IT: phase1 + phase3 (tag rules defined)', () =>
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-phase1-3', name: 'it-phase1-3',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step', initialMovement: 'step',
movements: [ movements: [
makeMovement('step', agentPath, [ makeMovement('step', agentPath, [
@ -317,7 +317,7 @@ describe('Three-Phase Execution IT: all three phases', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-all-phases', name: 'it-all-phases',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step', initialMovement: 'step',
movements: [ movements: [
makeMovement('step', agentPath, [ makeMovement('step', agentPath, [
@ -377,7 +377,7 @@ describe('Three-Phase Execution IT: phase3 tag → rule match', () => {
const config: PieceConfig = { const config: PieceConfig = {
name: 'it-phase3-tag', name: 'it-phase3-tag',
description: 'Test', description: 'Test',
maxIterations: 5, maxMovements: 5,
initialMovement: 'step1', initialMovement: 'step1',
movements: [ movements: [
makeMovement('step1', agentPath, [ makeMovement('step1', agentPath, [

View File

@ -330,7 +330,7 @@ function createMinimalContext(overrides: Partial<InstructionContext> = {}): Inst
return { return {
task: 'Test task', task: 'Test task',
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIteration: 1, movementIteration: 1,
cwd: '/tmp/test', cwd: '/tmp/test',
projectCwd: '/tmp/test', projectCwd: '/tmp/test',

View File

@ -81,7 +81,7 @@ describe('PieceConfigRawSchema', () => {
expect(result.name).toBe('test-piece'); expect(result.name).toBe('test-piece');
expect(result.movements).toHaveLength(1); expect(result.movements).toHaveLength(1);
expect(result.movements![0]?.allowed_tools).toEqual(['Read', 'Grep']); expect(result.movements![0]?.allowed_tools).toEqual(['Read', 'Grep']);
expect(result.max_iterations).toBe(10); expect(result.max_movements).toBe(10);
}); });
it('should parse movement with permission_mode', () => { it('should parse movement with permission_mode', () => {

View File

@ -145,7 +145,7 @@ describe('PieceConfigRawSchema with parallel movements', () => {
}, },
], ],
initial_movement: 'plan', initial_movement: 'plan',
max_iterations: 10, max_movements: 10,
}; };
const result = PieceConfigRawSchema.safeParse(raw); const result = PieceConfigRawSchema.safeParse(raw);

View File

@ -74,7 +74,7 @@ describe('ParallelLogger', () => {
writeFn, writeFn,
progressInfo: { progressInfo: {
iteration: 4, iteration: 4,
maxIterations: 30, maxMovements: 30,
}, },
taskLabel: 'override-persona-provider', taskLabel: 'override-persona-provider',
taskColorIndex: 0, taskColorIndex: 0,
@ -529,7 +529,7 @@ describe('ParallelLogger', () => {
writeFn, writeFn,
progressInfo: { progressInfo: {
iteration: 3, iteration: 3,
maxIterations: 10, maxMovements: 10,
}, },
}); });
@ -545,7 +545,7 @@ describe('ParallelLogger', () => {
writeFn, writeFn,
progressInfo: { progressInfo: {
iteration: 5, iteration: 5,
maxIterations: 20, maxMovements: 20,
}, },
}); });
@ -576,7 +576,7 @@ describe('ParallelLogger', () => {
writeFn, writeFn,
progressInfo: { progressInfo: {
iteration: 2, iteration: 2,
maxIterations: 5, maxMovements: 5,
}, },
}); });
const handler = logger.createStreamHandler('step-a', 0); const handler = logger.createStreamHandler('step-a', 0);

View File

@ -24,7 +24,7 @@ import {
const SAMPLE_PIECE = `name: test-piece const SAMPLE_PIECE = `name: test-piece
description: Test piece description: Test piece
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1

View File

@ -65,7 +65,7 @@ function createPieceMap(entries: { name: string; source: 'builtin' | 'user' | 'p
name: entry.name, name: entry.name,
movements: [], movements: [],
initialMovement: 'start', initialMovement: 'start',
maxIterations: 1, maxMovements: 1,
}, },
}); });
} }

View File

@ -78,7 +78,7 @@ function createPieceMap(entries: { name: string; source: 'user' | 'builtin' }[])
name: e.name, name: e.name,
movements: [], movements: [],
initialMovement: 'start', initialMovement: 'start',
maxIterations: 1, maxMovements: 1,
}, },
}); });
} }

View File

@ -170,7 +170,7 @@ describe('executePiece debug prompts logging', () => {
function makeConfig(): PieceConfig { function makeConfig(): PieceConfig {
return { return {
name: 'test-piece', name: 'test-piece',
maxIterations: 5, maxMovements: 5,
initialMovement: 'implement', initialMovement: 'implement',
movements: [ movements: [
{ {

View File

@ -16,7 +16,7 @@ import {
const SAMPLE_PIECE = `name: test-piece const SAMPLE_PIECE = `name: test-piece
description: Test piece description: Test piece
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -173,7 +173,7 @@ describe('loadAllPieces with project-local', () => {
const overridePiece = `name: project-override const overridePiece = `name: project-override
description: Project override description: Project override
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1

View File

@ -23,7 +23,7 @@ describe('getPieceDescription', () => {
const pieceYaml = `name: test-piece const pieceYaml = `name: test-piece
description: Test piece for workflow description: Test piece for workflow
initial_movement: plan initial_movement: plan
max_iterations: 3 max_movements: 3
movements: movements:
- name: plan - name: plan
@ -56,7 +56,7 @@ movements:
const pieceYaml = `name: coding const pieceYaml = `name: coding
description: Full coding workflow description: Full coding workflow
initial_movement: plan initial_movement: plan
max_iterations: 10 max_movements: 10
movements: movements:
- name: plan - name: plan
@ -98,7 +98,7 @@ movements:
it('should handle movements without descriptions', () => { it('should handle movements without descriptions', () => {
const pieceYaml = `name: minimal const pieceYaml = `name: minimal
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -132,7 +132,7 @@ movements:
it('should handle parallel movements without descriptions', () => { it('should handle parallel movements without descriptions', () => {
const pieceYaml = `name: test-parallel const pieceYaml = `name: test-parallel
initial_movement: parent initial_movement: parent
max_iterations: 1 max_movements: 1
movements: movements:
- name: parent - name: parent
@ -174,7 +174,7 @@ describe('getPieceDescription with movementPreviews', () => {
const pieceYaml = `name: preview-test const pieceYaml = `name: preview-test
description: Test piece description: Test piece
initial_movement: plan initial_movement: plan
max_iterations: 5 max_movements: 5
movements: movements:
- name: plan - name: plan
@ -237,7 +237,7 @@ movements:
it('should return empty previews when previewCount is 0', () => { it('should return empty previews when previewCount is 0', () => {
const pieceYaml = `name: test const pieceYaml = `name: test
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -256,7 +256,7 @@ movements:
it('should return empty previews when previewCount is not specified', () => { it('should return empty previews when previewCount is not specified', () => {
const pieceYaml = `name: test const pieceYaml = `name: test
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -275,7 +275,7 @@ movements:
it('should stop at COMPLETE movement', () => { it('should stop at COMPLETE movement', () => {
const pieceYaml = `name: test-complete const pieceYaml = `name: test-complete
initial_movement: step1 initial_movement: step1
max_iterations: 3 max_movements: 3
movements: movements:
- name: step1 - name: step1
@ -301,7 +301,7 @@ movements:
it('should stop at ABORT movement', () => { it('should stop at ABORT movement', () => {
const pieceYaml = `name: test-abort const pieceYaml = `name: test-abort
initial_movement: step1 initial_movement: step1
max_iterations: 3 max_movements: 3
movements: movements:
- name: step1 - name: step1
@ -331,7 +331,7 @@ movements:
const pieceYaml = `name: test-persona-file const pieceYaml = `name: test-persona-file
initial_movement: plan initial_movement: plan
max_iterations: 1 max_movements: 1
personas: personas:
planner: ./planner.md planner: ./planner.md
@ -355,7 +355,7 @@ movements:
it('should limit previews to maxCount', () => { it('should limit previews to maxCount', () => {
const pieceYaml = `name: test-limit const pieceYaml = `name: test-limit
initial_movement: step1 initial_movement: step1
max_iterations: 5 max_movements: 5
movements: movements:
- name: step1 - name: step1
@ -388,7 +388,7 @@ movements:
it('should handle movements without rules (stop after first)', () => { it('should handle movements without rules (stop after first)', () => {
const pieceYaml = `name: test-no-rules const pieceYaml = `name: test-no-rules
initial_movement: step1 initial_movement: step1
max_iterations: 3 max_movements: 3
movements: movements:
- name: step1 - name: step1
@ -411,7 +411,7 @@ movements:
it('should return empty previews when initial movement not found in list', () => { it('should return empty previews when initial movement not found in list', () => {
const pieceYaml = `name: test-missing-initial const pieceYaml = `name: test-missing-initial
initial_movement: nonexistent initial_movement: nonexistent
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -430,7 +430,7 @@ movements:
it('should handle self-referencing rule (prevent infinite loop)', () => { it('should handle self-referencing rule (prevent infinite loop)', () => {
const pieceYaml = `name: test-self-ref const pieceYaml = `name: test-self-ref
initial_movement: step1 initial_movement: step1
max_iterations: 5 max_movements: 5
movements: movements:
- name: step1 - name: step1
@ -453,7 +453,7 @@ movements:
it('should handle multi-node cycle A→B→A (prevent duplicate previews)', () => { it('should handle multi-node cycle A→B→A (prevent duplicate previews)', () => {
const pieceYaml = `name: test-cycle const pieceYaml = `name: test-cycle
initial_movement: stepA initial_movement: stepA
max_iterations: 10 max_movements: 10
movements: movements:
- name: stepA - name: stepA
@ -489,7 +489,7 @@ movements:
it('should use inline persona content when no personaPath', () => { it('should use inline persona content when no personaPath', () => {
const pieceYaml = `name: test-inline const pieceYaml = `name: test-inline
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -515,7 +515,7 @@ movements:
const pieceYaml = `name: test-unreadable-persona const pieceYaml = `name: test-unreadable-persona
initial_movement: plan initial_movement: plan
max_iterations: 1 max_movements: 1
personas: personas:
planner: ./unreadable-persona.md planner: ./unreadable-persona.md
@ -545,7 +545,7 @@ movements:
it('should include personaDisplayName in previews', () => { it('should include personaDisplayName in previews', () => {
const pieceYaml = `name: test-display const pieceYaml = `name: test-display
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -578,7 +578,7 @@ describe('getPieceDescription interactiveMode field', () => {
it('should return interactiveMode when piece defines interactive_mode', () => { it('should return interactiveMode when piece defines interactive_mode', () => {
const pieceYaml = `name: test-mode const pieceYaml = `name: test-mode
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
interactive_mode: quiet interactive_mode: quiet
movements: movements:
@ -598,7 +598,7 @@ movements:
it('should return undefined interactiveMode when piece omits interactive_mode', () => { it('should return undefined interactiveMode when piece omits interactive_mode', () => {
const pieceYaml = `name: test-no-mode const pieceYaml = `name: test-no-mode
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -618,7 +618,7 @@ movements:
for (const mode of ['assistant', 'persona', 'quiet', 'passthrough'] as const) { for (const mode of ['assistant', 'persona', 'quiet', 'passthrough'] as const) {
const pieceYaml = `name: test-${mode} const pieceYaml = `name: test-${mode}
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
interactive_mode: ${mode} interactive_mode: ${mode}
movements: movements:
@ -651,7 +651,7 @@ describe('getPieceDescription firstMovement field', () => {
it('should return firstMovement with inline persona content', () => { it('should return firstMovement with inline persona content', () => {
const pieceYaml = `name: test-first const pieceYaml = `name: test-first
initial_movement: plan initial_movement: plan
max_iterations: 1 max_movements: 1
movements: movements:
- name: plan - name: plan
@ -681,7 +681,7 @@ movements:
const pieceYaml = `name: test-persona-file const pieceYaml = `name: test-persona-file
initial_movement: plan initial_movement: plan
max_iterations: 1 max_movements: 1
personas: personas:
planner: ./planner-persona.md planner: ./planner-persona.md
@ -705,7 +705,7 @@ movements:
it('should return undefined firstMovement when initialMovement not found', () => { it('should return undefined firstMovement when initialMovement not found', () => {
const pieceYaml = `name: test-missing const pieceYaml = `name: test-missing
initial_movement: nonexistent initial_movement: nonexistent
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -724,7 +724,7 @@ movements:
it('should return empty allowedTools array when movement has no tools', () => { it('should return empty allowedTools array when movement has no tools', () => {
const pieceYaml = `name: test-no-tools const pieceYaml = `name: test-no-tools
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
movements: movements:
- name: step1 - name: step1
@ -749,7 +749,7 @@ movements:
const pieceYaml = `name: test-fallback const pieceYaml = `name: test-fallback
initial_movement: step1 initial_movement: step1
max_iterations: 1 max_movements: 1
personas: personas:
myagent: ./unreadable.md myagent: ./unreadable.md

View File

@ -27,7 +27,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
return { return {
task: 'Test task', task: 'Test task',
iteration: 1, iteration: 1,
maxIterations: 10, maxMovements: 10,
movementIteration: 1, movementIteration: 1,
cwd: '/tmp/test', cwd: '/tmp/test',
projectCwd: '/tmp/test', projectCwd: '/tmp/test',

View File

@ -36,8 +36,8 @@ describe('review-only piece (EN)', () => {
expect(raw.initial_movement).toBe('plan'); expect(raw.initial_movement).toBe('plan');
}); });
it('should have max_iterations of 10', () => { it('should have max_movements of 10', () => {
expect(raw.max_iterations).toBe(10); expect(raw.max_movements).toBe(10);
}); });
it('should have 4 movements: plan, reviewers, supervise, pr-comment', () => { it('should have 4 movements: plan, reviewers, supervise, pr-comment', () => {

View File

@ -311,7 +311,7 @@ describe('runAllTasks concurrency', () => {
name: 'default', name: 'default',
movements: [{ name: 'implement', personaDisplayName: 'coder' }], movements: [{ name: 'implement', personaDisplayName: 'coder' }],
initialMovement: 'implement', initialMovement: 'implement',
maxIterations: 10, maxMovements: 10,
}; };
beforeEach(() => { beforeEach(() => {

View File

@ -131,7 +131,7 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
name: 'default', name: 'default',
movements: [], movements: [],
initialMovement: 'start', initialMovement: 'start',
maxIterations: 1, maxMovements: 1,
}, },
}], }],
])); ]));

View File

@ -188,7 +188,7 @@ describe('NDJSON log', () => {
const abort: NdjsonPieceAbort = { const abort: NdjsonPieceAbort = {
type: 'piece_abort', type: 'piece_abort',
iterations: 1, iterations: 1,
reason: 'Max iterations reached', reason: 'Max movements reached',
endTime: '2025-01-01T00:00:03.000Z', endTime: '2025-01-01T00:00:03.000Z',
}; };
appendNdjsonLine(filepath, abort); appendNdjsonLine(filepath, abort);

View File

@ -22,7 +22,7 @@ function makeConfig(overrides: Partial<PieceConfig> = {}): PieceConfig {
name: 'test-piece', name: 'test-piece',
movements: [], movements: [],
initialMovement: 'start', initialMovement: 'start',
maxIterations: 10, maxMovements: 10,
...overrides, ...overrides,
}; };
} }

View File

@ -57,7 +57,7 @@ describe('switchPiece', () => {
name: 'default', name: 'default',
movements: [], movements: [],
initialMovement: 'start', initialMovement: 'start',
maxIterations: 1, maxMovements: 1,
}, },
}], }],
])); ]));

View File

@ -188,7 +188,7 @@ describe('TaskPrefixWriter', () => {
writer.setMovementContext({ writer.setMovementContext({
movementName: 'implement', movementName: 'implement',
iteration: 4, iteration: 4,
maxIterations: 30, maxMovements: 30,
movementIteration: 2, movementIteration: 2,
}); });
writer.writeLine('content'); writer.writeLine('content');

View File

@ -51,7 +51,7 @@ const defaultPieceConfig: PieceConfig = {
name: 'default', name: 'default',
description: 'Default piece', description: 'Default piece',
initialMovement: 'plan', initialMovement: 'plan',
maxIterations: 30, maxMovements: 30,
movements: [ movements: [
{ name: 'plan', persona: 'planner', instruction: '' }, { name: 'plan', persona: 'planner', instruction: '' },
{ name: 'implement', persona: 'coder', instruction: '' }, { name: 'implement', persona: 'coder', instruction: '' },

View File

@ -210,7 +210,7 @@ export interface PieceConfig {
reportFormats?: Record<string, string>; reportFormats?: Record<string, string>;
movements: PieceMovement[]; movements: PieceMovement[];
initialMovement: string; initialMovement: string;
maxIterations: number; maxMovements: number;
/** Loop detection settings */ /** Loop detection settings */
loopDetection?: LoopDetectionConfig; loopDetection?: LoopDetectionConfig;
/** Loop monitors for detecting cyclic patterns between movements */ /** Loop monitors for detecting cyclic patterns between movements */

View File

@ -281,7 +281,7 @@ export const PieceConfigRawSchema = z.object({
report_formats: z.record(z.string(), z.string()).optional(), report_formats: z.record(z.string(), z.string()).optional(),
movements: z.array(PieceMovementRawSchema).min(1), movements: z.array(PieceMovementRawSchema).min(1),
initial_movement: z.string().optional(), initial_movement: z.string().optional(),
max_iterations: z.number().int().positive().optional().default(10), max_movements: z.number().int().positive().optional().default(10),
loop_monitors: z.array(LoopMonitorSchema).optional(), loop_monitors: z.array(LoopMonitorSchema).optional(),
answer_agent: z.string().optional(), answer_agent: z.string().optional(),
/** Default interactive mode for this piece (overrides user default) */ /** Default interactive mode for this piece (overrides user default) */

View File

@ -12,7 +12,7 @@ export interface SessionState {
task: string; task: string;
projectDir: string; projectDir: string;
iteration: number; iteration: number;
maxIterations: number; maxMovements: number;
coderStatus: Status; coderStatus: Status;
architectStatus: Status; architectStatus: Status;
supervisorStatus: Status; supervisorStatus: Status;
@ -32,7 +32,7 @@ export function createSessionState(
task, task,
projectDir, projectDir,
iteration: 0, iteration: 0,
maxIterations: 10, maxMovements: 10,
coderStatus: 'pending', coderStatus: 'pending',
architectStatus: 'pending', architectStatus: 'pending',
supervisorStatus: 'pending', supervisorStatus: 'pending',

View File

@ -19,5 +19,5 @@ export const ERROR_MESSAGES = {
`Loop detected: movement "${movementName}" ran ${count} times consecutively without progress.`, `Loop detected: movement "${movementName}" ran ${count} times consecutively without progress.`,
UNKNOWN_MOVEMENT: (movementName: string) => `Unknown movement: ${movementName}`, UNKNOWN_MOVEMENT: (movementName: string) => `Unknown movement: ${movementName}`,
MOVEMENT_EXECUTION_FAILED: (message: string) => `Movement execution failed: ${message}`, MOVEMENT_EXECUTION_FAILED: (message: string) => `Movement execution failed: ${message}`,
MAX_ITERATIONS_REACHED: 'Max iterations reached', MAX_MOVEMENTS_REACHED: 'Max movements reached',
}; };

View File

@ -131,7 +131,7 @@ export class MovementExecutor {
movementIteration: number, movementIteration: number,
state: PieceState, state: PieceState,
task: string, task: string,
maxIterations: number, maxMovements: number,
): string { ): string {
this.ensurePreviousResponseSnapshot(state, step.name, movementIteration); this.ensurePreviousResponseSnapshot(state, step.name, movementIteration);
const policySnapshot = this.writeFacetSnapshot( const policySnapshot = this.writeFacetSnapshot(
@ -150,7 +150,7 @@ export class MovementExecutor {
return new InstructionBuilder(step, { return new InstructionBuilder(step, {
task, task,
iteration: state.iteration, iteration: state.iteration,
maxIterations, maxMovements,
movementIteration, movementIteration,
cwd: this.deps.getCwd(), cwd: this.deps.getCwd(),
projectCwd: this.deps.getProjectCwd(), projectCwd: this.deps.getProjectCwd(),
@ -182,14 +182,14 @@ export class MovementExecutor {
step: PieceMovement, step: PieceMovement,
state: PieceState, state: PieceState,
task: string, task: string,
maxIterations: number, maxMovements: number,
updatePersonaSession: (persona: string, sessionId: string | undefined) => void, updatePersonaSession: (persona: string, sessionId: string | undefined) => void,
prebuiltInstruction?: string, prebuiltInstruction?: string,
): Promise<{ response: AgentResponse; instruction: string }> { ): Promise<{ response: AgentResponse; instruction: string }> {
const movementIteration = prebuiltInstruction const movementIteration = prebuiltInstruction
? state.movementIterations.get(step.name) ?? 1 ? state.movementIterations.get(step.name) ?? 1
: incrementMovementIteration(state, step.name); : incrementMovementIteration(state, step.name);
const instruction = prebuiltInstruction ?? this.buildInstruction(step, movementIteration, state, task, maxIterations); const instruction = prebuiltInstruction ?? this.buildInstruction(step, movementIteration, state, task, maxMovements);
const sessionKey = buildSessionKey(step); const sessionKey = buildSessionKey(step);
log.debug('Running movement', { log.debug('Running movement', {
movement: step.name, movement: step.name,

Some files were not shown because too many files have changed in this diff Show More