Стройте план, который можно отклонить до patch
План полезен, если называет файлы, символы, риски, команды и ожидаемое доказательство. Перечень "написать код, добавить тесты" не помогает проверить scope и не выявляет скрытых действий. В companion-проекте шаг плана - это типизированная структура, а не свободный текст.
export interface PlanStep {
readonly id: string;
readonly title: string;
readonly reads: readonly string[];
readonly writes: readonly string[];
readonly commands: readonly CommandSpec[];
readonly evidence: string;
}
export interface ChangePlan {
readonly contract: TaskContract;
readonly steps: readonly PlanStep[];
}И раз план - это данные, его можно проверить кодом до исполнения: что каждый write входит в разрешённый scope, а каждая команда - в allowlist.
function validateStep(root: string, contract: TaskContract, step: PlanStep, policy: CommandPolicy): void {
if (step.id.trim() === "" || step.title.trim() === "" || step.evidence.trim() === "") {
throw new Error("Every plan step needs an id, title, and evidence target");
}
for (const path of [...step.reads, ...step.writes]) {
resolveContained(root, path);
}
for (const path of step.writes) {
const normalized = relative(root, resolveContained(root, path)).split("\\").join("/");
if (!pathAllowed(normalized, contract.allowedWritePaths)) {
throw new Error(`Plan writes outside allowed scope: ${path}`);
}
}
for (const command of step.commands) {
if (!policy.allowedCommandKeys.has(commandKey(command))) {
throw new Error(`Plan contains a command outside the allowlist: ${command.label}`);
}
}
}
export function validatePlan(root: string, plan: ChangePlan, policy: CommandPolicy): ChangePlan {
if (plan.steps.length === 0) {
throw new Error("Plan must have at least one step");
}
const ids = new Set<string>();
for (const step of plan.steps) {
if (ids.has(step.id)) {
throw new Error(`Duplicate plan step id: ${step.id}`);
}
ids.add(step.id);
validateStep(root, plan.contract, step, policy);
}
return plan;Смысл валидатора виден по вопросам review к каждому полю шага.
Поле шага · Вопрос review
reads- Достаточно ли фактов, чтобы менять этот слой?writes- Все ли пути входят в разрешенный scope?commands- Команда точная, безопасная и доступна в среде?evidence- Какой наблюдаемый результат докажет шаг?- risk - Что может сломаться за пределами happy path?
- stop - Какое открытие требует нового решения владельца?
Прогнать план через проверку полноты и увидеть, где он молчит о рисках или доказательстве, помогает лаборатория.
Проверьте полноту плана
Заполнено 0 из 5. Не разрешайте edit, пока открытые пункты меняют scope или проверку.
Главное - запретить скрытое расширение. Если во время реализации выяснилось, что нужно менять schema, dependency или публичный API, старый план больше не действует.
План - это точка отклонения, а не формальность. Агент должен вернуться с новым фактом и предложением, а не молча дописать patch за пределами согласованного. Отклонить план дёшево; откатить незапланированное изменение в проде - дорого.
План согласован. Но прежде чем писать код, надо решить, где живут правила - и здесь важно не смешивать общие инструкции с продуктовыми.