1f548dfc7a
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>
210 lines
9.8 KiB
Markdown
210 lines
9.8 KiB
Markdown
# План закрытия хвоста 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 <модель> <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** — оператор
|