docs(openspec): change enforce-upstream-loop — proposal и design гейтов (#59)
This commit is contained in:
parent
6351f0b00c
commit
a3928bc64a
2
openspec/changes/enforce-upstream-loop/.openspec.yaml
Normal file
2
openspec/changes/enforce-upstream-loop/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-25
|
||||
84
openspec/changes/enforce-upstream-loop/design.md
Normal file
84
openspec/changes/enforce-upstream-loop/design.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Design: enforce-upstream-loop — технические гейты цикла
|
||||
|
||||
Issue: #59. Статус: **черновик до builder-check**; владелец 25.10.2026:
|
||||
«оба гейта сразу», согласования отменены — решения ниже фиксируются в
|
||||
«Decisions made without discussion» и действуют как решения владельца.
|
||||
|
||||
## Контекст
|
||||
|
||||
Сессия ce9187ca: модель пропустила builder-check, рационализировала
|
||||
пропуск, план появился до утверждения дизайна. Цель — гейты, которые
|
||||
нельзя пропустить текстом.
|
||||
|
||||
## Артефакты и их писатели
|
||||
|
||||
| Артефакт | Пишет | Когда |
|
||||
|---|---|---|
|
||||
| `openspec/changes/<id>/builder-check.md` | контроллер (отчёт свежего субагента, скопированный в change) | после builder-check, до передачи дизайна владельцу |
|
||||
| `openspec/changes/<id>/approval.md` | только команда `/approve` | владелец утверждает design/plan |
|
||||
| План `docs/superpowers/plans/*.md` | writing-plans-флоу | после design-утверждения (гейт A) |
|
||||
| Диспатч implementer'а | sdd-флоу | после plan-утверждения (гейт B) |
|
||||
|
||||
Формат `approval.md` (yaml):
|
||||
|
||||
```yaml
|
||||
design:
|
||||
by: aep
|
||||
at: 2026-10-25T10:00:00+04:00
|
||||
plan:
|
||||
by: aep
|
||||
at: 2026-10-25T11:30:00+04:00
|
||||
```
|
||||
|
||||
## Механика гейтов (в бандле, DSH-only)
|
||||
|
||||
`ctx.tools.guard` уже зовёт bash-хуки (`lib/guards.js`). Новая
|
||||
`lib/gates.js` добавляет **content-проверки** после хуков, в той же
|
||||
цепочке (порядок: guard-main/require-plan → approval-gates; оба — deny с
|
||||
причиной, монотонно):
|
||||
|
||||
1. **Гейт A (план до approve дизайна).** Для Edit/Write/MultiEdit с
|
||||
путём под `docs/superpowers/plans/`: из содержимого (`tool_input.content`
|
||||
или `new_string`) извлечь `**Spec:** <путь>`; если путь указывает в
|
||||
`openspec/changes/<id>/` и `approval.md` без секции `design:` — DENY
|
||||
«design not approved; run /approve <id> design». Содержимое без
|
||||
Spec-ссылки на change — пропуск.
|
||||
2. **Гейт B (sdd до approve плана).** Для `subagent`/`subagent_fork`:
|
||||
в промпте найти ссылку на план — `docs/superpowers/plans/<имя>.md`
|
||||
или `.superpowers/sdd/<план>/`; из плана взять `**Spec:**` →
|
||||
change; `approval.md` без секции `plan:` — DENY «plan not approved;
|
||||
run /approve <id> plan». Нет ссылки на план — пропуск.
|
||||
3. **`/approve`** (`command-approve.js`): `design` требует существование
|
||||
proposal.md, design.md, builder-check.md; `plan` требует секцию
|
||||
`design:` и существование ровно одного плана со Spec на этот change
|
||||
(несколько — отказ с перечислением). Дописывает/обновляет секцию,
|
||||
остальные секции не трогает. Владелец-агент различие: команда пишет
|
||||
`by: owner`; вызовы не от владельца — отказ (как отличить: DSH не
|
||||
даёт ролей; используем факт вызова из интерактивной сессии и
|
||||
AskUserQuestion-подтверждение: `/approve` спрашивает подтверждение
|
||||
перед записью, ответ «да» = владелец).
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
| # | Решение | Обоснование | Цена ошибки |
|
||||
|---|---|---|---|
|
||||
| D-1 | `/approve` — единственный писатель approval.md | Маркер, который не может подделать модель (нет прямых записей в approval.md — гейт A защищает и его? нет: approval.md не в plans/; добавлю guard: любые Edit/Write в `**/approval.md` из сессии — DENY, кроме команды) | Обход остаётся через API-инструменты — принимаем как остаточный риск, фиксируем в спеке |
|
||||
| D-2 | Гейты в бандле (DSH-only), CC-хуки не трогаем | CC-поток не должен меняться; один enforcement-слой на рантайм | Два мира правил (CC/DSH) — уже так, осознанно |
|
||||
| D-3 | Презентация дизайна владельцу не гейтится напрямую; закрыта цепочкой артефактов | Гейтить сообщения модели технически нельзя; цепочка: builder-check → approve → план → sdd делает пропуск видимым на первом же BLOCKED | Модель может «показать» дизайн без builder-check.md — тогда `/approve` откажет, и это видно владельцу |
|
||||
| D-4 | `/approve` требует подтверждения (вопрос владельцу) перед записью | Отделить владельца от модели в DSH (ролей нет) | Лишний клик на каждое утверждение — приемлемо |
|
||||
| D-5 | builder-check.md пишет контроллер после завершения субагента | Единственная точка, где отчёт субагента гарантированно сохранён в change | Файл можно подделать вручную — принимаем: гейт против случайного пропуска, не против злого умысла |
|
||||
| D-6 | Гейты не срабатывают для bounded-задач (нет change/плана) | Bounded-цикл не проходит лупа и не должен блокироваться | Никакого |
|
||||
|
||||
## Тесты (идеи для плана)
|
||||
|
||||
- parse approval.md / отказы / до-записи существующих секций;
|
||||
- гейт A: write плана со Spec на неутверждённый change → DENY; без Spec → OK; утверждённый → OK;
|
||||
- гейт B: subagent с промптом-ссылкой на план: без plan-секции → DENY, с ней → OK; промпт без ссылки → OK;
|
||||
- `/approve design` без builder-check.md → отказ; полный флоу design→plan → OK;
|
||||
- guard на записи в `**/approval.md` вне команды → DENY.
|
||||
|
||||
## На потом (в план)
|
||||
|
||||
- wiring `lib/gates.js` в `ctx.tools.guard` цепочку + конфиг `approvalGates: true`;
|
||||
- `command-approve.js` + регистрация; sdd-skill текст; roles-заметка;
|
||||
- спека-дельта capability `approval-gates`; README/PORT; тесты.
|
||||
63
openspec/changes/enforce-upstream-loop/proposal.md
Normal file
63
openspec/changes/enforce-upstream-loop/proposal.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Proposal: enforce-upstream-loop — технические гейты цикла
|
||||
|
||||
Issue: #59. Источники: сессия ce9187ca 24.10.2026 (модель пропустила
|
||||
builder-check и рационализировала пропуск; спека и план ушли владельцу одним
|
||||
пакетом), решение владельца 25.10.2026 «оба гейта сразу», согласования по
|
||||
этой фиче владельцем отменены (решения — в design.md).
|
||||
|
||||
## Why
|
||||
|
||||
Луп brainstorm→design→builder-check→approve→plan→approve→sdd существует в
|
||||
superpowers 7.0.0 как инструкции. В DSH-сессии 24.10.2026 модель пропустила
|
||||
builder-check, предложила его «вхолостую» и сняла предложение; план был
|
||||
написан до того, как владелец утвердил дизайн. Инструкции не являются
|
||||
механизмом: нужен технический гейт, который нельзя рационализировать.
|
||||
|
||||
## What Changes
|
||||
|
||||
1. **Артефакты**: `openspec/changes/<id>/builder-check.md` (отчёт свежего
|
||||
builder-check субагента) и `openspec/changes/<id>/approval.md` (маркер
|
||||
утверждения, yaml-секции `design:` и `plan:` с `by`/`at`).
|
||||
2. **Команда `/approve <change-id> design|plan`** в бандле — единственный
|
||||
писатель `approval.md`:
|
||||
- `design` — отказ, пока нет `proposal.md`+`design.md`+`builder-check.md`;
|
||||
- `plan` — отказ, пока нет design-утверждения и файла плана в
|
||||
`docs/superpowers/plans/`, чей `**Spec:**` указывает на этот change.
|
||||
3. **Гейт A (вход)**: bundle-guard отклоняет Edit/Write/MultiEdit в
|
||||
`docs/superpowers/plans/**`, если в содержимом есть строка `**Spec:**`
|
||||
со ссылкой на change без design-утверждения — план физически не
|
||||
создаётся до утверждения дизайна. «Уход к владельцу» закрывается цепочкой:
|
||||
approve дизайна требует builder-check; план требует approve дизайна;
|
||||
исполнение требует approve плана.
|
||||
4. **Гейт B (исполнение)**: bundle-guard отклоняет диспатчи `subagent`/
|
||||
`subagent_fork`, чей промпт ссылается на план в
|
||||
`docs/superpowers/plans/` или на `.superpowers/sdd/<plan>/`, если
|
||||
`approval.md` change'а не содержит `plan:` — sdd не стартует без
|
||||
утверждённого плана. Причина отказа: «plan not approved; run
|
||||
`/approve <change-id> plan`».
|
||||
5. **Тексты**: `dsh/skills/sdd/SKILL.md` — шаг «проверь approval-маркер до
|
||||
первого диспатча, при BLOCKED не обходи»; `roles/dev.md` (DSH-заметка
|
||||
`--roles`) — builder-check обязателен, пропуск — только явное решение
|
||||
владельца.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected: `dsh/bundle/` (новая `lib/gates.js`, `command-approve.js`,
|
||||
wiring в `index.js`), `dsh/skills/sdd/SKILL.md`, `dsh/tests/`,
|
||||
`dsh/install.sh` (`--roles`-заметка), спека-дельта (новая capability
|
||||
`approval-gates`), `dsh/README.md`/`dsh/PORT.md`.
|
||||
- Не трогаем: CC-сторону (хуки, роли, скрипты) — гейты живут только в
|
||||
DSH-бандле; bounded-задачи без change/плана не задеваются.
|
||||
- Breaking: нет для существующих потоков с утверждёнными change/планами;
|
||||
для неутверждённых — ожидаемый BLOCKED с подсказкой `/approve`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
Свой UI апрува; переписывание sdd-протокола; гейт на сообщения модели
|
||||
(презентация дизайна владельцу не перехватывается — закрыта цепочкой
|
||||
артефактов); CC-сторона.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
Нет: решения по enforcement-точкам приняты (design.md, «Decisions made
|
||||
without discussion»), владелец согласования отменил до пробуждения.
|
||||
Loading…
x
Reference in New Issue
Block a user