mxr-development
GitHubmxr项目开发技能,涵盖源码修改、架构设计、测试验证、IPC通信及多客户端(CLI/TUI/Web)功能实现。提供构建验证流程、Crate边界定义及发布规范,指导全栈开发工作。
Trigger Scenarios
Install
npx skills add planetaryescape/mxr --skill mxr-development -g -y
SKILL.md
Frontmatter
{
"name": "mxr-development",
"description": "Use when changing mxr source, architecture, docs, tests, IPC, daemon handlers, store\/search\/sync\/semantic behavior, provider adapters, TUI\/web clients, release flow, or repo-level development process."
}
mxr Development
This skill holds the context that used to bloat always-on agent files. Load it only for source/docs/process work.
Product shape
- Email modes: the product model is five modes (Messages, To do, Updates, Reading, Archive) plus Now, per
docs/blueprint/22-email-modes.md(D107 to D114). Build them in its phase order. A phase ships with the site pages and repo docs its Docs line names, and user docs never describe a mode behaviour before it ships. - Local-first: SQLite is canonical, search indexes are rebuildable, and core mail works offline.
- Daemon-backed: TUI, CLI, web, and scripts are clients over Unix-socket IPC.
- CLI-first: new capabilities land in CLI at the same time as daemon support, with stable JSON/JSONL.
- Client parity: a user-facing daemon capability is incomplete until CLI, TUI, web, and MCP expose it. Interaction design may differ; capability may not silently disappear in one client.
- Provider-agnostic core: Gmail/IMAP/SMTP/Outlook behavior maps into the internal model in adapters.
- Compose uses
$EDITORwith YAML frontmatter plus markdown body. - Reader mode is plain text first; no inline images in the terminal rendering path.
Build and verify loop
For feature work:
- Implement in the narrowest crate surface.
- Run
scripts/cargo-test -p <crate> --tests. - Run
cargo build -p mxr. - Restart only the daemon owned by this worktree or isolated test profile. Verify its PID and runtime paths before stopping it; never stop all mxr daemons on a shared machine. Build in the shared queue, then launch the built binary with
daemon --foregroundoutside the queue. - Drive the feature through the CLI, preferably with
--format json. - Inspect daemon state with
mxr events,mxr logs,mxr doctor, andmxr activity. - Query persisted state back through the CLI, such as
mxr search ... --format json.
If the daemon will not start, check cargo build -p mxr, pgrep -af 'mxr|cargo', mxr daemon --foreground, and stale socket files.
Client and mutation contract
- TUI and CLI must use the same daemon request; avoid client-only capabilities.
- Every reusable daemon capability needs a CLI surface. TUI/web support should layer on it.
- Destructive or batch mutations need a dry-run/preview path before commit.
- Dry-run selection/query logic must match the real mutation path.
- Read/list/status/search/export surfaces must keep structured output pipeable.
Crate boundaries
coredepends on no internal crates.protocoldepends only oncore.- Provider crates depend on
coreplus shared mail utility crates such asmail-parseandoutbound. storeandsearchdepend only oncore.semanticowns embeddings/dense retrieval and must not depend on daemon, TUI, or provider crates.llmowns provider clients and prompt/result DTOs; higher layers pass plain data in.relationshipmay depend oncore,store,reader, andllm; not protocol/daemon/client/sync/search/semantic/provider crates.safetymay depend oncore,reader, andrelationship; not store/protocol/daemon/clients/sync/search/semantic/provider crates.syncdepends oncore,store, andsearch.daemonis the integration point, but talks to providers only throughMailSyncProviderandMailSendProvider.tuiandwebare clients; they must not depend on daemon, store, search, sync, semantic, or provider crates.- Use Cargo dependencies for architecture seams, never
#[path].
See docs/blueprint/01-architecture.md for longer rationale.
Domain invariants
- Semantic search is optional platform behavior layered above core mail runtime.
- Sync stores envelopes/bodies and commits lexical search before a sync batch finishes.
- Semantic chunks may be persisted during sync, but embeddings/ANN refresh only happen when semantic is enabled or explicitly reindexed.
- Hybrid search keeps BM25 exactness and adds dense recall with RRF.
- Activity log rows are local-only personal data. Never sync or transmit them, never store credentials/full bodies/attachment bytes, and write only through
state.activity.record(...). INSERT OR REPLACEcan triggerON DELETE CASCADE; preferINSERT ... ON CONFLICT UPDATEfor parent rows with dependents.mxr reset --hard/mxr burnwipe runtime state only by default; preserve config and credentials unless explicitly requested.
Local gates vs CI
CI is the workspace gate. scripts/ci_change_scope.sh selects Rust, web, docs and expensive checks from the changed paths. The workflow runs the applicable workspace checks; a docs-only PR does not run the Rust and web suites. Draft PRs also skip the two workspace test jobs and Web E2E until marked ready. Inspect .github/workflows/ci.yml and the final ready-PR checks rather than treating a skipped job as validation. Do not run --workspace clippy, cargo doc --workspace, cargo nextest run --workspace or cargo build --workspace --release on the developer machine. Locally run only what is scoped to the change:
scripts/pre-pr-rust-gate(fmt, clippy for touched crates only, boundary script, cargo deny).--listshows the crates it will check;--fullis for reproducing a CI failure you cannot narrow to one crate.scripts/cargo-test -p <crate> --testsfor the crates you touched.
Then push and read the PR checks. If one is red, reproduce that one failure with -p <crate>, fix, push again.
Open the PR as a draft while iterating. Test (fast / no semantic), Test (semantic-local) and Web E2E skip on draft PR pushes (they still run on every push to main and on workflow_dispatch); every other check still runs on each push, so you still get fast fmt/clippy/type feedback. Each of those three is the most expensive job in the matrix, and re-running the full set on every rebase/fixup push during iteration is most of the Blacksmith bill. Mark the PR ready for review (gh pr ready <N>) once it's genuinely final, which triggers the full suite once before merge; merge once it's green. If a fix is still needed after marking ready, push it and expect one more full run — that's expected and fine, just don't flip ready back and forth to dodge the gate.
Release shorthand
Releases go through release-please. ship it means run the whole chain below, including merging the release PR, so the manifest, Cargo.toml, CHANGELOG.md and tags never drift. Never create, push or move a v* tag by hand; never overwrite a tag or GitHub release. The flow is built so the laptop never rebuilds what CI already built.
- Preflight.
git fetch origin --tags, then compare.release-please-manifest.jsononorigin/mainwithgit tag -l 'v*' --sort=-v:refname | head -1. If they disagree, land a PR that sets the manifest to the latest tag's version first. Also checkgh pr list --head release-please--branches--main: an open release PR whose version is already tagged is stale; close it and let release-please regenerate it. - Commit release-ready changes on a branch with conventional commits. Run the scoped local gates above, nothing wider. Two separate gates decide whether a release PR appears. First,
release-please.ymlruns its job only whenscripts/release_change_scope.shfinds an artifact-affecting change since the last tag (crates/,apps/web/, real Cargo dependency changes, release workflows/scripts), or when the push is a merged release PR; otherwise the job is skipped. Second, release-please itself only opens a PR for releasable commits:feat:orfix:(pre-1.0 both bump the patch) or a breaking change (feat!:or aBREAKING CHANGE:footer, a minor bump pre-1.0).chore:/docs:/ci:alone do not release. - Push, open the PR, and wait on it:
gh pr checks <N> --watch. - Merge with
gh pr merge <N> --squash --admin(self-approval is blocked,--adminbypasses only the review). - Wait for the
release-please.ymlrun on the merge commit (gh run list --workflow=release-please.yml --commit <sha>), then forrelease-please--branches--mainto show the new version. If the job was skipped, step 1's conditions were not met. If it failed on "Require release token" or a 401,RELEASE_PLEASE_TOKENis missing or expired: stop and ask BK to rotate the PAT. Never set that secret fromgh auth token. Once the release PR is current and green, merge it withgh pr merge <N> --squash --admin. That merge makes release-please pushv{version}with the PAT, which starts the tag-driven release workflow. - Wait for the release workflow on the tag, not for a
mainrun:gh run list --workflow=release.yml --json databaseId,headBranch,status,conclusionand pick the row whoseheadBranchisv{version}, thengh run watch <id>. If no run exists for the tag, the tag was created without the PAT; re-run on the existing tag withgh workflow run release.yml --ref v{version}rather than re-tagging. release-please has already created the GitHub release as a draft, so nothing is visible or Latest yet. The workflow builds the macOS and Linux binaries (cargo build --release --locked -p mxrfrom a clean checkout), uploads them to the draft, publishes it as Latest only after every asset is attached, then pushes the Homebrew formula. A run that fails before publication leaves an unpublished draft, not a broken Latest (a re-run on an already published release writes nothing and fails on purpose if its rebuilt checksums differ, so Homebrew is not updated from them): fix and re-run the failed jobs (gh run rerun <id> --failed), see "What a failed release run leaves behind" in the blueprint.gh release view v{version} --json isDraft,assetsshows where it is. - Verify on this machine with downloads only; no step here compiles anything:
- Homebrew (the real install):
brew update && brew upgrade mxr;mxr versionmust report the new release. - install.sh:
D="$(mktemp -d)"; MXR_INSTALL_DIR="$D/bin" ./install.sh v{version}; "$D/bin/mxr" version; rm -rf "$D". It downloads the release tarball and checks the sha256. - cargo channel: covered by step 5. The release workflow's build-binaries job compiled
-p mxr --release --lockedon this exact tag from a clean checkout, which is the same compilecargo install --gitwould do. Do not runcargo install --git ...locally: it is a cold release build of the whole workspace and takes every core for 10+ minutes to re-prove a passed CI job.
- Homebrew (the real install):
- Converge this machine on Homebrew only. The sole
mxron PATH must be Homebrew's: remove any~/.cargo/bin/mxr(cargo uninstall mxr) and any~/.local/bin/mxrleft by past install.sh runs, then confirm withwhich -a mxr. Multiple mxr binaries on PATH carry different build ids, and every invocation restarts the daemon to "match the current binary", ping-ponging the daemon between versions. - Report: the version, tag, release URL, release workflow run URL, and each install check's
mxr versionoutput. Confirm the manifest onmainnow equals the new tag.
Source of truth: docs/blueprint/17-release-pipeline.md and the checked-in GitHub workflows.
Useful docs
docs/blueprint/- requirements, architecture, decisions.docs/blueprint/15-decision-log.md- settled decisions.docs/activity-log.md- activity table lifecycle and privacy rules.docs/implementation-journey.md- historical context and superseded plans.
Version History
-
d1f2b49
Current 2026-10-18 18:36
新增邮件五模式产品形态说明;优化构建验证循环中的守护进程重启与状态检查逻辑,提升安全性与稳定性。
-
a085c37
2026-09-27 12:44
优化发布流程:集成 release-please 完整链,增加预检和 stale PR 检查;精简本地 shipping 路径,移除冗余的 CI 重复构建步骤,聚焦作用域门控。
- 803ee56 2026-08-06 09:01
- c4ada04 2026-07-30 20:15


