takt/src/core/piece/instruction/ReportInstructionBuilder.ts
nrslib 2460dbdf61 refactor(output-contracts): unify OutputContractEntry to item format with use_judge and move runtime dir under .takt
- Remove OutputContractLabelPath (label:path format), unify to OutputContractItem only
- Add required format field and use_judge flag to output contracts
- Add getJudgmentReportFiles() to filter reports eligible for Phase 3 status judgment
- Add supervisor-validation output contract, remove review-summary
- Enhance output contracts with finding_id tracking (new/persists/resolved sections)
- Move runtime environment directory from .runtime to .takt/.runtime
- Update all builtin pieces, e2e fixtures, and tests
2026-02-15 11:17:55 +09:00

105 lines
3.6 KiB
TypeScript

/**
* Phase 2 instruction builder (report output)
*
* Builds the instruction for the report output phase.
* Assembles template variables and renders a single complete template.
*/
import type { PieceMovement, Language } from '../../models/types.js';
import type { InstructionContext } from './instruction-context.js';
import { replaceTemplatePlaceholders } from './escape.js';
import { isOutputContractItem, renderReportContext, renderReportOutputInstruction } from './InstructionBuilder.js';
import { loadTemplate } from '../../../shared/prompts/index.js';
/**
* Context for building report phase instruction.
*/
export interface ReportInstructionContext {
/** Working directory */
cwd: string;
/** Report directory path */
reportDir: string;
/** Movement iteration (for {movement_iteration} replacement) */
movementIteration: number;
/** Language */
language?: Language;
/** Target report file name (when generating a single report) */
targetFile?: string;
/** Last response from Phase 1 (used when report phase retries in a new session) */
lastResponse?: string;
}
/**
* Builds Phase 2 (report output) instructions.
*
* Renders a single complete template with all variables.
*/
export class ReportInstructionBuilder {
constructor(
private readonly step: PieceMovement,
private readonly context: ReportInstructionContext,
) {}
build(): string {
if (!this.step.outputContracts || this.step.outputContracts.length === 0) {
throw new Error(`ReportInstructionBuilder called for movement "${this.step.name}" which has no output contracts`);
}
const language = this.context.language ?? 'en';
let reportContext: string;
if (this.context.targetFile) {
reportContext = `- Report Directory: ${this.context.reportDir}/\n- Report File: ${this.context.reportDir}/${this.context.targetFile}`;
} else {
reportContext = renderReportContext(this.step.outputContracts, this.context.reportDir);
}
let reportOutput = '';
let hasReportOutput = false;
const instrContext: InstructionContext = {
task: '',
iteration: 0,
maxMovements: 0,
movementIteration: this.context.movementIteration,
cwd: this.context.cwd,
projectCwd: this.context.cwd,
userInputs: [],
reportDir: this.context.reportDir,
language,
};
const targetContract = this.context.targetFile
? this.step.outputContracts.find((entry) => entry.name === this.context.targetFile)
: this.step.outputContracts[0];
if (targetContract && isOutputContractItem(targetContract) && targetContract.order) {
reportOutput = replaceTemplatePlaceholders(targetContract.order.trimEnd(), this.step, instrContext);
hasReportOutput = true;
} else if (!this.context.targetFile) {
const output = renderReportOutputInstruction(this.step, instrContext, language);
if (output) {
reportOutput = output;
hasReportOutput = true;
}
}
let outputContract = '';
let hasOutputContract = false;
if (targetContract && isOutputContractItem(targetContract) && targetContract.format) {
outputContract = replaceTemplatePlaceholders(targetContract.format.trimEnd(), this.step, instrContext);
hasOutputContract = true;
}
return loadTemplate('perform_phase2_message', language, {
workingDirectory: this.context.cwd,
reportContext,
hasLastResponse: this.context.lastResponse != null && this.context.lastResponse.trim().length > 0,
lastResponse: this.context.lastResponse ?? '',
hasReportOutput,
reportOutput,
hasOutputContract,
outputContract,
});
}
}