docs: спека approval-gates — точные формулировки гейтов A/B и frozen-планов (T4.1-T4.3, #59)
This commit is contained in:
parent
734c80899d
commit
7e3b86b59a
@ -209,10 +209,12 @@ repository layout:
|
||||
enable them. They are active only in a session with the reinstalled bundle
|
||||
(D-13): an old install keeps the text-only loop, and the `sdd` skill checks the
|
||||
plan approval by hand.
|
||||
- Plan files are always rewritten in full: an `edit` fragment cannot carry the
|
||||
`**Spec:**` line, so gate A denies it — as it denies bash plan targets and
|
||||
`str_replace_editor` creates, whose content it cannot see. The deny text then
|
||||
names the missing `**Spec:**`, not the real reason.
|
||||
- Gate A is content-based: it reads `**Spec:**` from `content` / `new_string`.
|
||||
An `edit` fragment is checked against its own text, so a fragment carrying a
|
||||
`**Spec:**` line for an approved change passes, while the usual Spec-less
|
||||
fragment is denied (rewrite the plan file in full). Bash plan targets and
|
||||
`str_replace_editor` creates are denied because the gate cannot see their
|
||||
content; the deny text then names the missing `**Spec:**`, not the real reason.
|
||||
- `terminal_open` / `terminal_send` write files outside both the gates and the
|
||||
bash hooks — a residual risk not covered by the port.
|
||||
- `bundle/hooks` is a symlink for local-directory installation. A published
|
||||
|
||||
@ -46,7 +46,8 @@ plan:
|
||||
`**Spec:**` или со Spec-ссылкой вне `openspec/changes/` (legacy
|
||||
`docs/superpowers/specs/`) — DENY «plan must carry **Spec:** pointing to
|
||||
openspec/changes/<id>» (D-9); правка без содержимого (bash-цель,
|
||||
edit-фрагмент) тоже DENY — гейт не видит содержимого.
|
||||
`str_replace_editor` create) и фрагмент `edit` без `**Spec:**` тоже DENY —
|
||||
гейт проверяет только то содержимое, которое видит.
|
||||
2. **Гейт B (sdd до approve плана).** Два входа (D-7): (a) запись в
|
||||
`.superpowers/sdd/<план>/**` (ledger, брифы — старт sdd в Режиме 1) и
|
||||
(b) диспатч `subagent`/`subagent_fork`/`workflow`, в промпте или
|
||||
@ -66,10 +67,11 @@ plan:
|
||||
AskUserQuestion-подтверждение: `/approve` спрашивает подтверждение
|
||||
перед записью, ответ «да» = владелец).
|
||||
|
||||
Исторические планы: четыре файла — `2026-10-11-roles-and-memory`,
|
||||
`2026-10-24-dsh-decouple`, `2026-10-24-dsh-port-spike`,
|
||||
`2026-10-24-dsh-role-loader` — написаны до лупа и не несут `**Spec:**`
|
||||
(T1.3). По D-9 любая их правка — DENY: они заморожены, новая работа идёт
|
||||
Исторические планы: четыре файла написаны до лупа (T1.3, T4.3) —
|
||||
`2026-10-11-roles-and-memory` несёт legacy-`**Spec:**` в
|
||||
`docs/superpowers/specs/`, а `2026-10-24-dsh-decouple`,
|
||||
`2026-10-24-dsh-port-spike` и `2026-10-24-dsh-role-loader` — без `**Spec:**`.
|
||||
По D-9 запись в любой из них — DENY: они заморожены, новая работа идёт
|
||||
отдельным планом. Это ожидаемое поведение, а не регресс.
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
@ -101,19 +101,22 @@ MUST отказать с перечислением отсутствующих
|
||||
(`content` или `new_string`). Ссылка MUST указывать в `openspec/changes/<id>`;
|
||||
план без `**Spec:**` и план с legacy-ссылкой в `docs/superpowers/specs/**` MUST
|
||||
получать отказ `plan must carry **Spec:** pointing to openspec/changes/<id>`
|
||||
(D-9). Правка без содержимого (bash-цель, edit-фрагмент, `str_replace_editor`
|
||||
create) MUST получать тот же отказ: гейт не видит содержимого и не может
|
||||
подтвердить Spec — fail closed.
|
||||
(D-9). Правка без содержимого (bash-цель, `str_replace_editor` create) MUST
|
||||
получать тот же отказ: гейт не видит содержимого и не может подтвердить Spec —
|
||||
fail closed. Фрагмент `edit` проверяется по своему `new_string` (content-based):
|
||||
фрагмент, который сам несёт `**Spec:**` на утверждённый change, проходит; фрагмент
|
||||
без Spec, с legacy-ссылкой или с неутверждённым change — DENY.
|
||||
|
||||
Если change определён, а его design-секция не является утверждением, гейт MUST
|
||||
отказать `design not approved; run /approve <id> design`. Утверждённый дизайн
|
||||
разрешает запись плана; запись вне `docs/superpowers/plans/` гейтом A не
|
||||
проверяется.
|
||||
|
||||
Исторические планы, написанные до лупа и не несущие `**Spec:**`, заморожены —
|
||||
четыре файла: `2026-10-11-roles-and-memory`, `2026-10-24-dsh-decouple`,
|
||||
`2026-10-24-dsh-port-spike`, `2026-10-24-dsh-role-loader` (T1.3). Любая их правка
|
||||
MUST получать отказ по D-9; это ожидаемое поведение, а не дефект.
|
||||
Исторические планы заморожены — все написаны до лупа: четыре файла.
|
||||
`2026-10-11-roles-and-memory` несёт legacy-`**Spec:**` в
|
||||
`docs/superpowers/specs/`; `2026-10-24-dsh-decouple`, `2026-10-24-dsh-port-spike`
|
||||
и `2026-10-24-dsh-role-loader` — без `**Spec:**` (T1.3, T4.3). Любая запись в
|
||||
любой из них MUST получать отказ по D-9; это ожидаемое поведение, а не дефект.
|
||||
|
||||
#### Scenario: План со Spec на неутверждённый дизайн
|
||||
|
||||
@ -133,16 +136,30 @@ MUST получать отказ по D-9; это ожидаемое повед
|
||||
на этот change
|
||||
- **THEN** запись плана разрешена
|
||||
|
||||
#### Scenario: Правка плана фрагментом
|
||||
#### Scenario: Правка плана фрагментом без Spec
|
||||
|
||||
- **WHEN** edit плана передаёт `new_string` без `**Spec:**` (фрагмент правки)
|
||||
- **THEN** отказ: план пишется только full-file write
|
||||
- **WHEN** edit плана передаёт `new_string` без `**Spec:**` (обычный фрагмент
|
||||
правки)
|
||||
- **THEN** отказ `plan must carry **Spec:** …`: гейт видит только фрагмент, план
|
||||
целиком пишется full-file write
|
||||
|
||||
#### Scenario: Фрагмент edit со Spec на утверждённый change
|
||||
|
||||
- **WHEN** edit плана передаёт `new_string`, в котором есть `**Spec:**` на change
|
||||
с непустой design-секцией
|
||||
- **THEN** запись разрешена: гейт проверяет содержимое, а не инструмент
|
||||
|
||||
#### Scenario: Правка исторического плана
|
||||
|
||||
- **WHEN** write в `docs/superpowers/plans/2026-10-24-dsh-decouple.md`
|
||||
- **THEN** отказ `plan must carry **Spec:** …` по D-9
|
||||
|
||||
#### Scenario: Правка плана с legacy-Spec
|
||||
|
||||
- **WHEN** write в `docs/superpowers/plans/2026-10-11-roles-and-memory.md`, чей
|
||||
`**Spec:**` указывает в `docs/superpowers/specs/`
|
||||
- **THEN** отказ `plan must carry **Spec:** …` по D-9
|
||||
|
||||
### Requirement: Гейт B — sdd до утверждения плана
|
||||
|
||||
Гейт SHALL срабатывать на двух входах (D-7): (a) write в
|
||||
@ -154,12 +171,22 @@ MUST получать отказ по D-9; это ожидаемое повед
|
||||
Для каждого входа гейт MUST определить файл
|
||||
`docs/superpowers/plans/<plan-name>.md`, взять из него `**Spec:**` → change и
|
||||
потребовать plan-секцию `approval.md` с sha256, равным sha256 текущего
|
||||
содержимого плана. Нет секции или sha256 — отказ `plan not approved; run
|
||||
/approve <id> plan`; несовпадение хэша или нечитаемый план — отказ `plan changed
|
||||
after approval; re-approve`; нет файла плана для sdd-каталога — отказ `plan not
|
||||
found: <plan-name>`; план без `**Spec:**` — отказ `plan not approved` (id
|
||||
неизвестен). Проверяться MUST все план-ссылки, а не первая; возвращается первый
|
||||
отказ, чтобы утверждённый план не маскировал неутверждённый.
|
||||
содержимого плана. Отказы MUST различаться по причине:
|
||||
|
||||
- (a) файл плана по ссылке отсутствует или не читается — `plan not approved; run
|
||||
/approve <id> plan` (fail closed; id остаётся литеральным `<id>`, если план не
|
||||
прочитан);
|
||||
- (b) план читается, но без `**Spec:**` или с legacy-ссылкой вне
|
||||
`openspec/changes/` — тот же `plan not approved; run /approve <id> plan`;
|
||||
- (c) план и change определены, но plan-секции нет или в ней нет sha256 — тот же
|
||||
`plan not approved; run /approve <id> plan`, уже с id change;
|
||||
- (d) plan-секция есть, но sha256 не совпадает с текущим содержимым плана —
|
||||
`plan changed after approval; re-approve`.
|
||||
|
||||
Для входа через `.superpowers/sdd/<plan-name>/` без файла
|
||||
`docs/superpowers/plans/<plan-name>.md` отказ — `plan not found: <plan-name>`.
|
||||
Проверяться MUST все план-ссылки, а не первая; возвращается первый отказ, чтобы
|
||||
утверждённый план не маскировал неутверждённый.
|
||||
|
||||
Вызов без план-ссылки MUST NOT гейтиться. Разбор промпта — эвристика по
|
||||
подстрокам, поэтому разбор fail open: вызов, в котором ссылка не распознана, не
|
||||
@ -185,6 +212,13 @@ found: <plan-name>`; план без `**Spec:**` — отказ `plan not approv
|
||||
- **WHEN** промпт `subagent` ссылается на два плана, и второй не утверждён
|
||||
- **THEN** диспатч отклонён по второму плану, даже если первый утверждён
|
||||
|
||||
#### Scenario: План по ссылке не найден
|
||||
|
||||
- **WHEN** `subagent` получает промпт со ссылкой на
|
||||
`docs/superpowers/plans/missing.md`, которого нет
|
||||
- **THEN** отказ `plan not approved; run /approve <id> plan` (fail closed), а не
|
||||
`plan changed after approval`
|
||||
|
||||
#### Scenario: План изменён после утверждения
|
||||
|
||||
- **WHEN** содержимое плана изменилось после `/approve <id> plan`
|
||||
@ -245,9 +279,11 @@ Bounded-задача без change и плана MUST NOT гейтиться (D-
|
||||
бандлом (D-13): до переустановки их нет, и sdd-скилл делает ручную проверку
|
||||
маркера; после — её дублирует enforcement.
|
||||
|
||||
Правка плана фрагментом (`edit`) MUST всегда получать отказ, план пишется
|
||||
full-file write (T3.5). Bash-цели и `str_replace_editor` create с невидимым
|
||||
содержимым тоже отказывают; deny-текст про отсутствующий `**Spec:**` для них
|
||||
Правка плана фрагментом (`edit`) обычно получает отказ: гейт проверяет только
|
||||
`new_string`, а обычный фрагмент не несёт `**Spec:**`, поэтому план целиком пишется
|
||||
full-file write; фрагмент со своей строкой `**Spec:**` на утверждённый change
|
||||
проходит (T3.5, T4.1). Bash-цели и `str_replace_editor` create с невидимым
|
||||
содержимым отказывают всегда; deny-текст про отсутствующий `**Spec:**` для них
|
||||
вводит в заблуждение — реальная причина в невидимом содержимом (T3.4).
|
||||
|
||||
`terminal_open` / `terminal_send` MUST оставаться вне гейтов: они пишут файлы в
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user