takt: github-issue-212-max-iteration-max-movement-ostinato (#217)
This commit is contained in:
parent
0214f7f5e6
commit
de6b5b5c2c
@ -218,7 +218,7 @@ Builtin resources are embedded in the npm package (`builtins/`). User files in `
|
||||
```yaml
|
||||
name: piece-name
|
||||
description: Optional description
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_step: plan # First step to execute
|
||||
|
||||
steps:
|
||||
@ -291,7 +291,7 @@ Key points about parallel steps:
|
||||
|----------|-------------|
|
||||
| `{task}` | Original user request (auto-injected if not in template) |
|
||||
| `{iteration}` | Piece-wide iteration count |
|
||||
| `{max_iterations}` | Maximum iterations allowed |
|
||||
| `{max_movements}` | Maximum movements allowed |
|
||||
| `{step_iteration}` | Per-step iteration count |
|
||||
| `{previous_response}` | Previous step output (auto-injected if not in template) |
|
||||
| `{user_inputs}` | Accumulated user inputs (auto-injected if not in template) |
|
||||
|
||||
@ -335,7 +335,7 @@ TAKT uses YAML-based piece definitions and rule-based routing. Builtin pieces ar
|
||||
|
||||
```yaml
|
||||
name: default
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
# Section maps — key: file path (relative to this YAML)
|
||||
@ -709,7 +709,7 @@ takt eject default
|
||||
# ~/.takt/pieces/my-piece.yaml
|
||||
name: my-piece
|
||||
description: Custom piece
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: analyze
|
||||
|
||||
personas:
|
||||
@ -759,7 +759,7 @@ Variables available in `instruction_template`:
|
||||
|----------|-------------|
|
||||
| `{task}` | Original user request (auto-injected if not in template) |
|
||||
| `{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) |
|
||||
| `{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) |
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: coding
|
||||
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: compound-eye
|
||||
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
|
||||
movements:
|
||||
- name: evaluate
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: default
|
||||
description: Standard development piece with planning and specialized reviews
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: e2e-test
|
||||
description: E2E test focused piece (E2E analysis → E2E implementation → review → fix)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan_test
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: expert-cqrs
|
||||
description: CQRS+ES, Frontend, Security, QA Expert Review
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: expert
|
||||
description: Architecture, Frontend, Security, QA Expert Review
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: frontend
|
||||
description: Frontend, Security, QA Expert Review
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: magi
|
||||
description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: melchior
|
||||
movements:
|
||||
- name: melchior
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: minimal
|
||||
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: implement
|
||||
movements:
|
||||
- name: implement
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: passthrough
|
||||
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: execute
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: research
|
||||
description: Research piece - autonomously executes research without asking questions
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
@ -13,7 +13,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: plan
|
||||
|
||||
@ -48,7 +48,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: dig
|
||||
|
||||
@ -88,7 +88,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: supervise (research quality evaluation)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: review-fix-minimal
|
||||
description: Review and fix piece for existing code (starts with review, no implementation)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: reviewers
|
||||
movements:
|
||||
- name: implement
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: review-only
|
||||
description: Review-only piece - reviews code without making edits
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: structural-reform
|
||||
description: Full project review and structural reform - iterative codebase restructuring with staged file splits
|
||||
max_iterations: 50
|
||||
max_movements: 50
|
||||
initial_movement: review
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
@ -44,7 +44,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: review (full project review)
|
||||
|
||||
@ -126,7 +126,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: plan_reform (reform plan creation)
|
||||
|
||||
@ -323,7 +323,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: verify (build and test verification)
|
||||
|
||||
@ -378,7 +378,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## Piece Status
|
||||
- Iteration: {iteration}/{max_iterations} (piece-wide)
|
||||
- Iteration: {iteration}/{max_movements} (piece-wide)
|
||||
- Movement Iteration: {movement_iteration} (times this movement has run)
|
||||
- Movement: next_target (progress check and next target selection)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: unit-test
|
||||
description: Unit test focused piece (test analysis → test implementation → review → fix)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan_test
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -82,7 +82,7 @@ InstructionBuilder が instruction_template 内の `{変数名}` を展開する
|
||||
| 変数 | 内容 |
|
||||
|------|------|
|
||||
| `{iteration}` | ピース全体のイテレーション数 |
|
||||
| `{max_iterations}` | 最大イテレーション数 |
|
||||
| `{max_movements}` | 最大イテレーション数 |
|
||||
| `{movement_iteration}` | ムーブメント単位のイテレーション数 |
|
||||
| `{report_dir}` | レポートディレクトリ名(`.takt/runs/{slug}/reports`) |
|
||||
| `{report:filename}` | 指定レポートの内容展開(ファイルが存在する場合) |
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: coding
|
||||
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: compound-eye
|
||||
description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: evaluate
|
||||
|
||||
movements:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: default
|
||||
description: Standard development piece with planning and specialized reviews
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: e2e-test
|
||||
description: E2Eテスト追加に特化したピース(E2E分析→E2E実装→レビュー→修正)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan_test
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: expert-cqrs
|
||||
description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: expert
|
||||
description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: frontend
|
||||
description: フロントエンド・セキュリティ・QA専門家レビュー
|
||||
max_iterations: 30
|
||||
max_movements: 30
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: magi
|
||||
description: MAGI合議システム - 3つの観点から分析し多数決で判定
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: melchior
|
||||
movements:
|
||||
- name: melchior
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: minimal
|
||||
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: implement
|
||||
movements:
|
||||
- name: implement
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: passthrough
|
||||
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: execute
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: research
|
||||
description: 調査ピース - 質問せずに自律的に調査を実行
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
@ -13,7 +13,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピース状況
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: plan
|
||||
|
||||
@ -48,7 +48,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピース状況
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: dig
|
||||
|
||||
@ -88,7 +88,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピース状況
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメント実行回数: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: supervise (調査品質評価)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: review-fix-minimal
|
||||
description: 既存コードのレビューと修正ピース(レビュー開始、実装なし)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: reviewers
|
||||
movements:
|
||||
- name: implement
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: review-only
|
||||
description: レビュー専用ピース - コードをレビューするだけで編集は行わない
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
movements:
|
||||
- name: plan
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: structural-reform
|
||||
description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築
|
||||
max_iterations: 50
|
||||
max_movements: 50
|
||||
initial_movement: review
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
@ -44,7 +44,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピースステータス
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: review(プロジェクト全体レビュー)
|
||||
|
||||
@ -126,7 +126,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピースステータス
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: plan_reform(改革計画策定)
|
||||
|
||||
@ -323,7 +323,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピースステータス
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: verify(ビルド・テスト検証)
|
||||
|
||||
@ -378,7 +378,7 @@ movements:
|
||||
- WebFetch
|
||||
instruction_template: |
|
||||
## ピースステータス
|
||||
- イテレーション: {iteration}/{max_iterations}(ピース全体)
|
||||
- イテレーション: {iteration}/{max_movements}(ピース全体)
|
||||
- ムーブメントイテレーション: {movement_iteration}(このムーブメントの実行回数)
|
||||
- ムーブメント: next_target(進捗確認と次ターゲット選択)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
name: unit-test
|
||||
description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正)
|
||||
max_iterations: 20
|
||||
max_movements: 20
|
||||
initial_movement: plan_test
|
||||
loop_monitors:
|
||||
- cycle:
|
||||
|
||||
@ -83,7 +83,7 @@ $ARGUMENTS を以下のように解析する:
|
||||
3. 見つからない場合: 上記2ディレクトリを Glob で列挙し、AskUserQuestion で選択させる
|
||||
|
||||
YAMLから以下を抽出する(→ references/yaml-schema.md 参照):
|
||||
- `name`, `max_iterations`, `initial_movement`, `movements` 配列
|
||||
- `name`, `max_movements`, `initial_movement`, `movements` 配列
|
||||
- セクションマップ: `personas`, `policies`, `instructions`, `output_contracts`, `knowledge`
|
||||
|
||||
### 手順 2: セクションリソースの事前読み込み
|
||||
@ -130,7 +130,7 @@ TeamCreate tool を呼ぶ:
|
||||
|
||||
### 手順 5: チームメイト起動
|
||||
|
||||
**iteration が max_iterations を超えていたら → 手順 8(ABORT: イテレーション上限)に進む。**
|
||||
**iteration が max_movements を超えていたら → 手順 8(ABORT: イテレーション上限)に進む。**
|
||||
|
||||
current_movement のプロンプトを構築する(→ references/engine.md のプロンプト構築を参照)。
|
||||
|
||||
|
||||
@ -133,7 +133,7 @@ movement の `instruction:` キーから指示テンプレートファイルを
|
||||
- ワーキングディレクトリ: {cwd}
|
||||
- ピース: {piece_name}
|
||||
- Movement: {movement_name}
|
||||
- イテレーション: {iteration} / {max_iterations}
|
||||
- イテレーション: {iteration} / {max_movements}
|
||||
- Movement イテレーション: {movement_iteration} 回目
|
||||
```
|
||||
|
||||
@ -146,7 +146,7 @@ movement の `instruction:` キーから指示テンプレートファイルを
|
||||
| `{task}` | ユーザーが入力したタスク内容 |
|
||||
| `{previous_response}` | 前の movement のチームメイト出力 |
|
||||
| `{iteration}` | ピース全体のイテレーション数(1始まり) |
|
||||
| `{max_iterations}` | ピースの max_iterations 値 |
|
||||
| `{max_movements}` | ピースの max_movements 値 |
|
||||
| `{movement_iteration}` | この movement が実行された回数(1始まり) |
|
||||
| `{report_dir}` | レポートディレクトリパス(`.takt/runs/{slug}/reports`) |
|
||||
| `{report:ファイル名}` | 指定レポートファイルの内容(Read で取得) |
|
||||
@ -317,7 +317,7 @@ parallel のサブステップにも同様にタグ出力指示を注入する
|
||||
### 基本ルール
|
||||
|
||||
- 同じ movement が連続3回以上実行されたら警告を表示する
|
||||
- `max_iterations` に到達したら強制終了(ABORT)する
|
||||
- `max_movements` に到達したら強制終了(ABORT)する
|
||||
|
||||
### カウンター管理
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
```yaml
|
||||
name: piece-name # ピース名(必須)
|
||||
description: 説明テキスト # ピースの説明(任意)
|
||||
max_iterations: 10 # 最大イテレーション数(必須)
|
||||
max_movements: 10 # 最大イテレーション数(必須)
|
||||
initial_movement: plan # 最初に実行する movement 名(必須)
|
||||
|
||||
# セクションマップ(キー → ファイルパスの対応表)
|
||||
@ -192,7 +192,7 @@ quality_gates:
|
||||
| `{task}` | ユーザーのタスク入力(template に含まれない場合は自動追加) |
|
||||
| `{previous_response}` | 前の movement の出力(pass_previous_response: true 時、自動追加) |
|
||||
| `{iteration}` | ピース全体のイテレーション数 |
|
||||
| `{max_iterations}` | 最大イテレーション数 |
|
||||
| `{max_movements}` | 最大イテレーション数 |
|
||||
| `{movement_iteration}` | この movement の実行回数 |
|
||||
| `{report_dir}` | レポートディレクトリ名 |
|
||||
| `{report:ファイル名}` | 指定レポートファイルの内容を展開 |
|
||||
|
||||
@ -335,7 +335,7 @@ TAKTはYAMLベースのピース定義とルールベースルーティングを
|
||||
|
||||
```yaml
|
||||
name: default
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
# セクションマップ — キー: ファイルパス(このYAMLからの相対パス)
|
||||
@ -709,7 +709,7 @@ takt eject default
|
||||
# ~/.takt/pieces/my-piece.yaml
|
||||
name: my-piece
|
||||
description: カスタムピース
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: analyze
|
||||
|
||||
personas:
|
||||
@ -759,7 +759,7 @@ personas:
|
||||
|------|------|
|
||||
| `{task}` | 元のユーザーリクエスト(テンプレートになければ自動注入) |
|
||||
| `{iteration}` | ピース全体のターン数(実行された全ムーブメント数) |
|
||||
| `{max_iterations}` | 最大イテレーション数 |
|
||||
| `{max_movements}` | 最大イテレーション数 |
|
||||
| `{movement_iteration}` | ムーブメントごとのイテレーション数(このムーブメントが実行された回数) |
|
||||
| `{previous_response}` | 前のムーブメントの出力(テンプレートになければ自動注入) |
|
||||
| `{user_inputs}` | ピース中の追加ユーザー入力(テンプレートになければ自動注入) |
|
||||
|
||||
@ -498,7 +498,7 @@ TAKTのデータフローは以下の7つの主要なレイヤーで構成され
|
||||
while (state.status === 'running') {
|
||||
// 1. Abort & Iteration チェック
|
||||
if (abortRequested) { ... }
|
||||
if (iteration >= maxIterations) { ... }
|
||||
if (iteration >= maxMovements) { ... }
|
||||
|
||||
// 2. ステップ取得
|
||||
const step = getStep(state.currentStep);
|
||||
@ -646,7 +646,7 @@ const match = await detectMatchedRule(step, response.content, tagContent, {...})
|
||||
- `{previous_response}`: 前ステップの出力
|
||||
- `{user_inputs}`: 追加ユーザー入力
|
||||
- `{iteration}`: ピース全体のイテレーション
|
||||
- `{max_iterations}`: 最大イテレーション
|
||||
- `{max_movements}`: 最大イテレーション
|
||||
- `{step_iteration}`: ステップのイテレーション
|
||||
- `{report_dir}`: レポートディレクトリ
|
||||
|
||||
@ -824,7 +824,7 @@ new PieceEngine(pieceConfig, cwd, task, {
|
||||
|
||||
1. **コンテキスト収集**:
|
||||
- `task`: 元のユーザーリクエスト
|
||||
- `iteration`, `maxIterations`: イテレーション情報
|
||||
- `iteration`, `maxMovements`: イテレーション情報
|
||||
- `stepIteration`: ステップごとの実行回数
|
||||
- `cwd`, `projectCwd`: ディレクトリ情報
|
||||
- `userInputs`: blocked時の追加入力
|
||||
|
||||
@ -331,7 +331,7 @@ Faceted Promptingの中核メカニズムは**宣言的な合成**である。
|
||||
|
||||
```yaml
|
||||
name: my-workflow
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
movements:
|
||||
|
||||
@ -331,7 +331,7 @@ Key properties:
|
||||
|
||||
```yaml
|
||||
name: my-workflow
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
movements:
|
||||
|
||||
@ -25,7 +25,7 @@ A piece is a YAML file that defines a sequence of steps executed by AI agents. E
|
||||
```yaml
|
||||
name: my-piece
|
||||
description: Optional description
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_step: first-step # Optional, defaults to first step
|
||||
|
||||
steps:
|
||||
@ -55,7 +55,7 @@ steps:
|
||||
|----------|-------------|
|
||||
| `{task}` | Original user request (auto-injected if not in template) |
|
||||
| `{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) |
|
||||
| `{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) |
|
||||
@ -170,7 +170,7 @@ report:
|
||||
|
||||
```yaml
|
||||
name: simple-impl
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
steps:
|
||||
- name: implement
|
||||
@ -191,7 +191,7 @@ steps:
|
||||
|
||||
```yaml
|
||||
name: with-review
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
|
||||
steps:
|
||||
- name: implement
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
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
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-mock-no-match
|
||||
description: Piece with a strict rule condition that will not match mock output
|
||||
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-mock-single
|
||||
description: Minimal mock-only piece for CLI E2E
|
||||
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-mock-slow-multi-step
|
||||
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
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-mock-two-step
|
||||
description: Two-step sequential piece for E2E testing
|
||||
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
initial_movement: step-1
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-multi-step-parallel
|
||||
description: Multi-step piece with parallel sub-movements for E2E testing
|
||||
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
|
||||
initial_movement: plan
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-report-judge
|
||||
description: E2E piece that exercises report + judge phases
|
||||
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name: e2e-simple
|
||||
description: Minimal E2E test piece
|
||||
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
movements:
|
||||
- name: execute
|
||||
|
||||
@ -69,15 +69,15 @@ describe('E2E: Piece error handling (mock)', () => {
|
||||
expect(combined).toMatch(/failed|aborted|error/i);
|
||||
}, 240_000);
|
||||
|
||||
it('should abort when max_iterations is reached', () => {
|
||||
// Given: a piece with max_iterations=2 that loops between step-a and step-b
|
||||
it('should abort when max_movements is reached', () => {
|
||||
// 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 scenarioPath = resolve(__dirname, '../fixtures/scenarios/max-iter-loop.json');
|
||||
|
||||
// When: executing the piece
|
||||
const result = runTakt({
|
||||
args: [
|
||||
'--task', 'Test max iterations',
|
||||
'--task', 'Test max movements',
|
||||
'--piece', piecePath,
|
||||
'--create-worktree', 'no',
|
||||
'--provider', 'mock',
|
||||
@ -93,7 +93,7 @@ describe('E2E: Piece error handling (mock)', () => {
|
||||
// Then: piece aborts due to iteration limit
|
||||
expect(result.exitCode).not.toBe(0);
|
||||
const combined = result.stdout + result.stderr;
|
||||
expect(combined).toMatch(/Max iterations|iteration|aborted/i);
|
||||
expect(combined).toMatch(/Max movements|iteration|aborted/i);
|
||||
}, 240_000);
|
||||
|
||||
it('should pass previous response between sequential steps', () => {
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "takt",
|
||||
"version": "0.11.0",
|
||||
"version": "0.11.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "takt",
|
||||
"version": "0.11.0",
|
||||
"version": "0.11.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.37",
|
||||
|
||||
@ -22,7 +22,7 @@ describe('StreamDisplay', () => {
|
||||
describe('progress info display', () => {
|
||||
const progressInfo: ProgressInfo = {
|
||||
iteration: 3,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIndex: 1,
|
||||
totalMovements: 4,
|
||||
};
|
||||
@ -253,7 +253,7 @@ describe('StreamDisplay', () => {
|
||||
it('should format progress as (iteration/max) step index/total', () => {
|
||||
const progressInfo: ProgressInfo = {
|
||||
iteration: 5,
|
||||
maxIterations: 20,
|
||||
maxMovements: 20,
|
||||
movementIndex: 2,
|
||||
totalMovements: 6,
|
||||
};
|
||||
@ -267,7 +267,7 @@ describe('StreamDisplay', () => {
|
||||
it('should convert 0-indexed movementIndex to 1-indexed display', () => {
|
||||
const progressInfo: ProgressInfo = {
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIndex: 0, // First movement (0-indexed)
|
||||
totalMovements: 4,
|
||||
};
|
||||
|
||||
@ -188,7 +188,7 @@ describe('loadAllPieces', () => {
|
||||
const samplePiece = `
|
||||
name: test-piece
|
||||
description: Test piece
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
movements:
|
||||
- name: step1
|
||||
persona: coder
|
||||
|
||||
@ -65,7 +65,7 @@ describe('PieceEngine: Abort (SIGINT)', () => {
|
||||
function makeSimpleConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'step1',
|
||||
movements: [
|
||||
makeMovement('step1', {
|
||||
|
||||
@ -54,7 +54,7 @@ describe('PieceEngine agent overrides', () => {
|
||||
name: 'override-test',
|
||||
movements: [movement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -83,7 +83,7 @@ describe('PieceEngine agent overrides', () => {
|
||||
name: 'override-fallback',
|
||||
movements: [movement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -114,7 +114,7 @@ describe('PieceEngine agent overrides', () => {
|
||||
name: 'movement-defaults',
|
||||
movements: [movement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
|
||||
@ -75,7 +75,7 @@ function buildArpeggioPieceConfig(arpeggioConfig: ArpeggioMovementConfig, tmpDir
|
||||
return {
|
||||
name: 'test-arpeggio',
|
||||
description: 'Test arpeggio piece',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'process',
|
||||
movements: [
|
||||
{
|
||||
|
||||
@ -117,7 +117,7 @@ describe('PieceEngine Integration: Error Handling', () => {
|
||||
describe('Loop detection', () => {
|
||||
it('should abort when loop detected with action: abort', async () => {
|
||||
const config = buildDefaultPieceConfig({
|
||||
maxIterations: 100,
|
||||
maxMovements: 100,
|
||||
loopDetection: { maxConsecutiveSameStep: 3, action: 'abort' },
|
||||
initialMovement: 'loop-step',
|
||||
movements: [
|
||||
@ -156,7 +156,7 @@ describe('PieceEngine Integration: Error Handling', () => {
|
||||
// =====================================================
|
||||
describe('Iteration limit', () => {
|
||||
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 });
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -182,11 +182,11 @@ describe('PieceEngine Integration: Error Handling', () => {
|
||||
expect(limitFn).toHaveBeenCalledWith(2, 2);
|
||||
expect(abortFn).toHaveBeenCalledOnce();
|
||||
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 () => {
|
||||
const config = buildDefaultPieceConfig({ maxIterations: 2 });
|
||||
const config = buildDefaultPieceConfig({ maxMovements: 2 });
|
||||
|
||||
const onIterationLimit = vi.fn().mockResolvedValueOnce(10);
|
||||
|
||||
|
||||
@ -388,7 +388,7 @@ describe('PieceEngine Integration: Happy Path', () => {
|
||||
it('should pass instruction to movement:start for normal movements', async () => {
|
||||
const simpleConfig: PieceConfig = {
|
||||
name: 'test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', {
|
||||
@ -456,7 +456,7 @@ describe('PieceEngine Integration: Happy Path', () => {
|
||||
});
|
||||
|
||||
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 });
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -518,7 +518,7 @@ describe('PieceEngine Integration: Happy Path', () => {
|
||||
it('should emit phase:start and phase:complete events for Phase 1', async () => {
|
||||
const simpleConfig: PieceConfig = {
|
||||
name: 'test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', {
|
||||
@ -609,7 +609,7 @@ describe('PieceEngine Integration: Happy Path', () => {
|
||||
it('should throw when rule references nonexistent movement', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'step1',
|
||||
movements: [
|
||||
makeMovement('step1', {
|
||||
|
||||
@ -60,7 +60,7 @@ function buildConfigWithLoopMonitor(
|
||||
return {
|
||||
name: 'test-loop-monitor',
|
||||
description: 'Test piece with loop monitors',
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
initialMovement: 'implement',
|
||||
loopMonitors: [
|
||||
{
|
||||
|
||||
@ -54,7 +54,7 @@ function buildParallelOnlyConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'test-parallel-failure',
|
||||
description: 'Test parallel failure handling',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'reviewers',
|
||||
movements: [
|
||||
makeMovement('reviewers', {
|
||||
|
||||
@ -55,7 +55,7 @@ describe('PieceEngine persona_providers override', () => {
|
||||
name: 'persona-provider-test',
|
||||
movements: [movement],
|
||||
initialMovement: 'implement',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -84,7 +84,7 @@ describe('PieceEngine persona_providers override', () => {
|
||||
name: 'persona-provider-nomatch',
|
||||
movements: [movement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -114,7 +114,7 @@ describe('PieceEngine persona_providers override', () => {
|
||||
name: 'movement-over-persona',
|
||||
movements: [movement],
|
||||
initialMovement: 'implement',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -143,7 +143,7 @@ describe('PieceEngine persona_providers override', () => {
|
||||
name: 'no-persona-providers',
|
||||
movements: [movement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
@ -175,7 +175,7 @@ describe('PieceEngine persona_providers override', () => {
|
||||
name: 'multi-persona-providers',
|
||||
movements: [planMovement, implementMovement],
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 3,
|
||||
maxMovements: 3,
|
||||
};
|
||||
|
||||
mockRunAgentSequence([
|
||||
|
||||
@ -70,7 +70,7 @@ export function buildDefaultPieceConfig(overrides: Partial<PieceConfig> = {}): P
|
||||
return {
|
||||
name: 'test-default',
|
||||
description: 'Test piece',
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', {
|
||||
|
||||
@ -64,7 +64,7 @@ function buildSimpleConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'worktree-test',
|
||||
description: 'Test piece for worktree',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'review',
|
||||
movements: [
|
||||
makeMovement('review', {
|
||||
@ -133,7 +133,7 @@ describe('PieceEngine: worktree reportDir resolution', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'worktree-test',
|
||||
description: 'Test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'review',
|
||||
movements: [
|
||||
makeMovement('review', {
|
||||
@ -247,7 +247,7 @@ describe('PieceEngine: worktree reportDir resolution', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'snapshot-test',
|
||||
description: 'Test',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'implement',
|
||||
movements: [
|
||||
makeMovement('implement', {
|
||||
|
||||
@ -26,7 +26,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
|
||||
return {
|
||||
task: 'test task',
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIteration: 1,
|
||||
cwd: '/tmp/test',
|
||||
projectCwd: '/tmp/project',
|
||||
@ -78,10 +78,10 @@ describe('replaceTemplatePlaceholders', () => {
|
||||
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 ctx = makeContext({ iteration: 3, maxIterations: 20 });
|
||||
const template = 'Iteration {iteration}/{max_iterations}';
|
||||
const ctx = makeContext({ iteration: 3, maxMovements: 20 });
|
||||
const template = 'Iteration {iteration}/{max_movements}';
|
||||
|
||||
const result = replaceTemplatePlaceholders(template, step, ctx);
|
||||
expect(result).toBe('Iteration 3/20');
|
||||
@ -186,11 +186,11 @@ describe('replaceTemplatePlaceholders', () => {
|
||||
const ctx = makeContext({
|
||||
task: 'test task',
|
||||
iteration: 2,
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
movementIteration: 1,
|
||||
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);
|
||||
expect(result).toBe('test task - iter 2/5 - mv 1 - dir /reports');
|
||||
|
||||
@ -37,7 +37,7 @@ describe('getLabel', () => {
|
||||
it('replaces {variableName} placeholders with provided values', () => {
|
||||
const result = getLabel('piece.iterationLimit.maxReached', undefined, {
|
||||
currentIteration: '5',
|
||||
maxIterations: '10',
|
||||
maxMovements: '10',
|
||||
});
|
||||
expect(result).toContain('(5/10)');
|
||||
});
|
||||
|
||||
@ -27,7 +27,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
|
||||
return {
|
||||
task: 'test task',
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIteration: 1,
|
||||
cwd: '/tmp/test',
|
||||
projectCwd: '/tmp/project',
|
||||
|
||||
@ -41,7 +41,7 @@ function createMinimalContext(overrides: Partial<InstructionContext> = {}): Inst
|
||||
return {
|
||||
task: 'Test task',
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIteration: 1,
|
||||
cwd: '/project',
|
||||
projectCwd: '/project',
|
||||
@ -458,7 +458,7 @@ describe('instruction-builder', () => {
|
||||
step.name = 'implement';
|
||||
const context = createMinimalContext({
|
||||
iteration: 3,
|
||||
maxIterations: 20,
|
||||
maxMovements: 20,
|
||||
movementIteration: 2,
|
||||
language: 'en',
|
||||
});
|
||||
@ -1035,9 +1035,9 @@ describe('instruction-builder', () => {
|
||||
expect(result).toContain('Build the app');
|
||||
});
|
||||
|
||||
it('should replace {iteration} and {max_iterations}', () => {
|
||||
const step = createMinimalStep('Step {iteration}/{max_iterations}');
|
||||
const context = createMinimalContext({ iteration: 3, maxIterations: 20 });
|
||||
it('should replace {iteration} and {max_movements}', () => {
|
||||
const step = createMinimalStep('Step {iteration}/{max_movements}');
|
||||
const context = createMinimalContext({ iteration: 3, maxMovements: 20 });
|
||||
|
||||
const result = buildInstruction(step, context);
|
||||
|
||||
|
||||
@ -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 {
|
||||
name: 'it-error',
|
||||
description: 'IT error recovery piece',
|
||||
maxIterations,
|
||||
maxMovements,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', agentPaths.plan, [
|
||||
|
||||
@ -57,7 +57,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
|
||||
return {
|
||||
task: 'Test task description',
|
||||
iteration: 3,
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
movementIteration: 2,
|
||||
cwd: '/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', () => {
|
||||
it('should replace {iteration}, {max_iterations}, {movement_iteration} in template', () => {
|
||||
it('should replace {iteration}, {max_movements}, {movement_iteration} in template', () => {
|
||||
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);
|
||||
|
||||
@ -189,7 +189,7 @@ describe('Instruction Builder IT: iteration variables', () => {
|
||||
|
||||
it('should include iteration in Piece Context section', () => {
|
||||
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);
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ const {
|
||||
if (this.onIterationLimit) {
|
||||
await this.onIterationLimit({
|
||||
currentIteration: 10,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
currentMovement: 'step1',
|
||||
});
|
||||
}
|
||||
@ -201,7 +201,7 @@ import type { PieceConfig } from '../core/models/index.js';
|
||||
function makeConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'test-notify',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'step1',
|
||||
movements: [
|
||||
{
|
||||
|
||||
@ -105,7 +105,7 @@ function buildSimplePiece(agentPaths: Record<string, string>): PieceConfig {
|
||||
return {
|
||||
name: 'it-simple',
|
||||
description: 'IT simple piece',
|
||||
maxIterations: 15,
|
||||
maxMovements: 15,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', agentPaths.planner, [
|
||||
@ -128,7 +128,7 @@ function buildLoopPiece(agentPaths: Record<string, string>): PieceConfig {
|
||||
return {
|
||||
name: 'it-loop',
|
||||
description: 'IT piece with fix loop',
|
||||
maxIterations: 20,
|
||||
maxMovements: 20,
|
||||
initialMovement: 'plan',
|
||||
movements: [
|
||||
makeMovement('plan', agentPaths.planner, [
|
||||
@ -286,7 +286,7 @@ describe('Piece Engine IT: Max Iterations', () => {
|
||||
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
|
||||
const infiniteScenario = Array.from({ length: 10 }, (_, i) => ({
|
||||
status: 'done' as const,
|
||||
@ -295,7 +295,7 @@ describe('Piece Engine IT: Max Iterations', () => {
|
||||
setMockScenario(infiniteScenario);
|
||||
|
||||
const config = buildSimplePiece(agentPaths);
|
||||
config.maxIterations = 5;
|
||||
config.maxMovements = 5;
|
||||
|
||||
const engine = new PieceEngine(config, testDir, 'Looping task', {
|
||||
...buildEngineOptions(testDir),
|
||||
|
||||
@ -58,7 +58,7 @@ describe('Piece Loader IT: builtin piece loading', () => {
|
||||
expect(config!.name).toBe(name);
|
||||
expect(config!.movements.length).toBeGreaterThan(0);
|
||||
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'), `
|
||||
name: custom-wf
|
||||
description: Custom project piece
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: start
|
||||
|
||||
movements:
|
||||
@ -250,11 +250,11 @@ describe('Piece Loader IT: piece config validation', () => {
|
||||
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);
|
||||
expect(config).not.toBeNull();
|
||||
expect(typeof config!.maxIterations).toBe('number');
|
||||
expect(config!.maxIterations).toBeGreaterThan(0);
|
||||
expect(typeof config!.maxMovements).toBe('number');
|
||||
expect(config!.maxMovements).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should set initial_movement from YAML', () => {
|
||||
@ -397,7 +397,7 @@ describe('Piece Loader IT: quality_gates loading', () => {
|
||||
writeFileSync(join(piecesDir, 'with-gates.yaml'), `
|
||||
name: with-gates
|
||||
description: Piece with quality gates
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: implement
|
||||
|
||||
movements:
|
||||
@ -434,7 +434,7 @@ movements:
|
||||
writeFileSync(join(piecesDir, 'no-gates.yaml'), `
|
||||
name: no-gates
|
||||
description: Piece without quality gates
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: implement
|
||||
|
||||
movements:
|
||||
@ -461,7 +461,7 @@ movements:
|
||||
writeFileSync(join(piecesDir, 'empty-gates.yaml'), `
|
||||
name: empty-gates
|
||||
description: Piece with empty quality gates
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: implement
|
||||
|
||||
movements:
|
||||
@ -501,7 +501,7 @@ describe('Piece Loader IT: mcp_servers parsing', () => {
|
||||
writeFileSync(join(piecesDir, 'with-mcp.yaml'), `
|
||||
name: with-mcp
|
||||
description: Piece with MCP servers
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: e2e-test
|
||||
|
||||
movements:
|
||||
@ -541,7 +541,7 @@ movements:
|
||||
writeFileSync(join(piecesDir, 'no-mcp.yaml'), `
|
||||
name: no-mcp
|
||||
description: Piece without MCP servers
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: implement
|
||||
|
||||
movements:
|
||||
@ -568,7 +568,7 @@ movements:
|
||||
writeFileSync(join(piecesDir, 'multi-mcp.yaml'), `
|
||||
name: multi-mcp
|
||||
description: Piece with multiple MCP servers
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
initial_movement: test
|
||||
|
||||
movements:
|
||||
@ -625,7 +625,7 @@ describe('Piece Loader IT: structural-reform piece', () => {
|
||||
expect(config).not.toBeNull();
|
||||
expect(config!.name).toBe('structural-reform');
|
||||
expect(config!.movements.length).toBe(7);
|
||||
expect(config!.maxIterations).toBe(50);
|
||||
expect(config!.maxMovements).toBe(50);
|
||||
expect(config!.initialMovement).toBe('review');
|
||||
});
|
||||
|
||||
|
||||
@ -171,7 +171,7 @@ function createTestPieceDir(): { dir: string; piecePath: string } {
|
||||
const pieceYaml = `
|
||||
name: it-pipeline
|
||||
description: Pipeline test piece
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
movements:
|
||||
|
||||
@ -152,7 +152,7 @@ function createTestPieceDir(): { dir: string; piecePath: string } {
|
||||
const pieceYaml = `
|
||||
name: it-simple
|
||||
description: Integration test piece
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
initial_movement: plan
|
||||
|
||||
movements:
|
||||
|
||||
@ -196,7 +196,7 @@ describe('executePiece: SIGINT handler integration', () => {
|
||||
function makeConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'test-sigint',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
initialMovement: 'step1',
|
||||
movements: [
|
||||
{
|
||||
|
||||
@ -133,7 +133,7 @@ describe('Three-Phase Execution IT: phase1 only (no report, no tag rules)', () =
|
||||
const config: PieceConfig = {
|
||||
name: 'it-phase1-only',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step',
|
||||
movements: [
|
||||
makeMovement('step', agentPath, [
|
||||
@ -185,7 +185,7 @@ describe('Three-Phase Execution IT: phase1 + phase2 (report defined)', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'it-phase1-2',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step',
|
||||
movements: [
|
||||
makeMovement('step', agentPath, [
|
||||
@ -215,7 +215,7 @@ describe('Three-Phase Execution IT: phase1 + phase2 (report defined)', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'it-phase1-2-multi',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step',
|
||||
movements: [
|
||||
makeMovement('step', agentPath, [
|
||||
@ -266,7 +266,7 @@ describe('Three-Phase Execution IT: phase1 + phase3 (tag rules defined)', () =>
|
||||
const config: PieceConfig = {
|
||||
name: 'it-phase1-3',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step',
|
||||
movements: [
|
||||
makeMovement('step', agentPath, [
|
||||
@ -317,7 +317,7 @@ describe('Three-Phase Execution IT: all three phases', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'it-all-phases',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step',
|
||||
movements: [
|
||||
makeMovement('step', agentPath, [
|
||||
@ -377,7 +377,7 @@ describe('Three-Phase Execution IT: phase3 tag → rule match', () => {
|
||||
const config: PieceConfig = {
|
||||
name: 'it-phase3-tag',
|
||||
description: 'Test',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'step1',
|
||||
movements: [
|
||||
makeMovement('step1', agentPath, [
|
||||
|
||||
@ -330,7 +330,7 @@ function createMinimalContext(overrides: Partial<InstructionContext> = {}): Inst
|
||||
return {
|
||||
task: 'Test task',
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIteration: 1,
|
||||
cwd: '/tmp/test',
|
||||
projectCwd: '/tmp/test',
|
||||
|
||||
@ -81,7 +81,7 @@ describe('PieceConfigRawSchema', () => {
|
||||
expect(result.name).toBe('test-piece');
|
||||
expect(result.movements).toHaveLength(1);
|
||||
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', () => {
|
||||
|
||||
@ -145,7 +145,7 @@ describe('PieceConfigRawSchema with parallel movements', () => {
|
||||
},
|
||||
],
|
||||
initial_movement: 'plan',
|
||||
max_iterations: 10,
|
||||
max_movements: 10,
|
||||
};
|
||||
|
||||
const result = PieceConfigRawSchema.safeParse(raw);
|
||||
|
||||
@ -74,7 +74,7 @@ describe('ParallelLogger', () => {
|
||||
writeFn,
|
||||
progressInfo: {
|
||||
iteration: 4,
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
},
|
||||
taskLabel: 'override-persona-provider',
|
||||
taskColorIndex: 0,
|
||||
@ -529,7 +529,7 @@ describe('ParallelLogger', () => {
|
||||
writeFn,
|
||||
progressInfo: {
|
||||
iteration: 3,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
},
|
||||
});
|
||||
|
||||
@ -545,7 +545,7 @@ describe('ParallelLogger', () => {
|
||||
writeFn,
|
||||
progressInfo: {
|
||||
iteration: 5,
|
||||
maxIterations: 20,
|
||||
maxMovements: 20,
|
||||
},
|
||||
});
|
||||
|
||||
@ -576,7 +576,7 @@ describe('ParallelLogger', () => {
|
||||
writeFn,
|
||||
progressInfo: {
|
||||
iteration: 2,
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
},
|
||||
});
|
||||
const handler = logger.createStreamHandler('step-a', 0);
|
||||
|
||||
@ -24,7 +24,7 @@ import {
|
||||
const SAMPLE_PIECE = `name: test-piece
|
||||
description: Test piece
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
|
||||
@ -65,7 +65,7 @@ function createPieceMap(entries: { name: string; source: 'builtin' | 'user' | 'p
|
||||
name: entry.name,
|
||||
movements: [],
|
||||
initialMovement: 'start',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ function createPieceMap(entries: { name: string; source: 'user' | 'builtin' }[])
|
||||
name: e.name,
|
||||
movements: [],
|
||||
initialMovement: 'start',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ describe('executePiece debug prompts logging', () => {
|
||||
function makeConfig(): PieceConfig {
|
||||
return {
|
||||
name: 'test-piece',
|
||||
maxIterations: 5,
|
||||
maxMovements: 5,
|
||||
initialMovement: 'implement',
|
||||
movements: [
|
||||
{
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
const SAMPLE_PIECE = `name: test-piece
|
||||
description: Test piece
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -173,7 +173,7 @@ describe('loadAllPieces with project-local', () => {
|
||||
const overridePiece = `name: project-override
|
||||
description: Project override
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
|
||||
@ -23,7 +23,7 @@ describe('getPieceDescription', () => {
|
||||
const pieceYaml = `name: test-piece
|
||||
description: Test piece for workflow
|
||||
initial_movement: plan
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: plan
|
||||
@ -56,7 +56,7 @@ movements:
|
||||
const pieceYaml = `name: coding
|
||||
description: Full coding workflow
|
||||
initial_movement: plan
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
|
||||
movements:
|
||||
- name: plan
|
||||
@ -98,7 +98,7 @@ movements:
|
||||
it('should handle movements without descriptions', () => {
|
||||
const pieceYaml = `name: minimal
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -132,7 +132,7 @@ movements:
|
||||
it('should handle parallel movements without descriptions', () => {
|
||||
const pieceYaml = `name: test-parallel
|
||||
initial_movement: parent
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: parent
|
||||
@ -174,7 +174,7 @@ describe('getPieceDescription with movementPreviews', () => {
|
||||
const pieceYaml = `name: preview-test
|
||||
description: Test piece
|
||||
initial_movement: plan
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
movements:
|
||||
- name: plan
|
||||
@ -237,7 +237,7 @@ movements:
|
||||
it('should return empty previews when previewCount is 0', () => {
|
||||
const pieceYaml = `name: test
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -256,7 +256,7 @@ movements:
|
||||
it('should return empty previews when previewCount is not specified', () => {
|
||||
const pieceYaml = `name: test
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -275,7 +275,7 @@ movements:
|
||||
it('should stop at COMPLETE movement', () => {
|
||||
const pieceYaml = `name: test-complete
|
||||
initial_movement: step1
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -301,7 +301,7 @@ movements:
|
||||
it('should stop at ABORT movement', () => {
|
||||
const pieceYaml = `name: test-abort
|
||||
initial_movement: step1
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -331,7 +331,7 @@ movements:
|
||||
|
||||
const pieceYaml = `name: test-persona-file
|
||||
initial_movement: plan
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
personas:
|
||||
planner: ./planner.md
|
||||
@ -355,7 +355,7 @@ movements:
|
||||
it('should limit previews to maxCount', () => {
|
||||
const pieceYaml = `name: test-limit
|
||||
initial_movement: step1
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -388,7 +388,7 @@ movements:
|
||||
it('should handle movements without rules (stop after first)', () => {
|
||||
const pieceYaml = `name: test-no-rules
|
||||
initial_movement: step1
|
||||
max_iterations: 3
|
||||
max_movements: 3
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -411,7 +411,7 @@ movements:
|
||||
it('should return empty previews when initial movement not found in list', () => {
|
||||
const pieceYaml = `name: test-missing-initial
|
||||
initial_movement: nonexistent
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -430,7 +430,7 @@ movements:
|
||||
it('should handle self-referencing rule (prevent infinite loop)', () => {
|
||||
const pieceYaml = `name: test-self-ref
|
||||
initial_movement: step1
|
||||
max_iterations: 5
|
||||
max_movements: 5
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -453,7 +453,7 @@ movements:
|
||||
it('should handle multi-node cycle A→B→A (prevent duplicate previews)', () => {
|
||||
const pieceYaml = `name: test-cycle
|
||||
initial_movement: stepA
|
||||
max_iterations: 10
|
||||
max_movements: 10
|
||||
|
||||
movements:
|
||||
- name: stepA
|
||||
@ -489,7 +489,7 @@ movements:
|
||||
it('should use inline persona content when no personaPath', () => {
|
||||
const pieceYaml = `name: test-inline
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -515,7 +515,7 @@ movements:
|
||||
|
||||
const pieceYaml = `name: test-unreadable-persona
|
||||
initial_movement: plan
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
personas:
|
||||
planner: ./unreadable-persona.md
|
||||
@ -545,7 +545,7 @@ movements:
|
||||
it('should include personaDisplayName in previews', () => {
|
||||
const pieceYaml = `name: test-display
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -578,7 +578,7 @@ describe('getPieceDescription interactiveMode field', () => {
|
||||
it('should return interactiveMode when piece defines interactive_mode', () => {
|
||||
const pieceYaml = `name: test-mode
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
interactive_mode: quiet
|
||||
|
||||
movements:
|
||||
@ -598,7 +598,7 @@ movements:
|
||||
it('should return undefined interactiveMode when piece omits interactive_mode', () => {
|
||||
const pieceYaml = `name: test-no-mode
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -618,7 +618,7 @@ movements:
|
||||
for (const mode of ['assistant', 'persona', 'quiet', 'passthrough'] as const) {
|
||||
const pieceYaml = `name: test-${mode}
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
interactive_mode: ${mode}
|
||||
|
||||
movements:
|
||||
@ -651,7 +651,7 @@ describe('getPieceDescription firstMovement field', () => {
|
||||
it('should return firstMovement with inline persona content', () => {
|
||||
const pieceYaml = `name: test-first
|
||||
initial_movement: plan
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: plan
|
||||
@ -681,7 +681,7 @@ movements:
|
||||
|
||||
const pieceYaml = `name: test-persona-file
|
||||
initial_movement: plan
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
personas:
|
||||
planner: ./planner-persona.md
|
||||
@ -705,7 +705,7 @@ movements:
|
||||
it('should return undefined firstMovement when initialMovement not found', () => {
|
||||
const pieceYaml = `name: test-missing
|
||||
initial_movement: nonexistent
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -724,7 +724,7 @@ movements:
|
||||
it('should return empty allowedTools array when movement has no tools', () => {
|
||||
const pieceYaml = `name: test-no-tools
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
movements:
|
||||
- name: step1
|
||||
@ -749,7 +749,7 @@ movements:
|
||||
|
||||
const pieceYaml = `name: test-fallback
|
||||
initial_movement: step1
|
||||
max_iterations: 1
|
||||
max_movements: 1
|
||||
|
||||
personas:
|
||||
myagent: ./unreadable.md
|
||||
|
||||
@ -27,7 +27,7 @@ function makeContext(overrides: Partial<InstructionContext> = {}): InstructionCo
|
||||
return {
|
||||
task: 'Test task',
|
||||
iteration: 1,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
movementIteration: 1,
|
||||
cwd: '/tmp/test',
|
||||
projectCwd: '/tmp/test',
|
||||
|
||||
@ -36,8 +36,8 @@ describe('review-only piece (EN)', () => {
|
||||
expect(raw.initial_movement).toBe('plan');
|
||||
});
|
||||
|
||||
it('should have max_iterations of 10', () => {
|
||||
expect(raw.max_iterations).toBe(10);
|
||||
it('should have max_movements of 10', () => {
|
||||
expect(raw.max_movements).toBe(10);
|
||||
});
|
||||
|
||||
it('should have 4 movements: plan, reviewers, supervise, pr-comment', () => {
|
||||
|
||||
@ -311,7 +311,7 @@ describe('runAllTasks concurrency', () => {
|
||||
name: 'default',
|
||||
movements: [{ name: 'implement', personaDisplayName: 'coder' }],
|
||||
initialMovement: 'implement',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@ -131,7 +131,7 @@ describe('resolveAutoPr default in selectAndExecuteTask', () => {
|
||||
name: 'default',
|
||||
movements: [],
|
||||
initialMovement: 'start',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
},
|
||||
}],
|
||||
]));
|
||||
|
||||
@ -188,7 +188,7 @@ describe('NDJSON log', () => {
|
||||
const abort: NdjsonPieceAbort = {
|
||||
type: 'piece_abort',
|
||||
iterations: 1,
|
||||
reason: 'Max iterations reached',
|
||||
reason: 'Max movements reached',
|
||||
endTime: '2025-01-01T00:00:03.000Z',
|
||||
};
|
||||
appendNdjsonLine(filepath, abort);
|
||||
|
||||
@ -22,7 +22,7 @@ function makeConfig(overrides: Partial<PieceConfig> = {}): PieceConfig {
|
||||
name: 'test-piece',
|
||||
movements: [],
|
||||
initialMovement: 'start',
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ describe('switchPiece', () => {
|
||||
name: 'default',
|
||||
movements: [],
|
||||
initialMovement: 'start',
|
||||
maxIterations: 1,
|
||||
maxMovements: 1,
|
||||
},
|
||||
}],
|
||||
]));
|
||||
|
||||
@ -188,7 +188,7 @@ describe('TaskPrefixWriter', () => {
|
||||
writer.setMovementContext({
|
||||
movementName: 'implement',
|
||||
iteration: 4,
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
movementIteration: 2,
|
||||
});
|
||||
writer.writeLine('content');
|
||||
|
||||
@ -51,7 +51,7 @@ const defaultPieceConfig: PieceConfig = {
|
||||
name: 'default',
|
||||
description: 'Default piece',
|
||||
initialMovement: 'plan',
|
||||
maxIterations: 30,
|
||||
maxMovements: 30,
|
||||
movements: [
|
||||
{ name: 'plan', persona: 'planner', instruction: '' },
|
||||
{ name: 'implement', persona: 'coder', instruction: '' },
|
||||
|
||||
@ -210,7 +210,7 @@ export interface PieceConfig {
|
||||
reportFormats?: Record<string, string>;
|
||||
movements: PieceMovement[];
|
||||
initialMovement: string;
|
||||
maxIterations: number;
|
||||
maxMovements: number;
|
||||
/** Loop detection settings */
|
||||
loopDetection?: LoopDetectionConfig;
|
||||
/** Loop monitors for detecting cyclic patterns between movements */
|
||||
|
||||
@ -281,7 +281,7 @@ export const PieceConfigRawSchema = z.object({
|
||||
report_formats: z.record(z.string(), z.string()).optional(),
|
||||
movements: z.array(PieceMovementRawSchema).min(1),
|
||||
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(),
|
||||
answer_agent: z.string().optional(),
|
||||
/** Default interactive mode for this piece (overrides user default) */
|
||||
|
||||
@ -12,7 +12,7 @@ export interface SessionState {
|
||||
task: string;
|
||||
projectDir: string;
|
||||
iteration: number;
|
||||
maxIterations: number;
|
||||
maxMovements: number;
|
||||
coderStatus: Status;
|
||||
architectStatus: Status;
|
||||
supervisorStatus: Status;
|
||||
@ -32,7 +32,7 @@ export function createSessionState(
|
||||
task,
|
||||
projectDir,
|
||||
iteration: 0,
|
||||
maxIterations: 10,
|
||||
maxMovements: 10,
|
||||
coderStatus: 'pending',
|
||||
architectStatus: 'pending',
|
||||
supervisorStatus: 'pending',
|
||||
|
||||
@ -19,5 +19,5 @@ export const ERROR_MESSAGES = {
|
||||
`Loop detected: movement "${movementName}" ran ${count} times consecutively without progress.`,
|
||||
UNKNOWN_MOVEMENT: (movementName: string) => `Unknown movement: ${movementName}`,
|
||||
MOVEMENT_EXECUTION_FAILED: (message: string) => `Movement execution failed: ${message}`,
|
||||
MAX_ITERATIONS_REACHED: 'Max iterations reached',
|
||||
MAX_MOVEMENTS_REACHED: 'Max movements reached',
|
||||
};
|
||||
|
||||
@ -131,7 +131,7 @@ export class MovementExecutor {
|
||||
movementIteration: number,
|
||||
state: PieceState,
|
||||
task: string,
|
||||
maxIterations: number,
|
||||
maxMovements: number,
|
||||
): string {
|
||||
this.ensurePreviousResponseSnapshot(state, step.name, movementIteration);
|
||||
const policySnapshot = this.writeFacetSnapshot(
|
||||
@ -150,7 +150,7 @@ export class MovementExecutor {
|
||||
return new InstructionBuilder(step, {
|
||||
task,
|
||||
iteration: state.iteration,
|
||||
maxIterations,
|
||||
maxMovements,
|
||||
movementIteration,
|
||||
cwd: this.deps.getCwd(),
|
||||
projectCwd: this.deps.getProjectCwd(),
|
||||
@ -182,14 +182,14 @@ export class MovementExecutor {
|
||||
step: PieceMovement,
|
||||
state: PieceState,
|
||||
task: string,
|
||||
maxIterations: number,
|
||||
maxMovements: number,
|
||||
updatePersonaSession: (persona: string, sessionId: string | undefined) => void,
|
||||
prebuiltInstruction?: string,
|
||||
): Promise<{ response: AgentResponse; instruction: string }> {
|
||||
const movementIteration = prebuiltInstruction
|
||||
? state.movementIterations.get(step.name) ?? 1
|
||||
: 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);
|
||||
log.debug('Running movement', {
|
||||
movement: step.name,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user