From fa9d2016ccb25816046d68ec6f5e441c32c1be4e Mon Sep 17 00:00:00 2001 From: mixeme Date: Sun, 9 Aug 2026 00:52:49 +0300 Subject: [PATCH] release: cut 1.0.0 Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the CHANGELOG cut, retire implementation-plan and v1.x-closure-plan, and point e2e/CI comments at development.md. Co-Authored-By: Composer Co-authored-by: Cursor --- .github/workflows/release.yml | 4 +- CHANGELOG.md | 15 ++- Makefile | 2 +- README.md | 2 +- deploy/docker-compose.yml | 2 +- docs/development.md | 35 ++---- docs/guide.md | 17 ++- docs/implementation-plan.md | 32 ------ docs/roadmap.md | 83 ++------------ docs/security.md | 8 +- docs/v1.x-closure-plan.md | 209 ---------------------------------- test/e2e/main_test.go | 12 +- 12 files changed, 58 insertions(+), 363 deletions(-) delete mode 100644 docs/implementation-plan.md delete mode 100644 docs/v1.x-closure-plan.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b64c5c9..36f0c7c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ name: release # can never drift apart (the invariant restore's version check in spec 7.5.A # depends on). # -# Native per-architecture builds (plan implementation-plan.md C.4), not qemu: +# Native per-architecture builds (see docs/development.md), not qemu: # running the full Postfix/OpenDKIM stack under emulation for the e2e gate # below is impractically slow. Each arch builds, e2e-gates and pushes its own # tag on its own native runner; a merge job then combines them into the one @@ -68,7 +68,7 @@ jobs: go-version: "1.26" cache-dependency-path: test/e2e/go.sum - - name: e2e (gates publishing — see docs/implementation-plan.md C.4) + - name: e2e (gates publishing — see docs/development.md) run: cd test/e2e && go test -v -timeout 20m ./... - name: Log in to ghcr.io diff --git a/CHANGELOG.md b/CHANGELOG.md index 154722b..957978a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version ## [Unreleased] +## [1.0.0] - 2026-08-09 + ### Added - A **Delivery log** on each delivery's page (`/deliveries/{id}`): the @@ -61,10 +63,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version layering and route table match `web`→`store` and `POST /domains/import`; OpenDKIM drops to `opendkim` via `UserID`; guide drops the archived "spec 7.5" pointer, clarifies `POSTFIX_SENDER_LOGIN_MAPS` vs panel writes, - and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts vs - the still-pinned compose `0.1.0` image are called out in the guide and - `development.md`. Roadmap / implementation-plan point at CHANGELOG - `[0.5.0]` Security and refreshed `internal/web` size / symbol links. + and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts from + before the published `1.0.0` image are called out in the guide and + `development.md`. Roadmap points at CHANGELOG `[0.5.0]` Security and + refreshed `internal/web` size / symbol links. - Full backups no longer carry `/data/log`. It is Postfix's raw log plus its fourteen rotated copies — diagnostic output rather than state to restore, and otherwise by far the largest thing in the archive. @@ -94,6 +96,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version dev-host-specific workflow and `mixfed.ru` references removed from docs. - `docs/development.md` and `.cursor/rules/agent-rules.mdc` translated to English; `roadmap.md` remains Russian (internal tracker). +- Deploy pin and local-trial image tag set to `ghcr.io/mixeme/selfpost:1.0.0` + (compose and git tag `v1.0.0` cut together). Retired + `docs/implementation-plan.md` and `docs/v1.x-closure-plan.md`; Makefile, + `release.yml`, and e2e comments point at `docs/development.md`. Roadmap + v1.x documentation/deploy tail closed. ## [0.6.0] - 2026-08-08 diff --git a/Makefile b/Makefile index 491605c..f8ab028 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ test: clean: rm -rf bin -# Hermetic container e2e (plan implementation-plan.md C.4): separate Go module +# Hermetic container e2e (see docs/development.md): separate Go module # under test/e2e so its test-only dependencies (DKIM verification) never enter # this module's build graph. Builds the image fresh from this checkout, brings # up deploy/docker-compose.yml plus a test-only override on high ports and an diff --git a/README.md b/README.md index b7e1639..65e36e7 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ docker run --rm -d --name selfpost-try \ -e SELFPOST_HOSTNAME=mail.local.test \ -e PANEL_COOKIE_SECURE=false \ -v selfpost-try-data:/data \ - ghcr.io/mixeme/selfpost:0.1.0 + ghcr.io/mixeme/selfpost:1.0.0 ``` **Get the setup URL** (pick one): diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index dca15a1..813b3f3 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -22,7 +22,7 @@ services: selfpost: - image: ghcr.io/mixeme/selfpost:0.1.0 + image: ghcr.io/mixeme/selfpost:1.0.0 restart: unless-stopped environment: SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}" diff --git a/docs/development.md b/docs/development.md index 42b2b83..d98a8f9 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,8 +1,7 @@ # SelfPost — development **What this file is.** How to build, test, document, and ship changes. Open -work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag, -[v1.x-closure-plan.md](v1.x-closure-plan.md)). Product boundaries: +work for 2.x lives in [roadmap.md](roadmap.md). Product boundaries: [product.md](product.md). As-built layout: [architecture.md](architecture.md). --- @@ -12,10 +11,8 @@ work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag, After `/clear` or a fresh chat: 1. Read this file (process, docs rules, model routing). -2. Open [roadmap.md](roadmap.md) for open work; until `v1.0.0`, also - [v1.x-closure-plan.md](v1.x-closure-plan.md) for the remaining closure - checklist. Accepted risks — [security.md](security.md); as-built — - [architecture.md](architecture.md). +2. Open [roadmap.md](roadmap.md) for open work. Accepted risks — + [security.md](security.md); as-built — [architecture.md](architecture.md). 3. Skim [product.md](product.md) if scope is in doubt. 4. Continue from the next unchecked step in the active plan. @@ -31,7 +28,7 @@ not duplicated here. | Security, infra, file permissions, Postfix/`postqueue`, open-relay risk | **Opus** | `mail.log` under `/data`, entrypoint permissions, queue reconcile | | UI / JS / CSS, templates, documentation (English), README | **Sonnet** | adaptive polling, this file's Documentation section | | Trivial mechanics: retarget links, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile / release.yml comment fixes, deleting closed plan files | -| Security **review** (not authorship) | **Fable** | pre-release checklist pass ([implementation-plan.md](implementation-plan.md) § D — done) | +| Security **review** (not authorship) | **Fable** | pre-release checklist (CHANGELOG `[0.5.0]` Security / [security.md](security.md) — done) | Default rule: risk-critical → Opus; UI / docs / boilerplate → Sonnet; trivial mechanics → Haiku. Reviewers must not be the author of the code under review. @@ -154,20 +151,16 @@ The release image is published **only on tag** `vX.Y.Z` (not on every push to **Steps (on explicit request):** -1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md). -2. Create and push git tag `vX.Y.Z`. +1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md) and bump the pinned + tag in [deploy/docker-compose.yml](../deploy/docker-compose.yml) (and any + local-trial image references) in the **same** release commit. +2. Create and push git tag `vX.Y.Z` on that commit. 3. Workflow [release.yml](../.github/workflows/release.yml) builds, e2e-gates, and publishes `ghcr.io/mixeme/selfpost:X.Y.Z`. -4. Update the pinned tag in - [deploy/docker-compose.yml](../deploy/docker-compose.yml) in the **same** - commit as the tag (see [roadmap.md](roadmap.md) § «v1.x — documentation and - deploy tail»). -Ordinary commits **do not** publish an image. Intermediate CHANGELOG version -cuts (`0.2.0`…`0.6.0`) document history on `main`; the compose pin and the only -git release tag may lag until an explicit image publish (today: compose -`0.1.0`, tag `v0.0.1` — see [roadmap.md](roadmap.md) § «v1.x — documentation -and deploy tail»). +Ordinary commits **do not** publish an image. The compose pin and the git tag +must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate +CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut. --- @@ -280,13 +273,9 @@ There is no `docs/archive/` directory. | As-built design | [architecture.md](architecture.md) | | Development process (this file) | [development.md](development.md) | | Security requirements and accepted risks | [security.md](security.md) | -| Internal roadmap (v1.x tail, 2.x) | [roadmap.md](roadmap.md) | +| Internal roadmap (2.x) | [roadmap.md](roadmap.md) | | Release history | [CHANGELOG.md](../CHANGELOG.md) | -`implementation-plan.md` remains only until the release cut (describes the -closed release gate); delete it in the release commit. Temporary -[v1.x-closure-plan.md](v1.x-closure-plan.md) goes away with that cut too. - ### User-facing deliverables | Artefact | Role | diff --git a/docs/guide.md b/docs/guide.md index d06b043..e95a720 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -375,12 +375,11 @@ but it can look like an open port in external scans. ## Fixed image tag `deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`), -deliberately never `:latest`. Until the `v1.0.0` cut the shipped pin is still -`0.1.0` — intermediate CHANGELOG sections (`0.2.0`…`0.6.0`) record development -cuts and do not imply a published image of that tag. Pinning matters because of -the backup version check above: the panel binary's embedded version and the -image tag that produced it are the same value by construction (the release CI -stamps both from one git tag — see `.github/workflows/release.yml`), so the -pin is what makes "restore into the same version" a checkable fact rather than -a guess. Upgrade by bumping the tag deliberately, not by riding a moving -target. +deliberately never `:latest`. The current pin is `1.0.0`. Intermediate +CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that +image was published. Pinning matters because of the backup version check above: +the panel binary's embedded version and the image tag that produced it are the +same value by construction (the release CI stamps both from one git tag — see +`.github/workflows/release.yml`), so the pin is what makes "restore into the +same version" a checkable fact rather than a guess. Upgrade by bumping the tag +deliberately, not by riding a moving target. diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md deleted file mode 100644 index 6a58b58..0000000 --- a/docs/implementation-plan.md +++ /dev/null @@ -1,32 +0,0 @@ -# План реализации: SelfPost - -**Статус:** линия v1.0/v1.x до тега релиза **закрыта** — предрелизная ревизия -безопасности (§ D) выполнена 2026-08-06, релизный гейт (e2e + ревизия) открыт. -B.1–B.3 и C.4 закрыты — as-built в [architecture.md](architecture.md), -e2e/CI в [development.md](development.md), принятые риски в -[security.md](security.md). Текущее состояние и следующий шаг: -[roadmap.md](roadmap.md) и [v1.x-closure-plan.md](v1.x-closure-plan.md). -Объём 2.x.x — [roadmap.md](roadmap.md). - -**Основа:** [product.md](product.md) v1.0. - ---- - -## D. Предрелизная ревизия безопасности — ВЫПОЛНЕНО (2026-08-06) - -**Проверка моделью Fable** по дифу от аудита v1.0 (Фаза 11, `bd64e80` — тега -`v1.0.0` в репозитории нет, это его фактический эквивалент) до HEAD, плюс -полный повторный проход по чек-листу безопасности (бывшее ТЗ 7.6, теперь -[security.md](security.md)). Приоритеты из плана покрыты: аутентификация и -сессии, валидация ввода, запись в конфиги/map-файлы, `os/exec`, права в -`/data`, секреты. - -**Результат.** Эксплуатируемых уязвимостей (high/medium) не найдено. Одна -находка defence-in-depth закрыта правкой до тега: логин приложения передаётся -в `saslpasswd2` после `--`, чтобы значение, начинающееся с `-` (допустимо -whitelist'ом), не могло быть разобрано getopt как флаг -([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в -[security.md](security.md) не пополнились — существующие записи (origin-check -fallback, отсутствие CSRF-токенов, ложно-отрицательный `bounced` при сверке -с `postqueue`) покрывают всё найденное. -Сводка ревизии — в записи `Security` CHANGELOG `[0.5.0]`. diff --git a/docs/roadmap.md b/docs/roadmap.md index 746debd..950fc52 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -11,84 +11,25 @@ решением. **Основа:** [product.md](product.md) v1.0. Процесс и правила документации — -[development.md](development.md). Несделанное для v1.0/v1.x до тега — в -[implementation-plan.md](implementation-plan.md) и -[v1.x-closure-plan.md](v1.x-closure-plan.md). Хвост закрытого -документационного прохода (D1–D9) — в секции ниже. +[development.md](development.md). История закрытых фаз v1.x — в `git log` и +[CHANGELOG.md](../CHANGELOG.md). --- ## v1.x — хвост документации и деплоя -**Статус:** не блокирует релизный тег; бывший хвост закрытого -документационного прохода (D1–D9). Делать по желанию или в релизном коммите, -где указано. Сводка чек-листов до тега — -[v1.x-closure-plan.md](v1.x-closure-plan.md). +**Статус: закрыто** в релизе `1.0.0` / git-тег `v1.0.0` +(`ghcr.io/mixeme/selfpost:1.0.0`). План закрытия и `implementation-plan.md` +удалены — история в git и CHANGELOG; `docs/archive/` не храним. -**Тег образа в compose + git tag — один релизный коммит (R1).** В -[deploy/docker-compose.yml](../deploy/docker-compose.yml) поле `image:` бампить -до версии релиза **в том же коммите**, что и git-тег `vX.Y.Z` — не раньше. -Сейчас там `0.1.0`, то есть отстаёт от целевой версии; несовпадение мешает -только до первого выката по тегу. Сам тег — последний шаг релизного гейта: -содержательная часть (e2e C.4, ревизия § D) закрыта, режется по явной команде -оператора ([development.md](development.md) § Commits and release build). После -тега `release.yml` собирает и публикует `ghcr.io/mixeme/selfpost:X.Y.Z`, -поэтому compose с новым тегом и сам тег обязаны появиться вместе — иначе -compose неделю ссылается на несуществующий образ. +| Тема | Итог | +|---|---| +| Адаптивный опрос мониторинга | 5 с / 30 с / 0 (скрытая вкладка) в `panel.js` | +| `mail.log` + reconcile | `/data/log/mail.log`; сверка с `postqueue -p` | +| Docs consolidation | процесс в [development.md](development.md); README Documentation | +| Compose pin + git tag | `1.0.0` / `v1.0.0` в одном релизном коммите | -**Убрать `implementation-plan.md` — в релизном коммите.** Документ закрыт: -уникального содержания в нём нет, § D (предрелизная ревизия безопасности) -продублирован в [security.md](security.md) и CHANGELOG `[0.5.0]/Security`, -а разделы B.1–B.3 и C.4 вырезаны ещё в `22f86d1`. Держится до тега только -потому, что описывает релизный гейт, пока тот формально не закрыт. При резке -версии: - -1. Удалить файл (история § D — в git и CHANGELOG; `docs/archive/` не храним). -2. Перецелить ссылки из кода и CI ([Makefile](../Makefile), - [.github/workflows/release.yml](../.github/workflows/release.yml), - [test/e2e/main_test.go](../test/e2e/main_test.go)) — они ссылаются на «план - C.4», секцию, которой в файле уже нет; актуальное описание e2e — в - [development.md](development.md). -3. Перецелить оставшиеся ссылки из документации на - [development.md](development.md) / [security.md](security.md) / - [roadmap.md](roadmap.md). -4. Удалить [v1.x-closure-plan.md](v1.x-closure-plan.md) в том же или следующем - коммите. - -**Готово, когда:** тег образа в compose совпадает с релизом и рядом стоит -git-тег `vX.Y.Z`; `implementation-plan.md` и `v1.x-closure-plan.md` удалены, -ссылок на них в активных документах и в коде/CI не осталось. - -(Закрыто и действия не требует: `docs/logo` как каталога нет — критерию «либо -содержит файлы, либо отсутствует» удовлетворяет; Quick start в -[README.md](../README.md) тянет `docker-compose.yml` и `.env.example` с -`raw.githubusercontent.com` — это и есть единственная площадка проекта, зеркал -больше нет.) - -**Сводный индекс документации в README.** ~~Ссылки на `docs/` разбросаны по -тексту README…~~ **Закрыто (v1.x-closure Фаза 3):** секция Documentation в -[README.md](../README.md) — единый список operator docs + roadmap. - -**Опрос мониторинга у открытой, но незанятой вкладки.** ~~Скрытая вкладка уже не -опрашивает сервер (фильтр на `htmx:beforeRequest` в -[panel.js](../internal/web/static/panel.js)). Остаток: вкладка на переднем -плане, с которой не работают, всё равно ходит раз в 5 с. Кандидат — адаптивный -интервал (5 с при активности, 30 с при простое) по `htmx:afterRequest` без -изменения `hx-trigger`. Ценность низкая: нагрузка — один SQL-запрос и рендер -фрагмента, так что это скорее гигиена, чем экономия. Допустимый исход — -осознанно не делать.~~ **Закрыто (v1.x-closure Фаза 1):** адаптивный интервал -5 с / 30 с / 0 (скрытая вкладка) в `panel.js` через `data-poll`. - -**Send-log vs `mail.log`.** ~~Persist позиции чтения сделан (таблица -`logtail_state`, миграция `0003`): после рестарта панели log-tailer дочитывает -пропущенный хвост. Остаётся пересоздание контейнера — `mail.log` не в `/data` и -теряется вместе с ним, такие строки навсегда останутся `queued`. Кандидаты, если -станет больно: volume для лога, сверка зависших строк через `postqueue`.~~ -**Закрыто (v1.x-closure Фаза 2):** сделаны оба кандидата — `mail.log` переехал в -`/data/log/`, а строки, чьи delivery-строки потеряны безвозвратно, закрываются -сверкой с `postqueue -p` (grace 2 мин → `bounced`). As-built и оставшийся риск -(ложный `bounced`): [architecture.md](architecture.md) § Log tailer, -[security.md](security.md). +Открытая работа дальше — только секции 2.x ниже. --- diff --git a/docs/security.md b/docs/security.md index e5e964a..dba4d94 100644 --- a/docs/security.md +++ b/docs/security.md @@ -94,8 +94,8 @@ Hardening сверх обязательного (security-заголовки, п ## Принятые риски -Здесь, а не в [implementation-plan.md](implementation-plan.md): план — про -несделанную работу, принятый риск — решение с условием возврата. +Принятый риск — решение с условием возврата, а не отложенная задача из +дорожной карты. - **`POST` без `Sec-Fetch-Site` и без `Origin` пропускается.** Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или @@ -171,7 +171,7 @@ Origin-проверка закрывает это без изменения ни ## Как этот список пополняется -Предрелизная проверка на уязвимости ([implementation-plan.md](implementation-plan.md) -§ D, модель Fable) закрывает каждую находку одним из двух способов: правка до +Предрелизная проверка на уязвимости (модель Fable; история — CHANGELOG +`[0.5.0]` Security) закрывает каждую находку одним из двух способов: правка до тега — либо запись сюда, с обоснованием и условием возврата, как у пунктов выше. Третьего варианта («посмотрели и ладно») нет. diff --git a/docs/v1.x-closure-plan.md b/docs/v1.x-closure-plan.md deleted file mode 100644 index 45a3958..0000000 --- a/docs/v1.x-closure-plan.md +++ /dev/null @@ -1,209 +0,0 @@ -# План закрытия хвоста v1.x - -**Статус:** в работе. **Целевой релиз:** `v1.0.0` / `ghcr.io/mixeme/selfpost:1.0.0`. - -**Временный файл:** после закрытия v1.x удалить (история — git + CHANGELOG). Не часть постоянного пакета docs. - -**Контекст:** релизный гейт по коду закрыт (B.1–B.3, C.4 e2e, § D Fable). Остаток — [roadmap.md](roadmap.md) § «v1.x — хвост документации и деплоя» + код из того же секции. - -**Политика docs:** `docs/archive/` не храним; устаревшие планы удаляем после переноса полезного в [development.md](development.md). - ---- - -## Рекомендация по модели - -Общее правило (переносится в development.md): - -| Тип работы | Модель | Примеры в этом плане | -|------------|--------|---------------------| -| Безопасность, инфра, права файлов, postfix/postqueue, open-relay-риски | **Opus** | mail.log в `/data`, entrypoint permissions, postqueue reconcile | -| UI/JS/CSS, шаблоны, документация (English), README | **Sonnet** | adaptive polling, development.md § Documentation, README index | -| Тривиальная механика: retarget ссылок, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile/release.yml комментарии, удаление файлов | -| Ревизия безопасности (**review**, не authorship) | **Fable** | не в этом плане (§ D уже выполнен) | - -| Фаза / коммит | Модель | Почему | -|---------------|--------|--------| -| 1. Polling | Sonnet | panel.js + HTMX, CSP | -| 2. mail.log + reconcile | Opus (+ Sonnet на unit-тесты) | postfix path, logrotate, entrypoint, postqueue | -| 3. Docs consolidation | Sonnet | development.md, README, agent-rules | -| 4. Release `1.0.0` | Haiku / Sonnet | механика релиза, без новой логики | -| 5. Tag / push | **Оператор** | явная команда | - -Сообщение коммита: трейлер `Co-Authored-By: Claude <модель> ` с моделью, которая делала шаг. - ---- - -## Общий чек-лист прогресса - -- [x] **Фаза 1** — адаптивный опрос мониторинга -- [x] **Фаза 2** — mail.log в `/data` + postqueue reconcile -- [x] **Фаза 3** — docs: development.md, README, удаление планов и `docs/archive/` -- [ ] **Фаза 4** — релизный коммит `1.0.0` (по явной команде) -- [ ] **Фаза 5** — tag `v1.0.0` + push (по явной команде) -- [ ] **Фаза 6** — выкат на прод (оператор) - -**Гейт перед тегом:** - -- [ ] `gofmt -l .` чистый -- [ ] `go vet ./...` чистый -- [ ] `go test ./...` чистый (Windows-падения domain/logtail — известны, не блокер) -- [ ] `make e2e` зелёный (если Docker доступен) -- [ ] grep: нет ссылок на удалённые docs в живых файлах - ---- - -## Фаза 1 — Адаптивный опрос мониторинга - -**Модель:** Sonnet. - -**Цель:** 5 s при активности, 30 s при простое; скрытая вкладка — 0 запросов. Без `hx-trigger="every 5s [expr]"` (CSP / `unsafe-eval`). - -### Чек-лист - -- [x] `status_body.html`, `mail_queue_body.html`, `system_log_body.html`, `deliveries_rows.html` — `hx-trigger="load"` + маркер polling -- [x] `panel.js` — `lastActivity`, schedule после `htmx:afterRequest` (5s / 30s) -- [x] `htmx:afterSwap` — реинициализация polling-элементов -- [x] `beforeRequest` для hidden tab — оставлен -- [x] `architecture.md` — одна строка про polling -- [x] CHANGELOG `[Unreleased]` -- [ ] Стенд (опционально): idle → ~30s; активность → ~5s; hidden → 0 запросов - ---- - -## Фаза 2 — Send-log vs `mail.log` - -**Модель:** Opus (infra); Sonnet (тесты). - -### 2.1 `mail.log` → `/data/log/mail.log` - -- [x] `build/postfix-config.sh` — `maillog_file` из `MAIL_LOG` -- [x] `build/logrotate-mail.conf` — путь + `create 0640 postfix selfpost` -- [x] `build/entrypoint.sh` — `mkdir`, права (`2750 postfix:selfpost`, файл `0640`), - исключение `/data/log` из общего `chown` на `panel` -- [x] `cmd/panel/main.go` — default `MAIL_LOG` -- [x] `internal/backup` — исключить `log/` из архива (+ тест) -- [x] `test/e2e/mail_helpers.go` — путь в контейнере -- [x] `guide.md` — `MAIL_LOG`, System log, бэкап `./data`; README — рост диска -- [x] `envdoc_test` — `MAIL_LOG` в `buildScriptKeys` - -### 2.2 Postqueue reconcile - -- [x] `internal/postfix` — `QueueIDs` / парсер queue-id из `postqueue -p` + тест -- [x] `internal/store` — `ListQueuedOlderThan(cutoff)` -- [x] `internal/logtail` — periodic sweep (5 min), grace 2 min → `bounced`, - старт только после того, как tailer дочитал лог до конца -- [x] Тесты reconcile - -### 2.3 Документация и риски - -- [x] `architecture.md` — `/data/log`, reconcile; gap «container recreate» убран -- [x] `security.md` — риск «вечный queued» снят; на его месте — ложный `bounced` - и права на лог -- [x] CHANGELOG `[Unreleased]` - -**Стенд (остаток, для оператора):** Docker на машине разработки недоступен — -сборка образа и старт контейнера не проверены. Проверить при выкате: `/data/log` -создаётся с нужными правами, панель читает `mail.log`, logrotate проворачивает -файл, `postqueue -p` читается из-под `panel`. - ---- - -## Фаза 3 — Документация - -**Модель:** Sonnet. - -### 3.1 `documentation-plan.md` → development.md, затем delete - -- [x] development.md § **Documentation**: - - [x] Documentation map (без archive; история = git + CHANGELOG) - - [x] User-facing deliverables - - [x] Maintaining documentation (§3 правила) - - [x] Verifying docs against code (полная таблица §2) -- [x] `architecture.md` шапка → development.md -- [x] Удалить `documentation-plan.md` -- [x] Retarget `roadmap.md` - -### 3.2 `progress.md` → development.md, затем delete - -- [x] development.md § **Resuming work** -- [x] development.md § **Model routing** -- [x] development.md § **Commits** (слить с Release build) -- [x] development.md § **Phase closure** (roadmap, не progress) -- [x] `agent-rules.mdc` → development.md -- [x] Удалить `progress.md` -- [x] Retarget все ссылки - -### 3.3 Удалить `docs/archive/` - -- [x] Удалить `docs/archive/specification-v1.0.md` -- [x] Удалить каталог `docs/archive/` -- [x] Убрать ссылки на archive из живых docs (CHANGELOG историю ниже 0.6.0 не трогать) - -### 3.4 README - -- [x] § Documentation: operator docs + roadmap (internal, Russian) -- [x] Без секции Archive - -### 3.5 Проверка - -- [x] `gofmt` / `vet` / `test` -- [x] grep живых ссылок на удалённые файлы - ---- - -## Фаза 4 — Релизный коммит R1 - -**Модель:** Haiku / Sonnet. **Только по явной команде оператора.** - -- [ ] CHANGELOG: `[Unreleased]` → `[1.0.0] - дата`, новая `[Unreleased]` -- [ ] `deploy/docker-compose.yml` → `ghcr.io/mixeme/selfpost:1.0.0` -- [ ] Удалить `implementation-plan.md` (если ещё есть) -- [ ] Makefile, `.github/workflows/release.yml`, `test/e2e/main_test.go` → development.md -- [ ] `roadmap.md` — секция v1.x **закрыта**; убрать «переместить в archive» -- [ ] Финальный grep ссылок -- [ ] Удалить этот файл (`v1.x-closure-plan.md`) в том же или следующем коммите - ---- - -## Фаза 5 — Tag и push - -**Оператор.** Явная команда. - -```sh -git tag v1.0.0 -git push origin v1.0.0 -``` - -- [ ] `release.yml` — build amd64+arm64, e2e gate, push GHCR `1.0.0` -- [ ] compose tag и git tag совпадают - ---- - -## Фаза 6 — Прод (оператор) - -- [ ] `docker compose pull` → `1.0.0` -- [ ] Разлогин (cookie `__Host-`) -- [ ] Reverse-proxy передаёт `Host` -- [ ] `./data/log/` создался после upgrade - ---- - -## Критерий «хвост v1.x закрыт» - -- [ ] Все чек-листы фаз 1–4 отмечены -- [ ] `compose` = `1.0.0`, тег `v1.0.0` в репо -- [ ] Нет `implementation-plan.md`, `documentation-plan.md`, `progress.md`, `docs/archive/`, `v1.x-closure-plan.md` -- [ ] Нет активных ссылок на удалённые пути -- [ ] `development.md` — процесс + Documentation + model routing -- [ ] `roadmap.md` — единственный internal tracker -- [ ] Polling 5s/30s; mail.log в `/data`; reconcile; риск security снят - ---- - -## Разбивка коммитов - -1. **Polling** — panel.js, templates, architecture (+ CHANGELOG) -2. **mail.log + reconcile** — build, logtail, store, postfix, docs (+ CHANGELOG) -3. **Docs** — development.md, README, agent-rules; delete documentation-plan, progress, archive; retarget (+ CHANGELOG) -4. **Release 1.0.0** — CHANGELOG cut, compose, delete implementation-plan, roadmap closure, delete `v1.x-closure-plan.md` -5. **Tag/push** — оператор diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go index 2eb58ba..62883e0 100644 --- a/test/e2e/main_test.go +++ b/test/e2e/main_test.go @@ -1,13 +1,13 @@ -// Package e2e is the hermetic container e2e gate (plan C.4): it drives the -// shipped deploy/docker-compose.yml (plus a test-only override) exactly as an +// Package e2e is the hermetic container e2e gate: it drives the shipped +// deploy/docker-compose.yml (plus a test-only override) exactly as an // administrator and their applications would, so the class of failure unit // tests cannot see — broken container wiring — has one place to be caught // before an image is published. // -// It is a separate module on purpose (see ../../docs/implementation-plan.md, -// item C.4): `go test ./...` in the main module never pulls this in, and its -// test-only dependencies (DKIM verification) never enter the shipped -// binaries' build graph. +// It is a separate module on purpose (see ../../docs/development.md): +// `go test ./...` in the main module never pulls this in, and its test-only +// dependencies (DKIM verification) never enter the shipped binaries' build +// graph. package e2e import (