AgentSkillsCN

moon-v2

Moon v2 构建系统专家,专为 Rust 仓库提供支持。适用于 Moon v2 的搭建、CI/CD 管道设计、任务规划、缓存策略、远程缓存,以及与 Rust 工作流的无缝集成。涵盖 Moon CI、Moon Run、工具链配置、工作区配置、三层缓存(sccache + bazel-remote + Cargo),并深入实践 Rust 的各类功能型模式,包括 Clippy、Nextest,以及严格的 Lint 门控(对源代码实行零容忍标准)。

SKILL.md
--- frontmatter
name: moon-v2
description: "Moon v2 build system expert for Rust repositories. Use for Moon v2 setup, CI/CD pipelines, task design, caching, remote cache, and integration with Rust workflows. Covers moon ci, moon run, toolchains config, workspace config, 3-layer caching (sccache + bazel-remote + Cargo), and functional Rust patterns including clippy, nextest, and strict lint gates (zero tolerance for source code)."
allowed-tools: ["bash"]
version:1.2.0
model: sonnet
user-invocable: true
argument-hint: [moon command, workflow question, or CI/CD setup]
jsonl
{"kind":"meta","skill":"moon-v2","version":"1.2.0","format":"markdown-with-embedded-jsonl","scope":"moon-build-system","core_truths":["ci_gate_is_canonical","source_lint_zero_tolerance","tests_lenient"]}
{"kind":"claude_best_practice","id":"ci_gate_is_canonical","text":"CI GATE IS CANONICAL (MANDATORY): moon ci is the canonical gate for Moon v2. moon run :ci is only valid as a repo-specific alias when explicit root :ci task exists. Run tests in pipeline; don't enforce strict clippy quality on test code."}
{"kind":"claude_best_practice","id":"source_lint_zero_tolerance","text":"SOURCE LINT ZERO TOLERANCE (MANDATORY): Source code must have 0 clippy issues (no errors, no warnings, no clippy lints). moon ci fails if ANY clippy issue detected in source. Tests are lenient (must compile, no strictness)."}
{"kind":"rule","id":"moon_ci_preferred","level":"warn","text":"Prefer moon ci over moon run :ci. moon ci is canonical in v2. Only use moon run :ci when a root :ci task is explicitly defined in repo.","bans":["moon run :ci as primary gate when root :ci task does not exist"],"preferred":["moon ci","moon run :<specific-task>"],"notes":["moon ci respects runInCI automatically. moon run :ci requires explicit :ci task definition to behave as expected."]}
{"kind":"rule","id":"lint_src_zero_tolerance","level":"error","text":"ZERO CLIPPY ISSUES IN SOURCE CODE (MANDATORY): moon ci gate must fail if clippy reports ANY issue in source (errors, warnings, or lints). Source lint uses -W clippy::all to catch everything. Tests are lenient (compile-only requirement).","bans":["allowing clippy warnings in source code","allowing clippy lints in source code","allowing clippy errors in source code","running moon ci with any clippy issues in source"],"preferred":["moon run :lint-src (catches all clippy issues)","cargo clippy --lib --bins --examples -- -D warnings -W clippy::all"],"notes":["Source code must be flawless (0 issues). Tests only need to compile. Fix ALL clippy issues before CI."]}
{"kind":"rule","id":"lint_src_separate_from_tests","level":"warn","text":"Separate lint-src task from test tasks. Use dedicated lint-src task that runs clippy only on source targets (--lib --bins --examples) and excludes tests. Tests should run without strict clippy quality gates.","bans":["adding clippy --tests-allowed to build/test tasks"],"preferred":["moon run :lint-src","cargo clippy --lib --bins --examples -- -D warnings -W clippy::all"],"notes":["Tests are for correctness; linting is for code quality. Don't block test work due to test clippy warnings."]}
{"kind":"rule","id":"functional_rust_posture","level":"warn","text":"Maintain functional Rust posture with zero tolerance: deny warnings (-D warnings) + deny all clippy lints (-W clippy::all). Source code must be flawless.","bans":["allowing unwrap/expect in production code","suppressing clippy warnings or lints","ignoring any clippy issue in source code"],"preferred":["-D warnings -W clippy::all"],"notes":["ALL clippy lints are caught and treated as failures in source code. Tests are lenient (compile-only)."]}
{"kind":"rule","id":"test_tasks_in_pipeline","level":"info","text":"RUN TEST TASKS IN PIPELINE: moon ci should include test tasks by default. Tests run with NO strict clippy enforcement (compile-only). Tests are exempt from source lint zero-tolerance policy.","bans":["excluding tests from pipeline","requiring tests to pass strict lint rules"],"preferred":["moon ci includes :test by default","tests run with cargo test (compile-only)"],"notes":["Tests verify behavior. Linting checks code quality. Don't block test work due to test lint issues."]}
{"kind":"pattern","id":"moon_ci_workflow","text":"MOON CI WORKFLOW (v2 canonical)","example":"moon ci\n# CI runs affected targets respecting runInCI\n# Check CI report action for PR summaries","notes":["moon ci detects affected files automatically. All tasks with runInCI=true run (or runInCI unset). v2: affected calculation excludes graph relations unless --include-relations flag. Run report action adds PR comments with timing and status."]}
{"kind":"pattern","id":"lint_src_workflow","text":"LINT-SRC TASK DESIGN","example":"moon run :lint-src\n# In .moon/tasks/all.yml:\n# lint-src:\n#   command: 'cargo clippy --lib --bins --examples -- -D warnings -W clippy::all'\n#   options:\n#     runInCI: true\n#   inputs:\n#       - '@globs(sources)'\n#   toolchains:\n#     - rust\n# This excludes tests via --tests-allowed=false clippy default","notes":["Use -W clippy::all to catch ALL clippy issues (zero tolerance). Source code must have 0 issues (errors, warnings, lints). Tests are lenient (compile-only). Run moon run :lint-src for CI, or cargo clippy --lib --bins --examples -- -D warnings -W clippy::all locally."]}
{"kind":"pattern","id":"functional_rust_clippy","text":"FUNCTIONAL RUST CLIPPY (ZERO TOLERANCE)","example":"cargo clippy --lib --bins --examples -- -D warnings -W clippy::all","notes":["Deny ALL clippy lints: -D warnings -W clippy::all (zero tolerance). Every clippy issue (error, warning, lint) causes CI failure. Source code must be flawless (0 issues). Tests are lenient (compile-only, no clippy enforcement)."]}
{"kind":"pattern","id":"moon_v2_workspace_config","text":"MOON V2 WORKSPACE CONFIGURATION","example":"# .moon/workspace.yml\nvcs:\n  client: git\n  provider: github\n  defaultBranch: main\n  versionConstraint: '>=1.0.0'\nremote:\n  host: 'grpcs://127.0.0.1:9092'  # Local bazel-remote cache (use 127.0.0.1 not localhost)\n  cache:\n    compression: 'zstd'\n    instanceName: 'your-repo-name'\n    verifyIntegrity: false  # Skip for local trusted cache (faster)\n    localReadOnly: false  # Allow uploads from local dev\npipeline:\n  installDependencies: true\n  syncWorkspace: true\nhasher:\n  optimization: 'accuracy'  # accuracy for CI, performance for dev (optional)\n  walkStrategy: 'vcs'\ntelemetry: false","notes":["v2 uses 'remote' (not 'unstable_remote'). For local bazel-remote, use 127.0.0.1:9092 (gRPC) + HTTP on 9090 for health. 'remote.cache.verifyIntegrity: false' for trusted local cache. 'remote.cache.localReadOnly: false' allows uploads from local dev. sccache runs independently as Layer 1 cache."]}
{"kind":"pattern","id":"moon_v2_toolchains_config","text":"MOON V2 TOOLCHAINS CONFIGURATION","example":"# .moon/toolchains.yml\nrust:\n  version: '1.84.0'  # Pin Rust version\n  bins:\n    - 'cargo-nextest@latest'  # Pre-built via cargo-binstall\n    - 'cargo-audit@latest'\n  syncToolchainConfig: true  # Syncs to rust-toolchain.toml","notes":["v2 uses .moon/toolchains.yml (plural), not .moon/toolchain.yml. 'unstable_' prefix removed from toolchains. 'bins' installs binaries with cargo-binstall for speed. 'syncToolchainConfig' writes rust-toolchain.toml automatically."]}
{"kind":"pattern","id":"moon_v2_tasks_config","text":"MOON V2 TASKS CONFIGURATION (.moon/tasks/all.yml)","example":"# .moon/tasks/all.yml\n# Core lanes inherited by all projects\nfmt:\n  command: 'cargo fmt --all --check'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n    inputs:\n      - '@globs(sources)'\n\nlint-src:\n  command: 'cargo clippy --lib --bins --examples -- -D warnings'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n  inputs:\n      - '@globs(sources)'\n\ncheck:\n  command: 'cargo check --workspace'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n    inputs:\n      - '@globs(sources)'\n\ntest:\n  command: 'cargo nextest run --workspace'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n    inputs:\n      - '@globs(sources)'\n      - '@globs(tests)'\n\nbuild:\n  command: 'cargo build --workspace --release'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n    inputs:\n      - '@globs(sources)'\n      - '@globs(tests)'\n    outputs:\n      - 'target/release/<binary-name>'\n\naudit:\n  command: 'cargo audit'\n  toolchains:\n    - rust\n  options:\n    runInCI: true\n    inputs:\n      - 'Cargo.lock'\n      - 'Cargo.toml'\n\n# Composite lanes\nci:\n  command: 'moon ci'  # Canonical CI gate\n  options:\n    runInCI: true\n\nquick:\n  command: 'moon run :fmt :lint-src :check'  # Fast dev loop\n\nlint:\n  command: 'moon run :lint-src'  # Strict source linting\n","notes":["v2 uses .moon/tasks/all.yml for inherited tasks. 'toolchains' setting per-task (not 'platform'). Deep merge for inheritance. 'inputs'/'outputs' use @globs references defined in workspace fileGroups. Composite tasks like :ci and :quick don't define commands—just call other tasks."]}
{"kind":"pattern","id":"moon_v2_ci_workflow","text":"MOON V2 CI IN GITHUB ACTIONS","example":"# .github/workflows/ci.yml\nname: CI\non:\n  push:\n    branches: [main]\n  pull_request:\njobs:\n  ci:\n    name: CI\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - uses: moonrepo/setup-toolchain@v0\n      - run: moon ci\n      - uses: moonrepo/run-report-action@v1\n        if: success() || failure()\n        with:\n          access-token: ${{ secrets.GITHUB_TOKEN }}","notes":["fetch-depth: 0 is CRITICAL for moon ci's affected file detection. moonrepo/setup-toolchain@v0 installs moon and toolchains. moonrepo/run-report-action@v1 posts PR comment with results. moon ci respects runInCI automatically."]}
{"kind":"pattern","id":"local_bazel_remote","text":"LOCAL BAZEL REMOTE CACHE (Layer 2 of 3-layer caching)","example":"# Start bazel-remote (Layer 2: Moon task outputs AC+CAS cache)\nbazel-remote --dir ~/.cache/bazel-remote --max-size 500 --storage_mode zstd --grpc_address 127.0.0.1:9092 --http_address 127.0.0.1:9090 &\n\n# sccache (Layer 1: compiler artifacts) runs independently via ~/.cargo/config.toml wrapper\n# Moon remote config points to Layer 2: grpcs://127.0.0.1:9092\n# 3-Layer cache: sccache -> bazel-remote -> Cargo incremental target/\n# Layer 1: sccache (in-memory, ~9GB, 12 jobs, fastest rustc artifact hits)\n# Layer 2: bazel-remote (disk-based, AC+CAS, ~500MB, Moon task outputs)\n# Layer 3: Cargo (per-repo, incremental compilation in target/)","notes":["Start bazel-remote with 127.0.0.1 (not localhost). HTTP on 9090 for health, gRPC on 9092 for AC+CAS. sccache configured in ~/.cargo/config.toml rustc-wrapper. Moon caches to bazel-remote (Layer 2), sccache caches rustc artifacts (Layer 1). Use remote.cache.verifyIntegrity: false for trusted local cache (faster). remote.cache.localReadOnly: false allows uploads from local dev."]}
{"kind":"gate","id":"moon_ci_gate","commands":["moon ci"],"notes":"Canonical Moon v2 CI gate. Runs affected targets respecting runInCI. All tasks with runInCI=true run automatically. Use moonrepo/run-report-action@v1 for PR summaries. Pipeline green only when no clippy errors."]}

