docs: roadmap checklists, Progress column, commit/version protocol
test / test (push) Waiting to run

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-17 21:47:04 +03:00
parent b99f60c1d6
commit 46191172d6
9 changed files with 210 additions and 11 deletions
+6
View File
@@ -7,6 +7,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
### Added ### Added
- docs: **Plan checklists** in [development.md](docs/development.md) — format,
`Progress` column in [roadmap.md](docs/roadmap.md), per-step commit +
CHANGELOG, version cuts `1.3.1``1.8.0` per roadmap stage.
- docs: **Implementation checklists** with model routing in
[docs/plans/](docs/plans/) (queue-retries, inbound-relay, send-log-retention,
domain-stats-auto-ratelimit, dmarc-reports, panel-docs).
- docs: roadmap candidates **send-log-retention** (panel Settings for delivery - docs: roadmap candidates **send-log-retention** (panel Settings for delivery
journal retention) and **domain-stats-auto-ratelimit** (30-day send stats per journal retention) and **domain-stats-auto-ratelimit** (30-day send stats per
domain/application, auto level-2 rate limit from avg × multiplier) — plans in domain/application, auto level-2 rate limit from avg × multiplier) — plans in
+52
View File
@@ -66,6 +66,58 @@ mechanics → Haiku. Reviewers must not be the author of the code under review.
--- ---
## Plan checklists
Open [roadmap.md](roadmap.md) items with a linked plan file carry an
**Implementation checklist** in [plans/](plans/). After a context reset, work
the **active** plan — not the roadmap prose — from the first unchecked line.
**Checklist format**
- `- [ ] Step description — **Model**` (model from the routing table above).
- Mark `[x]` only in the commit that actually finishes the step.
- `candidate` items: checklist may exist, but no code until the roadmap status
is agreed.
**Progress column** in the roadmap index (`0/N`) counts every checklist line in
that plan (including docs and `go vet` / `go test` steps). Update `N` when the
checklist changes; update the numerator when steps are checked off.
### Roadmap track: commit and version
For the current 1.x+ roadmap queue ([roadmap.md](roadmap.md) index), stricter
than the default «meaningful step» rule in [§ Commits and release build](#commits-and-release-build):
**After each checklist step**
1. Check off the step in the plan file; bump `Progress` in the roadmap index.
2. Append [CHANGELOG.md](../CHANGELOG.md) under `[Unreleased]` for that step.
3. Commit on `main` (push / tag / Release only on explicit request).
**After each roadmap stage** (all checklist steps done; «Done when» satisfied)
1. Version cut in one commit: rename `[Unreleased]` to `[X.Y.Z] - date`, open a
fresh `[Unreleased]`, bump the image pin in
[deploy/docker-compose.yml](../deploy/docker-compose.yml).
2. Git tag `vX.Y.Z` and publish the GitHub Release only on explicit request (see
[§ Release image](#release-image)).
**Planned version cuts** (from pin `1.3.0`; adjust if semver changes mid-track):
| Stage | ID | Cut |
|---|---|---|
| 1 | queue-retries | `1.3.1` (PATCH) |
| 2 | inbound-relay | `1.4.0` |
| 3 | send-log-retention | `1.5.0` |
| 4 | domain-stats-auto-ratelimit | `1.6.0` |
| 5 | dmarc-reports | `1.7.0` |
| 6 | panel-docs | `1.8.0` |
Docs-only prep (checklists in plans, no product code) uses the same per-step
commit rule but **no** version cut until stage 1 ships.
---
## Technology stack and tools ## Technology stack and tools
| Component | Version / notes | | Component | Version / notes |
+15
View File
@@ -48,3 +48,18 @@ remain a separate, opt-in feature that does not forward mail upstream.
- Attack surface of accepting mail (mitigate: strict recipient allow-list). - Attack surface of accepting mail (mitigate: strict recipient allow-list).
- Report volume and retention (mitigate: caps + pruning). - Report volume and retention (mitigate: caps + pruning).
## Implementation checklist
Target version cut: **`1.7.0`** (MINOR). One commit per step; code only after
roadmap status is **agreed**. Expand the sketch sections above before step 1
if still thin. See [development.md](../development.md) § Plan checklists.
- [ ] Expand plan: ingest path, `dmarc_reports` schema, retention caps — **Sonnet**
- [ ] Opt-in inbound SMTP for report addresses only (allow-list) — **Opus**
- [ ] Worker: gzip/XML parse → SQLite — **Opus**
- [ ] Panel: domain roll-up + parsed report (panel-ui mockups) — **Sonnet**
- [ ] Tie-in `dmarc_report_email` / `domains.dmarc_rua`**Sonnet**
- [ ] Tests and [guide.md](../guide.md) — **Sonnet**
- [ ] Security review ingest path — **Fable**
- [ ] `go vet`, `go test` on touched packages — **Haiku**
+18
View File
@@ -185,3 +185,21 @@ change in [guide.md](../guide.md) § Export.
from the panel. from the panel.
**Version:** `1.x` MINOR. **Version:** `1.x` MINOR.
## Implementation checklist
Target version cut: **`1.6.0`** (MINOR). One commit per step; code only after
roadmap status is **agreed**. See [development.md](../development.md) § Plan
checklists.
- [ ] Migration `0006_rate_limit_auto.sql` (`mode`, `auto_multiplier`, `auto_updated_at`) — **Opus**
- [ ] `internal/store/stats.go`: total / peak / avg over 30 days — **Opus**
- [ ] Auto recalc job (6h): `ceil(avg × multiplier)`, L1 cap, fail-open on error — **Opus**
- [ ] Extend `RateLimit` + handler forms (manual/auto) — **Opus**
- [ ] Domain page stats card + per-app stats (`domain_detail.html`) — **Sonnet**
- [ ] Domain export JSON includes rate limits — **Opus**
- [ ] Milter tests — enforced limit matches stored ceiling — **Opus**
- [ ] Store and handler tests — **Sonnet**
- [ ] [guide.md](../guide.md) — **Sonnet**
- [ ] Security review rate-limit path — **Fable**
- [ ] `go vet`, `go test` on touched packages — **Haiku**
+22
View File
@@ -169,3 +169,25 @@ the SelfPost image, brought up by the operator when the option is enabled.
A finished outbound path (already implemented). Agreement obtained — see the A finished outbound path (already implemented). Agreement obtained — see the
status above. status above.
## Implementation checklist
Target version cut: **`1.4.0`** (MINOR). One commit per step; see
[development.md](../development.md) § Plan checklists. UI reference:
[panel-ui inbound mockups](../assets/panel-ui/inbound.html).
- [ ] Migration: inbound domain / recipient / transport tables under `/data`**Opus**
- [ ] `INBOUND_RELAY_ENABLE` (default false) in entrypoint + `postfix-config.sh`**Opus**
- [ ] `master.cf`: inbound `smtp inet` on 25; separate from 465/587 — **Opus**
- [ ] Generate `relay_domains`, `transport_maps`, `relay_recipient_maps` (injection-safe) — **Opus**
- [ ] `smtpd_relay_restrictions` / recipient maps — no open relay, no backscatter — **Opus**
- [ ] `internal/store` CRUD + validation (domain, host, port) — **Opus**
- [ ] Panel: list, add, domain detail, recipients, danger zone (mockups) — **Sonnet**
- [ ] Rate limit + `message_size_limit` on inbound smtpd — **Opus**
- [ ] Optional `INBOUND_ANTISPAM_MILTER` + compose fragment — **Opus**
- [ ] DNS MX copy in README/guide; `.env.example`**Sonnet**
- [ ] Backup/export inbound config (per plan optional flag) — **Opus**
- [ ] Unit + handler tests; image build and container smoke — **Opus**
- [ ] [guide.md](../guide.md) and [security.md](../security.md) — **Sonnet**
- [ ] Security review inbound path — **Fable**
- [ ] `go vet`, `go test`, e2e if applicable — **Haiku**
+54
View File
@@ -0,0 +1,54 @@
# Plan: panel-docs (in-panel operator documentation)
**Status:** candidate
**Date:** 2026-08-17
**Version:** `1.x` MINOR; `candidate` until explicitly agreed.
---
## Goal
Built-in operator documentation in the panel — short pages or a help drawer that
explain what each Status check and other controls mean, without sending the
operator out to [guide.md](../guide.md).
## Scope
**In:**
- Help drawer or short help pages (CSS-checkbox pattern from
[panel-ui mockups](../assets/panel-ui/system.html)).
- Seed content: Status blurbs removed from cards for a denser layout — Machine
(kernel counters / rate window), TLS certificate (port 465, reverse-proxy
mount), Hostname / reverse DNS (FCrDNS, PTR at the hosting provider), and
similar notes for other surfaces as inline commentary is removed.
- «?» entry points on domain cards (mockups).
**Out:**
- A second copy of the full operator guide.
- Translation workflow beyond English (same as the rest of the panel).
## Done when
An operator can open help from the panel for those topics; the removed Status
blurbs are preserved there (or equivalent); no requirement to read the git
tree for day-to-day meaning of a card.
## Risks
Copy ownership and keeping help in sync when checks change; not bloating every
page with a second column of prose.
## Implementation checklist
Target version cut: **`1.8.0`** (MINOR). One commit per step; code only after
roadmap status is **agreed**. See [development.md](../development.md) § Plan
checklists.
- [ ] Help drawer / pages shell (CSS checkbox pattern from mockups) — **Sonnet**
- [ ] Seed Status blurbs (Machine, TLS, PTR, …) — **Sonnet**
- [ ] «?» entry points on domain cards — **Sonnet**
- [ ] [guide.md](../guide.md) boundary: in-panel help vs full guide — **Sonnet**
- [ ] Template tests — **Sonnet**
- [ ] `go vet`, `go test` on touched packages — **Haiku**
+15
View File
@@ -139,3 +139,18 @@ lifetime runs out.
- Showing compiled-in fallbacks when `postconf` failed would mislead if the - Showing compiled-in fallbacks when `postconf` failed would mislead if the
operator had overridden them — mitigate with the muted note on the card. operator had overridden them — mitigate with the muted note on the card.
- Inventing a max-attempt count would be false; the copy must stay time-based. - Inventing a max-attempt count would be false; the copy must stay time-based.
## Implementation checklist
Target version cut: **`1.3.1`** (PATCH). One commit per step; see
[development.md](../development.md) § Plan checklists.
- [ ] `internal/postfix`: parse Postfix time units (`5d`, `300s`, bare seconds) + tests — **Opus**
- [ ] `internal/postfix`: one-shot `postconf -h` (six keys), fallback + warn — **Opus**
- [ ] Load policy at HTTP start in `cmd/panel/httpserver.go`; cache on handlers config — **Opus**
- [ ] Human-readable duration formatter (shared by Mail queue card and delivery history) — **Sonnet**
- [ ] «How delivery retries work» card on `/mail-queue` (outside HTMX fragment) — **Sonnet**
- [ ] `deliveryEvents(row, policy)` — intervals in deferred/bounced copy — **Sonnet**
- [ ] Handler and template tests (`handlers_monitor_test.go`, `templates_test.go`) — **Sonnet**
- [ ] [guide.md](../guide.md) and [architecture.md](../architecture.md) — **Sonnet**
- [ ] `go vet`, `go test` on touched packages — **Haiku**
+15
View File
@@ -122,3 +122,18 @@ Domain administrators keep the narrow credentials-only settings page.
stale int from panel start only. stale int from panel start only.
**Version:** `1.x` MINOR. **Version:** `1.x` MINOR.
## Implementation checklist
Target version cut: **`1.5.0`** (MINOR). One commit per step; code only after
roadmap status is **agreed**. See [development.md](../development.md) § Plan
checklists.
- [ ] `GetSendLogRetentionDays` / `SetSetting` key `send_log_retention_days` (7365) — **Opus**
- [ ] Bootstrap from `SEND_LOG_RETENTION_DAYS` when settings empty — **Opus**
- [ ] `logtail.retentionLoop`: read setting each prune cycle — **Opus**
- [ ] Settings card on `/settings` (`settings.html`) — **Sonnet**
- [ ] Remove hardcoded «ninety days» in handlers and templates — **Sonnet**
- [ ] Tests: save/load, range, loop without restart — **Sonnet**
- [ ] [guide.md](../guide.md) — **Sonnet**
- [ ] `go vet`, `go test` on touched packages — **Haiku**
+13 -11
View File
@@ -26,16 +26,16 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
## Index ## Index
| ID | Topic | Status | Plan | | ID | Topic | Status | Progress | Plan |
|---|---|---|---| |---|---|---|---|---|
| queue-retries | Postfix retry policy in the panel (queue lifetime, backoff) | **agreed** | [plans/queue-retries.md](plans/queue-retries.md) | | queue-retries | Postfix retry policy in the panel (queue lifetime, backoff) | **agreed** | 0/9 | [plans/queue-retries.md](plans/queue-retries.md) |
| inbound-relay | Inbound relay (backup-MX / forwarding) | **agreed** | [plans/inbound-relay.md](plans/inbound-relay.md) | | inbound-relay | Inbound relay (backup-MX / forwarding) | **agreed** | 0/15 | [plans/inbound-relay.md](plans/inbound-relay.md) |
| send-log-retention | Send-log retention days in panel Settings | candidate | [plans/send-log-retention.md](plans/send-log-retention.md) | | send-log-retention | Send-log retention days in panel Settings | candidate | 0/8 | [plans/send-log-retention.md](plans/send-log-retention.md) |
| domain-stats-auto-ratelimit | 30-day send stats + auto level-2 rate limit | candidate | [plans/domain-stats-auto-ratelimit.md](plans/domain-stats-auto-ratelimit.md) | | domain-stats-auto-ratelimit | 30-day send stats + auto level-2 rate limit | candidate | 0/11 | [plans/domain-stats-auto-ratelimit.md](plans/domain-stats-auto-ratelimit.md) |
| contributing | `CONTRIBUTING.md` | candidate | — | | contributing | `CONTRIBUTING.md` | candidate | — | — |
| dmarc-reports | DMARC aggregate report ingestion and panel UI | candidate | [plans/dmarc-reports.md](plans/dmarc-reports.md) | | dmarc-reports | DMARC aggregate report ingestion and panel UI | candidate | 0/8 | [plans/dmarc-reports.md](plans/dmarc-reports.md) |
| panel-docs | In-panel operator documentation | candidate | | | panel-docs | In-panel operator documentation | candidate | 0/6 | [plans/panel-docs.md](plans/panel-docs.md) |
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | | schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
**Recommended order** (not binding): **queue-retries** is a small panel item **Recommended order** (not binding): **queue-retries** is a small panel item
that can land first or in parallel; the next feature is **inbound-relay**. that can land first or in parallel; the next feature is **inbound-relay**.
@@ -47,7 +47,9 @@ fail-closed paths, docs, GUI, tests, licence) are closed — history in
explicit agreement before they join the queue. explicit agreement before they join the queue.
After a context reset, pick an item marked `agreed` or `in progress`, then work After a context reset, pick an item marked `agreed` or `in progress`, then work
the checklist in its linked plan. the **Implementation checklist** in its linked plan. The `Progress` column above
is `done/total` checklist steps in that plan ([development.md](development.md)
§ Plan checklists).
--- ---