Repository navigation
STORY WORKFLOW
You have been assigned one GitHub issue ("story") from the Nuncio roadmap and are working on your own machine. This is the canonical protocol for doing it successfully and getting it merged. Every story issue links here — read this first, then the issue.
Product-owner / reviewer: the coordinating session (referred to as "the PO"). You implement; the PO reviews and merges. Do not merge your own work.
-
CLAUDE.md— what the repo is, build/test/gates, conventions. -
docs/ROADMAP.md— where your story fits (milestones M1–M7). -
docs/ARCHITECTURE.mdanddocs/adr/0001-engine-first-grpc-architecture.md— the engine-first, daemon-owns-everything, gRPC-contract model. -
docs/DEVELOPING-IN-RUSTROVER.md/docs/RUNNING.md— running the daemon + CLI locally.
A story is done only when all of these hold:
- engine + proto (if the API surface changes) + CLI command + offline E2E test exist together and prove the capability end to end.
- The full local gate is green — that is
cargo fmt --all -- --check,cargo check-all(clippy--all-targets --workspace -D warnings, zero warnings), andcargo test-all(cargo test --workspace), run as three separate commands (there is no combinedcargo verifyalias — Cargo aliases can't chain subcommands). 0 ignored tests. -
No fabricated success. A path that cannot do the real thing returns an
honest
Unimplemented/error — never canned data, never a fake "sent"/"ok". (Anti-pattern to never imitate: a client that logs "fetching…" and returns a hardcoded result; a test that asserts on fabricated output.) - All external protocols are mocked (
wiremock/Mock*Backend); live network calls in tests are forbidden. Never connect to a real server. - The proto contract-stability test passes (see §5).
- Rust edition 2021. No
unsafe. Nounwrap/expect/panic/todoin non-test code (tests are exempt via each crate's#![cfg_attr(test, allow(...))]). Usethiserrorerror types. -
Comment convention: code comments (
.rs/.proto) state the code's intent/constraints/rationale ONLY. No backlog/story numbers, no GitHub issue refs (#NNN,GH-,Refs #), no "Phase N"/"story"/"backlog" breadcrumbs in comments. Keep genuine technical rationale (RFC citations, algorithm notes, why-not-X). That traceability lives in git history + PRs. - Toolchain is pinned (
rust-toolchain.toml= 1.97.1). Green locally == green in CI. The pre-commit hook runs the full gate.
Every gRPC service MUST be mounted behind its own BearerAuthInterceptor.
See the doc comment in nuncio.proto and the mounting code in
crates/nunciod/src/grpc.rs (serve_on_listener_with_overrides). If you add a
service, wrap it exactly like Mail/Filters. Your E2E must assert that an
unauthenticated call to your service is rejected (mirror
crates/nunciod/tests/grpc_auth_test.rs).
-
A full API vertical (proto → daemon → CLI → E2E): the Mail vertical is
the reference.
service Mailincrates/nuncio-proto/proto/nuncio/v1/nuncio.proto;MailGrpcService+MailEngineOverrides+serve_on_listener_with_overridesincrates/nunciod/src/grpc.rs;connect_mailincrates/nuncio-proto/src/client.rs;handle_*/connect_mail_clientincrates/nuncio-cli/src/runner.rs; the capstonecrates/nunciod/tests/spine_e2e_test.rs. -
An engine backend + transport (real protocol client + mock + trait): the
Calendar engine (
crates/nuncio-cal/src/{backend.rs,caldav.rs,parser.rs}) mirrorsnuncio_mail::MailBackend; its wiremock test iscrates/nuncio-cal/tests/wiremock_caldav_test.rs. The JMAP wiremock testcrates/nuncio-mail/tests/wiremock_jmap_test.rsis the protocol-level template. -
Store CRUD: mirror
DatabaseEngine::save_emailand the calendar CRUD incrates/nuncio-store/src/db.rs. -
Changing the proto → regenerate the golden. Any change to
nuncio.protowill (by design) failnuncio-proto'scontract_stability::descriptor_matches_committed_goldentest. The failure message incrates/nuncio-proto/src/lib.rsprints the exact recipe: rebuild-p nuncio-proto, copy the freshly generatednuncio_v1_descriptor.binfrom this build'sOUT_DIRovercrates/nuncio-proto/proto/nuncio/v1/descriptor.bin, re-test, and commit the new golden with your change. Keep additions additive withinnuncio.v1.
Several stories add to the same nuncio.proto + its golden. To avoid
conflicts, only one proto-touching story is "in flight" at a time. Before
starting a story that changes nuncio.proto, confirm with the PO that no other
proto story is open. Non-proto stories (engine-only, CLI-only) can run in parallel.
- Branch from
dev:story/<milestone>-<slug>(e.g.story/m1-calendar-api,story/m6-zeroize-keys). Never commit todevormaindirectly. - TDD where practical. Keep commits Conventional (
feat(scope): …,fix(scope): …), imperative, <72-char subject, no AI attribution, no issue refs in code (aRefs #NNNin the commit message is fine). - Before pushing, make sure
cargo fmt --all -- --check,cargo check-all, andcargo test-allare all green. The pre-commit hook enforces this. - Open a PR to
dev(never tomain). In the PR body: link the issue, summarize what you built, paste the gate output (fmt/clippy/test results + test counts) as evidence, and list any decisions or follow-ups. If you found a real bug outside your scope, file a new issue — do not silently fix or ignore it. - The PO reviews spec compliance + code quality (and, for auth/crypto/transport, an adversarial pass). Address Critical/Important findings. The issue closes only after the review passes — never on a green build alone.
Report honestly: what's blocking, the exact error, and what you tried. Do not
weaken the auth invariant, skip the hook (--no-verify), fabricate data, or hit
a live server to make something pass. An honest BLOCKED beats a green lie.