Files
James Maes c6b69bb1ec fix(build): remove unfixable cargo verify alias, reword docs
Cargo aliases are a single command argv and cannot chain subcommands, so
`cargo verify = ["fmt --all -- --check", "check-all", "test-all"]` tried to
run a literal subcommand named "fmt --all -- --check" and always errored.
The pre-commit hook already runs the three checks as separate commands, so
it was unaffected. Drop the broken alias, drop the matching RustRover run
configuration, and reword every doc that told readers to run `cargo verify`
to instead run fmt/check-all/test-all as three commands.
2026-07-29 11:34:06 -05:00

7.5 KiB
Raw Permalink Blame History

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.