Files
selfpost/docs/v1.x-closure-plan.md
T
mixeme 9d6d89c79d
test / test (push) Has been cancelled
fix(logtail): keep mail.log in /data and reconcile stuck rows (v1.x closure phase 2)
Move the delivery log from the ephemeral /var/log to /data/log/mail.log so
the lines that resolve a queued send-log row survive a container recreate.
postlogd writes it as postfix, the panel reads it through the selfpost group
(dir 2750, file 0640, normalised every start); backups exclude log/.

Close the residual gap with a queue sweep: rows queued for over two minutes
whose id postqueue -p no longer lists are marked bounced. The sweep waits
until the tailer has read the log to its end and does nothing when the queue
cannot be listed, so a message in flight is never touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 11:35:27 +03:00

210 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План закрытия хвоста 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
- [ ] **Фаза 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
- [ ] development.md § **Documentation**:
- [ ] Documentation map (без archive; история = git + CHANGELOG)
- [ ] User-facing deliverables
- [ ] Maintaining documentation (§3 правила)
- [ ] Verifying docs against code (полная таблица §2)
- [ ] `architecture.md` шапка → development.md
- [ ] Удалить `documentation-plan.md`
- [ ] Retarget `roadmap.md`
### 3.2 `progress.md` → development.md, затем delete
- [ ] development.md § **Resuming work**
- [ ] development.md § **Model routing**
- [ ] development.md § **Commits** (слить с Release build)
- [ ] development.md § **Phase closure** (roadmap, не progress)
- [ ] `agent-rules.mdc` → development.md
- [ ] Удалить `progress.md`
- [ ] Retarget все ссылки
### 3.3 Удалить `docs/archive/`
- [ ] Удалить `docs/archive/specification-v1.0.md`
- [ ] Удалить каталог `docs/archive/`
- [ ] Убрать ссылки на archive из живых docs (CHANGELOG историю ниже 0.6.0 не трогать)
### 3.4 README
- [ ] § Documentation: operator docs + roadmap (internal, Russian)
- [ ] Без секции Archive
### 3.5 Проверка
- [ ] `gofmt` / `vet` / `test`
- [ ] 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** — оператор