docs: plan specification retirement via D9 migration map

After the documentation pass, specification.md moves to archive once its content lives in product, architecture, development, and security docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 00:24:13 +03:00
parent 2640ef4fd6
commit 865cf67966
2 changed files with 105 additions and 50 deletions
+6
View File
@@ -5,6 +5,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
## [Unreleased]
### Changed
- docs: documentation plan now targets retiring `specification.md` after D9 —
migration map to `product.md`, `architecture.md`, `development.md`, and
expanded `security.md`; D9 added to the release gate.
## [0.4.0] - 2026-08-04
### Added
+99 -50
View File
@@ -1,11 +1,22 @@
# План документации SelfPost
**Зачем этот файл.** Документация — часть поставки (ТЗ раздел 11, пп. 5, 7, 9),
а не сопроводительный текст. Перед тегом релиза она обязана описывать **то, что
делает код**, а не то, что задумывалось: расхождение здесь — такой же дефект,
как несоответствие ТЗ, только обнаруживает его пользователь на своём проде.
План описывает, из чего состоит пакет, как он сверяется с кодом, что уже
разошлось (первый проход выполнен, результаты ниже) и что с этим делать.
**Зачем этот файл.** Документация — часть поставки (deliverables v1.0, пп. 5, 7, 9
в [specification.md](specification.md)), а не сопроводительный текст. Перед тегом
релиза она обязана описывать **то, что делает код**, а не то, что задумывалось:
расхождение здесь — такой же дефект, как несоответствие требованиям, только
обнаруживает его пользователь на своём проде. План описывает, из чего состоит
пакет, как он сверяется с кодом, что уже разошлось (первый проход выполнен,
результаты ниже) и что с этим делать.
**Цель после выполнения плана:** [specification.md](specification.md) (ТЗ v1.0)
**выводится из обращения** — не потому что требования исчезли, а потому что v1.0
реализован и каждый блок ТЗ получает постоянный дом: пользовательское — в
README, устройство — в `architecture.md`, продуктовые границы — в
`product.md`, обязательная безопасность — в `security.md`, процесс разработки —
в `development.md`. Живой `specification.md` после этого держит два риска:
дублирование с кодом и ложное ощущение «источника истины», который уже не
сверяют. Задача **D9** — миграция остатков и снятие файла; до её закрытия
ссылки на ТЗ в этом файле — исторические.
**Место в общем плане:** документационный проход идёт вместе с **D.5**
([implementation-plan.md](implementation-plan.md)) — до тега следующего релиза,
@@ -32,38 +43,49 @@
### Рабочие документы проекта (не поставка, но обязаны быть верны)
[specification.md](specification.md) — ТЗ v1.0, источник истины по требованиям.
[specification.md](specification.md) — ТЗ v1.0; **на вывод после D9** (см. карту
миграции ниже). До закрытия D9 — ещё используется как историческая ссылка в
находках раздела 3.
[implementation-plan.md](implementation-plan.md) — открытые вопросы для v1.0/v1.x.
[roadmap.md](roadmap.md) — линия 2.x.x.
[progress.md](progress.md) — живой трекер, читается первым после `/clear`.
[security.md](security.md) — принятые риски. Этот файл — план по документации.
[security.md](security.md) — обязательные требования безопасности + принятые
риски. Этот файл — план по документации.
**Новое: `docs/architecture.md` и `docs/development.md` нужны, решение
принято.** ТЗ их не требует (ТЗ 11 не относит их к обязательной поставке), но
без них понимание устройства и повторная сборка проекта держатся только на
памяти и контексте текущей сессии — то есть не переживают её потерю ни для
будущего контрибьютора, ни для будущего прохода без доступа к истории чата.
Обе — рабочие документы проекта (не часть поставки пользователю, не
упоминаются в README как обязательные), задача заведена в разделе 4 (**D8**).
**Новые `product.md`, `architecture.md`, `development.md` — решение принято.**
В обязательную поставку пользователю (README) они не входят, но без них
понимание проекта держится на памяти сессии. Их содержимое **заменяет** живой
`specification.md` для всех будущих проходов — задачи **D8** и **D9**.
- `docs/architecture.md` — компоненты контейнера (`supervisord`,
`postfix`, `opendkim`, `panel`, milter-цепочка) и как они связаны; путь
письма через тракт (SMTP submission → milter'ы → OpenDKIM → доставка);
что читает/пишет `/data` и в каком формате; где проходит граница
привилегий (root vs непривилегированный uid панели) и почему —
источник истины: код (`build/supervisord.conf`, `internal/`), а не
ТЗ, план сверки — тот же метод из раздела 2.
- `docs/development.md`как собрать и прогнать тесты локально (Go
локально, без Docker — см. память проекта), когда нужен полный
контейнерный цикл на dev-сервере (сборка образа, milter/почтовые
изменения) и почему локальной сборки недостаточно, порядок ручной
проверки на `selfpost.example.com` перед мёржем. Заменяет часть, что
раньше держалась только в памяти проекта и `progress.md` — там
остаётся текущее состояние и следующий шаг, а не воспроизводимая
процедура.
- `docs/product.md` — из ТЗ §1–3 и §4.1: зачем SelfPost, инфраструктурные
предпосылки, out of scope, мультидоменная модель «домен ↔ приложения ↔
режим From». Границы продукта, меняющиеся только явным решением.
- `docs/architecture.md` — as-built по коду: `supervisord`, `postfix`,
`opendkim`, panel (HTTP + journal-milter + log-tailer); порядок старта;
milter-цепочка; Postfix/SASL/TLS; журнал отправки и L1/L2 rate-limit;
бэкап/restore; персистентность `/data`. Источник истины — код, не ТЗ.
- `docs/development.md`Go локально, `go test`/`go vet`, `make e2e`, когда
нужен полный контейнер на dev-сервере, ручная проверка на
`selfpost.example.com`, протокол коммитов/CHANGELOG, правила для агента
(бывшее ТЗ §12). В `progress.md` — текущее состояние, не процедура.
### Карта миграции из `specification.md`
| Блок ТЗ | Новый дом | Задача |
|---|---|---|
| §13, §4.1 | `product.md` | D9 |
| §4, §5–7 (техника), §9 | `architecture.md` | D8 |
| §7.6 | `security.md` («Обязательные требования») | D9 |
| §8 | README (таблица env) | D2 |
| §9 (путь `tar` для пользователя) | README | D3 |
| §1011 | README + таблица «Поставка» выше | D1–D4 |
| §12 | `development.md` | D8 |
После D9: `specification.md``docs/archive/specification-v1.0.md` (снимок,
не правится); в `docs/` живых ссылок на него нет.
**Границы (без изменений):** отдельные `CONTRIBUTING.md`, man-страницы и сайт
документации ТЗ по-прежнему не требует и в объём v1.x не входят.
документации в объём v1.x не входят.
---
@@ -82,11 +104,16 @@
| Сессии, вход, смена пароля | [internal/store/sessions.go](../internal/store/sessions.go), [internal/web/session.go](../internal/web/session.go), [internal/web/handlers_account.go](../internal/web/handlers_account.go) |
| Ротация лога, периодический reload, интервалы | [build/logrotate-mail.conf](../build/logrotate-mail.conf), [build/logrotate-loop.sh](../build/logrotate-loop.sh), [build/postfix-cert-reload.sh](../build/postfix-cert-reload.sh) |
| Деплой: тег образа, порты, монтирования, capabilities | [deploy/docker-compose.yml](../deploy/docker-compose.yml), [build/Dockerfile](../build/Dockerfile) |
| Требования, а не реализация (что вообще должно быть описано) | [specification.md](specification.md) разделы 9, 10, 11 |
| Обязательное содержание README (чеклист поставки) | таблица «Поставка» в §1 этого файла (бывшее ТЗ §10–11) |
| Границы продукта, out of scope | [product.md](product.md) *(после D9)* |
| As-built устройство (не пользовательский текст) | [architecture.md](architecture.md) *(после D8)* |
| Обязательные требования безопасности | [security.md](security.md) *(после D9)* |
Порядок прохода: сначала перечислить фактическое (env-ключи, маршруты,
`postconf`-настройки), потом искать каждый пункт в README — так находится и
неверное, и **отсутствующее**.
`postconf`-настройки), потом искать каждый пункт в README / `architecture.md`
так находится и неверное, и **отсутствующее**. До закрытия D9 для чеклиста
README допустима сверка с `specification.md` §10–11; после D9 — только с §1
этого файла.
---
@@ -230,7 +257,8 @@ DNS-карточка домена); прогрев IP; смысл фиксиро
«на живом контейнере так делать не надо, потому что SQLite в WAL». Заодно
упомянуть, что `manifest.json` после успешного восстановления потребляется.
**D4. Точечные правки.** Находки 5, 7, 8, 11: баннер статуса и ссылки; комментарий
**D4. Точечные правки.** Находки 5, 7, 8, 11: баннер статуса и ссылки (убрать
ссылку на `specification.md` — после D9 продукт в `product.md`); комментарий
в шапке compose про `.env`; строка про 587; бамп тега образа (делается в
релизном коммите, не раньше).
@@ -245,17 +273,36 @@ DNS-карточка домена); прогрев IP; смысл фиксиро
**D7. Регресс-защита (см. раздел 5).**
**D8. Новые документы: `docs/architecture.md` и `docs/development.md`.**
Пишутся с нуля (в разделе 3 их нет — это не расхождение с кодом, а
отсутствовавший артефакт). `architecture.md` сверяется тем же методом, что и
остальное (раздел 2): по факту компоновки образа и кода, не по памяти.
`development.md` фиксирует уже существующий, но нигде не описанный процесс
(Go локально / полный цикл на dev-сервере для Docker-зависимых изменений).
**D8. Новые документы: `architecture.md` и `development.md`.** Пишутся с нуля и
перенимают техническую часть ТЗ (§4–7, §9, §12) в форму as-built / процесса.
`architecture.md` сверяется методом из раздела 2 — по коду, не по памяти.
`development.md` фиксирует воспроизводимый процесс (Go локально / e2e /
dev-сервер).
*Готово, когда:* `architecture.md` перечисляет все managed-процессы из
[build/supervisord.conf](../build/supervisord.conf) и путь письма через
milter-цепочку без противоречий коду; `development.md` позволяет с нуля
повторить локальную сборку + прогон тестов и понять, когда нужен
dev-сервер, не заглядывая в память проекта.
повторить локальную сборку + прогон тестов и понять, когда нужен dev-сервер,
не заглядывая в память проекта и не открывая `specification.md`.
**D9. Вывод `specification.md` из обращения (после D1–D8).** Закрывает цель
плана: живой ТЗ больше не нужен.
1. Создать `docs/product.md` — §1–3, §4.1 (см. карту миграции в §1).
2. Дополнить [security.md](security.md) разделом «Обязательные требования» —
самодостаточный чеклист из бывшего §7.6 (setup-link, SASL, сессии,
rate-limit логина, `html/template`, не-root и т.д.), без отсылки «см. ТЗ».
3. Убедиться, что D1–D4 и D8 покрыли всё из §8–11, что должно жить в README /
`architecture.md` (пройти карту миграции построчно).
4. Перенести `specification.md``docs/archive/specification-v1.0.md` без
правок текста; в начале архива — одна строка: «исторический снимок v1.0,
не источник истины».
5. Обновить ссылки во всём репозитории: `progress.md` (убрать «ТЗ:
specification.md», заменить на `product.md` + `architecture.md`),
`implementation-plan.md` и `roadmap.md` («Основа» → `product.md`),
`security.md`, `README.md` (баннер D4 — не на ТЗ). `rg specification\.md`
по репо — только архив и CHANGELOG/история.
*Готово, когда:* в `docs/` нет `specification.md`; новый агент после `/clear`
может понять продукт, устройство, безопасность и процесс разработки, не
открывая архив.
---
@@ -270,16 +317,18 @@ dev-сервер, не заглядывая в память проекта.
новом недокументированном ключе. Ловит самый частый класс расхождений
(находка 3) без ручного прохода. Границу «внутренних» ключей задать явным
списком-исключением в самом тесте.
3. **Перед каждым тегом** — короткий проход по разделу 2 этого файла (семь
источников истины), а не полная ревизия текста.
3. **Перед каждым тегом** — короткий проход по разделу 2 (источники истины в
коде + README + `architecture.md` + `product.md`), а не полная ревизия текста
и не сверка с архивным ТЗ.
---
## 6. Гейт релиза
Документационный проход — часть того же гейта, что e2e (C.4) и ревизия
безопасности (D.5): **D1–D6 закрыты до тега**. D7 и D8 — желательны, но тег не
блокируют: D8 не покрывает пробел против ТЗ (архитектура/разработка не входят
в обязательную поставку), поэтому может тянуться в фон после релиза. Находки,
обнаруженные позже, дописываются сюда, а не исправляются молча: этот файл —
журнал состояния документации, а не одноразовый список дел.
безопасности (D.5): **D1D6 и D9 закрыты до тега**. D7 желателен, но тег не
блокирует. **D8** — обязателен до D9 (архитектура должна существовать до
миграции); D8 один тег не блокирует, если D9 отложен, но **полное закрытие
плана (= вывод specification) — только D1–D9 вместе**. Находки, обнаруженные
позже, дописываются сюда, а не исправляются молча: этот файл — журнал
состояния документации, а не одноразовый список дел.