release: cut 1.0.0
test / test (push) Has been cancelled
release / prepare (push) Has been cancelled
release / build (amd64, ubuntu-latest) (push) Has been cancelled
release / build (arm64, ubuntu-24.04-arm) (push) Has been cancelled
release / merge (push) Has been cancelled

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 <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mixeme
2026-08-09 00:52:49 +03:00
parent e0b754bafe
commit fa9d2016cc
12 changed files with 58 additions and 363 deletions
+2 -2
View File
@@ -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
+11 -4
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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}"
+12 -23
View File
@@ -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 |
+8 -9
View File
@@ -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.
-32
View File
@@ -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]`.
+12 -71
View File
@@ -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 ниже.
---
+4 -4
View File
@@ -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) закрывает каждую находку одним из двух способов: правка до
тега — либо запись сюда, с обоснованием и условием возврата, как у пунктов выше.
Третьего варианта («посмотрели и ладно») нет.
-209
View File
@@ -1,209 +0,0 @@
# План закрытия хвоста v1.x
**Статус:** в работе. **Целевой релиз:** `v1.0.0` / `ghcr.io/mixeme/selfpost:1.0.0`.
**Временный файл:** после закрытия v1.x удалить (история — git + CHANGELOG). Не часть постоянного пакета docs.
**Контекст:** релизный гейт по коду закрыт (B.1B.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 <модель> <noreply@anthropic.com>` с моделью, которая делала шаг.
---
## Общий чек-лист прогресса
- [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** — оператор
+6 -6
View File
@@ -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 (