From 51344d498808be09bb65b5c255fc4393103d5d0d Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sun, 23 Aug 2026 17:49:52 +0300 Subject: [PATCH] docs(specs): harden OpenSpec authority lifecycle --- AGENTS.md | 5 +- .../tasks.md | 15 ++- .../.openspec.yaml | 2 + .../design.md | 81 ++++++++++++ .../proposal.md | 29 +++++ .../specs/audit-remediation-workflow/spec.md | 12 ++ .../specs/brokerage-account-ledger/spec.md | 56 +++++++++ .../openspec-authority-and-lifecycle/spec.md | 49 ++++++++ .../specs/project-documentation/spec.md | 12 ++ .../specs/project-foundation/spec.md | 12 ++ .../tasks.md | 42 +++++++ .../specs/audit-remediation-workflow/spec.md | 4 +- .../specs/brokerage-account-ledger/spec.md | 10 +- .../openspec-authority-and-lifecycle/spec.md | 50 ++++++++ openspec/specs/project-documentation/spec.md | 11 ++ openspec/specs/project-foundation/spec.md | 6 +- scripts/verify-openspec-authority.sh | 109 ++++++++++++++++ scripts/verify-openspec-authority.test.sh | 116 ++++++++++++++++++ 18 files changed, 608 insertions(+), 13 deletions(-) create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/.openspec.yaml create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/design.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/proposal.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/audit-remediation-workflow/spec.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/brokerage-account-ledger/spec.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/openspec-authority-and-lifecycle/spec.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-documentation/spec.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-foundation/spec.md create mode 100644 openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/tasks.md create mode 100644 openspec/specs/openspec-authority-and-lifecycle/spec.md create mode 100644 scripts/verify-openspec-authority.sh create mode 100644 scripts/verify-openspec-authority.test.sh diff --git a/AGENTS.md b/AGENTS.md index bae51c2..da71a38 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,13 +2,14 @@ ## Назначение -Репозиторий SDD-артефактов и OpenSpec store: `openspec/`. Все функциональные изменения планируются в `openspec/changes//` до реализации в frontend или backend. +Репозиторий единственных versioned SDD-артефактов и canonical OpenSpec store: `wealth-ledger-specs` в `specs/openspec` (относительно workspace). Все функциональные изменения планируются в `openspec/changes//` до реализации в frontend или backend. ## Проверка ```bash openspec validate --type change --store wealth-ledger-specs openspec instructions apply --change --json --store wealth-ledger-specs +bash scripts/verify-openspec-authority.sh ``` ## Границы @@ -17,3 +18,5 @@ openspec instructions apply --change --json --store wealth-ledger-specs - Реализация отмечается в tasks только после выполненной работы. Перед архивированием change проходит `opsx-verify` и получает связанную документацию в `../docs`. - Не хранить код приложений и долгоживущие ADR в этом репозитории. - Для задачи по Audit ID, продолжения remediation или выбора следующего улучшения сначала прочитать [`../docs/development/audit-remediation.md`](../docs/development/audit-remediation.md); продолжать или создавать change только в store `wealth-ledger-specs`. +- Все OpenSpec commands, читающие или изменяющие change, обязаны указывать `--store wealth-ledger-specs`; nearest-root и global-default selection не поддерживаются. +- Не создавать и не коммитить framework-specific planning/spec/task/design artifacts вне `openspec/` (то есть `specs/openspec` от workspace), включая `superpowers/plans`; `../references/**` остаётся read-only исключением. diff --git a/openspec/changes/archive/2026-08-17-redesign-position-cards/tasks.md b/openspec/changes/archive/2026-08-17-redesign-position-cards/tasks.md index 70f097e..834a558 100644 --- a/openspec/changes/archive/2026-08-17-redesign-position-cards/tasks.md +++ b/openspec/changes/archive/2026-08-17-redesign-position-cards/tasks.md @@ -33,5 +33,16 @@ ## 6. Верификация и архив - [x] 6.1 Прогнать `opsx-verify` по change -- [ ] 6.2 Архив change через `openspec archive` и синхронизация delta в main spec -- [ ] 6.3 Закоммитить по репозиториям: specs, docs, backend, frontend +- [x] 6.2 Архив change через `openspec archive` и синхронизация delta в main spec +- [x] 6.3 Закоммитить по репозиториям: specs, docs, backend, frontend + +## Сверка evidence — 2026-08-23 + +Задачи 6.2–6.3 были незакрытыми administrative tasks после фактического archive. Их статус согласован по текущему read-only evidence; эта запись не меняет archive date, implementation files, delta specs или прошлые Git commits. + +- `git -C specs show --stat --oneline --summary 6795ee6` — commit `6795ee6` от 2026-08-17 создаёт `openspec/changes/archive/2026-08-17-redesign-position-cards/` и одновременно изменяет `openspec/specs/portfolio-view/spec.md`; это подтверждает archive и sync delta. +- `git -C docs show --stat --oneline --summary c628535` — commit `c628535` от 2026-08-17 обновляет `features/portfolio-view.md`. +- `git -C backend show --stat --oneline --summary 5aa9bd3` — commit `5aa9bd3` от 2026-08-17 содержит ISIN DTO/service/client/spec changes. +- `git -C frontend show --stat --oneline --summary 5168870` — commit `5168870` от 2026-08-17 содержит Position Card, OpenAPI, i18n и tests changes. + +В local history отсутствует отдельный terminal transcript вызова `openspec archive`; evidence выше подтверждает его наблюдаемый результат и связанные commits, не приписывая прошлому workflow незафиксированные команды. diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/.openspec.yaml b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/.openspec.yaml new file mode 100644 index 0000000..44f55ff --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-23 diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/design.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/design.md new file mode 100644 index 0000000..5fc872c --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/design.md @@ -0,0 +1,81 @@ +## Контекст + +Мотивация приведена в `proposal.md`, а поведенческий контракт — в delta specs. `specs/openspec` уже является единственным project-owned физическим OpenSpec root, но unscoped OpenSpec CLI resolution сейчас зависит от изменяемого machine default. Workspace и четыре независимых репозитория имеют agent instructions, тогда как восемь `.opencode` commands и восемь соответствующих skills сохраняют сгенерированный selection guidance, допускающий nearest-root fallback. Текущие archive instructions также позволяют owner-confirmed archive с incomplete artifacts или tasks. + +`docs/development/audit-remediation.md` является durable evidence, а не вторым SDD store. `references/**` содержит импортированные framework plans и должен остаться нетронутым. Archive `2026-08-17-redesign-position-cards` имеет два unchecked lifecycle tasks, поэтому он является historical evidence, противоречащим целевой policy. + +## Цели и нецели + +**Цели:** + +- Сделать каждую supported OpenSpec operation детерминированной из любого project-owned entrypoint. +- Сохранить единственный versioned SDD authority, оставив `docs` durable documentation и evidence. +- Превратить authority и archive rules в read-only, воспроизводимый verification result. +- Убрать incomplete-task исключение из archive и честно согласовать известное historical exception. + +**Не входит в scope:** + +- Не изменять OpenSpec CLI, глобальную OpenSpec configuration или несвязанные registered stores. +- Не удалять, не перемещать и не нормализовать artifacts под `references/**`. +- Не вводить Superpowers plans, другой planning framework, CI, runtime application changes или external tracker integration. +- Не переписывать прошлые Git commits и не утверждать, что historical lifecycle steps произошли в другую дату. + +## Решения + +### Canonical store contract заменяет contextual selection + +`wealth-ledger-specs` — фиксированный идентификатор store, а `specs/openspec` — его единственный project-owned artifact root. Root, frontend, backend, specs и docs `AGENTS.md` используют одну краткую policy: requirements, design, delta specs, tasks, changes и archives принадлежат только этому store; все OpenSpec commands, читающие или изменяющие change, используют `--store wealth-ledger-specs`. + +Восемь `.opencode/commands/opsx-*.md` и восемь `.opencode/skills/openspec-*/SKILL.md` заменят сгенерированные conditional store-selection blocks на блок для WealthLedger. Каждый конкретный OpenSpec invocation в этих project-owned workflows включает фиксированный flag; archive workflow использует тот же fixed selection. `references/**` исключается, поскольку не является project-owned workflow surface. + +Сохранение существующих entrypoints не ломает discovery агентов и имена workflows. Опора на `openspec` root pointer или global default отклонена, поскольку они зависят от текущей директории или состояния машины. Удаление entrypoints отклонено, поскольку это ослабит, а не усилит документированный локальный workflow. + +### Границы artifacts определяются их lifecycle role + +Policy различает SDD state и durable documentation: + +- `specs/openspec` владеет mutable и archived versioned SDD state: specs, changes, proposals, designs, delta specs и tasks. +- `docs` владеет durable architecture, development, feature, audit и remediation documentation, включая completion evidence со ссылкой на archive. +- `frontend` и `backend` владеют runtime code и tests; change planning artifacts в них не создаются. +- `references/**` остаётся read-only и исключён из cleanup и policy scanning. + +Instructions запретят коммит framework-specific planning/spec/task/design artifacts вне canonical store, явно включая `superpowers/plans`. Запрет относится к параллельным sources of truth, а не к использованию инструмента или временным заметкам в разговоре. Полный запрет любого Markdown вне `specs` отклонён, поскольку он ошибочно запретит durable docs и существующий reference content. + +### Repository-local verifier проверяет стабильные policy signals + +Change добавит POSIX shell verifier в репозитории `specs` и документированную command, работающую из любого workspace cwd. Script определяет workspace по собственному расположению, не делает writes и сообщает о каждом нарушенном инварианте перед возвратом ненулевого результата. + +Он проверит metadata зарегистрированного store и canonical path `specs/openspec`; отсутствие любого дополнительного project-owned `openspec/` directory; явное canonical-store wording и flag в root и четырёх repository `AGENTS.md`; а также compliance фиксированного набора из шестнадцати `.opencode` entrypoint documents. Он отклонит сгенерированные формулировки nearest-root/global-default и известные framework planning locations вне `specs/openspec`, исключая `references/**`, `.git`, dependencies и generated outputs из discovery. + +Verifier намеренно является structural guardrail, а не general natural-language classifier для каждого возможного будущего framework. Policy text запрещает все parallel framework artifacts; verifier защищает known paths и entrypoints, которые могут воспроизвести audited failure. Широкое сканирование всего Markdown или всех directories с именем `plan` отклонено, поскольку оно создаст false positives в durable docs и reference material. + +### Archive становится hard gate, а evidence фиксируется post-archive, не в task + +Project-owned archive command и skill остановятся до sync или move change, если любой required planning artifact незавершён, если отсутствует `tasks.md` или если он содержит `- [ ]`. Owner-confirmation override отсутствует. До archive workflow также требует normal verification evidence; после archive он указывает implementer обновить durable registry/history evidence и выполнить commits затронутых repositories. + +Post-archive work не представляется checkbox в change, потому что archive path нельзя фактически подтвердить до успешного move. Registry/history entry фиксирует actual archive path, commit IDs, commands и observed results после этого события. Это жертвует atomic cross-repository bookkeeping, но не позволяет archived change заявлять о completed tasks при известной incomplete work. Archive workflow, автоматически отмечающий произвольную работу, отклонён, поскольку он производил бы вымышленное evidence. + +### Historical reconciliation фиксирует current evidence без изменения history + +Historical archive `2026-08-17-redesign-position-cards` будет согласован на месте только после того, как read-only evidence установит actual archive path и commits затронутых repositories. Его две lifecycle tasks будут отмечены complete рядом с dated reconciliation note, отличающей original archive от текущей evidence review. Не изменяются implementation files, delta specs, archive date или past commits. + +Оставить его без изменений отклонено, поскольку он останется контрпримером policy. Воссоздать change или изменить Git history отклонено, поскольку это скроет фактическую временную линию. + +## Риски и компромиссы + +- [Будущая OpenSpec command не содержит fixed store flag] → verifier использует фиксированный manifest всех шестнадцати текущих entrypoints и должен расширяться в том же change, который добавляет entrypoint. +- [Новый framework artifact не распознан path-based detection] → instructions задают широкое ограничение, а review и known-location checks verifier дают многоуровневую защиту без false-positive scanning durable docs. +- [Post-archive registry update прервано] → finding остаётся non-terminal, пока его durable evidence не заполнено; в archive не скрывается incomplete task. +- [Historical commits невозможно доказать из local history] → tasks остаются unresolved, а недостающее evidence фиксируется вместо ложного утверждения о complete. +- [Пользователь вручную запускает raw unscoped OpenSpec CLI] → supported commands и instructions остаются scoped; policy не изменяет внешнее поведение CLI. + +## План миграции + +1. Добавить формулировки canonical authority и artifact boundaries во все project-owned `AGENTS.md`, затем hard-bind шестнадцать `.opencode` entrypoints. +2. Добавить read-only verifier и запустить его из workspace и каждого independent repository context. +3. Заменить archive confirmation branches для incomplete artifacts/tasks на hard failures, потребовать `tasks.md` и задокументировать post-archive evidence sequence. +4. Добавить durable documentation, объясняющую authority/lifecycle contract, и обновить remediation registry: WL-P1-03 и WL-P1-04 получают статус `active` на время change. +5. Согласовать historical archive только по observed Git/OpenSpec evidence; если evidence неполно, не отмечать его tasks complete. +6. Проверить completed change, sync delta specs, архивировать его только при отмеченных tasks, затем добавить actual archive/commit/check evidence в registry и history. + +Откат ограничен revert commits, добавляющими policy и instructions. Runtime data, API contract, package dependency или external configuration не мигрируются. diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/proposal.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/proposal.md new file mode 100644 index 0000000..e6a7e34 --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/proposal.md @@ -0,0 +1,29 @@ +## Why + +Технический аудит подтвердил, что выбор OpenSpec store зависит от текущей директории и изменяемого глобального значения по умолчанию, а archive workflow допускает незакрытые tasks. Это создаёт два источника правды о SDD-работе и позволяет считать change завершённым без полного доказательства выполнения. + +## What Changes + +- Объявить `wealth-ledger-specs` с физическим root `specs/openspec` единственным поддерживаемым местом для versioned SDD-артефактов WealthLedger. +- Обязать project-owned OpenSpec entrypoints и agent instructions явно использовать `--store wealth-ledger-specs`; удалить поддержку выбора ближайшего root или fallback через глобальное значение по умолчанию. +- Запретить создание и коммит framework-specific planning/spec/task/design артефактов вне `specs/openspec`, включая `superpowers/plans`; сохранить `references/**` как read-only исключение. +- Разделить SDD-состояние и постоянную документацию: `docs` хранит только долговечную документацию и evidence, но не change-specific requirements, design или tasks. +- Сделать archive невозможным при любом незакрытом task и вынести post-archive bookkeeping из `tasks.md` в детерминированный evidence workflow. +- Добавить read-only verifier authority/lifecycle policy и исправить historical incomplete archive `2026-08-17-redesign-position-cards` проверяемыми фактами без переписывания Git-истории. + +## Capabilities + +### New Capabilities + +- `openspec-authority-and-lifecycle`: canonical store, допустимые locations SDD-артефактов, обязательный scoped entrypoint, archive gate и machine-verifiable policy. + +### Modified Capabilities + +- `project-foundation`: локальные agent instructions должны однозначно направлять SDD-работу в canonical OpenSpec store и исключать параллельные framework artifacts. +- `audit-remediation-workflow`: terminal evidence audit-derived changes должно опираться на archive только после завершения всех tasks и определённого post-archive evidence workflow. +- `project-documentation`: durable docs отделяются от change-specific SDD-artifacts и фиксируют completion evidence без создания второго SDD source of truth. +- `brokerage-account-ledger`: русские нормативные формулировки используют RFC 2119 маркеры, совместимые со strict OpenSpec validation. + +## Impact + +Затрагиваются `AGENTS.md` в workspace и независимых репозиториях, `.opencode` OpenSpec commands/skills, `specs/openspec` configuration и verification tooling, historical archive и durable development documentation. Runtime-код frontend/backend, API, зависимости и пользовательское поведение не меняются. diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/audit-remediation-workflow/spec.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/audit-remediation-workflow/spec.md new file mode 100644 index 0000000..3b252b4 --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/audit-remediation-workflow/spec.md @@ -0,0 +1,12 @@ +## MODIFIED Requirements + +### Requirement: Terminal status подтверждён проверяемым evidence +Registry SHALL NOT представлять finding как `done` без даты, archive path связанного OpenSpec change, commit identifiers для каждого затронутого Git-репозитория и точных non-mutating verification commands с результатами. Архивированный audit-derived change MUST иметь все tasks завершёнными до archive; completion evidence после archive MUST быть отдельной записью registry или history, а не незакрытым task в archived change. Статусы `deferred` и `accepted` MUST содержать дату, rationale и условие пересмотра либо ссылку на явное решение владельца. + +#### Scenario: Change готовится к завершению +- **WHEN** implementer хочет поставить связанный finding в `done` +- **THEN** все tasks change и требуемая OpenSpec verification завершены до archive, а registry содержит полный набор post-archive evidence + +#### Scenario: Риск сознательно не исправляется сейчас +- **WHEN** владелец переносит finding или принимает риск +- **THEN** registry сохраняет причину решения и условия, при которых оно должно быть пересмотрено diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/brokerage-account-ledger/spec.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/brokerage-account-ledger/spec.md new file mode 100644 index 0000000..edd5943 --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/brokerage-account-ledger/spec.md @@ -0,0 +1,56 @@ +## MODIFIED Requirements + +### Requirement: User can retrieve an account ledger +Система MUST предоставлять аутентифицированный журнал только для чтения одного Brokerage Account, принадлежащего текущему User. Журнал MUST содержать только исполненные операции T-Bank и при отсутствии периода по умолчанию охватывать интервал от начала текущего календарного года до момента запроса. + +#### Scenario: Получение журнала с периодом по умолчанию +- **WHEN** аутентифицированный User запрашивает журнал принадлежащего ему Brokerage Account без периода +- **THEN** система возвращает его исполненные операции от начала текущего календарного года до момента запроса + +#### Scenario: Отклонение недоступного счёта +- **WHEN** аутентифицированный User запрашивает журнал счёта, который не является открытым поддерживаемым счётом, доступным через его T-Bank connection +- **THEN** система возвращает существующий ответ о недоступности счёта и не раскрывает операции + +### Requirement: User can browse a filtered ledger +Система MUST позволять User ограничивать журнал включительным периодом дат и любым подмножеством категорий trade, income, tax, fee, transfer, other. Система MUST разбивать упорядоченный результат на страницы с непрозрачным cursor и возвращать признак доступности следующей страницы. + +#### Scenario: Фильтрация доходных операций за период +- **WHEN** User запрашивает период дат и категорию income +- **THEN** результат содержит только исполненные доходные операции с датой в пределах этого периода + +#### Scenario: Продолжение истории +- **WHEN** User запрашивает cursor следующей страницы, возвращённый предыдущим ответом журнала +- **THEN** система возвращает следующую страницу без повторения записей предыдущей страницы + +### Requirement: Ledger entries preserve broker operation meaning +Система MUST представлять одну upstream-операцию одной записью журнала и MUST передавать её timestamp, тип операции, категорию, описание, доступные идентификаторы инструмента, денежную выплату, количество, цену, НКД и комиссию. Денежные поля MUST сохранять исходную валюту и точное десятичное значение. + +#### Scenario: Отображение купонной выплаты +- **WHEN** T-Bank возвращает исполненную купонную операцию с выплатой и данными инструмента +- **THEN** запись журнала определяет её как income и передаёт исходную сумму выплаты, валюту и данные инструмента + +#### Scenario: Отображение комиссии сделки без синтетической записи +- **WHEN** T-Bank возвращает исполненную сделку, содержащую выплату и комиссию +- **THEN** журнал содержит одну запись сделки с комиссией в качестве её детали + +#### Scenario: Отображение движения бумаг без денег +- **WHEN** T-Bank возвращает исполненную операцию, изменяющую состав инструментов, но без денежной выплаты +- **THEN** запись журнала передаёт доступное количество и не содержит искусственной денежной выплаты с нулевым значением + +### Requirement: Account overview includes recent ledger events +Обзор счёта MUST показывать пять последних исполненных записей журнала этого счёта и действие перехода к его полному журналу. Неуспешный или пустой запрос последних записей MUST оставаться локальным состоянием блока последних событий и MUST NOT заменять успешно отображённый обзор Portfolio. + +#### Scenario: Отображение последних событий +- **WHEN** загружается обзор счёта и его журнал содержит исполненные операции +- **THEN** обзор показывает не более пяти последних записей и ссылку на полный журнал + +#### Scenario: Последние записи недоступны +- **WHEN** обзор счёта успешно загружается, но запрос последних записей завершается ошибкой +- **THEN** обзор Portfolio остаётся доступным, а блок последних событий отображает собственное состояние восстановления + +### Requirement: Full ledger presents compact rows +Страница полного журнала MUST показывать для каждой записи дату, тип операции, инструмент и денежное изменение в исходной валюте. Она MUST обозначать записи без движения денег и MUST NOT показывать цену или количество. + +#### Scenario: Отображение записи только с инструментом +- **WHEN** полный журнал отображает запись без денежной выплаты +- **THEN** страница определяет её как запись без движения денег diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/openspec-authority-and-lifecycle/spec.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/openspec-authority-and-lifecycle/spec.md new file mode 100644 index 0000000..6056ded --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/openspec-authority-and-lifecycle/spec.md @@ -0,0 +1,49 @@ +## Purpose + +Определяет единственный источник полномочий для OpenSpec и проверяемый жизненный цикл SDD-изменений независимо от текущей директории, глобальной конфигурации или agent framework. + +## ADDED Requirements + +### Requirement: Канонический authority OpenSpec задан явно +WealthLedger SHALL использовать зарегистрированный store `wealth-ledger-specs` с физическим OpenSpec root `specs/openspec` как единственный поддерживаемый authority для versioned SDD artifacts. Каждый project-owned OpenSpec entrypoint и documented OpenSpec command MUST явно выбирать `--store wealth-ledger-specs`; он MUST NOT выбирать store из ближайшего root, текущей директории или глобального значения по умолчанию машины. + +#### Scenario: Агент начинает SDD-работу из репозитория приложения +- **WHEN** агент начинает, продолжает, проверяет, синхронизирует или архивирует OpenSpec change из `frontend`, `backend`, `docs` или workspace root +- **THEN** documented workflow явно выбирает `wealth-ledger-specs` и разрешает artifacts под `specs/openspec` + +#### Scenario: В окружении есть другой OpenSpec store или значение по умолчанию +- **WHEN** машина имеет другой зарегистрированный OpenSpec store или другое глобальное значение по умолчанию +- **THEN** project-owned entrypoints всё равно используют `wealth-ledger-specs` и не выполняют fallback к другому root + +### Requirement: Versioned planning artifacts имеют единственное допустимое расположение +Система SHALL хранить committed OpenSpec specifications, changes, designs, delta specifications, tasks и archives только под `specs/openspec`. Она MUST NOT создавать или коммитить framework-specific planning, specification, task или design artifacts вне этого расположения, включая `superpowers/plans`. Это ограничение MUST NOT изменять или удалять read-only material под `references/**`. + +#### Scenario: Агент фиксирует функциональное изменение +- **WHEN** агенту нужно зафиксировать requirements, design decisions или implementation tasks для функционального change +- **THEN** он создаёт versioned SDD artifacts в canonical OpenSpec store и не создаёт parallel framework artifact вне него + +#### Scenario: Агент изучает reference material +- **WHEN** агент встречает framework artifacts под `references/**` +- **THEN** он рассматривает их как read-only reference content и не классифицирует их как нарушение policy + +### Requirement: Authority policy имеет read-only verifier +Canonical store и policy расположения artifacts SHALL иметь documented read-only verifier. Verifier MUST возвращать ненулевой результат, когда project-owned OpenSpec entrypoint не выбирает canonical store, когда запрещённый project-owned planning artifact существует вне `specs/openspec`, или когда существует дополнительный project-owned `openspec/` root; он MUST исключать `references/**` из проверок расположения artifacts. + +#### Scenario: Canonical authority остаётся целостным +- **WHEN** verifier запускается в compliant workspace +- **THEN** он завершается без изменения tracked files и сообщает об успехе + +#### Scenario: Entry point вновь допускает неявный выбор store +- **WHEN** project-owned OpenSpec entrypoint допускает выбор nearest-root или global-default +- **THEN** verifier завершается ошибкой и указывает non-compliant entrypoint + +### Requirement: Archive блокируется незавершёнными tasks +Project-owned archive workflow MUST отклонять archive, пока его `tasks.md` содержит любой unchecked task. Post-archive bookkeeping MUST NOT появляться как unchecked task; он SHALL фиксироваться через детерминированное completion evidence после успешного archive. + +#### Scenario: Change имеет unchecked task +- **WHEN** implementer запрашивает archive и остаётся хотя бы один unchecked task +- **THEN** workflow не архивирует change и сообщает incomplete tasks как blocking condition + +#### Scenario: Change завершил все tasks +- **WHEN** каждый task отмечен и required verification успешно завершена +- **THEN** workflow допускает archive и фиксирует его результат через определённый completion-evidence path без создания post-archive task diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-documentation/spec.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-documentation/spec.md new file mode 100644 index 0000000..ef1fd44 --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-documentation/spec.md @@ -0,0 +1,12 @@ +## ADDED Requirements + +### Requirement: Durable documentation is not a parallel SDD store +Репозиторий `docs` SHALL хранить только долговечную architecture, development, feature, audit и remediation documentation вместе с completion evidence. Change-specific requirements, designs, delta specifications и tasks MUST храниться только в canonical OpenSpec store и MUST NOT создаваться как framework-specific planning artifacts в `docs`. + +#### Scenario: Change требует постоянной документации +- **WHEN** завершённый change должен оставить устойчивое объяснение решения или evidence +- **THEN** documentation фиксирует реализованное решение или completion evidence и ссылается на canonical OpenSpec change без дублирования его requirements, design или tasks + +#### Scenario: Agent планирует незавершённый change +- **WHEN** агент создаёт requirements, design или tasks для незавершённого изменения +- **THEN** он сохраняет их под `specs/openspec` и не создаёт framework-specific planning document в `docs` diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-foundation/spec.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-foundation/spec.md new file mode 100644 index 0000000..4fb7b6b --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/specs/project-foundation/spec.md @@ -0,0 +1,12 @@ +## MODIFIED Requirements + +### Requirement: Репозитории содержат локальные инструкции для AI-агентов +Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Каждая инструкция MUST объявлять `wealth-ledger-specs` и `specs/openspec` как canonical OpenSpec authority, требовать явный `--store wealth-ledger-specs` для поддерживаемых OpenSpec операций и запрещать versioned framework-specific planning artifacts вне canonical store. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита. + +#### Scenario: Агент начинает работу в отдельном репозитории +- **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs +- **THEN** он может определить локальные правила работы, canonical OpenSpec authority и источник связанных спецификаций из `AGENTS.md` этого репозитория + +#### Scenario: Агент получает audit-derived задачу из отдельного репозитория +- **WHEN** задача ссылается на finding технического аудита +- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow с явным store selection до начала изменения кода diff --git a/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/tasks.md b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/tasks.md new file mode 100644 index 0000000..3f53e3a --- /dev/null +++ b/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/tasks.md @@ -0,0 +1,42 @@ +## 1. Canonical OpenSpec authority + +- [x] 1.1 Обновить корневой `AGENTS.md` и `frontend/AGENTS.md`, `backend/AGENTS.md`, `specs/AGENTS.md`, `docs/AGENTS.md`: объявить `wealth-ledger-specs`/`specs/openspec` единственным SDD store, потребовать `--store wealth-ledger-specs`, запретить versioned framework-specific planning artifacts вне store и сохранить исключение `references/**`. +- [x] 1.2 В каждом из восьми `.opencode/commands/opsx-*.md` и восьми `.opencode/skills/openspec-*/SKILL.md` заменить conditional store-selection guidance на fixed `wealth-ledger-specs`; все OpenSpec commands, читающие или изменяющие change, должны содержать `--store wealth-ledger-specs` и не должны допускать nearest-root/global-default fallback. +- [x] 1.3 Изменить `.opencode/commands/opsx-archive.md` и `.opencode/skills/openspec-archive-change/SKILL.md`: блокировать archive без подтверждающего обхода, если отсутствует `tasks.md`, любой planning artifact не `done`/`skipped` или в tasks есть `- [ ]`; описать post-archive evidence и commits как действие после archive, не как task change. + +## 2. Read-only authority verifier + +- [x] 2.1 Создать `specs/scripts/verify-openspec-authority.sh`, который по собственному расположению определяет workspace, не делает writes и проверяет `specs/.openspec-store/store.yaml`, единственный project-owned `specs/openspec`, пять `AGENTS.md`, все шестнадцать `.opencode` entrypoints, отсутствие generated fallback wording и известных framework planning locations вне store; исключить `references/**`, `.git`, dependencies и generated outputs. +- [x] 2.2 Добавить в verifier понятные сообщения для каждого нарушенного инварианта и ненулевой exit status; success path должен сообщать проверенное canonical authority без изменения tracked files. +- [x] 2.3 Добавить в `specs/AGENTS.md` documented command verifier и выполнить read-only проверки success path, дополнительного `openspec/` root, неявного entrypoint и запрещённого planning location на временных fixtures, не изменяя project-owned tracked files. + +## 3. Durable documentation и remediation state + +- [x] 3.1 Создать в `docs/development` краткую durable documentation authority/lifecycle contract: границы `specs/openspec`, `docs`, application repos и `references/**`; объяснить, что requirements, design и tasks не дублируются вне canonical store. +- [x] 3.2 Добавить документ в `docs/development/README.md` и убедиться, что он ссылается на canonical OpenSpec store, а не создаёт второй change-specific SDD artifact. +- [x] 3.3 Обновить `docs/development/audit-remediation.md`: сделать `harden-openspec-authority-and-lifecycle` active change для WL-P1-03/WL-P1-04 и убрать его из формулировки «ещё не создан», не помечая findings как `done` до фактического archive evidence. + +## 4. Historical archive reconciliation + +- [x] 4.1 Собрать read-only evidence из OpenSpec archive и Git history specs, docs, backend и frontend для `2026-08-17-redesign-position-cards`; установить фактические archive path и commits либо документировать недостающий факт. +- [x] 4.2 Только если evidence подтверждено, отметить tasks 6.2 и 6.3 archived change как complete и добавить dated reconciliation note с exact commands, observed results и различием между исходным archive и текущей проверкой; не менять implementation files, delta specs, archive date или историю Git. + +## 5. Pre-archive verification + +- [x] 5.1 Запустить authority verifier из workspace root и из `frontend`, `backend`, `specs` и `docs`; зафиксировать в change точные команды и observed results для дальнейшего completion evidence. +- [x] 5.2 Нормализовать RFC 2119 маркеры в `brokerage-account-ledger` через delta-spec без изменения требований или сценариев; выполнить `openspec validate harden-openspec-authority-and-lifecycle --type change --strict --store wealth-ledger-specs`, `openspec validate --specs --strict --store wealth-ledger-specs` и `git diff --check` в каждом затронутом Git repository; устранить все blocking results. +- [x] 5.3 Выполнить независимый read-only review delta specs, policy boundaries, archive gate, verifier exclusions и historical reconciliation; подтвердить, что tasks не содержат post-archive bookkeeping или commit/registry-completion checkbox. + +## Pre-archive verification evidence — 2026-08-23 + +- `bash specs/scripts/verify-openspec-authority.sh` из workspace root — `OpenSpec authority verified: wealth-ledger-specs at specs/openspec`. +- `cd frontend && bash ../specs/scripts/verify-openspec-authority.sh` — success с тем же результатом. +- `cd backend && bash ../specs/scripts/verify-openspec-authority.sh` — success с тем же результатом. +- `cd specs && bash scripts/verify-openspec-authority.sh` — success с тем же результатом. +- `cd docs && bash ../specs/scripts/verify-openspec-authority.sh` — success с тем же результатом. +- `bash specs/scripts/verify-openspec-authority.test.sh` — success; fixture подтверждает success path, дополнительный project-owned OpenSpec root, неявный store selection (включая вызов в скобках), conditional `--store ""`, archive override wording, запрещённый `docs/superpowers/plans` и исключение `references/**`. +- `openspec validate harden-openspec-authority-and-lifecycle --type change --strict --store wealth-ledger-specs` — `Change 'harden-openspec-authority-and-lifecycle' is valid`. +- Первый запуск `openspec validate --specs --strict --store wealth-ledger-specs` выявил baseline `brokerage-account-ledger`: strict validator требует RFC 2119 `SHALL` или `MUST`, тогда как main spec использовала русское «ДОЛЖНА». По явному решению владельца change получил delta-spec, нормализующий только маркеры без изменения requirements или scenarios. +- Повторный `openspec validate --specs --strict --store wealth-ledger-specs` — 14 passed, 0 failed. +- `git diff --check` в frontend, backend, specs и docs — exit 0. +- Независимый read-only review 2026-08-23 — Critical/Important findings отсутствуют; проверены все 16 `.opencode` entrypoints, hard archive gate, exclusions verifier и historical reconciliation. diff --git a/openspec/specs/audit-remediation-workflow/spec.md b/openspec/specs/audit-remediation-workflow/spec.md index 32c6ea5..88169dd 100644 --- a/openspec/specs/audit-remediation-workflow/spec.md +++ b/openspec/specs/audit-remediation-workflow/spec.md @@ -29,11 +29,11 @@ - **THEN** агент продолжает связанный change и не создаёт второй remediation change без явного решения владельца ### Requirement: Terminal status подтверждён проверяемым evidence -Registry SHALL NOT представлять finding как `done` без даты, archive path связанного OpenSpec change, commit identifiers для каждого затронутого Git-репозитория и точных non-mutating verification commands с результатами. Статусы `deferred` и `accepted` MUST содержать дату, rationale и условие пересмотра либо ссылку на явное решение владельца. +Registry SHALL NOT представлять finding как `done` без даты, archive path связанного OpenSpec change, commit identifiers для каждого затронутого Git-репозитория и точных non-mutating verification commands с результатами. Архивированный audit-derived change MUST иметь все tasks завершёнными до archive; completion evidence после archive MUST быть отдельной записью registry или history, а не незакрытым task в archived change. Статусы `deferred` и `accepted` MUST содержать дату, rationale и условие пересмотра либо ссылку на явное решение владельца. #### Scenario: Change готовится к завершению - **WHEN** implementer хочет поставить связанный finding в `done` -- **THEN** registry содержит полный набор evidence, а tasks change и требуемая OpenSpec verification завершены до обновления статуса +- **THEN** все tasks change и требуемая OpenSpec verification завершены до archive, а registry содержит полный набор post-archive evidence #### Scenario: Риск сознательно не исправляется сейчас - **WHEN** владелец переносит finding или принимает риск diff --git a/openspec/specs/brokerage-account-ledger/spec.md b/openspec/specs/brokerage-account-ledger/spec.md index d60b11a..e8316a6 100644 --- a/openspec/specs/brokerage-account-ledger/spec.md +++ b/openspec/specs/brokerage-account-ledger/spec.md @@ -7,7 +7,7 @@ ## Requirements ### Requirement: User can retrieve an account ledger -Система ДОЛЖНА предоставлять аутентифицированный журнал только для чтения одного Brokerage Account, принадлежащего текущему User. Журнал ДОЛЖЕН содержать только исполненные операции T-Bank и при отсутствии периода по умолчанию охватывать интервал от начала текущего календарного года до момента запроса. +Система MUST предоставлять аутентифицированный журнал только для чтения одного Brokerage Account, принадлежащего текущему User. Журнал MUST содержать только исполненные операции T-Bank и при отсутствии периода по умолчанию охватывать интервал от начала текущего календарного года до момента запроса. #### Scenario: Получение журнала с периодом по умолчанию - **WHEN** аутентифицированный User запрашивает журнал принадлежащего ему Brokerage Account без периода @@ -18,7 +18,7 @@ - **THEN** система возвращает существующий ответ о недоступности счёта и не раскрывает операции ### Requirement: User can browse a filtered ledger -Система ДОЛЖНА позволять User ограничивать журнал включительным периодом дат и любым подмножеством категорий trade, income, tax, fee, transfer, other. Система ДОЛЖНА разбивать упорядоченный результат на страницы с непрозрачным cursor и возвращать признак доступности следующей страницы. +Система MUST позволять User ограничивать журнал включительным периодом дат и любым подмножеством категорий trade, income, tax, fee, transfer, other. Система MUST разбивать упорядоченный результат на страницы с непрозрачным cursor и возвращать признак доступности следующей страницы. #### Scenario: Фильтрация доходных операций за период - **WHEN** User запрашивает период дат и категорию income @@ -29,7 +29,7 @@ - **THEN** система возвращает следующую страницу без повторения записей предыдущей страницы ### Requirement: Ledger entries preserve broker operation meaning -Система ДОЛЖНА представлять одну upstream-операцию одной записью журнала и ДОЛЖНА передавать её timestamp, тип операции, категорию, описание, доступные идентификаторы инструмента, денежную выплату, количество, цену, НКД и комиссию. Денежные поля ДОЛЖНЫ сохранять исходную валюту и точное десятичное значение. +Система MUST представлять одну upstream-операцию одной записью журнала и MUST передавать её timestamp, тип операции, категорию, описание, доступные идентификаторы инструмента, денежную выплату, количество, цену, НКД и комиссию. Денежные поля MUST сохранять исходную валюту и точное десятичное значение. #### Scenario: Отображение купонной выплаты - **WHEN** T-Bank возвращает исполненную купонную операцию с выплатой и данными инструмента @@ -44,7 +44,7 @@ - **THEN** запись журнала передаёт доступное количество и не содержит искусственной денежной выплаты с нулевым значением ### Requirement: Account overview includes recent ledger events -Обзор счёта ДОЛЖЕН показывать пять последних исполненных записей журнала этого счёта и действие перехода к его полному журналу. Неуспешный или пустой запрос последних записей ДОЛЖЕН оставаться локальным состоянием блока последних событий и НЕ ДОЛЖЕН заменять успешно отображённый обзор Portfolio. +Обзор счёта MUST показывать пять последних исполненных записей журнала этого счёта и действие перехода к его полному журналу. Неуспешный или пустой запрос последних записей MUST оставаться локальным состоянием блока последних событий и MUST NOT заменять успешно отображённый обзор Portfolio. #### Scenario: Отображение последних событий - **WHEN** загружается обзор счёта и его журнал содержит исполненные операции @@ -55,7 +55,7 @@ - **THEN** обзор Portfolio остаётся доступным, а блок последних событий отображает собственное состояние восстановления ### Requirement: Full ledger presents compact rows -Страница полного журнала ДОЛЖНА показывать для каждой записи дату, тип операции, инструмент и денежное изменение в исходной валюте. Она ДОЛЖНА обозначать записи без движения денег и НЕ ДОЛЖНА показывать цену или количество. +Страница полного журнала MUST показывать для каждой записи дату, тип операции, инструмент и денежное изменение в исходной валюте. Она MUST обозначать записи без движения денег и MUST NOT показывать цену или количество. #### Scenario: Отображение записи только с инструментом - **WHEN** полный журнал отображает запись без денежной выплаты diff --git a/openspec/specs/openspec-authority-and-lifecycle/spec.md b/openspec/specs/openspec-authority-and-lifecycle/spec.md new file mode 100644 index 0000000..3fac624 --- /dev/null +++ b/openspec/specs/openspec-authority-and-lifecycle/spec.md @@ -0,0 +1,50 @@ +# openspec-authority-and-lifecycle Specification + +## Purpose +Определяет единственный источник полномочий для OpenSpec и проверяемый жизненный цикл SDD-изменений независимо от текущей директории, глобальной конфигурации или agent framework. + +## Requirements + +### Requirement: Канонический authority OpenSpec задан явно +WealthLedger SHALL использовать зарегистрированный store `wealth-ledger-specs` с физическим OpenSpec root `specs/openspec` как единственный поддерживаемый authority для versioned SDD artifacts. Каждый project-owned OpenSpec entrypoint и documented OpenSpec command MUST явно выбирать `--store wealth-ledger-specs`; он MUST NOT выбирать store из ближайшего root, текущей директории или глобального значения по умолчанию машины. + +#### Scenario: Агент начинает SDD-работу из репозитория приложения +- **WHEN** агент начинает, продолжает, проверяет, синхронизирует или архивирует OpenSpec change из `frontend`, `backend`, `docs` или workspace root +- **THEN** documented workflow явно выбирает `wealth-ledger-specs` и разрешает artifacts под `specs/openspec` + +#### Scenario: В окружении есть другой OpenSpec store или значение по умолчанию +- **WHEN** машина имеет другой зарегистрированный OpenSpec store или другое глобальное значение по умолчанию +- **THEN** project-owned entrypoints всё равно используют `wealth-ledger-specs` и не выполняют fallback к другому root + +### Requirement: Versioned planning artifacts имеют единственное допустимое расположение +Система SHALL хранить committed OpenSpec specifications, changes, designs, delta specifications, tasks и archives только под `specs/openspec`. Она MUST NOT создавать или коммитить framework-specific planning, specification, task или design artifacts вне этого расположения, включая `superpowers/plans`. Это ограничение MUST NOT изменять или удалять read-only material под `references/**`. + +#### Scenario: Агент фиксирует функциональное изменение +- **WHEN** агенту нужно зафиксировать requirements, design decisions или implementation tasks для функционального change +- **THEN** он создаёт versioned SDD artifacts в canonical OpenSpec store и не создаёт parallel framework artifact вне него + +#### Scenario: Агент изучает reference material +- **WHEN** агент встречает framework artifacts под `references/**` +- **THEN** он рассматривает их как read-only reference content и не классифицирует их как нарушение policy + +### Requirement: Authority policy имеет read-only verifier +Canonical store и policy расположения artifacts SHALL иметь documented read-only verifier. Verifier MUST возвращать ненулевой результат, когда project-owned OpenSpec entrypoint не выбирает canonical store, когда запрещённый project-owned planning artifact существует вне `specs/openspec`, или когда существует дополнительный project-owned `openspec/` root; он MUST исключать `references/**` из проверок расположения artifacts. + +#### Scenario: Canonical authority остаётся целостным +- **WHEN** verifier запускается в compliant workspace +- **THEN** он завершается без изменения tracked files и сообщает об успехе + +#### Scenario: Entry point вновь допускает неявный выбор store +- **WHEN** project-owned OpenSpec entrypoint допускает выбор nearest-root или global-default +- **THEN** verifier завершается ошибкой и указывает non-compliant entrypoint + +### Requirement: Archive блокируется незавершёнными tasks +Project-owned archive workflow MUST отклонять archive, пока его `tasks.md` содержит любой unchecked task. Post-archive bookkeeping MUST NOT появляться как unchecked task; он SHALL фиксироваться через детерминированное completion evidence после успешного archive. + +#### Scenario: Change имеет unchecked task +- **WHEN** implementer запрашивает archive и остаётся хотя бы один unchecked task +- **THEN** workflow не архивирует change и сообщает incomplete tasks как blocking condition + +#### Scenario: Change завершил все tasks +- **WHEN** каждый task отмечен и required verification успешно завершена +- **THEN** workflow допускает archive и фиксирует его результат через определённый completion-evidence path без создания post-archive task diff --git a/openspec/specs/project-documentation/spec.md b/openspec/specs/project-documentation/spec.md index 858e970..78ecb79 100644 --- a/openspec/specs/project-documentation/spec.md +++ b/openspec/specs/project-documentation/spec.md @@ -37,3 +37,14 @@ #### Scenario: Завершён audit-derived change - **WHEN** связанный OpenSpec change архивирован после успешной проверки - **THEN** registry обновляется с archive path, commits затронутых репозиториев и результатами exact verification commands + +### Requirement: Durable documentation is not a parallel SDD store +Репозиторий `docs` SHALL хранить только долговечную architecture, development, feature, audit и remediation documentation вместе с completion evidence. Change-specific requirements, designs, delta specifications и tasks MUST храниться только в canonical OpenSpec store и MUST NOT создаваться как framework-specific planning artifacts в `docs`. + +#### Scenario: Change требует постоянной документации +- **WHEN** завершённый change должен оставить устойчивое объяснение решения или evidence +- **THEN** documentation фиксирует реализованное решение или completion evidence и ссылается на canonical OpenSpec change без дублирования его requirements, design или tasks + +#### Scenario: Agent планирует незавершённый change +- **WHEN** агент создаёт requirements, design или tasks для незавершённого изменения +- **THEN** он сохраняет их под `specs/openspec` и не создаёт framework-specific planning document в `docs` diff --git a/openspec/specs/project-foundation/spec.md b/openspec/specs/project-foundation/spec.md index 5cdfca4..cc7d7dd 100644 --- a/openspec/specs/project-foundation/spec.md +++ b/openspec/specs/project-foundation/spec.md @@ -7,15 +7,15 @@ ## Requirements ### Requirement: Репозитории содержат локальные инструкции для AI-агентов -Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита. +Система SHALL содержать обзорный `AGENTS.md` в корне рабочей области и отдельный `AGENTS.md` в репозиториях frontend, backend, specs и docs. Инструкции MUST описывать назначение области, локальные команды проверки, границы ответственности и связь с SDD-артефактами. Каждая инструкция MUST объявлять `wealth-ledger-specs` и `specs/openspec` как canonical OpenSpec authority, требовать явный `--store wealth-ledger-specs` для поддерживаемых OpenSpec операций и запрещать versioned framework-specific planning artifacts вне canonical store. Локальные инструкции MUST направлять агента к durable remediation registry, когда задача связана с findings технического аудита. #### Scenario: Агент начинает работу в отдельном репозитории - **WHEN** агент открывает любой из репозиториев frontend, backend, specs или docs -- **THEN** он может определить локальные правила работы и источник связанных спецификаций из `AGENTS.md` этого репозитория +- **THEN** он может определить локальные правила работы, canonical OpenSpec authority и источник связанных спецификаций из `AGENTS.md` этого репозитория #### Scenario: Агент получает audit-derived задачу из отдельного репозитория - **WHEN** задача ссылается на finding технического аудита -- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow до начала изменения кода +- **THEN** локальный `AGENTS.md` указывает на remediation registry и canonical OpenSpec workflow с явным store selection до начала изменения кода ### Requirement: Локальный план MVP не является источником спецификации Система SHALL содержать корневой локальный `MVP.md` с границами MVP, принятыми решениями, порядком поставок и критериями готовности. Требования к функциональности MUST храниться в OpenSpec, а устойчивая документация MUST храниться в репозитории docs. diff --git a/scripts/verify-openspec-authority.sh b/scripts/verify-openspec-authority.sh new file mode 100644 index 0000000..6c91e92 --- /dev/null +++ b/scripts/verify-openspec-authority.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P) +workspace=${WEALTH_LEDGER_WORKSPACE:-$(cd "$script_dir/../.." && pwd -P)} +failures=0 + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + failures=$((failures + 1)) +} + +require_file_contains() { + local file=$1 + local text=$2 + + if [[ ! -f "$file" ]]; then + fail "missing required file: ${file#$workspace/}" + elif ! grep -Fq -- "$text" "$file"; then + fail "${file#$workspace/} must contain: $text" + fi +} + +store_file="$workspace/specs/.openspec-store/store.yaml" +require_file_contains "$store_file" 'id: wealth-ledger-specs' + +canonical_root="$workspace/specs/openspec" +if [[ ! -d "$canonical_root" ]]; then + fail "missing canonical OpenSpec root: specs/openspec" +fi + +openspec_roots=() +while IFS= read -r root; do + openspec_roots+=("$root") +done < <( + find "$workspace" \ + \( -path "$workspace/references" -o -path "$workspace/.git" -o -path '*/node_modules' -o -path '*/dist' -o -path '*/coverage' \) -prune -o \ + -type d -name openspec -print | sort +) + +if [[ ${#openspec_roots[@]} -ne 1 || ${openspec_roots[0]:-} != "$canonical_root" ]]; then + fail "expected exactly one project-owned OpenSpec root at specs/openspec" +fi + +agent_files=(AGENTS.md frontend/AGENTS.md backend/AGENTS.md specs/AGENTS.md docs/AGENTS.md) +for relative_path in "${agent_files[@]}"; do + file="$workspace/$relative_path" + require_file_contains "$file" 'wealth-ledger-specs' + require_file_contains "$file" '--store wealth-ledger-specs' + require_file_contains "$file" 'specs/openspec' +done + +entrypoints=( + .opencode/commands/opsx-apply.md + .opencode/commands/opsx-archive.md + .opencode/commands/opsx-continue.md + .opencode/commands/opsx-explore.md + .opencode/commands/opsx-new.md + .opencode/commands/opsx-propose.md + .opencode/commands/opsx-sync.md + .opencode/commands/opsx-verify.md + .opencode/skills/openspec-apply-change/SKILL.md + .opencode/skills/openspec-archive-change/SKILL.md + .opencode/skills/openspec-continue-change/SKILL.md + .opencode/skills/openspec-explore/SKILL.md + .opencode/skills/openspec-new-change/SKILL.md + .opencode/skills/openspec-propose/SKILL.md + .opencode/skills/openspec-sync-specs/SKILL.md + .opencode/skills/openspec-verify-change/SKILL.md +) + +for relative_path in "${entrypoints[@]}"; do + file="$workspace/$relative_path" + require_file_contains "$file" '--store wealth-ledger-specs' + if [[ -f "$file" ]] && grep -Eqi 'nearest local openspec|global default(store)?|defaultstore|without a store|store: pointer|--store |--store ""|when applicable' "$file"; then + fail "$relative_path permits conditional OpenSpec store selection" + fi + if [[ -f "$file" ]]; then + while IFS= read -r line; do + if [[ $line =~ (^|[^[:alnum:]_])openspec[[:space:]]+(new[[:space:]]+change[[:space:]]+|status[[:space:]]+|instructions[[:space:]]+|list[[:space:]]+|show[[:space:]]+|validate[[:space:]]+|archive[[:space:]]+|doctor[[:space:]]+|context[[:space:]]+|schemas[[:space:]]+|view[[:space:]]+) ]] && [[ $line != *'--store wealth-ledger-specs'* ]]; then + fail "$relative_path contains an unscoped OpenSpec invocation: $line" + fi + done < "$file" + fi +done + +archive_entrypoints=( + .opencode/commands/opsx-archive.md + .opencode/skills/openspec-archive-change/SKILL.md +) +for relative_path in "${archive_entrypoints[@]}"; do + file="$workspace/$relative_path" + require_file_contains "$file" 'Do not ask for or accept a confirmation override.' + if [[ -f "$file" ]] && grep -Eqi 'archive complete \(with warnings\)|don.t block archive on warnings|archived with [0-9]+ incomplete' "$file"; then + fail "$relative_path permits archive with incomplete work" + fi +done + +for relative_path in docs/superpowers/plans superpowers/plans .superpowers; do + if [[ -e "$workspace/$relative_path" ]]; then + fail "prohibited framework planning location: $relative_path" + fi +done + +if [[ $failures -gt 0 ]]; then + exit 1 +fi + +printf 'OpenSpec authority verified: wealth-ledger-specs at specs/openspec\n' diff --git a/scripts/verify-openspec-authority.test.sh b/scripts/verify-openspec-authority.test.sh new file mode 100644 index 0000000..3c53d71 --- /dev/null +++ b/scripts/verify-openspec-authority.test.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P) +verifier="$script_dir/verify-openspec-authority.sh" + +if [[ ! -f "$verifier" ]]; then + echo "expected verifier at $verifier" >&2 + exit 1 +fi + +fixture=$(mktemp -d) +trap 'rm -rf "$fixture"' EXIT + +write_file() { + local relative_path=$1 + local content=$2 + mkdir -p "$fixture/$(dirname "$relative_path")" + printf '%s\n' "$content" > "$fixture/$relative_path" +} + +write_file "specs/.openspec-store/store.yaml" $'version: 1\nid: wealth-ledger-specs' +mkdir -p "$fixture/specs/openspec" + +for path in AGENTS.md frontend/AGENTS.md backend/AGENTS.md specs/AGENTS.md docs/AGENTS.md; do + write_file "$path" $'OpenSpec store: wealth-ledger-specs\nUse --store wealth-ledger-specs\nSDD artifacts only in specs/openspec\nreferences/** is read-only.' +done + +entrypoints=( + .opencode/commands/opsx-apply.md + .opencode/commands/opsx-archive.md + .opencode/commands/opsx-continue.md + .opencode/commands/opsx-explore.md + .opencode/commands/opsx-new.md + .opencode/commands/opsx-propose.md + .opencode/commands/opsx-sync.md + .opencode/commands/opsx-verify.md + .opencode/skills/openspec-apply-change/SKILL.md + .opencode/skills/openspec-archive-change/SKILL.md + .opencode/skills/openspec-continue-change/SKILL.md + .opencode/skills/openspec-explore/SKILL.md + .opencode/skills/openspec-new-change/SKILL.md + .opencode/skills/openspec-propose/SKILL.md + .opencode/skills/openspec-sync-specs/SKILL.md + .opencode/skills/openspec-verify-change/SKILL.md +) + +for path in "${entrypoints[@]}"; do + write_file "$path" 'openspec status --store wealth-ledger-specs' +done + +archive_gate='openspec status --store wealth-ledger-specs +Do not ask for or accept a confirmation override.' +write_file ".opencode/commands/opsx-archive.md" "$archive_gate" +write_file ".opencode/skills/openspec-archive-change/SKILL.md" "$archive_gate" + +run_verifier() { + WEALTH_LEDGER_WORKSPACE="$fixture" bash "$verifier" +} + +run_verifier + +mkdir -p "$fixture/frontend/openspec" +if run_verifier; then + echo "expected additional OpenSpec root to fail" >&2 + exit 1 +fi +rmdir "$fixture/frontend/openspec" + +write_file ".opencode/commands/opsx-apply.md" 'Without a store, commands act on the nearest local openspec root.' +if run_verifier; then + echo "expected implicit store selection to fail" >&2 + exit 1 +fi +write_file ".opencode/commands/opsx-apply.md" 'openspec status --store wealth-ledger-specs' + +write_file ".opencode/commands/opsx-apply.md" $'openspec status --store wealth-ledger-specs\nFor another store, append --store "" when applicable.' +if run_verifier; then + echo "expected conditional store selection to fail" >&2 + exit 1 +fi +write_file ".opencode/commands/opsx-apply.md" 'openspec status --store wealth-ledger-specs' + +write_file ".opencode/commands/opsx-apply.md" $'openspec status --store wealth-ledger-specs\nRun `openspec status --change "example" --json` before continuing.' +if run_verifier; then + echo "expected unscoped OpenSpec invocation to fail" >&2 + exit 1 +fi +write_file ".opencode/commands/opsx-apply.md" 'openspec status --store wealth-ledger-specs' + +write_file ".opencode/commands/opsx-apply.md" $'openspec status --store wealth-ledger-specs\nUse artifact graph (openspec status --change "example" --json) before continuing.' +if run_verifier; then + echo "expected parenthesized unscoped OpenSpec invocation to fail" >&2 + exit 1 +fi +write_file ".opencode/commands/opsx-apply.md" 'openspec status --store wealth-ledger-specs' + +write_file ".opencode/commands/opsx-archive.md" $'openspec status --store wealth-ledger-specs\nDon\x27t block archive on warnings - just inform and confirm.' +if run_verifier; then + echo "expected archive override guidance to fail" >&2 + exit 1 +fi +write_file ".opencode/commands/opsx-archive.md" "$archive_gate" + +mkdir -p "$fixture/docs/superpowers/plans" +if run_verifier; then + echo "expected prohibited planning location to fail" >&2 + exit 1 +fi +rmdir "$fixture/docs/superpowers/plans" +rmdir "$fixture/docs/superpowers" + +mkdir -p "$fixture/references/example/superpowers/plans" +run_verifier + +echo "verify-openspec-authority tests passed"