4
HANDOFF
github-actions[bot] edited this page 2026-07-30 03:00:56 +00:00
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, docs/ROADMAP.md, docs/STORY-WORKFLOW.md, docs/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).