From d138465291d5825c240cced1ff39bb967e3372db Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:14:47 +0900 Subject: [PATCH] takt: remove-instruction-template-co --- e2e/fixtures/pieces/mock-cycle-detect.yaml | 4 +- e2e/fixtures/pieces/mock-max-iter.yaml | 4 +- e2e/fixtures/pieces/mock-no-match.yaml | 2 +- e2e/fixtures/pieces/mock-single-step.yaml | 2 +- e2e/fixtures/pieces/mock-slow-multi-step.yaml | 16 ++-- e2e/fixtures/pieces/mock-two-step.yaml | 4 +- e2e/fixtures/pieces/multi-step-parallel.yaml | 8 +- e2e/fixtures/pieces/report-judge.yaml | 2 +- e2e/fixtures/pieces/simple.yaml | 2 +- e2e/fixtures/pieces/structured-output.yaml | 2 +- .../pieces/team-leader-refill-threshold.yaml | 2 +- .../pieces/team-leader-worker-pool.yaml | 2 +- e2e/fixtures/pieces/team-leader.yaml | 2 +- e2e/specs/codex-permission-mode.e2e.ts | 2 +- e2e/specs/piece-selection-branches.e2e.ts | 2 +- e2e/specs/runtime-config-provider.e2e.ts | 2 +- src/__tests__/arpeggio-schema.test.ts | 2 +- src/__tests__/config.test.ts | 2 +- src/__tests__/facet-resolution.test.ts | 16 ++-- src/__tests__/instructionBuilder.test.ts | 2 +- src/__tests__/knowledge.test.ts | 6 +- src/__tests__/parallel-and-loader.test.ts | 70 +++++++-------- src/__tests__/policy-persona.test.ts | 87 ++++--------------- .../team-leader-schema-loader.test.ts | 14 +-- src/core/models/schemas.ts | 7 +- src/core/piece/instruction/escape.ts | 6 +- src/infra/config/loaders/pieceParser.ts | 19 +--- 27 files changed, 104 insertions(+), 185 deletions(-) diff --git a/e2e/fixtures/pieces/mock-cycle-detect.yaml b/e2e/fixtures/pieces/mock-cycle-detect.yaml index 30c1c785..dce50ac5 100644 --- a/e2e/fixtures/pieces/mock-cycle-detect.yaml +++ b/e2e/fixtures/pieces/mock-cycle-detect.yaml @@ -23,7 +23,7 @@ loop_monitors: movements: - name: review persona: ../agents/test-reviewer-a.md - instruction_template: | + instruction: | Review the code. rules: - condition: approved @@ -34,7 +34,7 @@ movements: persona: ../agents/test-coder.md edit: true required_permission_mode: edit - instruction_template: | + instruction: | Fix the issues found in review. rules: - condition: fixed diff --git a/e2e/fixtures/pieces/mock-max-iter.yaml b/e2e/fixtures/pieces/mock-max-iter.yaml index 11716e80..a696c3dc 100644 --- a/e2e/fixtures/pieces/mock-max-iter.yaml +++ b/e2e/fixtures/pieces/mock-max-iter.yaml @@ -13,7 +13,7 @@ movements: edit: true persona: ../agents/test-coder.md required_permission_mode: edit - instruction_template: | + instruction: | {task} rules: - condition: Done @@ -22,7 +22,7 @@ movements: edit: true persona: ../agents/test-coder.md required_permission_mode: edit - instruction_template: | + instruction: | Continue the task. rules: - condition: Done diff --git a/e2e/fixtures/pieces/mock-no-match.yaml b/e2e/fixtures/pieces/mock-no-match.yaml index a7962e70..2b4ab92e 100644 --- a/e2e/fixtures/pieces/mock-no-match.yaml +++ b/e2e/fixtures/pieces/mock-no-match.yaml @@ -12,7 +12,7 @@ movements: edit: true persona: ../agents/test-coder.md required_permission_mode: edit - instruction_template: | + instruction: | {task} rules: - condition: SpecificMatchThatWillNotOccur diff --git a/e2e/fixtures/pieces/mock-single-step.yaml b/e2e/fixtures/pieces/mock-single-step.yaml index a47b4b31..84e11609 100644 --- a/e2e/fixtures/pieces/mock-single-step.yaml +++ b/e2e/fixtures/pieces/mock-single-step.yaml @@ -18,7 +18,7 @@ movements: - Write - Edit required_permission_mode: edit - instruction_template: | + instruction: | {task} rules: - condition: Done diff --git a/e2e/fixtures/pieces/mock-slow-multi-step.yaml b/e2e/fixtures/pieces/mock-slow-multi-step.yaml index bd57d457..867a32bc 100644 --- a/e2e/fixtures/pieces/mock-slow-multi-step.yaml +++ b/e2e/fixtures/pieces/mock-slow-multi-step.yaml @@ -12,7 +12,7 @@ movements: - name: step-1 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | {task} rules: - condition: Done @@ -20,7 +20,7 @@ movements: - name: step-2 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -28,7 +28,7 @@ movements: - name: step-3 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -36,7 +36,7 @@ movements: - name: step-4 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -44,7 +44,7 @@ movements: - name: step-5 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -52,7 +52,7 @@ movements: - name: step-6 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -60,7 +60,7 @@ movements: - name: step-7 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Continue task execution. rules: - condition: Done @@ -68,7 +68,7 @@ movements: - name: step-8 edit: true persona: ../agents/test-coder.md - instruction_template: | + instruction: | Finalize task execution. rules: - condition: Done diff --git a/e2e/fixtures/pieces/mock-two-step.yaml b/e2e/fixtures/pieces/mock-two-step.yaml index 2395481b..7809eb06 100644 --- a/e2e/fixtures/pieces/mock-two-step.yaml +++ b/e2e/fixtures/pieces/mock-two-step.yaml @@ -13,7 +13,7 @@ movements: edit: true persona: ../agents/test-coder.md required_permission_mode: edit - instruction_template: | + instruction: | {task} rules: - condition: Done @@ -22,7 +22,7 @@ movements: edit: true persona: ../agents/test-coder.md required_permission_mode: edit - instruction_template: | + instruction: | Continue the task. rules: - condition: Done diff --git a/e2e/fixtures/pieces/multi-step-parallel.yaml b/e2e/fixtures/pieces/multi-step-parallel.yaml index d06b8811..e42ce786 100644 --- a/e2e/fixtures/pieces/multi-step-parallel.yaml +++ b/e2e/fixtures/pieces/multi-step-parallel.yaml @@ -13,7 +13,7 @@ movements: persona: ../agents/test-coder.md edit: true required_permission_mode: edit - instruction_template: | + instruction: | Create a plan for the task. rules: - condition: Plan complete @@ -22,14 +22,14 @@ movements: parallel: - name: arch-review persona: ../agents/test-reviewer-a.md - instruction_template: | + instruction: | Review the architecture. rules: - condition: approved - condition: needs_fix - name: security-review persona: ../agents/test-reviewer-b.md - instruction_template: | + instruction: | Review security. rules: - condition: approved @@ -43,7 +43,7 @@ movements: persona: ../agents/test-coder.md edit: true required_permission_mode: edit - instruction_template: | + instruction: | Fix the issues found in review. rules: - condition: Fix applied diff --git a/e2e/fixtures/pieces/report-judge.yaml b/e2e/fixtures/pieces/report-judge.yaml index 89e1a85c..ea0e5aa1 100644 --- a/e2e/fixtures/pieces/report-judge.yaml +++ b/e2e/fixtures/pieces/report-judge.yaml @@ -22,7 +22,7 @@ movements: report: - name: report.md format: report - instruction_template: | + instruction: | {task} rules: - condition: Done diff --git a/e2e/fixtures/pieces/simple.yaml b/e2e/fixtures/pieces/simple.yaml index 4818e9a9..138329bb 100644 --- a/e2e/fixtures/pieces/simple.yaml +++ b/e2e/fixtures/pieces/simple.yaml @@ -18,7 +18,7 @@ movements: - Write - Edit required_permission_mode: edit - instruction_template: | + instruction: | {task} rules: - condition: Task completed diff --git a/e2e/fixtures/pieces/structured-output.yaml b/e2e/fixtures/pieces/structured-output.yaml index 6de27cd7..34d59289 100644 --- a/e2e/fixtures/pieces/structured-output.yaml +++ b/e2e/fixtures/pieces/structured-output.yaml @@ -12,7 +12,7 @@ movements: edit: false persona: ../agents/test-coder.md required_permission_mode: readonly - instruction_template: | + instruction: | Reply with exactly: "Task completed successfully." Do not do anything else. rules: diff --git a/e2e/fixtures/pieces/team-leader-refill-threshold.yaml b/e2e/fixtures/pieces/team-leader-refill-threshold.yaml index bb02faaf..da32d860 100644 --- a/e2e/fixtures/pieces/team-leader-refill-threshold.yaml +++ b/e2e/fixtures/pieces/team-leader-refill-threshold.yaml @@ -21,7 +21,7 @@ movements: - Read - Write - Edit - instruction_template: | + instruction: | {task} rules: - condition: Task completed diff --git a/e2e/fixtures/pieces/team-leader-worker-pool.yaml b/e2e/fixtures/pieces/team-leader-worker-pool.yaml index 8c9a47a4..fa6d1f8f 100644 --- a/e2e/fixtures/pieces/team-leader-worker-pool.yaml +++ b/e2e/fixtures/pieces/team-leader-worker-pool.yaml @@ -20,7 +20,7 @@ movements: - Read - Write - Edit - instruction_template: | + instruction: | {task} rules: - condition: Task completed diff --git a/e2e/fixtures/pieces/team-leader.yaml b/e2e/fixtures/pieces/team-leader.yaml index 5a300f55..b9b02af9 100644 --- a/e2e/fixtures/pieces/team-leader.yaml +++ b/e2e/fixtures/pieces/team-leader.yaml @@ -20,7 +20,7 @@ movements: - Read - Write - Edit - instruction_template: | + instruction: | {task} rules: - condition: Task completed diff --git a/e2e/specs/codex-permission-mode.e2e.ts b/e2e/specs/codex-permission-mode.e2e.ts index 7e96aba1..deebfb68 100644 --- a/e2e/specs/codex-permission-mode.e2e.ts +++ b/e2e/specs/codex-permission-mode.e2e.ts @@ -30,7 +30,7 @@ describe('E2E: Codex permission mode readonly/full', () => { ' allowed_tools:', ' - Bash', ' required_permission_mode: readonly', - ' instruction_template: |', + ' instruction: |', ' Run this exact command in repository root:', ' /bin/sh -lc \'printf "ok\\n" > epperm-check.txt\'', ' If file creation succeeds, reply exactly: COMPLETE', diff --git a/e2e/specs/piece-selection-branches.e2e.ts b/e2e/specs/piece-selection-branches.e2e.ts index 6afb3573..e204b3e4 100644 --- a/e2e/specs/piece-selection-branches.e2e.ts +++ b/e2e/specs/piece-selection-branches.e2e.ts @@ -39,7 +39,7 @@ function writeMinimalPiece(piecePath: string): void { ' - Write', ' - Edit', ' required_permission_mode: edit', - ' instruction_template: |', + ' instruction: |', ' {task}', ' rules:', ' - condition: Done', diff --git a/e2e/specs/runtime-config-provider.e2e.ts b/e2e/specs/runtime-config-provider.e2e.ts index c19d4ddd..1835f2df 100644 --- a/e2e/specs/runtime-config-provider.e2e.ts +++ b/e2e/specs/runtime-config-provider.e2e.ts @@ -84,7 +84,7 @@ describe('E2E: runtime.prepare with provider', () => { ' - Read', ' - Bash', ' required_permission_mode: edit', - ' instruction_template: |', + ' instruction: |', ' {task}', ' rules:', ' - condition: Task completed', diff --git a/src/__tests__/arpeggio-schema.test.ts b/src/__tests__/arpeggio-schema.test.ts index e121945b..2341bee3 100644 --- a/src/__tests__/arpeggio-schema.test.ts +++ b/src/__tests__/arpeggio-schema.test.ts @@ -297,7 +297,7 @@ describe('PieceMovementRawSchema with arpeggio', () => { const raw = { name: 'normal-step', persona: 'coder.md', - instruction_template: 'Do work', + instruction: 'Do work', }; const result = PieceMovementRawSchema.safeParse(raw); diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 9f618fab..64c806d5 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -71,7 +71,7 @@ describe('getBuiltinPiece', () => { expect(piece!.name).toBe('default'); }); - it('should resolve builtin instruction_template without projectCwd', () => { + it('should resolve builtin instruction without projectCwd', () => { const piece = getBuiltinPiece('default', process.cwd()); expect(piece).not.toBeNull(); diff --git a/src/__tests__/facet-resolution.test.ts b/src/__tests__/facet-resolution.test.ts index 6f983796..03b2a5dd 100644 --- a/src/__tests__/facet-resolution.test.ts +++ b/src/__tests__/facet-resolution.test.ts @@ -509,7 +509,7 @@ describe('normalizePieceConfig with layer resolution', () => { expect(config.movements[0]!.knowledgeContents![0]).toBe('# Domain Knowledge'); }); - it('should resolve instruction_template from section map before layer resolution', () => { + it('should resolve instruction from section map before layer resolution', () => { const raw = { name: 'test-piece', instructions: { @@ -519,7 +519,7 @@ describe('normalizePieceConfig with layer resolution', () => { { name: 'step1', persona: 'coder', - instruction_template: 'implement', + instruction: 'implement', }, ], }; @@ -530,7 +530,7 @@ describe('normalizePieceConfig with layer resolution', () => { expect(config.movements[0]!.instruction).toBe('Mapped instruction template'); }); - it('should resolve instruction_template by name via layer resolution', () => { + it('should resolve instruction by name via layer resolution', () => { const instructionsDir = join(projectDir, '.takt', 'facets', 'instructions'); mkdirSync(instructionsDir, { recursive: true }); writeFileSync(join(instructionsDir, 'implement.md'), 'Project implement template'); @@ -541,7 +541,7 @@ describe('normalizePieceConfig with layer resolution', () => { { name: 'step1', persona: 'coder', - instruction_template: 'implement', + instruction: 'implement', }, ], }; @@ -552,7 +552,7 @@ describe('normalizePieceConfig with layer resolution', () => { expect(config.movements[0]!.instruction).toBe('Project implement template'); }); - it('should keep inline instruction_template when no facet is found', () => { + it('should keep inline instruction when no facet is found', () => { const inlineTemplate = `Use this inline template. Second line remains inline.`; const raw = { @@ -561,7 +561,7 @@ Second line remains inline.`; { name: 'step1', persona: 'coder', - instruction_template: inlineTemplate, + instruction: inlineTemplate, }, ], }; @@ -572,7 +572,7 @@ Second line remains inline.`; expect(config.movements[0]!.instruction).toBe(inlineTemplate); }); - it('should resolve loop monitor judge instruction_template via layer resolution', () => { + it('should resolve loop monitor judge instruction via layer resolution', () => { const instructionsDir = join(projectDir, '.takt', 'facets', 'instructions'); mkdirSync(instructionsDir, { recursive: true }); writeFileSync(join(instructionsDir, 'judge-template.md'), 'Project judge template'); @@ -599,7 +599,7 @@ Second line remains inline.`; threshold: 2, judge: { persona: 'coder', - instruction_template: 'judge-template', + instruction: 'judge-template', rules: [{ condition: 'continue', next: 'step2' }], }, }, diff --git a/src/__tests__/instructionBuilder.test.ts b/src/__tests__/instructionBuilder.test.ts index cc9d770a..a8db39b2 100644 --- a/src/__tests__/instructionBuilder.test.ts +++ b/src/__tests__/instructionBuilder.test.ts @@ -700,7 +700,7 @@ describe('instruction-builder', () => { expect(result).not.toContain('Custom order instruction'); }); - it('should still replace {report:filename} in instruction_template', () => { + it('should still replace {report:filename} in instruction', () => { const step = createMinimalStep('Write to {report:00-plan.md}'); const context = createMinimalContext({ reportDir: '/project/.takt/runs/20260129-test/reports', diff --git a/src/__tests__/knowledge.test.ts b/src/__tests__/knowledge.test.ts index 43cf15ed..f8ce866c 100644 --- a/src/__tests__/knowledge.test.ts +++ b/src/__tests__/knowledge.test.ts @@ -129,7 +129,7 @@ describe('ParallelSubMovementRawSchema knowledge field', () => { name: 'sub-step', persona: 'reviewer.md', knowledge: 'security', - instruction_template: 'Review security', + instruction: 'Review security', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -144,7 +144,7 @@ describe('ParallelSubMovementRawSchema knowledge field', () => { name: 'sub-step', persona: 'reviewer.md', knowledge: ['security', 'performance'], - instruction_template: 'Review', + instruction: 'Review', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -238,7 +238,7 @@ describe('normalizePieceConfig knowledge resolution', () => { name: 'sec-review', persona: 'reviewer.md', knowledge: 'security', - instruction_template: 'Review security', + instruction: 'Review security', }, ], rules: [{ condition: 'approved', next: 'COMPLETE' }], diff --git a/src/__tests__/parallel-and-loader.test.ts b/src/__tests__/parallel-and-loader.test.ts index 3d723411..4eb48937 100644 --- a/src/__tests__/parallel-and-loader.test.ts +++ b/src/__tests__/parallel-and-loader.test.ts @@ -20,17 +20,17 @@ describe('ParallelSubMovementRawSchema', () => { const raw = { name: 'arch-review', persona: '~/.takt/agents/default/reviewer.md', - instruction_template: 'Review architecture', + instruction: 'Review architecture', }; const result = ParallelSubMovementRawSchema.safeParse(raw); expect(result.success).toBe(true); }); - it('should accept a sub-movement without persona (instruction_template only)', () => { + it('should accept a sub-movement without persona (instruction only)', () => { const raw = { name: 'no-agent-step', - instruction_template: 'Do something', + instruction: 'Do something', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -51,8 +51,8 @@ describe('ParallelSubMovementRawSchema', () => { expect(result.success).toBe(true); }); - it('should accept a sub-movement when instruction and instruction_template are both provided', () => { - // Given: both canonical and deprecated fields are present during migration + it('should reject legacy instruction_template in sub-movement schema', () => { + // Given: legacy field is present in raw input const raw = { name: 'dual-field-sub-step', instruction: 'Canonical instruction', @@ -62,12 +62,8 @@ describe('ParallelSubMovementRawSchema', () => { // When: validating the sub-movement schema const result = ParallelSubMovementRawSchema.safeParse(raw); - // Then: schema keeps backward compatibility and accepts both fields - expect(result.success).toBe(true); - if (result.success) { - expect((result.data as unknown as Record).instruction).toBe('Canonical instruction'); - expect((result.data as unknown as Record).instruction_template).toBe('Legacy instruction'); - } + // Then: validation fails to prevent silent legacy fallback + expect(result.success).toBe(false); }); it('should accept optional fields', () => { @@ -82,7 +78,7 @@ describe('ParallelSubMovementRawSchema', () => { }, model: 'haiku', edit: false, - instruction_template: 'Do work', + instruction: 'Do work', report: '01-report.md', pass_previous_response: false, }; @@ -104,7 +100,7 @@ describe('ParallelSubMovementRawSchema', () => { model: 'gpt-5.3', network_access: true, }, - instruction_template: 'Review', + instruction: 'Review', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -118,7 +114,7 @@ describe('ParallelSubMovementRawSchema', () => { type: 'claude', network_access: true, }, - instruction_template: 'Review', + instruction: 'Review', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -129,7 +125,7 @@ describe('ParallelSubMovementRawSchema', () => { const raw = { name: 'reviewed', persona: '~/.takt/agents/default/reviewer.md', - instruction_template: 'Review', + instruction: 'Review', rules: [ { condition: 'No issues', next: 'COMPLETE' }, { condition: 'Issues found', next: 'fix' }, @@ -147,7 +143,7 @@ describe('ParallelSubMovementRawSchema', () => { const raw = { name: 'invalid-sub-step', allowed_tools: ['Read'], - instruction_template: 'Review', + instruction: 'Review', }; const result = ParallelSubMovementRawSchema.safeParse(raw); @@ -160,8 +156,8 @@ describe('PieceMovementRawSchema with parallel', () => { const raw = { name: 'parallel-review', parallel: [ - { name: 'arch-review', persona: 'reviewer.md', instruction_template: 'Review arch' }, - { name: 'sec-review', persona: 'security.md', instruction_template: 'Review security' }, + { name: 'arch-review', persona: 'reviewer.md', instruction: 'Review arch' }, + { name: 'sec-review', persona: 'security.md', instruction: 'Review security' }, ], rules: [ { condition: 'All pass', next: 'COMPLETE' }, @@ -172,10 +168,10 @@ describe('PieceMovementRawSchema with parallel', () => { expect(result.success).toBe(true); }); - it('should accept a movement with neither agent nor parallel (instruction_template only)', () => { + it('should accept a movement with neither agent nor parallel (instruction only)', () => { const raw = { name: 'orphan-step', - instruction_template: 'Do something', + instruction: 'Do something', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -196,8 +192,8 @@ describe('PieceMovementRawSchema with parallel', () => { expect(result.success).toBe(true); }); - it('should accept a movement when instruction and instruction_template are both provided', () => { - // Given: movement includes both canonical and deprecated instruction fields + it('should reject legacy instruction_template in movement schema', () => { + // Given: movement includes both canonical and legacy instruction fields const raw = { name: 'orphan-step', instruction: 'Canonical movement instruction', @@ -207,19 +203,15 @@ describe('PieceMovementRawSchema with parallel', () => { // When: validating the movement schema const result = PieceMovementRawSchema.safeParse(raw); - // Then: schema accepts both fields for deprecation window - expect(result.success).toBe(true); - if (result.success) { - expect((result.data as unknown as Record).instruction).toBe('Canonical movement instruction'); - expect((result.data as unknown as Record).instruction_template).toBe('Legacy movement instruction'); - } + // Then: validation fails to prevent silent legacy fallback + expect(result.success).toBe(false); }); it('should accept a movement with persona (no parallel)', () => { const raw = { name: 'normal-step', persona: 'coder.md', - instruction_template: 'Code something', + instruction: 'Code something', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -243,7 +235,7 @@ describe('PieceMovementRawSchema with parallel', () => { { name: 'arch-review', provider: 'codex', - instruction_template: 'Review architecture', + instruction: 'Review architecture', }, ], }; @@ -272,8 +264,8 @@ describe('LoopMonitorJudgeSchema', () => { } }); - it('should accept judge configuration during deprecation window when both fields exist', () => { - // Given: judge config with both new and deprecated fields + it('should reject legacy instruction_template in judge schema', () => { + // Given: judge config with both canonical and legacy fields const raw = { persona: 'reviewer', instruction: 'Judge loop health', @@ -284,12 +276,8 @@ describe('LoopMonitorJudgeSchema', () => { // When: validating judge schema const result = LoopMonitorJudgeSchema.safeParse(raw); - // Then: it is accepted for backward compatibility - expect(result.success).toBe(true); - if (result.success) { - expect((result.data as unknown as Record).instruction).toBe('Judge loop health'); - expect((result.data as unknown as Record).instruction_template).toBe('legacy judge instruction'); - } + // Then: validation fails to prevent silent legacy fallback + expect(result.success).toBe(false); }); }); @@ -306,8 +294,8 @@ describe('PieceConfigRawSchema with parallel movements', () => { { name: 'review', parallel: [ - { name: 'arch-review', persona: 'arch-reviewer.md', instruction_template: 'Review architecture' }, - { name: 'sec-review', persona: 'sec-reviewer.md', instruction_template: 'Review security' }, + { name: 'arch-review', persona: 'arch-reviewer.md', instruction: 'Review architecture' }, + { name: 'sec-review', persona: 'sec-reviewer.md', instruction: 'Review security' }, ], rules: [ { condition: 'All approved', next: 'COMPLETE' }, @@ -475,7 +463,7 @@ describe('all()/any() condition in PieceMovementRawSchema', () => { const raw = { name: 'parallel-review', parallel: [ - { name: 'arch-review', persona: 'reviewer.md', instruction_template: 'Review' }, + { name: 'arch-review', persona: 'reviewer.md', instruction: 'Review' }, ], rules: [ { condition: 'all("approved")', next: 'COMPLETE' }, diff --git a/src/__tests__/policy-persona.test.ts b/src/__tests__/policy-persona.test.ts index 52c684d1..23b3447c 100644 --- a/src/__tests__/policy-persona.test.ts +++ b/src/__tests__/policy-persona.test.ts @@ -9,7 +9,7 @@ * - File-based policy content loading via resolveContentPath */ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; @@ -601,7 +601,7 @@ describe('section reference resolution', () => { expect(config.movements[0]!.persona).toBe('nonexistent'); }); - it('should prefer instruction over instruction_template when both are provided', () => { + it('should resolve movement instruction from instructions section', () => { const raw = { name: 'test-piece', instructions: { implement: './instructions/implement.md' }, @@ -609,7 +609,6 @@ describe('section reference resolution', () => { name: 'impl', persona: 'coder', instruction: 'implement', - instruction_template: 'Inline template takes priority.', }], }; @@ -617,76 +616,22 @@ describe('section reference resolution', () => { expect(config.movements[0]!.instruction).toBe('Implement the feature.'); }); - it('should emit deprecation warning when movement uses instruction_template', () => { - // Given: deprecated instruction_template is used on a movement - const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - try { - const raw = { - name: 'test-piece', - movements: [{ - name: 'impl', - persona: 'coder', - instruction_template: 'Legacy movement instruction', - }], - }; - - // When: normalizing piece config - normalizePieceConfig(raw, testDir); - - // Then: deprecation warning is emitted - expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('instruction_template')); - } finally { - warnSpy.mockRestore(); - } - }); - - it('should emit deprecation warning when loop monitor judge uses instruction_template', () => { - // Given: deprecated instruction_template is used on loop monitor judge - const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - try { - const raw = { - name: 'test-piece', - movements: [ - { - name: 'step1', - persona: 'coder', - instruction: '{task}', - rules: [{ condition: 'next', next: 'step2' }], - }, - { - name: 'step2', - persona: 'coder', - instruction: '{task}', - rules: [{ condition: 'done', next: 'COMPLETE' }], - }, - ], - loop_monitors: [ - { - cycle: ['step1', 'step2'], - threshold: 2, - judge: { - persona: 'coder', - instruction_template: 'Legacy judge instruction', - rules: [{ condition: 'continue', next: 'step2' }], - }, - }, - ], - }; - - // When: normalizing piece config - normalizePieceConfig(raw, testDir); + it('should reject legacy instruction_template on movement', () => { + const raw = { + name: 'test-piece', + movements: [{ + name: 'impl', + persona: 'coder', + instruction_template: 'Legacy movement instruction', + }], + }; - // Then: deprecation warning is emitted - expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('instruction_template')); - } finally { - warnSpy.mockRestore(); - } + expect(() => normalizePieceConfig(raw, testDir)).toThrowError(); }); - it('should prefer loop monitor judge instruction over instruction_template when both are provided', () => { + it('should reject legacy instruction_template on loop monitor judge', () => { const raw = { name: 'test-piece', - instructions: { judge_template: './instructions/implement.md' }, movements: [ { name: 'step1', @@ -707,16 +652,14 @@ describe('section reference resolution', () => { threshold: 2, judge: { persona: 'coder', - instruction: 'judge_template', - instruction_template: 'Legacy judge template', + instruction_template: 'Legacy judge instruction', rules: [{ condition: 'continue', next: 'step2' }], }, }, ], }; - const config = normalizePieceConfig(raw, testDir); - expect(config.loopMonitors?.[0]?.judge.instruction).toBe('Implement the feature.'); + expect(() => normalizePieceConfig(raw, testDir)).toThrowError(); }); it('should expose normalized loop monitor judge instruction on instruction field', () => { diff --git a/src/__tests__/team-leader-schema-loader.test.ts b/src/__tests__/team-leader-schema-loader.test.ts index 23afea8b..25262fdd 100644 --- a/src/__tests__/team-leader-schema-loader.test.ts +++ b/src/__tests__/team-leader-schema-loader.test.ts @@ -12,7 +12,7 @@ describe('team_leader schema', () => { max_parts: 3, timeout_ms: 120000, }, - instruction_template: 'decompose', + instruction: 'decompose', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -25,7 +25,7 @@ describe('team_leader schema', () => { team_leader: { max_parts: 4, }, - instruction_template: 'decompose', + instruction: 'decompose', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -39,7 +39,7 @@ describe('team_leader schema', () => { max_parts: 2, refill_threshold: 3, }, - instruction_template: 'decompose', + instruction: 'decompose', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -49,11 +49,11 @@ describe('team_leader schema', () => { it('parallel と team_leader の同時指定は拒否する', () => { const raw = { name: 'implement', - parallel: [{ name: 'sub', instruction_template: 'x' }], + parallel: [{ name: 'sub', instruction: 'x' }], team_leader: { max_parts: 2, }, - instruction_template: 'decompose', + instruction: 'decompose', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -71,7 +71,7 @@ describe('team_leader schema', () => { team_leader: { max_parts: 2, }, - instruction_template: 'decompose', + instruction: 'decompose', }; const result = PieceMovementRawSchema.safeParse(raw); @@ -96,7 +96,7 @@ describe('normalizePieceConfig team_leader', () => { part_edit: true, part_permission_mode: 'edit', }, - instruction_template: 'decompose', + instruction: 'decompose', }, ], }; diff --git a/src/core/models/schemas.ts b/src/core/models/schemas.ts index fc889721..aa64b53e 100644 --- a/src/core/models/schemas.ts +++ b/src/core/models/schemas.ts @@ -307,7 +307,7 @@ export const ParallelSubMovementRawSchema = z.object({ provider_options: MovementProviderOptionsSchema, edit: z.boolean().optional(), instruction: z.string().optional(), - instruction_template: z.string().optional(), + instruction_template: z.never().optional(), rules: z.array(PieceRuleSchema).optional(), /** Output contracts for this movement (report definitions) */ output_contracts: OutputContractsFieldSchema, @@ -343,7 +343,7 @@ export const PieceMovementRawSchema = z.object({ /** Whether this movement is allowed to edit project files */ edit: z.boolean().optional(), instruction: z.string().optional(), - instruction_template: z.string().optional(), + instruction_template: z.never().optional(), /** Rules for movement routing */ rules: z.array(PieceRuleSchema).optional(), /** Output contracts for this movement (report definitions) */ @@ -379,8 +379,7 @@ export const LoopMonitorJudgeSchema = z.object({ persona: z.string().optional(), /** Custom judge instruction */ instruction: z.string().optional(), - /** Deprecated alias */ - instruction_template: z.string().optional(), + instruction_template: z.never().optional(), /** Rules for the judge's decision */ rules: z.array(LoopMonitorRuleSchema).min(1), }); diff --git a/src/core/piece/instruction/escape.ts b/src/core/piece/instruction/escape.ts index cc8c1cbe..77cd9aa1 100644 --- a/src/core/piece/instruction/escape.ts +++ b/src/core/piece/instruction/escape.ts @@ -1,7 +1,7 @@ /** * Template escaping and placeholder replacement utilities * - * Used by instruction builders to process instruction_template content. + * Used by instruction builders to process instruction content. * * escapeTemplateChars is re-exported from faceted-prompting. * replaceTemplatePlaceholders is TAKT-specific and stays here. @@ -14,9 +14,9 @@ import { escapeTemplateChars } from 'faceted-prompting'; export { escapeTemplateChars } from 'faceted-prompting'; /** - * Replace template placeholders in the instruction_template body. + * Replace template placeholders in the instruction body. * - * These placeholders may still be used in instruction_template for + * These placeholders may still be used in instruction for * special cases or legacy templates. */ export function replaceTemplatePlaceholders( diff --git a/src/infra/config/loaders/pieceParser.ts b/src/infra/config/loaders/pieceParser.ts index a7eb4bd2..92ffe4a2 100644 --- a/src/infra/config/loaders/pieceParser.ts +++ b/src/infra/config/loaders/pieceParser.ts @@ -272,12 +272,6 @@ function normalizeStepFromRaw( const expandedInstruction = step.instruction ? resolveRefToContent(step.instruction, sections.resolvedInstructions, pieceDir, 'instructions', context) : undefined; - if (step.instruction_template !== undefined) { - console.warn(`Movement "${step.name}" uses deprecated field "instruction_template". Use "instruction" instead.`); - } - const expandedLegacyInstruction = step.instruction_template - ? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) - : undefined; const result: PieceMovement = { name: step.name, @@ -292,7 +286,7 @@ function normalizeStepFromRaw( requiredPermissionMode: step.required_permission_mode, providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizedProvider.providerOptions), edit: step.edit, - instruction: expandedInstruction || expandedLegacyInstruction || '{task}', + instruction: expandedInstruction || '{task}', rules, outputContracts: normalizeOutputContracts(step.output_contracts, pieceDir, sections.resolvedReportFormats, context), qualityGates: applyQualityGateOverrides( @@ -339,20 +333,15 @@ function normalizeStepFromRaw( /** Normalize a raw loop monitor judge from YAML into internal format. */ function normalizeLoopMonitorJudge( - raw: { persona?: string; instruction?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> }, + raw: { persona?: string; instruction?: string; rules: Array<{ condition: string; next: string }> }, pieceDir: string, sections: PieceSections, context?: FacetResolutionContext, ): LoopMonitorJudge { const { personaSpec, personaPath } = resolvePersona(raw.persona, sections, pieceDir, context); - if (raw.instruction_template !== undefined) { - console.warn('loop_monitors judge uses deprecated field "instruction_template". Use "instruction" instead.'); - } const resolvedInstruction = raw.instruction ? resolveRefToContent(raw.instruction, sections.resolvedInstructions, pieceDir, 'instructions', context) - : raw.instruction_template - ? resolveRefToContent(raw.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context) - : undefined; + : undefined; return { persona: personaSpec, @@ -366,7 +355,7 @@ function normalizeLoopMonitorJudge( * Normalize raw loop monitors from YAML into internal format. */ function normalizeLoopMonitors( - raw: Array<{ cycle: string[]; threshold: number; judge: { persona?: string; instruction?: string; instruction_template?: string; rules: Array<{ condition: string; next: string }> } }> | undefined, + raw: Array<{ cycle: string[]; threshold: number; judge: { persona?: string; instruction?: string; rules: Array<{ condition: string; next: string }> } }> | undefined, pieceDir: string, sections: PieceSections, context?: FacetResolutionContext,