Skip to content

BACKLOG

github-actions[bot] edited this page Jul 29, 2026 · 4 revisions

Backlog — Phase 0 & Phase 1 (engineering-ready)

Status: Phase 0 and Phase 1 are both complete and on dev — this document is now the historical story-level record of that work. For where the project stands today (Phase 3 in progress, milestones M1–M7), see ROADMAP.md.

Ordered, executable stories for the first two phases of ROADMAP.md. Each story has acceptance criteria (AC) and a rough size (S ≤1d, M ≈2–4d, L ≈1wk). Do them roughly top-to-bottom; within an epic, stories are mostly independent unless noted. Definition of done for every story: engine + (where applicable) proto + CLI + offline test, and the local gate green (cargo fmt --all -- --check, cargo check-all, cargo test-all).


Phase 0 — Honesty & foundation

Epic 0.A — Documentation reset

  • 0.A.1 Archive fabricated docs (S). Move the superseded PLAN-*, TODO.md, TICKET-*, and EXECUTIVE-* files into docs/_archive/ with a README stating they are historical and unverified. AC: docs/ top level contains only ROADMAP.md, BACKLOG.md, adr/, a rewritten ARCHITECTURE.md, and _archive/.
  • 0.A.2 Rewrite CLAUDE.md to reality (M). Correct crate count (12), define the cargo aliases (check-all, test-all, cov, verify), document the gRPC/engine-first direction, and replace the "100% four-shell parity" mandate with "parity via the published contract." AC: every command and claim in CLAUDE.md is locally verifiable.
  • 0.A.3 Rewrite README.md (S). Remove fabricated claims and benchmarks; state honest pre-alpha status. AC: no unverifiable claim remains.

Epic 0.B — Release hygiene

  • 0.B.1 Mark public releases pre-alpha (S). Convert v1.0.0 and rolling releases to prerelease with a NOTICE that they are non-functional and the updater is unsafe. AC: no GitHub release is presented as GA.
  • 0.B.2 Disable the fail-open auto-updater (S). Guard the 24h update loop off until Phase 4. AC: the daemon never downloads/replaces a binary.

Epic 0.C — Green CI

  • 0.C.1 Clear the clippy backlog (M). Commit or revert the ~1,600 lines of abandoned edits so cargo check-all passes (76 current denials in nuncio-core). AC: cargo check-all exits 0 locally and in CI.
  • 0.C.2 Align CI to the local gate (S). CI runs cargo clippy --workspace without --all-targets; make CI run cargo check-all so CI and local are identical. AC: CI and the local gate enforce the same lints.
  • 0.C.3 Replace the coverage gate (M). Drop the gamed 100%-line gate (it excludes all main.rs and incentivized stub+matching-test theater) for a realistic threshold on engine crates plus a required mock-server E2E job. AC: coverage gate reflects real confidence; full CI green on dev/main.

Epic 0.D — Shrink to engine + CLI

  • 0.D.1 Extract the API-surface draft (S, do before 0.D.2). Mine the MCP shell's ~20 tool→engine mappings into docs/api-surface-draft.md as candidate RPCs. AC: every operation the MCP shell exposed is listed as a candidate RPC.
  • 0.D.2 Move UIs to _reference/ (M). Remove nuncio-tui, nuncio-gui (+ src-tauri, ui), and nuncio-mcp from the workspace members into _reference/. AC: workspace = core, mail, cal, contacts, store, filter, cli, nunciod; cargo build/CI green.

Epic 0.E — Kill hardcoded keys / real vault

  • 0.E.1 Purge key literals (M). Remove DEFAULT_STORAGE_KEY, DEFAULT_WORM_KEY, 'secret_ledger_key', and b'nuncio_ledger_secret'; fail closed when no key is present. AC: grep finds no crypto key literal in src/; missing-key paths error rather than silently use a default.
  • 0.E.2 Real OS keyring vault (L). Replace MockKeyring in production with the keyring crate; keys minted and read from the OS keyring; MockKeyring remains test-only. AC: an integration test (with a mock keyring provider) proves the daemon reads keys from the vault, not from source.

Phase 1 — gRPC skeleton + the spine

Epic 1.A — gRPC transport

  • 1.A.1 Proto skeleton + build (M). Create proto/nuncio/v1 with a Status RPC; wire tonic/prost codegen. AC: proto compiles; Rust server+client stubs generate in the build.
  • 1.A.2 Authenticated daemon server (M). Daemon serves gRPC over loopback; bearer token from the keyring; unauthenticated calls rejected. AC: an unauthenticated Status call is refused; an authenticated one returns real daemon state.
  • 1.A.3 CLI becomes a gRPC client (M). Rewrite nuncio status as a tonic client; fence/retire the legacy JSON-RPC IPC. AC: nuncio status round-trips daemon↔CLI over gRPC in an E2E test.
  • 1.A.4 Events stream skeleton (M). Subscribe server-streaming RPC emitting CoreEvents. AC: the CLI can subscribe and receive a test event; no response/notification interleaving bug (regression test).

Epic 1.B — Store correctness (prerequisite for real data)

  • 1.B.1 FTS-vs-encryption strategy (L). Decide and implement: index plaintext before encryption, or drop body encryption for searchable fields — document the choice. AC: body search returns real hits on the production store path (not via raw-SQL plaintext injection); E2E proves it.
  • 1.B.2 FTS backfill at migration (M). Backfill the FTS index so messages saved before first search are searchable. AC: a message saved before any search call is found by a later search.
  • 1.B.3 Fix salvage schema mismatch (M). Recovery restores filter rules against the real schema. AC: a recovery test restores a nonzero rule count.
  • 1.B.4 Transient error ≠ corruption (M). Distinguish SQLITE_BUSY/pool timeouts from genuine corruption; never salvage/delete the live DB on a transient error. AC: an injected transient error does not trigger salvage/deletion; a genuinely corrupt DB still does.

Epic 1.C — The spine (one account, real mail)

  • 1.C.1 Persistent DB path (M). Daemon opens a real store (e.g. ~/.nuncio/…); remove HeadlessRunner::ephemeral() from the CLI/production path. AC: account add → restart daemon → account list shows the account.
  • 1.C.2 Account setup + credentials (M). Collect IMAP/SMTP config; store the password in the keyring only. AC: account config persists; the password is never written to SQLite or source; only a keyring reference is stored.
  • 1.C.3 Real IMAP fetch → store loop (L). Replace the status-flag "sync" with a real fetch loop in the daemon using ImapEngine. AC: against a mock IMAP server, messages land in the store and sync status reflects reality (no stuck Syncing).
  • 1.C.4 Read path over gRPC (M). List folders/messages, read a body, mark read — via CLI over gRPC. AC: offline E2E does list → read → mark-read against the mock server.
  • 1.C.5 Real SMTP send via outbox (L). Delete the "Simulate remote IMAP/JMAP mutation execution" worker; real send with retry/backoff. AC: against a mock SMTP server a message actually sends; a failure retries; there is no fabricated "Message sent" when it did not.
  • 1.C.6 Mock protocol server harness (M, enables 1.C.3–1.C.5 E2E). Stand up offline mock IMAP/SMTP (and wiremock scaffolding for later JMAP/CalDAV). AC: the full fetch → store → read → send E2E runs offline in CI.

Phase 1 exit: a real account syncs and sends end-to-end through the CLI over gRPC, persisted across restarts, tested entirely offline, with CI green.

Clone this wiki locally