Глава 14

Генерируйте минимальный связный patch

Минимальный patch меняет ровно тот контракт, который объясняет дефект, и добавляет достаточное доказательство. Это не обязательно одна строка, но в нём нет побочных переименований, форматирования и улучшений вне цели. В companion-проекте запись устроена так, что предпросмотр требует ровно одного совпадения, а сама запись атомарна.

TypeScript
export function previewReplacement(before: string, operation: TextReplacement): string {
  if (operation.expected.length === 0 || operation.expected === operation.replacement) {
    throw new Error("Replacement must change one non-empty fragment");
  }
  const matchCount = occurrences(before, operation.expected);
  if (matchCount !== 1) {
    throw new Error(`Expected exactly one match in ${operation.path}, found ${matchCount}`);
  }
  return before.replace(operation.expected, operation.replacement);
}

export async function applyTextReplacement(root: string, operation: TextReplacement): Promise<void> {
  resolveContained(root, operation.path);
  const path = await assertRegularContainedFile(root, operation.path);
  const before = await readFile(path, "utf8");
  const after = previewReplacement(before, operation);
  const temporaryPath = join(dirname(path), `.${randomUUID()}.agent-write`);
  await writeFile(temporaryPath, after, { encoding: "utf8", flag: "wx", mode: 0o600 });
  await rename(temporaryPath, path);
}

Порядок изменения дисциплинирует: сохранить baseline, сопоставить каждый planned write с разрешённым scope, сформировать patch и проверить, что он меняет ожидаемый fragment, записать атомарно, снова построить snapshot и отклонить результат при незапланированных изменениях.

Типичные антипаттерны и контроль к каждому стоит держать перед глазами.

Антипаттерн · Риск · Контроль

  • Переписать файл целиком - Скрытый formatting diff и потеря комментариев; Patch по конкретным hunks
  • Заменить все совпадения - Изменить другую семантику; Ожидать точное число matches
  • Заодно обновить dependencies - Новая supply-chain и compatibility поверхность; Отдельная задача и review
  • Исправить тест вместо кода - Убрать сигнал дефекта; Сначала показать, почему test неверен
  • Форматировать весь repository - Скрыть содержательный diff; Formatter только на затронутых файлах

Две оговорки уточняют слово "минимальный". Первая - что связный не значит микроскопический: если изменение public behavior требует реализации, regression test и документации контракта, эти три файла могут составлять один минимальный связный patch. Удаление теста ради меньшего числа строк patch не улучшает.

Patch не подтверждает поведение. Даже идеальный diff остаётся гипотезой до test, build, static analysis или наблюдаемой проверки UI. Review кода и проверка выполнения нужны вместе, а не по отдельности.

Patch готов. Но чтобы его проверить, агент запускает команды - и запускать их надо как данные, а не как shell-текст.

Ссылки