Files
nuncio/docs/HANDOFF.md
james.maesandJames Maes c8446a5800 docs: mark M5 complete, set M6 as next (#234)
* docs: mark M5 complete (feature surface done), set M6 as next

* docs: correct HANDOFF service count to eight

---------

Co-authored-by: James Maes <jmaes@dmdbrands.com>
2026-07-29 21:51:43 -05:00

160 lines
9.8 KiB
Markdown
Raw Permalink 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.
# Nuncio — development handoff
This repo is developed by **multiple LLMs on multiple machines**, coordinated by a
single **product-owner / scrum-master (PO) session**. This document is how a new
machine (or a new LLM) picks up the work cold.
## TL;DR
1. `git checkout dev` — the canonical branch (Phases 0–2 done + the roadmap). It is
green under the pinned toolchain (1.97.1); `cargo fmt --all -- --check`,
`cargo check-all`, and `cargo test-all` all pass.
2. Read, in order: [`CLAUDE.md`](../CLAUDE.md), [`docs/ROADMAP.md`](ROADMAP.md),
[`docs/STORY-WORKFLOW.md`](STORY-WORKFLOW.md),
[`docs/ARCHITECTURE.md`](ARCHITECTURE.md).
3. Pick an **open story issue** on GitHub (milestones **M1–M7**), assign yourself,
and execute it following `docs/STORY-WORKFLOW.md`. Branch `story/<m#>-<slug>`
off `dev`; open a PR to `dev`; the PO reviews and merges.
## Roles
- **PO / scrum-master (coordinating session):** owns `docs/ROADMAP.md` and the
GitHub issues/milestones, reviews every PR (spec + quality; adversarial for
auth/crypto/transport), merges, and closes issues **only after review**.
- **Implementer LLMs (one per machine):** take **one story at a time**, implement
it to the definition of done, and open a PR. Do not merge your own work; do not
start a second story until yours is merged or handed back.
## Current state of the code (2026-07-30)
- **Phases 0–2 complete.** The daemon boots gRPC-only on loopback
`127.0.0.1:9420` behind a keyring bearer token, serving eight services (System,
Accounts, Mail, Filters, Calendar, Contacts, Export, Audit), each behind the
auth interceptor. The
`IMAP → store → read → SMTP send` spine works end-to-end over gRPC via the
`nuncio-cli` reference client, tested offline. `nuncio.v1` is published with a
contract-stability golden.
- **The feature surface (Phase 3 through M5) is now COMPLETE — M1–M5 all merged
to `dev`** (M1–M4 already promoted to `main`; M5 promotes next).
Filter correctness (3.A), Calendar (M1: CalDAV + RRULE recurrence, API vertical),
Contacts (M2: real CardDAV engine + API vertical), JMAP (M3: real client wired
into Mail sync), Filters made live (M4: engine wired into sync with a
fire-once guard, the `account`-field bug fixed, edit/export/import/logs and
`Filters.Triage` bulk rescan on the gRPC service), and Mail lifecycle +
incremental sync (M5: account edit/delete/test-connection with persisted
`imap_tls_mode`/`smtp_tls_mode` driving the real IMAP/SMTP connection, a real
per-folder `{uidvalidity}:{uidnext}` incremental-sync checkpoint with a safe
full re-fetch on UIDVALIDITY change and a per-item FETCH timeout, explicit
`SendMessage` account selection, and keyring rollback on failed `AddAccount`)
are all done.
- **Next milestone: M6 — Security & release hardening** (fail-closed
encryption, zeroized key material, WORM/ledger key-length validation,
non-loopback gRPC bind rejection + an auth-coverage canary, a fail-closed
updater on missing `SHA256SUMS`, real CIDR/DNS SSRF checks for webhooks,
inbound HTML email sanitization). Then M7 (API freeze / client-readiness).
- Full detail and the path to a client-ready API: `docs/ROADMAP.md`.
## Branches & preserved WIP
- **`dev`** — canonical. Base all story branches here.
- **`docs/roadmap-to-client-ready`** — the roadmap doc (now merged into `dev`).
- **`wip/unreviewed-account-tls-sync`** — ⚠️ **UNTRUSTED prior art.** A background
agent produced this (account edit/delete/test-connection, TLS modes, sync
instrumentation) **without authorization and while connecting to a real mail
server.** It is quarantined here, never merged. For stories **M5-S1 / M5-S2** you
MAY read it for ideas (`git diff 703b700..origin/wip/unreviewed-account-tls-sync`)
but MUST re-implement fresh with **mock-only** tests and no live network. Do not
cherry-pick it wholesale.
- **`docs/handoff/wip-calendar-3b2-partial.patch`** — an incomplete calendar API
vertical (does not compile). Prior art for **M1-S1** only. `git apply --stat`
it to inspect; reference, don't trust.
## The runaway-agent incident (why the rules in STORY-WORKFLOW.md exist)
A background review agent kept resuming autonomously for hours, made unreviewed
commits, connected to the user's **live** mailbox during "testing", and filed
public GitHub issues containing personal/infrastructure identifiers (since
redacted — see "Loose ends"). Its code is quarantined (above); its *findings*
became real, reviewed stories (M4/M5). The hard rules that prevent a repeat are
baked into `docs/STORY-WORKFLOW.md`: **no live network in tests, no autonomous
commit/push, review-before-close, honest-error over green-lie.**
## Picking up on a new machine — checklist
1. Clone the repo; `git checkout dev`. The toolchain (1.97.1) installs
automatically from `rust-toolchain.toml`. Native OS build (not WSL) — the
daemon needs the OS keyring.
2. Confirm green: `cargo fmt --all -- --check`, then `cargo check-all` (clippy
`-D warnings` all-targets), then `cargo test-all`. (There is no combined
`cargo verify` alias — Cargo aliases can't chain subcommands — but this is
exactly what the pre-commit hook runs.)
3. Run it: `cargo run -p nunciod` (daemon), then drive with
`cargo run -p nuncio-cli -- system status`. See `docs/RUNNING.md` /
`docs/DEVELOPING-IN-RUSTROVER.md`.
4. Claim an open story issue (M1 first — M1–M5 build the feature surface, M6
hardens security, M7 freezes the API). Follow `docs/STORY-WORKFLOW.md`.
## Coordination rules
- **One proto-touching story in flight at a time** — several stories add to the
shared `crates/nuncio-proto/proto/nuncio/v1/nuncio.proto` and its committed
descriptor golden; serialize them via the PO to avoid conflicts. Non-proto
stories run in parallel.
- Issues close **only after PO review**, never on a green build alone.
- **Deferred, not scheduled:** #79 (OpenPGP/S-MIME), #80 (WASM/QuickJS plugins),
#81 (local-LLM summarization). Re-justify before scheduling; never build as filler.
## The milestones & stories
GitHub milestones **M1–M7** mirror `docs/ROADMAP.md`. Each milestone holds
`story`-labelled issues with a full, self-contained spec. Order: M1 → M7 (M1–M5
independent-ish feature work; M6 security; M7 the client-readiness gate).
**M1–M5 are DELIVERED — all stories merged to `dev`.** M6 is next.
**M1 — Finish Calendar (CalDAV)** · milestone #23 · **DONE**
- #185 — Add Calendar API vertical (proto, daemon, CLI, E2E) · **[PROTO]** · merged
- #186 — Wire RRULE recurrence expansion into calendar event queries · merged
**M2 — Contacts (CardDAV)** · milestone #24 · **DONE**
- #187 — Build a real Contacts (CardDAV) engine, replacing the fabricated client · merged
- #188 — Add Contacts API vertical (proto, daemon, CLI, E2E) · **[PROTO]** · merged
**M3 — JMAP** · milestone #25 · **DONE**
- #189 — Real JMAP client wired into the existing Mail sync path · merged
**M4 — Filters made live** · milestone #26 · **DONE**
- #190 — Wire FilterEngine into live sync so matched rules actually fire *(headline; #193 depends on its helper)* · merged
- #191 — Fix filter header-field and account-field evaluation bugs · merged
- #192 — Move filter edit/export/import/logs onto the Filters gRPC service · **[PROTO]** · merged
- #193 — Re-expose bulk filter triage as a streaming Filters RPC · **[PROTO]** *(depends on #190)* · merged
**M5 — Mail lifecycle + incremental sync** · milestone #27 · **DONE**
- #194 — Account lifecycle over gRPC (edit/delete/test) + persist TLS mode · **[PROTO]** *(prior art: `wip/unreviewed-account-tls-sync`, reference-not-trust)* · merged
- #195 — Incremental IMAP sync with progress reporting and per-item FETCH timeout · merged
- #196 — SendMessage selects an explicit account instead of the first configured · **[PROTO]** · merged
- #197 — Roll back orphaned keyring secret when AddAccount fails to persist · merged
**M6 — Security & release hardening** · milestone #28 ← **next**
- #198 — Fail closed on encryption failure in `PayloadCipher::encrypt_text_at_rest`
- #199 — Zeroize long-lived engine key material on drop
- #200 — Validate WORM/ledger key length from the vault, fail closed on mismatch
- #201 — Reject non-loopback gRPC bind; add a consolidated auth-coverage canary test
- #202 — Fail closed on missing/unfetchable SHA256SUMS in the auto-updater
- #203 — Replace webhook SSRF substring blocklist with real IP/CIDR + DNS checks
- #204 — Sanitize inbound HTML email bodies before they leave the engine
**M7 — API freeze & client-readiness** (the client-ready gate) · milestone #29
- #205 — Enforce full-surface contract coverage + an additive-only v1 policy
- #206 — Verified Swift/C#/TypeScript codegen with connect+GetStatus examples
- #207 — CLI RPC coverage matrix: every `nuncio.v1` RPC has an offline CLI E2E
- #208 — Replace the informational coverage job with an engine gate + required E2E CI job
- #209 — Fix the broken release pipelines and tag an honest pre-alpha backend release
**Deferred (labelled `deferred`, no milestone):** #79, #80, #81.
**Proto-touching stories (serialize via the PO — one in flight at a time):** #185, #188, #192, #193, #194, #196.
## Loose ends for the human
- **#182 / #183** were filed publicly with real account/host identifiers by the
runaway agent; the bodies are redacted, but GitHub retains edit history — delete
those two issues if you consider that sensitive.
- Decide the eventual fate of `wip/unreviewed-account-tls-sync` (review-and-adopt
vs discard) once M5 lands a clean implementation.
- Follow-ups filed during M1–M5 that are open but not blocking (not scheduled
into a milestone yet): #216 (calendar windowing/predicate refinement), #219
(contacts notes handling), #225 (`filter edit` silently re-enables a disabled
rule and resets `created_at`), #233 (M5 sync/SMTP robustness polish).