Moon v2 Build System Expert

You are an expert in Moon v2 for Rust repositories. Moon is a build system that orchestrates tasks, manages dependencies, and provides CI/CD primitives — not a replacement for Cargo. Your role is to help users configure Moon workspaces, design tasks, set up CI pipelines, and integrate Moon with Rust workflows using functional Rust patterns.

Core Mental Model

  • Moon orchestrates Cargo — Moon runs cargo commands but does not replace Cargo workflows
  • Rust workspace = single Moon project — For Cargo workspaces, configure a single Moon project covering all crates (avoids duplicate cargo --workspace runs)
  • Canonical CI gatemoon ci is the recommended primary CI gate in Moon v2
  • Affected-based executionmoon ci only runs tasks affected by touched files (plus graph relations with --include-relations)
  • Toolchains — v2 uses .moon/toolchains.yml (plural), not .moon/toolchain.yml
  • Remote cache — Optional AC+CAS via Bazel Remote API (supports grpc:// and http://)
  • Deep merge inheritance — v2 merges global task configs sequentially (not shallow), respecting merge options

Workspace Configuration (.moon/workspace.yml)

Moon v2 workspace configuration in Rust repositories:

yaml
# .moon/workspace.yml
vcs:
  client: git
  provider: github
  defaultBranch: main

versionConstraint: '>=1.0.0'  # Enforce Moon binary version

remote:  # Optional: bazel-remote AC+CAS cache
  host: 'grpcs://localhost:9092'  # or 'grpcs://team-cache:9092'
  cache:
    compression: 'zstd'  # or 'none' for I/O-bound setups
    instanceName: 'your-repo-name'  # Distinguishes repos
    verifyIntegrity: true  # Validate digests on download

pipeline:
  installDependencies: true
  syncWorkspace: true

hasher:
  optimization: 'accuracy'  # Use 'performance' for dev speed (tradeoff: hash precision)

telemetry: false

Key Settings Explained

SettingPurposeRust Context
vcs.defaultBranchBranch for moon ci base comparisonMust match your repo's default
versionConstraintLock Moon binary version across machinesPrevents drift in CI
remote.cache.compressionCompress cached artifactszstd reduces bandwidth, none faster for local NVMe SSD
remote.cache.verifyIntegrityValidate blob digests on downloadPrevents corruption, slight perf cost
hasher.optimizationHash strategy tradeoffaccuracy = use lockfile versions (slower), performance = use manifest (faster)
remote.cache.localReadOnlyDev mode optimizationLocal dev only downloads cache, never uploads
pipeline.installDependenciesAuto-run InstallWorkspaceDeps/InstallProjectDepsUseful for Cargo, but optional
pipeline.syncWorkspaceAuto-run SyncWorkspace actionSyncs hooks, codeowners, VCS state

Toolchains Configuration (.moon/toolchains.yml)

Moon v2 uses .moon/toolchains.yml (plural) instead of v1's .moon/toolchain.yml:

yaml
# .moon/toolchains.yml
rust:
  version: '1.84.0'  # Pin Rust version (matches rust-toolchain.toml)
  bins:                     # Binaries installed via cargo-binstall (faster than cargo install)
    - 'cargo-nextest@latest'
    - 'cargo-audit@latest'
  syncToolchainConfig: true  # Sync version to rust-toolchain.toml automatically

Toolchain Settings Explained

SettingPurposeNotes
rust.versionPin Rust across all machinesMust align with rust-toolchain.toml
rust.binsPre-install global binariesUses cargo-binstall for speed (pre-built binaries)
rust.syncToolchainConfigAuto-sync Rust versionWrites rust-toolchain.toml automatically
unstable_ prefixRemoved in v2Toolchains are stable; no unstable_rust needed

Important:

  • Moon v2 toolchains use simple keys (e.g., rust, javascript), not nested under node/unstable_
  • All toolchains are stable now except Python (still uses unstable_python)

Task Configuration (.moon/tasks/all.yml)

Moon v2 uses .moon/tasks/all.yml for tasks inherited by all projects. Deep merge inheritance ensures all configs merge in sequence:

yaml
# .moon/tasks/all.yml

# Core lanes (inherited by all projects)
fmt:
  command: 'cargo fmt --all --check'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - '@globs(sources)'

lint-src:
  command: 'cargo clippy --lib --bins --examples -- -D warnings -W clippy::all'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - '@globs(sources)'

check:
  command: 'cargo check --workspace'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - '@globs(sources)'

test:
  command: 'cargo nextest run --workspace'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - '@globs(sources)'
    - '@globs(tests)'

build:
  command: 'cargo build --workspace --release'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - '@globs(sources)'
    - '@globs(tests)'
  outputs:
    - 'target/release/<binary-name>'

audit:
  command: 'cargo audit'
  toolchains:
    - rust
  options:
    runInCI: true
  inputs:
    - 'Cargo.lock'
    - 'Cargo.toml'

# Composite lanes (call other tasks)
ci:
  command: 'moon ci'  # Canonical CI gate
  options:
    runInCI: true

quick:
  command: 'moon run :fmt :lint-src :check'  # Fast dev loop

lint:
  command: 'moon run :lint-src'  # Strict source linting

v2 Changes from v1

Areav1v2Impact
Config file.moon/toolchain.yml.moon/toolchains.ymlFile renamed (plural)
Remote settingunstable_remoteremoteSimplified top-level key
Workspace tasks.moon/tasks.yml.moon/tasks/all.ymlFile renamed for inherited tasks
InheritanceShallow mergeDeep mergeBetter composition, predictable merges
Task configoptions.platformoptions.toolchainsRenamed for clarity
Affected detectionAlways includes relationsChanged files only (unless --include-relations)More predictable CI
runInCIOnly respected in moon ciRespected across moon ci, moon run, moon checkMore consistent behavior
local task settinglocal: trueUse preset: 'server'Clarifies persistent dev servers
Command syntaxComplex shell features in commandUse script for pipes/redirectsSimpler commands only

Task Design Principles

  1. Use toolchains per task — Specifies which toolchain(s) are required (e.g., toolchains: [rust])
  2. Use runInCI correctly — Set to true for CI tasks, false for long-running dev servers (preset: 'server')
  3. Define inputs using @globs() — References file groups defined in .moon/workspace.yml
  4. Use outputs sparingly — Cache specific artifacts (like release binaries), not entire target/ directory
  5. Composite tasks don't define commands — Tasks like :ci and :quick should only list commands or call other tasks

Source vs Test Linting

The lint-src task is designed for source code quality and excludes tests:

bash
# lint-src: ZERO TOLERANCE for ALL clippy issues (source only)
cargo clippy --lib --bins --examples -- -D warnings -W clippy::all

# Test code: lenient, compile-only (no strict clippy enforcement)
cargo nextest run --workspace  # No clippy flags, just runs tests

This aligns with the functional Rust posture:

  • Source linting: Strict (deny warnings, deny critical categories)
  • Test code: No strict quality gates (tests verify behavior)

Functional Rust Patterns

Clippy Configuration for Zero-Tolerance Source Linting

Source code must have 0 clippy issues (errors, warnings, lints — ALL treated as failures):

bash
# Zero-tolerance clippy flags for source code
cargo clippy --lib --bins --examples \
  -D warnings \                    # Deny all warnings as errors
  -W clippy::all \                 # Deny ALL clippy lints (zero tolerance)

Explanation of flags:

FlagPurposeEffect
-D warningsDeny all compiler warningsTreat warnings as errors (zero tolerance)
-W clippy::allDeny ALL clippy lintsEvery clippy lint is treated as failure in source

Zero-Tolerance Policy:

  • Source code: 0 issues (no errors, no warnings, no clippy lints)
    • Every clippy lint, warning, or error causes CI failure
    • Fix ALL issues before proceeding (no suppression, no workarounds)
  • Test code: Compile-only requirement (no strict clippy enforcement)
    • Tests only need to compile
    • No clippy flags applied
    • Test code quality is not blocked by linting

Fast Development Loops

For rapid iteration during development (local checks before committing):

bash
# Ultra-fast: skip clippy, skip build
moon run :fmt :check

# Fast: format + source lint (zero-tolerance), skip build
moon run :fmt :lint-src

# Normal: full validation
moon run :fmt :lint-src :check

Moon CI/CD Workflow

Primary CI Command: moon ci

Why moon ci is canonical in v2:

  • Affected-based execution — Only runs tasks affected by touched files (plus relations with --include-relations)
  • Respects runInCI — Automatically honors task-level CI configuration
  • Graph relations excluded by default — More predictable CI (changed files only)
  • Consistent behavior — Same semantic across moon ci, moon run, moon check

When to Use moon run :ci

The moon run :ci form should only be used when:

  1. A root-level :ci task exists in .moon/tasks/all.yml
  2. You need explicit control over which tasks run (e.g., to run a subset in CI)
  3. The task name conflicts with another tool's primary gate command

Never use moon run :ci as the primary gate when no explicit :ci task exists. It adds unnecessary complexity.

GitHub Actions Integration

Standard Moon v2 CI workflow for Rust repositories:

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  ci:
    name: CI
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # CRITICAL for affected file detection

      - uses: moonrepo/setup-toolchain@v0

      - run: moon ci

      - uses: moonrepo/run-report-action@v1
        if: success() || failure()
        with:
          access-token: ${{ secrets.GITHUB_TOKEN }}

Key configuration points:

SettingWhy RequiredMoon v2 Behavior
fetch-depth: 0moon ci needs full Git historyDetects changed files correctly
setup-toolchain@v0Installs moon and toolchainsUses .moon/toolchains.yml config
moon ciRuns affected targetsRespects runInCI automatically
run-report-action@v1Posts PR commentsAdds workflow summary, timing data

Affected File Detection

moon ci determines affected files by comparing HEAD against base branch (defaulting to vcs.defaultBranch).

v2 change: Graph relations (dependencies and dependents) are not included in affected calculation by default. To include them, use --include-relations flag.

bash
# Default: affected by changed files only
moon ci

# Include graph relations
moon ci --include-relations

3-Layer Caching Strategy (sccache + bazel-remote + Cargo)

For maximum build speed on this machine:

code
Layer 1: sccache (in-memory compiler artifacts)
    ↓
Layer 2: bazel-remote (disk-based AC+CAS cache for Moon)
    ↓
Layer 3: Cargo incremental (per-repo target/ compilation)

Layer 1: sccache (Fastest)

  • Wraps rustc via ~/.cargo/config.toml
  • In-memory cache: ~9GB, 12 parallel jobs
  • Caches rustc artifacts across all Rust projects globally
  • First cache hit: microsecond response time

Layer 2: bazel-remote (Moon task outputs)

  • AC+CAS protocol for Moon tasks
  • Disk-based: ~/.cache/bazel-remote, ~500MB
  • Shared across all Moon-enabled repos
  • HTTP health endpoint: 127.0.0.1:9090
  • gRPC AC+CAS endpoint: 127.0.0.1:9092
  • Compressed with zstd

Layer 3: Cargo incremental (Per-repo)

  • target/ directory in each repository
  • Cargo's built-in incremental compilation
  • Local per-repo compilation state

Cache Flow:

code
rustc -> sccache (check)
         ↓ miss
       Cargo -> build -> target/
         ↓
      Moon -> bazel-remote (check/upload task outputs)
         ↓ miss
       Build task -> upload to bazel-remote

Configuration:

toml
# ~/.cargo/config.toml (Layer 1)
[build]
rustc-wrapper = "/home/lewis/.cargo/bin/sccache"
jobs = 12
bash
# Start bazel-remote (Layer 2)
bazel-remote --dir ~/.cache/bazel-remote --max-size 500 \
  --storage_mode zstd \
  --grpc_address 127.0.0.1:9092 \
  --http_address 127.0.0.1:9090 &
yaml
# .moon/workspace.yml (Moon -> Layer 2)
remote:
  host: 'grpcs://127.0.0.1:9092'
  cache:
    compression: 'zstd'
    verifyIntegrity: false  # Trusted local cache
    localReadOnly: false  # Allow uploads from local dev

Optimization Notes:

  • sccache runs globally (shared across ALL Rust projects)
  • bazel-remote runs per-machine (shared across Moon projects)
  • Each layer serves different purposes:
    • sccache: compiler artifacts (fastest)
    • bazel-remote: Moon task outputs (portable AC+CAS)
    • Cargo: per-repo incremental (local state)

Remote Caching with Bazel Remote

Local Development Cache (Recommended First):

Start local bazel-remote for development speed:

bash
# Start bazel-remote in background (persists to ~/.moon/cache/bazel)
bazel-remote --dir ~/.moon/cache/bazel --max-size 50g --host 0.0.0.0 --port 9092 &
BAZEL_PID=$!

# Moon workspace.yml points to same cache
# remote:
#   host: 'grpcs://localhost:9092'

Benefits:

  • Ultra-fast local cache hits (shared across terminal sessions)
  • Same AC+CAS protocol as team cache (scales easily)
  • No network dependency during development

Team CI Cache (Optional):

Update .moon/workspace.yml to point to shared cache:

yaml
remote:
  host: 'grpcs://team-cache.company.com:9092'
  auth:
    token: 'MOON_REMOTE_TOKEN'  # Environment variable with auth token

Compression tradeoff:

SettingUse CasePerformance Impact
compression: 'none'Local NVMe SSD developmentFastest I/O, no CPU overhead
compression: 'zstd'Network-bound CI, shared cache~2-3x smaller blobs, slower CPU
verifyIntegrity: trueSecurity-critical reposValidates digests, slight perf cost

Cache Output Strategy

DO NOT cache entire target/ directory:

Moon uses tarball-based caching. Caching target/ is not viable:

  • 50GB+ cache artifacts (very slow uploads/downloads)
  • Tarball overhead for massive directories
  • Cargo incremental compilation already provides target/ caching

What to cache instead:

yaml
# Cache release binaries only
build:
  outputs:
    - 'target/release/my-binary'

# Cache test reports or specific artifacts
test:
  outputs:
    - 'target/nextest/reports/**'

Let Cargo handle target/ with its own incremental compilation. Moon caches the final deliverables.

Common Workflows

Initial Setup

bash
# 1. Install moon (via proto or cargo)
curl -fsSL https://moonrepo.dev/install.sh | bash

# 2. Initialize moon workspace in Rust repo
cd /path/to/rust-repo
moon init

# 3. Configure workspace (edit generated .moon/workspace.yml)
# - Set vcs.provider
# - Add remote cache (optional)
# - Set versionConstraint

# 4. Create .moon/tasks/all.yml with Rust task definitions
# See "Task Configuration (.moon/tasks/all.yml)" section above

# 5. Start local bazel-remote (optional but recommended)
bazel-remote --dir ~/.moon/cache/bazel --max-size 50g --host 0.0.0.0 --port 9092 &

Local Development Workflow

bash
# 1. Check and format code
moon run :fmt

# 2. Run source lint (strict, no tests)
moon run :lint-src

# 3. Type check
moon run :check

# 4. Run tests (normal clippy)
moon run :test

# 5. If all pass, build release
moon run :build

CI Workflow (Manual Push)

bash
# 1. Ensure repo is up-to-date
git pull origin main

# 2. Run Moon CI gate (canonical)
moon ci

# 3. Check exit code (moon ci fails on clippy errors)
if [ $? -eq 0 ]; then
  echo "CI passed. Ready to push."
else
  echo "CI failed. Fix issues before push."
fi

CI Workflow with moon run :ci (Custom Targets)

Only use when you need to run specific tasks in CI (e.g., skip build):

bash
# Run only fmt and lint in CI (no tests, no build)
moon run :ci :fmt :lint-src

Migration from Moon v1 (Specific to Your Setup)

Your current setup uses Moon v1 with unstable_remote. To migrate to v2:

v1 Settingv2 SettingMigration Action
.moon/toolchain.yml.moon/toolchains.ymlRename file
unstable_remote.host: 'grpc://localhost:9092'remote.host: 'grpcs://127.0.0.1:9092'Update host (127.0.0.1 vs localhost, add protocol prefix)
.moon/tasks.yml.moon/tasks/all.ymlRename file
No mutex configoptions: { mutex: 'rust_heavy' }Add mutex to heavy tasks (prevents concurrent sccache + bazel-remote overload)

Your specific migration steps:

bash
# 1. Rename toolchain config
mv /home/lewis/src/<project>/.moon/toolchain.yml \
   /home/lewis/src/<project>/.moon/toolchains.yml

# 2. Update workspace.yml remote setting
# Change from:
#   unstable_remote:
#     host: 'grpc://localhost:9092'
# To:
#   remote:
#     host: 'grpcs://127.0.0.1:9092'
#     cache:
#       compression: 'zstd'
#       verifyIntegrity: false  # Trusted local cache
#       localReadOnly: false  # Allow uploads

# 3. Ensure bazel-remote is running
bazel-remote --dir ~/.cache/bazel-remote --max-size 500 \
  --storage_mode zstd \
  --grpc_address 127.0.0.1:9092 \
  --http_address 127.0.0.1:9090 &

# 4. Verify sccache is configured
# Should see: rustc-wrapper = "/home/lewis/.cargo/bin/sccache"
cat ~/.cargo/config.toml | grep rustc-wrapper

Migration from Moon v1 (General)

If migrating from v1 to v2:

v1 Settingv2 SettingMigration Action
.moon/toolchain.yml.moon/toolchains.ymlRename file
unstable_remoteremoteUpdate top-level key
.moon/tasks.yml.moon/tasks/all.ymlRename file
runner.autoCleanCachepipeline.cacheLifetimeSimplified config
runner.*pipeline.*Renamed runner settings to pipeline
constraints.enforceProjectTypeRelationshipsconstraints.enforceLayerRelationshipsRenamed
vcs.managervcs.clientRenamed
vcs.syncHooksvcs.syncRenamed
rust.syncToolchainConfigrust.syncToolchainConfig (same)No change
toolchain.*.disabledtoolchain: null/falseUse null/false instead
project.typeproject.layerRenamed setting
task.options.localtask.options.preset: 'server'Renamed for clarity
runInCI only in moon cirunInCI in all commandsBetter consistency

Breaking CLI changes:

v1 Commandv2 EquivalentNotes
moon query hashmoon hashCommand renamed
moon query touched-filesmoon query changed-filesTerminology updated
--update-cache, -u--force, -fRenamed flag
--profile(removed)Use composite tasks instead
--no-bail(removed)Use moon exec directly
--remote--affected-remoteRenamed flag
moon docker prune --install-toolchain-depsmoon docker prune --installToolchainDependenciesSnake case
moon migrate from-package-json(use extension instead)Removed

Anti-Patterns to Avoid

Anti-PatternProblemMoon v2 Solution
Allowing clippy warnings in sourceNot zero-toleranceUse -W clippy::all in lint-src task (catches ALL clippy issues)
Allowing clippy lints in sourceSource code not flawlessFix ALL clippy issues before CI (no suppression, no workarounds)
Using moon run :ci as primary gateNo explicit :ci taskDefine :ci task in .moon/tasks/all.yml OR use moon ci directly
Caching entire target/Slow uploads, tarball overheadCache specific outputs (release binaries, reports)
Ignoring runInCI in local devInconsistent behaviorrunInCI respected across all commands in v2
Hardcoding paths in scriptsBreaks portabilityUse workspace-relative paths, $MOON_WORKSPACE_ROOT, or @globs() references
Running cargo --workspace in multiple projectsDuplicate workUse single Moon project for Cargo workspace
Not using affected detectionRunning all tasks unnecessarilyUse moon ci for CI, moon run <specific-task> for local
Setting local: true for dev serversConfusing nameUse preset: 'server' or explicit runInCI: false
Complex shell commands in task.commandHard to maintainUse script for pipes/redirects, or keep commands simple
Ignoring v2 behavior differencesAffected calculation, inheritance changesRead migration guide, adapt workflows

Best Practices

Task Design

  • Single Rust project per Cargo workspace — Avoids duplicate cargo --workspace invocations
  • Separate lint-src from test tasks — Maintain zero-tolerance for source, lenient for tests
  • Use -W clippy::all in lint-src — Catches ALL clippy issues in source (zero tolerance)
  • Use inputs and outputs correctly — Leverage Moon's hashing for efficient caching
  • Set runInCI: true for all CI tasks — Ensures tasks run in moon ci without extra configuration

CI Configuration

  • Always use fetch-depth: 0 — Critical for moon ci affected file detection
  • Use moonrepo/setup-toolchain@v0 — Installs Moon and configured toolchains automatically
  • Use moonrepo/run-report-action@v1 — Adds PR comments with timing and status
  • Prefer moon ci over moon run :ci — Canonical gate respects runInCI automatically
  • Pin Moon version — Use versionConstraint to prevent drift across machines

Development Workflow

  • Run moon run :lint-src before committing — Zero-tolerance check for source code (fixes ALL clippy issues)
  • Run moon run :fmt :lint-src :check locally — Fast dev loop before committing
  • Run moon ci before pushing — Validates entire pipeline with affected detection
  • Use local bazel-remote — Dramatically speeds up iteration with shared cache

Performance

  • Set hasher.optimization: 'performance' for local dev — Faster hashing (trades some accuracy)
  • Set remote.cache.compression: 'none' for local NVMe SSD — Faster I/O
  • Set remote.cache.verifyIntegrity: false for trusted networks — Skip digest validation for speed

Troubleshooting

"moon ci: no affected targets"

Cause: No files changed compared to base branch.

Solutions:

  1. Verify you're on the correct branch (git branch)
  2. Check vcs.defaultBranch in .moon/workspace.yml matches your repo
  3. Use --head/--base flags or MOON_HEAD/MOON_BASE environment variables

"moon ci: target already running"

Cause: Target process already running (from previous incomplete run).

Solutions:

  1. Check for background Moon processes: ps aux | grep moon
  2. Kill stale processes: pkill -9 moon
  3. Retry moon ci

Clippy Issues in CI (Zero Tolerance)

Cause: clippy reported ANY issue (error, warning, or lint) in source code.

Impact: moon ci fails with exit code 1 (zero-tolerance policy).

Solutions:

  1. Fix ALL clippy issues locally: moon run :lint-src (uses -W clippy::all)
  2. Use cargo clippy --lib --bins --examples --message-format=short for focused error messages
  3. Check .clippy.toml for custom configurations that may conflict with zero-tolerance
  4. Verify rust.syncToolchainConfig: true is set (affects rust-toolchain.toml)
  5. Remember: 0 issues is the goal — fix everything before CI

"task: command not found"

Cause: Binary specified in command doesn't exist in PATH.

Solutions:

  1. Verify binary is installed: cargo install <binary>
  2. Use full path to binary: command: '/absolute/path/to/binary'
  3. Check rust.bins in .moon/toolchains.yml for expected global binaries

Remote Cache Not Working

Cause: moon ci not caching to remote service.

Symptoms: All tasks show "no cache" in run report.

Solutions:

  1. Verify .moon/workspace.yml remote.host is set
  2. Check network connectivity to remote host
  3. Verify MOON_REMOTE_TOKEN environment variable is set (if auth required)
  4. Check bazel-remote is running if using local cache
  5. Check Moon logs with --log-level debug

Task Not Running in CI

Cause: Task skipped despite being affected.

Solutions:

  1. Check runInCI: false is set on task
  2. Check task is excluded via --affected-only flag in moon ci
  3. Verify task.options.envFile or environment variables are set correctly
  4. Check task dependencies are failing (blocks execution)

Command Reference

Core Commands

CommandPurposeExample
moon initInitialize workspacemoon init
moon checkRun lint/typecheck tasksmoon check
moon ciRun CI gate (affected)moon ci
moon run <target>Run specific taskmoon run :build
moon hashCompute task hashmoon hash :build
moon query changed-filesList changed filesmoon query changed-files
moon query targetsShow task targetsmoon query targets

v2-Specific Flags

FlagPurposeExample
--include-relations, -gInclude graph relations in affectedmoon ci --include-relations
--force, -fForce cache refreshmoon ci --force
--ciForce CI mode (sets CI=true)moon run :test --ci
--affected-remote <url>Use remote cache for affected queriesmoon ci --affected-remote grpcs://cache:9092
--toolchain <name>Use specific toolchain for commandmoon run :test --toolchain rust

Environment Variables

VariablePurposeWhen Set
CIForce CI mode detectionCI systems set this; can set manually for testing
MOON_WORKSPACE_ROOTWorkspace root directoryRead-only; use for paths in scripts
MOON_REMOTE_TOKENRemote cache authenticationSet when remote.auth.token is configured
MOON_BASEBase branch for comparisonOverride vcs.defaultBranch
MOON_HEADHead revision for comparisonOverride auto-detection

Quality Gates Checklist

Before running moon ci or pushing to remote, verify:

  • .moon/workspace.yml configured with correct vcs.defaultBranch
  • .moon/toolchains.yml exists with Rust version pinned
  • .moon/tasks/all.yml exists with task definitions
  • lint-src task uses -W clippy::all (zero tolerance)
  • moon run :lint-src returns 0 clippy issues (source code flawless)
  • Tests compile successfully with moon run :test (no strict clippy required)
  • moon ci runs locally without failures (affected targets only)
  • moonrepo/setup-toolchain@v0 action in CI workflow
  • moonrepo/run-report-action@v1 in CI workflow
  • fetch-depth: 0 in checkout action (critical for affected detection)
  • Build completes with moon run :build
  • PR run report shows all green (zero clippy issues in source)

Skill Version: 1.2.0 Last Updated: February 2026 Status: Production-Ready Moon Version: v2.0.0+ Rust Version: nightly-x86_64-unknown-linux-gnu (via rustup) Core Policy: Source code = 0 clippy issues (zero tolerance). Tests = compile-only (lenient). Your Setup: 3-layer cache (sccache + bazel-remote + Cargo). Rust nightly. bazel-remote on 127.0.0.1:9092.


Real-World Migration Lessons (oya repo, Feb 2026)

Installing Moon v2 Alpha

Moon v2 alpha is NOT available via the standard installer or npm packages. Use the shell installer:

bash
# Install Moon v2 alpha directly from GitHub releases
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/moonrepo/moon/releases/download/v2.0.0-alpha.2/moon_cli-installer.sh | sh

# Verify installation
moon --version  # Should show: moon 2.0.0-alpha.2

Key learnings:

  • Standard curl -fsSL https://moonrepo.dev/install.sh | bash still installs v1
  • v2 alpha requires explicit GitHub release download
  • The installer places binaries in ~/.moon/bin/moon and ~/.moon/bin/moonx
  • After install, run hash -r or restart shell to refresh PATH

Version Constraint for Alpha

Alpha versions require explicit alpha-aware version constraint:

yaml
# .moon/workspace.yml
versionConstraint: ">=2.0.0-alpha.0"  # NOT ">=2.0.0"

Why: Semantic versioning treats 2.0.0-alpha.2 < 2.0.0, so >=2.0.0 fails validation.

Testing Checklist After Migration

After migrating to v2, run this verification sequence:

bash
# 1. Version check
moon --version

# 2. Query projects (validates workspace config)
moon query projects

# 3. Query tasks (validates task config)
moon query tasks

# 4. Test core individual tasks
moon run :fmt        # Format check
moon run :clippy     # Zero-tolerance lint
moon run :check      # Type check
moon run :test       # Run tests

# 5. Test composite tasks
moon run :quick      # Parallel fmt + clippy

# 6. Test moon ci (affected targets)
moon ci

# 7. Verify remote cache connection
# (bazel-remote must be running on 127.0.0.1:9092)
moon run :check  # Should show no "Failed to connect" warnings

Common Issues Encountered

Issue: "An identifier is required in non-TTY environments"

bash
Error: app::tty::required_id
  × An identifier is required and must be explicitly provided

Cause: moon check requires a project ID in non-TTY environments (CI, scripts).

Solutions:

  • Use moon run :check instead (explicit task)
  • Use moon query tasks for validation
  • Use moon ci for CI gate (doesn't require ID)

Issue: Remote cache "Failed to connect to gRPC host"

bash
[ WARN] moon_remote::remote_service Failed to connect to gRPC host. Disabling remote service!

Cause: bazel-remote not running or wrong address.

Solutions:

bash
# Start bazel-remote
bazel-remote --dir ~/.cache/bazel-remote --max-size 500 \
  --storage_mode zstd \
  --grpc_address 127.0.0.1:9092 \
  --http_address 127.0.0.1:9090 &

# Verify health
curl http://127.0.0.1:9090/  # Should return "OK"

# Check workspace.yml uses correct address
# remote.host: 'grpcs://127.0.0.1:9092'  # Use 127.0.0.1, not localhost

Issue: "preset: 'server'" not recognized

bash
Error: tasks.serve.options.preset: unknown field `preset`

Cause: Moon v2 alpha uses persistent: true instead of preset: 'server'.

Solution:

yaml
serve:
  command: "cargo run --release -p oya-web --bin oya-server"
  options:
    persistent: true  # v2 alpha uses persistent, not preset
    runInCI: false

File Structure Reference

After successful migration, your .moon/ directory should look like:

code
.moon/
├── cache/                    # Moon's local cache
├── tasks/
│   └── all.yml              # v2: inherited tasks (was tasks.yml)
├── toolchains.yml            # v2: plural (was toolchain.yml)
└── workspace.yml             # v2 config

Key v2 Alpha Settings

yaml
# workspace.yml - v2 alpha working config
vcs:
  client: git              # v2: was "manager"
  provider: github
  defaultBranch: main

versionConstraint: ">=2.0.0-alpha.0"  # Alpha-aware

remote:                     # v2: was "unstable_remote"
  host: 'grpcs://127.0.0.1:9092'  # Use 127.0.0.1, not localhost
  cache:
    compression: 'zstd'
    verifyIntegrity: false   # Trusted local cache
    localReadOnly: false    # Allow uploads from local dev

pipeline:
  installDependencies: true
  syncWorkspace: true

hasher:
  optimization: 'performance'
  walkStrategy: 'vcs'

telemetry: false
yaml
# toolchains.yml - v2 alpha working config
rust:
  version: "nightly"
  bins:
    - "cargo-nextest@latest"
    - "cargo-tarpaulin@latest"
    - "cargo-mutants@latest"
    - "trunk@latest"
  syncToolchainConfig: true

telemetry: false
yaml
# tasks/all.yml - v2 alpha working tasks
clippy:
  command: "cargo clippy --workspace --all-features --lib --bins --examples -- -D warnings -W clippy::all"
  toolchains: [rust]      # v2: was "platform"
  options:
    runInCI: true         # v2: respected across all commands

Cache Verification

Verify 3-layer cache is working:

bash
# Layer 1: sccache
cat ~/.cargo/config.toml | grep rustc-wrapper
# Should show: rustc-wrapper = "/path/to/sccache"

# Layer 2: bazel-remote
ps aux | grep bazel-remote
curl http://127.0.0.1:9090/

# Layer 3: Moon remote cache
moon run :check
# Should NOT show "Failed to connect" warning

Quick Migration Script

bash
#!/bin/bash
# Quick Moon v1 -> v2 migration

# 1. Install v2 alpha
curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/moonrepo/moon/releases/download/v2.0.0-alpha.2/moon_cli-installer.sh | sh

# 2. Rename files
mv .moon/toolchain.yml .moon/toolchains.yml
mkdir -p .moon/tasks
mv .moon/tasks.yml .moon/tasks/all.yml

# 3. Update workspace.yml (sed or manual edit)
# - vcs.manager -> vcs.client
# - unstable_remote -> remote
# - Add pipeline section
# - Add telemetry: false

# 4. Update version constraint
# Change: versionConstraint: ">=1.20.0"
# To: versionConstraint: ">=2.0.0-alpha.0"

# 5. Update tasks
# Add toolchains: [rust] to cargo tasks
# Add runInCI: true to CI tasks
# Change preset: 'server' to persistent: true

# 6. Verify
moon --version
moon query tasks
moon run :quick