docs(openspec): спека approval-gates — request_approval как легитимный писатель (T1.1, #77)
This commit is contained in:
parent
1723806e16
commit
b81afaf59a
@ -16,9 +16,11 @@
|
|||||||
builder-check субагента, скопированный в change контроллером; он MUST быть записан
|
builder-check субагента, скопированный в change контроллером; он MUST быть записан
|
||||||
после builder-check и до передачи дизайна владельцу.
|
после builder-check и до передачи дизайна владельцу.
|
||||||
|
|
||||||
`openspec/changes/<id>/approval.md` SHALL быть маркером утверждения владельцем;
|
`openspec/changes/<id>/approval.md` SHALL быть маркером утверждения владельцем.
|
||||||
его единственный писатель — команда `/approve` (D-1), инструменты модели писать
|
Писать его MUST только код бандла — команда `/approve` или инструмент
|
||||||
его MUST NOT (гейт C ниже).
|
`request_approval` (D-1), и оба MUST писать только после подтверждения владельца.
|
||||||
|
Прямые записи модели (`write`, `edit`, `multi_edit`, `str_replace_editor`, `bash`)
|
||||||
|
MUST NOT создавать или менять этот файл (гейт C ниже, без изменений).
|
||||||
|
|
||||||
Формат `approval.md` — yaml с секциями `design:` (поля `by`, `at`) и `plan:`
|
Формат `approval.md` — yaml с секциями `design:` (поля `by`, `at`) и `plan:`
|
||||||
(поля `by`, `at`, `sha256`); `sha256` — hex-хэш sha256 содержимого файла плана на
|
(поля `by`, `at`, `sha256`); `sha256` — hex-хэш sha256 содержимого файла плана на
|
||||||
@ -42,11 +44,14 @@ builder-check субагента, скопированный в change конт
|
|||||||
|
|
||||||
### Requirement: Команда /approve
|
### Requirement: Команда /approve
|
||||||
|
|
||||||
`/approve <change-id> design|plan` SHALL быть единственным писателем
|
`/approve <change-id> design|plan` SHALL быть ручным писателем `approval.md` —
|
||||||
`approval.md` и MUST регистрироваться при `enabled` и `approvalGates: true`,
|
одним из двух писателей бандла, наравне с инструментом `request_approval`
|
||||||
независимо от флагов `roleCommand` и `checkCommand`: иначе гейты остаются без
|
(следующее требование); оба пишут только после подтверждения владельца, прямые
|
||||||
команды-разблокировки (T3.2). `change-id` MUST быть одним безопасным сегментом
|
записи модели — MUST NOT (гейт C). Команда MUST регистрироваться при `enabled` и
|
||||||
пути; небезопасный id или неизвестный вид — usage-отказ без записи.
|
`approvalGates: true`, независимо от флагов `roleCommand` и `checkCommand`: иначе
|
||||||
|
гейты остаются без команды-разблокировки (T3.2). `change-id` MUST быть одним
|
||||||
|
безопасным сегментом пути; небезопасный id или неизвестный вид — usage-отказ без
|
||||||
|
записи.
|
||||||
|
|
||||||
Вид `design` SHALL требовать существования `proposal.md`, `design.md` и
|
Вид `design` SHALL требовать существования `proposal.md`, `design.md` и
|
||||||
`builder-check.md` в `openspec/changes/<id>/`; при отсутствии файла команда
|
`builder-check.md` в `openspec/changes/<id>/`; при отсутствии файла команда
|
||||||
@ -93,6 +98,71 @@ MUST отказать с перечислением отсутствующих
|
|||||||
- **THEN** команда отказывает с `userQuestions service unavailable; /approve
|
- **THEN** команда отказывает с `userQuestions service unavailable; /approve
|
||||||
requires it` и `approval.md` не пишется
|
requires it` и `approval.md` не пишется
|
||||||
|
|
||||||
|
### Requirement: Инструмент request_approval
|
||||||
|
|
||||||
|
Инструмент модели `request_approval` SHALL быть вторым писателем `approval.md`:
|
||||||
|
модель инициирует утверждение, владелец только подтверждает в диалоге, а маркер
|
||||||
|
пишет код бандла (`lib/approval.js`) — с теми же проверками предпосылок и той же
|
||||||
|
записью, что у `/approve`. Инструмент MUST регистрироваться при `enabled` и
|
||||||
|
`approvalGates: true`, если в контексте доступен `ctx.tools.register`;
|
||||||
|
`userQuestions` MUST резолвиться лениво (не через `inject`), чтобы отсутствие
|
||||||
|
сервиса не парковало весь бандл.
|
||||||
|
|
||||||
|
Параметры инструмента — `change_id` (строка) и `kind` (`design` | `plan`).
|
||||||
|
Валидация MUST быть собственной, в инструменте: пустой или небезопасный
|
||||||
|
`change_id` и неизвестный `kind` — usage-отказ без записи и без вопроса
|
||||||
|
владельцу; проверки предпосылок и отказы те же, что у `/approve` (design —
|
||||||
|
proposal/design/builder-check, plan — утверждённый дизайн, ровно один план,
|
||||||
|
sha256), и любой отказ MUST NOT писать `approval.md`. Подтверждением SHALL
|
||||||
|
считаться только ответ `answers[0].selected[0] === 'Approve'`; Cancel,
|
||||||
|
отсутствие ответа и ошибка диалога — отказ `not confirmed` без записи. При
|
||||||
|
отсутствии сервиса `userQuestions` инструмент MUST отказать (`userQuestions
|
||||||
|
service unavailable`), а не писать.
|
||||||
|
|
||||||
|
#### Scenario: Модель просит утвердить дизайн
|
||||||
|
|
||||||
|
- **WHEN** модель вызывает `request_approval` с `{change_id: <id>, kind: design}`,
|
||||||
|
предпосылки выполнены, а владелец выбрал Approve
|
||||||
|
- **THEN** в `approval.md` пишется design-секция (`by: owner`, `at` в ISO 8601
|
||||||
|
UTC), инструмент возвращает `{approved: true, message}`
|
||||||
|
|
||||||
|
#### Scenario: Модель просит утвердить план
|
||||||
|
|
||||||
|
- **WHEN** модель вызывает `request_approval` с `{change_id: <id>, kind: plan}`,
|
||||||
|
дизайн утверждён, план единственный, владелец выбрал Approve
|
||||||
|
- **THEN** plan-секция содержит `by: owner`, `at` в ISO 8601 и sha256 текущего
|
||||||
|
содержимого плана
|
||||||
|
|
||||||
|
#### Scenario: Владелец отменил утверждение
|
||||||
|
|
||||||
|
- **WHEN** в диалоге `request_approval` владелец выбрал Cancel
|
||||||
|
- **THEN** инструмент возвращает `{approved: false, message: 'not confirmed'}`, а
|
||||||
|
`approval.md` не меняется
|
||||||
|
|
||||||
|
#### Scenario: Некорректные аргументы request_approval
|
||||||
|
|
||||||
|
- **WHEN** модель передала неизвестный `kind` или небезопасный `change_id`
|
||||||
|
(например, `../x`)
|
||||||
|
- **THEN** инструмент возвращает usage-отказ без записи, владелец не спрашивается
|
||||||
|
|
||||||
|
#### Scenario: Отсутствует builder-check при утверждении дизайна моделью
|
||||||
|
|
||||||
|
- **WHEN** `kind: design`, а `builder-check.md` в change нет
|
||||||
|
- **THEN** инструмент отказывает с перечислением отсутствующего файла, владелец не
|
||||||
|
спрашивается, `approval.md` не создаётся
|
||||||
|
|
||||||
|
#### Scenario: request_approval plan без утверждённого дизайна
|
||||||
|
|
||||||
|
- **WHEN** `kind: plan`, а design-секции нет
|
||||||
|
- **THEN** инструмент отказывает `design not approved; run /approve <id> design`,
|
||||||
|
владелец не спрашивается, `approval.md` не меняется
|
||||||
|
|
||||||
|
#### Scenario: request_approval без сервиса вопросов
|
||||||
|
|
||||||
|
- **WHEN** `userQuestions` отсутствует
|
||||||
|
- **THEN** инструмент возвращает `{approved: false}` с `userQuestions service
|
||||||
|
unavailable`, владелец не спрашивается, `approval.md` не пишется
|
||||||
|
|
||||||
### Requirement: Гейт A — план до утверждения дизайна
|
### Requirement: Гейт A — план до утверждения дизайна
|
||||||
|
|
||||||
Для write/edit/multi_edit/`str_replace_editor` с целевым путём под
|
Для write/edit/multi_edit/`str_replace_editor` с целевым путём под
|
||||||
@ -231,9 +301,12 @@ fail closed. Фрагмент `edit` проверяется по своему `n
|
|||||||
|
|
||||||
### Requirement: Гейт C и bash-обход
|
### Requirement: Гейт C и bash-обход
|
||||||
|
|
||||||
`approval.md` MUST писаться только командой `/approve`: любой write/edit с целью,
|
`approval.md` MUST писаться только кодом бандла — командой `/approve` или
|
||||||
чей basename — `approval.md`, и любая bash-цель с таким именем MUST получать
|
инструментом `request_approval`: любой write/edit с целью, чей basename —
|
||||||
отказ `approval.md is written only by /approve` (D-1).
|
`approval.md`, и любая bash-цель с таким именем MUST получать отказ `approval.md
|
||||||
|
is written only by /approve` (D-1). Deny-текст не меняется: он адресован прямым
|
||||||
|
записям модели (`write`/`edit`/`bash`), а `request_approval` пишет маркер кодом
|
||||||
|
бандла и под этот запрет не попадает.
|
||||||
|
|
||||||
Bash-обход MUST быть закрыт (D-8): если команда содержит грубую подстроку
|
Bash-обход MUST быть закрыт (D-8): если команда содержит грубую подстроку
|
||||||
`docs/superpowers/plans`, `approval.md` или `.superpowers/sdd`, её файловые цели
|
`docs/superpowers/plans`, `approval.md` или `.superpowers/sdd`, её файловые цели
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user