Files
nuncio/docs/BACKLOG.md
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

128 lines
7.5 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.
# 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`](ROADMAP.md).
Ordered, executable stories for the first two phases of [`ROADMAP.md`](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
`CoreEvent`s. **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.