Превратите тесты в обратную связь, а не финальный ритуал
Цикл надёжен, когда агент видит красный baseline, меняет код, получает зелёный focused test и затем проверяет более широкий набор. Это отличает исправление причины от случайного зелёного результата. В companion-проекте весь цикл проходит сквозным демо: fixture обязан воспроизвести дефект до patch, иначе демонстрация падает.
validatePlan(repositoryBefore.root, plan, policy);
const baseline = await runCommand(temporaryRoot, testCommand, policy);
if (baseline.code === 0) {
throw new Error("The demonstration fixture must reproduce the bug before the patch");
}
const operation: TextReplacement = {
path: "src/discount.js",
expected: " return subtotal * (1 - percent);",
replacement: " return subtotal * (1 - percent / 100);"
};
await applyTextReplacement(repositoryBefore.root, operation);
const verification = await runCommand(temporaryRoot, testCommand, policy);
const repositoryAfter = await indexRepository(temporaryRoot, { maxFiles: 100, maxBytesPerFile: 128_000 });
const snapshotAfter = await snapshotRepository(repositoryAfter);
const changed = changedFiles(snapshotBefore, snapshotAfter);
const review = reviewChange(contract, changed, [verification]);
if (!review.passed) {
throw new Error(`Review gate failed: ${review.findings.join("; ")}`);
}
const fixedSource = await readFile(join(temporaryRoot, "src", "discount.js"), "utf8");
const output = {
repositoryFiles: repositoryBefore.entries.length,
promptSections: ["Goal", "Context", "Allowed write scope", "Constraints", "Procedure", "Done when", "Final report"],
planSteps: plan.steps.map((step) => step.id),
baselineExitCode: baseline.code,
finalExitCode: verification.code,
changedFiles: review.changedFiles,
reviewPassed: review.passed,
selectedSurfaceForRepeatableWorkflow: chooseGuidanceSurface({
repeats: true,
isWorkflow: true,
mustBeDeterministic: false,
isObjectiveMergeGate: false
}),
fixedLinePresent: fixedSource.includes("percent / 100")
};
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
if (!prompt.includes("# Done when")) {
throw new Error("Prompt contract is incomplete");За кодом стоят пять сигналов, и пропуск любого делает "зелёный" ненадёжным: reproduction (тест падает до patch по ожидаемой причине), focused verification (тот же тест проходит после), regression envelope (соседние тесты, typecheck и build не поймали побочный эффект), diff review (изменены только ожидаемые файлы) и evidence report (сохранены команды, exit codes и значимые строки вывода).
Как реагировать на срыв проверки - и как не реагировать, - лучше держать перед глазами.
Сбой проверки · Неправильная реакция · Правильный следующий шаг
- Test не воспроизводит дефект - Все равно менять код; Уточнить fixture, вход и ожидаемое поведение
- Unrelated tests уже красные - Объявить их результатом patch; Зафиксировать baseline и отделить новые failures
- Test завис - Ждать без ограничения; Timeout, лог и анализ ресурса или deadlock
- Output обрезан - Считать code 0 достаточным для diagnosis; Сохранить artifact или сузить команду
- UI test зеленый - Не смотреть результат; Проверить screenshot и browser console
Доказательство задаётся в самом prompt: Claude Code советует дать агенту проверку, которую он может выполнить, Codex рекомендует формулировать Done when, Cursor включает verification steps в план. Это общий фундамент трёх продуктов.
Не ослабляйте oracle. Удаление assertion, широкий snapshot update и увеличение timeout могут сделать pipeline зелёным, не исправив поведение. Любое изменение теста должно объяснять, почему прежнее ожидание неверно, - иначе вы чините индикатор, а не дефект.
Тесты дают сигнал самому исполнителю. Но окончательную готовность должен проверять кто-то другой - в свежем контексте.