# Сценарий «обслуживание» Способ решения известен, а **того, что нормирует спека, задача не трогает**: тулчейн и сборка, зависимости, гит-хуки и шаги гейта, перенос и чистка. Сценарий **пишет код**, но не заводит change и не пишет требований. Исход — работающая оснастка и синхронная ей документация. Сценарий выбирается развилкой на входе скилла ([SKILL.md](../SKILL.md), раздел «Развилка: какой сценарий») и называется вслух первой репликой. Здесь только его ход; общее для всех трёх сценариев — вход, чего может не быть, правило записанного вопроса, правило необратимого — живёт в SKILL.md и тут не пересказывается. ## Почему цикл SDD здесь не урезан, а остался без входа Это не поблажка по цене, и называть сценарий «коротким путём для мелких задач» нельзя: путь, выбираемый по самооценке размера, и есть тот самый дешёвый способ «ускориться», против которого написана вся защита сценария решения. **У обслуживания нет дельта-спек по построению.** Тип `chore` определён через «наблюдаемое поведение не меняется», а на дельтах стоит весь цикл: `propose` их порождает, разметка выведена **из них**, `review-specs` сверяет **с ними**, объяснение чекпоинта собирается из `proposal.md` и `design.md`, `archive` вливает их в актуальные спеки. Change без дельт — пустой артефакт, который потом надо архивировать, и разметчик по нему назовёт не те темы. Шаги цикла здесь не пропущены — **им нечего обрабатывать**. Отсюда и состав сценария: выпали ровно те шаги, у которых нет предмета, и не выпал ни один из тех, у которых он есть. ## Признак — связка, а не одно условие Сценарий выбирается двумя проверками сразу, и обе обязательны: 1. **тип записи предлагает** — `chore`, реже `fix`, чьё исправление возвращает поведение к уже записанному в спеке; 2. **отсутствие дельт подтверждает** — прочитав постановку, ты не находишь требования, которое пришлось бы добавить, изменить или снять. Один признак без второго не выбирает сценарий. Тип объявляет автор записи, и он может ошибиться в обе стороны; отсутствие дельт — суждение исполнителя, и оно принимается только тогда, когда согласуется с объявленным типом. Расхождение двух признаков — это не развилка, а стоп: скажи, что тип и предмет работы разошлись, и остановись. **Имя сценария не равно имени типа, и это намеренно.** `fix` без дельта-спеки идёт сюда законно — поведение разошлось с **заявленным**, значит заявленное уже записано, и менять спеку не нужно. Сценарий, названный именем типа, такую задачу либо отправил бы в полный цикл ради пустого change, либо принял бы как исключение, а исключения не исполняются. ## Дельта нашлась по ходу — стоп, и у него свой порядок Признак тот же, что на шаге 7 сценария решения: **меняется ли то, что записано в `openspec/specs/`**. Обнаружилось, что меняется, — работа перестала быть обслуживанием в ту же секунду, и продолжать её нельзя: коммит обслуживания заявляет «поведение не менялось», а оно меняется. **Задача при этом не сломалась — она оказалась шире своего типа.** Поэтому стоп здесь не «бросить и доложить», а три шага по порядку. **1. Назови тип, которым задача оказалась.** Разрез тот же, по которому типы и разведены: - **`fix`** — поведение расходится с **заявленным**: спека уже описывает верное, и правка возвращает систему к записанному; - **`feature`** — снаружи появляется то, чего не было: спеке нужно новое требование. Тип называется прямо и с причиной. «Нужно менять спеки» без имени типа перекладывает классификацию на человека в тот момент, когда весь материал для неё у тебя. **2. Объясни человеку простым языком.** Экран текста, не больше: - **что просили сделать** — одной фразой из записи; - **что нашлось** — какое поведение меняется, словами домена, а не именами файлов и функций; - **почему это перестало быть обслуживанием** — одной фразой: у обслуживания поведение не меняется по определению; - **чем задача становится** — `fix` или `feature`, с причиной из разреза выше; - **что уже сделано** и что из этого лежит в рабочем дереве. Проверка на простой язык та же, что у чекпоинтов двух других сценариев: **в тексте нет `SHALL`, нет имён файлов и функций там, где вещь называется по-русски, и нет слов, которых нет в паспорте проекта.** **3. Дай два решения и жди ответа.** Их ровно два, и оба законны: - **переформулировать запись** — тип меняется на названный, и дальше задача идёт **процессом своего типа**: сценарием решения, следующим прогоном. Формат записи правит `av-dev:task-track`, а не ты: у нового типа своя схема разделов, и готовность её проверит `ready` — той же машиной, что и на входе. Прогон обслуживания на этом кончается, исход — «меняется спека»; - **прекратить работу** — человек не готов расширять задачу сейчас. Исход тот же, запись остаётся как была, вопрос записывается там, где проект держит вопросы. **Третьего решения — «доделать как обслуживание» — нет.** Оно и есть то самое молчаливое изменение поведения, против которого стоит весь разрез: под коммитом, заявляющим «поменяли оснастку», уехала бы правка, не прошедшая ни ревью дизайна, ни чекпоинта, и не оставившая следа в спеках. **Сделанное не выбрасывается ни при каком из двух решений.** Оно остаётся в рабочем дереве незакоммиченным: при переформулировке уезжает в change следующим прогоном, при отказе — человек решает сам, откатить или оставить. Коммитить его сообщением про обслуживание нельзя. **Прогон, дошедший до этого стопа, стоит дороже обычного** — и это довод за проверку признака на шаге 1, а не после написанного кода. ## OpenSpec здесь не предпосылка Как и разведке, обслуживанию OpenSpec не нужен: оно не заводит change, не пишет дельта-спек и не архивирует. Ни один из проходов его плана ревью на дельта-спеки не завязан — это сказано и в `av-dev:code-review`, раздел «Прогон без change». Каталога в проекте нет — обслуживание идёт целиком, и деградацией это не является. ## Планового стопа у этого сценария нет **И это следствие, а не упрощение.** Чекпоинт решения объясняет человеку **выбор**: в чём проблема, как решаем, чем рискуем. У обслуживания выбора нет по построению — что делать, сказано в записи, а критерии приёмки у него самые дешёвые из всех типов: команда, которая раньше падала или требовала трёх шагов. Объяснение свелось бы к пересказу задачи её же автору. Стоп, на котором нечего решать, вырождается в обряд одобрения и обесценивает те стопы, где решать есть что. **Место, где ответа всё же ждут, одно, и плановым оно не является** — стоп по найденной дельте (раздел «Дельта нашлась по ходу»). Через него проходят не все прогоны, а только те, где задача оказалась не тем, чем объявлена. **Правило необратимого при этом действует полностью** (SKILL.md, «Когда спрашивать вне чекпоинта»), и здесь оно опаснее, чем кажется. Единственный сценарий без планового стопа — ровно тот, чья работа чаще прочих лезет в выкладку, в токены, в хуки и в чужие данные. Правка оснастки выглядит безобидной до момента, когда её уже не откатить. ## Ход работы ```mermaid flowchart TD in["сценарий выбран: обслуживание"] s1["1. прочитать задачу
критерии приёмки и границы"] s2["2. сделать правку
гейт тронут — сверить состав, не цвет"] s3["3. гейт проекта до зелёного"] s4["4. ревью фиксированным планом
av-dev:code-review, без change"] s5["5. синк документации — av-dev:doc-sync"] s6["6. коммит работы — av-dev-git:commit"] s7["7. закрыть задачу — av-dev:task-track,
вторым коммитом учёта"] out["исход назван"] in --> s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> out s1 -.->|"форма правки неизвестна"| stop1["стоп: нужна разведка"] s2 -.->|"нашлась дельта-спека"| stop2["стоп: назвать тип,
объяснить, дать два решения"] ``` Схема — **сводка**: содержание каждого шага в его разделе ниже, и при расхождении прав текст. ## Наблюдаемые исходы сценария Четыре, и каждый обязан быть назван в докладе прямо: - **сделана** — определение сделанного выполнено целиком; - **не доведена** — с причиной и с записанным вопросом; названо, что именно сделано и до какой границы; - **меняется спека** — работа оказалась шире своего типа. Стоп с объяснением и двумя решениями человека: переформулировать запись в `fix` или `feature` и решать её процессом того типа следующим прогоном — либо прекратить. Сделанное остаётся в рабочем дереве незакоммиченным. Доклад называет **оба**: какой тип предложен и что человек выбрал; - **нужна разведка** — форма правки неизвестна (мажорное обновление, смена сборщика, переезд гейта на другой инструмент) **или сработал триггер ADR**: дорогой откат, намеренный отказ от очевидного подхода, пересмотр прежнего решения. Стоп с названной причиной, разведка идёт следующим прогоном. **Последний исход — не редкость, и его стоит ждать.** Незнакомое обслуживание — это выбор подхода с ценой и с тем, что становится невозможным, — предмет чекпоинта вариантов, а не работы без стопа. И там же решение получает законный источник для ADR: список источников канон закрыл двумя — архивный `design.md` и записка разведки, — а обслуживание не производит ни того ни другого. ## Определение сделанного Задача сделана, когда верно всё: 1. гейт проекта зелёный, и **если правка трогала сам гейт — сверен его состав**, а не только цвет; 2. ревью проведено фиксированным планом сценария, исход назван по каждой теме плана, а темы, которых в плане нет, названы в границах покрытия; 3. **документация синхронизирована с принуждённым отрицанием** — каждый документ канона получил строку; 4. коммит сделан в текущую ветку; 5. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван оракул и наблюдаемый исход.** Это доклад приёмщику, а не отметка «принято»: исполнитель и приёмщик здесь совпали, и правило то же, что в решении. ## Шаги ### 1. Прочитать задачу Прочитай запись. У `chore` обязательны два раздела, и оба нужны тебе прямо сейчас: **«Затрагивает»** — границы, которые у обслуживания часто не в коде (конфиг и его образцы, версия зависимости, команда сборки, файл CI), и **«Критерии приёмки»** — с оракулами. Здесь же обе проверки признака: тип предлагает, отсутствие дельт подтверждает (раздел «Признак — связка»). И здесь же — проверка на незнакомое: если форма правки не известна до начала, а нащупывается по ходу, объявляй исход **нужна разведка** и не начинай. **Проверка на «заодно».** Обслуживание любит склеиваться в пачку — обновить зависимости, переписать сборку и убрать мёртвый код одной задачей. Не мерджится порознь — это несколько задач: объявляй исход **не доведена** с причиной «задача не одна» и останавливайся. Нарезкой владеет `av-dev:task-track`, а не ты, и делать её по ходу нельзя — получится один коммит, в котором обновление зависимости не отделить от чистки. ### 2. Сделать правку Код и конфиги — по конвенциям проекта. Правка по размеру задачи: чинится названное в записи, соседнее не улучшается заодно. **Гейт правится — сверь состав, а не цвет.** Красный, ставший зелёным, виден сразу; убыли проверок гейт не покажет — он зелёный и до, и после. Это самая дорогая из возможных правок оснастки: молча выключено то, чем проверяется всё остальное. Состав проверок и способ его снять — **дело проекта**: он объявляет их семантикой гейта в `CLAUDE.md`. Снимай исходное состояние **до** правки, по тому, как проект это описал. **Проект состав не описал — скажи строкой доклада, что сверен только цвет.** Обходного пути не выдумывай: угаданный состав хуже отсутствующего, потому что читается как сверенный. Это же строка и повод — предложить проекту дописать слот в `CLAUDE.md`. ### 3. Гейт до зелёного Прогони гейт и добейся зелёного — он же условие следующего шага: пока гейт красный, проходы с мнением не запускаются. **Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг сборки отрабатывает, хук ставится на чистом клоне. Правка, которую нельзя проверить ничем, кроме «у меня локально работает», называется в докладе строкой. ### 4. Ревью — план фиксирован сценарием Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим и **план сценария**. Change ты не передаёшь — его нет. **Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым он судит, у обслуживания не определены: размер он меряет по `proposal.md`, `design.md`, `tasks.md` и дельта-спекам, а незнакомость — по форме решения, которой здесь нет (незнакомое ушло в разведку шагом 1). Разметчик без своего корпуса вернул бы метку, выведенную из ничего. Поэтому план у сценария **свой и постоянный**, и глубину он называет сам — проходы берут её из метки, а метки здесь нет: | Тема | Дом | Кто закрывает | Глубина и вход | Когда | | --- | --- | --- | --- | --- | | `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда | | `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда | | `conventions` + технический разбор | `conventions.*` | `review-code` | вход `small`: только индекс конвенций; потолки 3 технических и 2 конвенционных; **третья половина включена** — сверка с инвариантами `CLAUDE.md`, потолок 1 | дифф трогает код, а не только оснастку | **Глубина названа в плане потому, что иначе её неоткуда взять.** Вход и потолки `review-code` заданы меткой, у `review-basics` меткой задана и сама возможность запуска; на прогоне без метки оба взяли бы их наугад — то есть по-разному от прогона к прогону, и молча. **Третья половина `review-code` включена намеренно.** В конвейере она живёт при метке `small`, где приёмник тем не запускается, и сверяет дифф с записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и `architecture`. Здесь у неё та же работа: без неё `security` не смотрит вообще никто. Триаж обязателен, как и на всяком прогоне: он единственный сток и единственный, кто сверяет план с исходом. На его вход подаётся этот план — вместо плана разметки, которого нет. **Условие третьей строки проверяемое, и смотрится оно по диффу**, а не по намерению: обновление зависимости или правка файла CI кода не трогают, чистка и перенос — трогают. `review-code` — единственный проход, который вообще говорит «здесь ошибка в логике», и чистка, прошедшая без него, проверена только на то, что она собирается. **Сигнал о заниженной метке на этом прогоне не работает** — метки нет, и поднимать нечего. Его место занимает признак сценария: показалось, что глубины мало, потому что задача крупнее заявленного, — ищи дельту, а не метку. **Границы покрытия называются полностью:** - `requirements` — предмета нет, дельта-спек не существует; - `security` — своего прохода нет; сверена против записанных инвариантов внутри `review-code`, а он шёл не всегда. Не шёл — тему не смотрел никто, и это говорится прямо; - `architecture` — то же: только против инвариантов, и только если шёл `code`. Отчёт, из которого исчезло «что не смотрел никто», сообщает «проверено», не сообщая, что именно. Отработка — как в решении: помеченное `инлайн` чини сам и не логируй, `развилка` — вопросом в запись. После правок снова гейт. Отложенные находки собери в секцию доклада `Урожай`; задачи из него заводит `av-dev:task-track`, не ты. ### 5. Синк документации — главный шаг этого сценария **Вызови Skill `av-dev:doc-sync`.** Правило то же и такое же жёсткое: **принуждённое отрицание** — каждый документ канона либо назван обновлённым, либо получает «не требуется, потому что…». Нетронутые группируются одной строкой. **Здесь этот шаг весит больше, чем в решении, и вот почему.** Обслуживание не меняет поведения — значит, почти всё, что оно меняет, это документация: команды, шаги гейта, зависимости поимённо, пути, имя основной ветки, настройки с числовым значением, место механизации правила. Ровно эти факты `doc-code-drift` и сверяет с кодом (перечень закрыт, живёт в каноне) — то есть сценарий, чаще всех прочих двигающий сверяемые факты, обязан отчитаться по ним раньше всех прочих. Отдельно один документ, которого нет в перечне тем, а синку он нужен: **`conventions.*`, раздел «Механизировано»** — если правило переехало в линтер, и тогда его проза из конвенций **удаляется**, а не остаётся вторым домом. **`adr/` этот сценарий не пополняет, и это не пропуск.** У ADR закрытый список источников — архивный `design.md` либо записка разведки, — и ни того ни другого обслуживание не производит. Поэтому триггеры ADR здесь работают **стоп-признаком, а не поводом завести запись**: сработал дорогой откат, намеренный отказ от очевидного подхода или пересмотр прежнего решения — сценарий выбран неверно, объявляй исход **нужна разведка** и останавливайся. Решение с ценой обязано пройти чекпоинт вариантов, а не появиться в коммите обслуживания, чей смысл — «ничего не решали, поменяли оснастку». Список документов и их триггеров здесь не дублируется — он в чек-листе скилла `av-dev:doc-sync`; копия уже однажды разошлась с оригиналом. Документов канона в проекте нет — синка нет вовсе: скажи это исходом и предложи завести канон скиллом `av-dev:doc-canon`. ### 6. Коммит Коммить **в текущую ветку** (`git rev-parse --abbrev-ref HEAD`), сам ветку не создавай и не переключай, ничего не пушь. Сообщение — по-русски, **скиллом `av-dev-git:commit`**. Вызов не разрешился — напиши сам и скажи строкой, что форму коммита не сверял никто. Одна задача — один осмысленный коммит. ### 7. Закрыть задачу — после коммита, не раньше **Вызови Skill `av-dev:task-track`** и попроси закрыть задачу как реализованную. Порядок обязателен: закрытие удаляет файл задачи, и сделанное до коммита оно оставило бы задачу закрытой без следа работы, если шаг 6 упадёт. **Закрытие тоже коммитится — вторым коммитом, тут же**, сообщением про учёт: `закрыта задача `. Каталога задач в проекте нет — ничего не выдумывай: скажи, что учёт остаётся за владельцем, и назови исход. ## Границы: чего обслуживание не делает - **Не меняет поведения.** Обнаружилось, что меняет, — стоп с исходом «меняется спека»: назвать тип, объяснить, дать два решения. Это единственная граница сценария, у которой есть проверяемый признак, и она же единственная, которую выгодно нарушить молча. - **Не переписывает запись задачи сам.** Тип ты **предлагаешь** с причиной, меняет его `av-dev:task-track` и только после ответа человека: исполнитель, переклеивший тип на ходу, назначает себе другой процесс и другую глубину проверки. - **Не решает, нужна ли работа.** Как и оба соседних сценария: «надо ли» — вопрос человека. - **Не нарезает пачку на задачи.** «Обновить зависимости и переписать сборку» — это `av-dev:task-track` и его правила нарезки. - **Не выбирает форму правки, когда она незнакома, и не принимает решений с ценой.** Мажорное обновление, смена инструмента, намеренный отказ идут разведкой: там есть чекпоинт вариантов и законный источник для ADR, здесь нет ни того ни другого. - **Не заводит задачи из урожая ревью.** Урожай передаётся списком. ## Доклад обслуживания Общее ядро доклада — в SKILL.md; сверх него сценарий обязан назвать: - **что подтвердило признак** — тип записи и то, что дельта-спек не нашлось; дельта нашлась — **какой тип предложен, с причиной, и что человек выбрал**: переформулировать или прекратить; - **что стало иначе для разработчика** — одной фразой, адресуясь ему, а не выдуманному пользователю; - **состав гейта до и после**, если правка его трогала; не сверялся — почему; - по каждому критерию приёмки: **оракул и наблюдаемый исход**; - **`Урожай`** — отложенные находки списком; - **строка границ покрытия**: план сценария фиксирован, разметчик не запускался, `requirements` не смотрел никто, а `security` и `architecture` — только против записанных инвариантов, и то если шёл проход `code`. ## Тонкости сценария - **Самый частый способ соврать этим сценарием — назвать `chore` то, что меняет поведение.** Тип, оставшийся от первой формулировки, врёт ровно там, где по нему выбирают путь; проверка признака стоит одного чтения записи и делается на шаге 1, а не после написанного кода. - **Отсутствие чекпоинта не делает сценарий автономнее прочих.** Правило необратимого здесь то же, и срабатывает оно чаще: выкладка, токены, хуки, чужие данные — обычное содержимое задач обслуживания. - **Зелёный гейт после правки гейта ничего не доказывает.** Это единственное место конвейера, где инструмент проверяет сам себя, и потому состав сверяется отдельно от цвета. - **Правка оснастки, сделанная «заодно» внутри чужой задачи, этим сценарием не проходит вовсе** — она едет в чужом коммите и не получает ни своего ревью, ни своей строки синка. Заметил нужную правку по ходу решения — вопрос в запись, а не правка мимоходом.