Files
MercuryToolbox/docs/jade-discipline.md
T

12 KiB

Jade Discipline

Jade Discipline is the repository-wide Rust standard for Mercury Toolbox / 水星工具箱.

Goals

  • Default strictness: rules are either deny or allow; warn is not part of the policy surface.
  • Default observability: every command must remain easy to inspect from plain CLI output and machine-readable JSON.
  • Default auditability: lint gates, toolchain choices, and verification commands are written down and reproducible.
  • Default optimizability: development and release profiles are explicit instead of ad hoc.
  • Default narrow exemptions: repository-wide allow is treated as hazardous; when a lint must be suppressed, prefer the smallest code-local scope that still explains the exception.
  • Default hardening: Miri, fuzzing, sanitizer, no-panic, and Loom checks are Jade gates, not optional tiers.

Repository Interpretation

  • The workspace applies the Jade lint baseline through root Cargo.toml plus per-crate [lints] workspace = true.
  • The required hard gate is cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery.
  • PowerShell scripts under scripts/ are part of the same discipline surface. Executable .ps1 files must opt into Set-StrictMode -Version Latest, set $ErrorActionPreference = 'Stop', avoid automatic-variable reassignment, and stay free of Invoke-Expression.
  • Root Cargo.toml should not carry broad allow exemptions for design, API, or optimization lints. If a lint is not globally denied, that is not the same thing as globally allowing it.
  • Global allow is reserved for two cases only: a rule that is provably irrelevant to the repository with no realistic future exception, or a naming-only rule that would conflict with the project naming constitution. If a narrower suppression is possible, the narrower suppression wins.
  • Local suppressions should start at the smallest reasonable scope: item before impl block, impl block before module, module before crate. When const, borrowing, or #[must_use] can be added instead of suppressing, code changes are preferred over lint waivers.
  • cargo_common_metadata = "deny" is kept, and every crate is explicitly marked publish = false because Mercury Toolbox is a local toolbox rather than a published crate set.
  • Tokio-specific guidance is intentionally not applied because the current project has no async runtime, no Tokio dependencies, and no task scheduler to observe.
  • tracing, tracing-subscriber, criterion, proptest, insta, rstest, anyhow, and miette are approved workspace dependencies for future expansion. They are pinned now, but only crates with a concrete use case should inherit them to avoid dependency drift and udeps noise.
  • Workspace line coverage is enforced on logic-bearing source files. Thin binary entrypoints in src/main.rs and repetitive CLI argument shims in src/cli.rs are excluded from the line-rate denominator because they are covered instead by command-specific integration tests plus the PowerShell ecosystem gate.
  • Auto-generated lookup tables such as managed/src/flow_opcode_table.rs are also excluded from the line-rate denominator. They are generated data snapshots rather than authored control flow, and they are verified indirectly through decoder tests that exercise the consuming logic in managed/src/flow.rs.
  • Because Mercury Toolbox is a workspace of many tiny binaries rather than one deep library crate, the repository coverage floor is 80% line coverage after those wrapper exclusions, paired with a mandatory end-to-end pwsh ecosystem run.
  • Hardening checks are introduced into the main Jade gate. If a machine cannot run one, the run must use the matching -ExemptMiri, -ExemptFuzz, -ExemptSanitizers, -ExemptNoPanic, or -ExemptLoom flag plus a non-empty reason. Missing tools, missing harnesses, or platform discomfort are failures by default, not quiet skips.
  • Exemptions follow the same narrowness rule as lint suppressions: exempt one gate for one reviewed reason, fix the root cause as soon as the underlying toolchain supports it, and never convert the exemption into a broad permanent policy.

Hard Gates

pwsh -NoProfile -File .\scripts\check-jade.ps1 -VerificationConfiguration ReleaseFast

The script is the authoritative entry point. Expanded, it runs cargo fmt --all --check, cargo check --all-targets --all-features, coverage-backed cargo llvm-cov --jobs 1 nextest --all-features --summary-only, strict Clippy, root and fuzz-workspace cargo +nightly udeps, root and fuzz-workspace cargo deny, the mandatory hardening gates, generated AI asset drift checks, PowerShell analysis, the ecosystem probe, and the selected verification build profile.

The hardening sub-gate is scripts/check-jade-hardening.ps1. Its default -Only All path runs the no-panic source scan, Miri JSON-family tests, Loom runtime capture model, JSON-family fuzz target, and address sanitizer tests. The -Only switch exists only for focused local repair loops; it is not a Jade tier and it does not prove the repository is clean.

The fuzz/ workspace is intentionally separate from the root Cargo workspace. Jade still audits it by running locked metadata generation, cargo +nightly udeps, and cargo deny against the fuzz manifest. Keep fuzz/Cargo.lock committed so fuzz dependency resolution is reproducible.

Hardening Exemptions

Hardening exemptions are explicit audit records, not convenience switches.

  • Use the exact gate flag, such as -ExemptFuzz, and include the matching non-empty reason parameter.
  • Prefer fixing missing tooling with just jade-tools or pwsh -NoProfile -File .\scripts\install-jade-tooling.ps1 before considering an exemption.
  • Do not bake exemptions into just recipes, CI defaults, docs, or agent prompts.
  • Keep exemption reasons narrow enough to identify one machine, one platform issue, or one temporary toolchain gap.
  • Remove the exemption as soon as the underlying issue is fixed.

The no-panic source gate has its own local escape hatch for rare invariants. The only accepted form is an adjacent Rust comment: // jade: allow-panic because: <reason>. Prefer Result, Option handling, ok_or_else, and ? over adding that comment.

Install

Install or refresh the toolchain and cargo subcommands used by the standard.

pwsh -NoProfile -File .\scripts\install-jade-tooling.ps1

The tooling script installs cargo-udeps and cargo-llvm-cov, so they stay in the active toolchain set as required Jade commands. install-jade-tooling.ps1 also ensures PSScriptAnalyzer is available for the PowerShell gate. The hardening baseline also installs nightly miri, nightly rust-src, and cargo-fuzz. The repository no-panic gate is an audited source scan that denies panic!, .unwrap(), and .expect() in JSON-family production paths unless a narrow // jade: allow-panic because: <reason> comment is placed directly beside the call site. just and bacon are part of the local Jade tooling baseline now: just is the thin task runner over the repo scripts, and bacon is the continuous Rust feedback loop for the edit-compile-test idle gap. Generated AI assets are part of the repository surface too: the compact prompt under docs\ai\ and the generated Codex skill under skills\mercury-toolbox\ should both stay reproducible from their scripts instead of being hand-edited drift. On Windows, profiler helpers have extra OS prerequisites that Cargo cannot install for you: cargo flamegraph falls back to blondie unless DTrace is configured, and that blondie path requires an elevated shell; samply additionally requires xperf.exe from Windows Performance Toolkit. For local one-shot profiling runs, use scripts/cargo-flamegraph-windows.ps1 to relay through UAC instead of trying to teach the full Jade gate to self-elevate. When you pass -LogPath, that wrapper writes a compact relay log to the requested path and the full elevated-session transcript to a sibling *.transcript.txt file so PowerShell-side failures stay auditable. sccache is part of the local Jade tooling baseline now and is configured as the default Cargo rustc-wrapper; use sccache --show-stats when verifying cache behavior after cargo clean.

Profiles And Cargo Config

  • dev is optimized for local debugging without dropping compile throughput.
  • release is the day-to-day shipping profile.
  • release-fast and release-size are reserved for speed-first and size-first builds.
  • .cargo/config.toml owns warning-to-error promotion, future incompatibility reports, and PowerShell-friendly network behavior.

PowerShell Gate

  • scripts/check-powershell.ps1 is the Jade gate for repository PowerShell.
  • The gate runs Invoke-ScriptAnalyzer with PSScriptAnalyzerSettings.psd1 and treats any reported diagnostic as a hard failure.
  • Naming-only analyzer rules such as approved verbs or singular nouns are intentionally left out of the hard gate because Jade reserves hard failures for completion and safety issues, not broad stylistic noise.
  • scripts/toolbox-commands.ps1 is treated as a dot-sourced helper inventory rather than an executable script, so the strict-mode header requirement applies to the executable entry scripts instead.

AI Asset Gate

  • scripts/generate-ai-prompt.ps1 and scripts/generate-ai-skill.ps1 are the only supported writers for the generated AI prompt and Codex skill.
  • scripts/check-ai-prompt.ps1 and scripts/check-ai-skill.ps1 self-heal generated asset drift by regenerating once, then fail only if the regenerated files still do not match.
  • The generated skill exists partly to stop dumb whole-file reads in agent workflows: it explicitly routes AI toward fileprobe, outline, snip, chunkcat, hitsnip, defsnip, codeshape, refs, and ctxpack before falling back to raw dumps.

Local Workflow

  1. Install or update the required cargo tooling with scripts/install-jade-tooling.ps1; the local loop expects cargo-nextest, cargo-llvm-cov, cargo-deny, cargo-udeps, just, and bacon.
  2. Start new user-facing features from the Jade feature template so behavior, tests, generated assets, and handoff notes stay aligned from the first commit. For daily low-risk maintenance, start from the daily maintenance template and .codex-progress\quality-security-backlog.md.
  3. Keep public APIs fully documented, including # Errors and # Panics where applicable.
  4. Use just check, just test, just clippy, and just jade-skip-coverage for the broad local loop, then switch to subsystem loops such as just code-check, just data-test, just runtime-check, just windows-test, just managed-clippy, or just ai-check when the edit is narrower. Keep just test as the fast normal path; use just stable-test for warning-free incremental cleanup plus ignored slow Windows integrations.
  5. Use just miri, just fuzz, just sanitizer, just no-panic, and just loom only as focused edit loops for a specific failing gate. They are not exemptions and not an optional Jade tier: just jade runs the complete hardening gate set through scripts/check-jade.ps1.
  6. Use bacon when you want background compile or test feedback while you keep editing, and prefer the matching scoped jobs such as bacon code, bacon runtime, bacon windows, bacon managed-test, or bacon ai before falling back to the whole-workspace jobs.
  7. Run pwsh -NoProfile -File .\scripts\check-jade.ps1 before claiming the repository is clean. If a hardening gate truly cannot run, pass the specific -Exempt* flag and reason so the missing coverage is visible in logs.
  8. Treat any new lint, warning, panic surface, UB finding, fuzz crash, sanitizer report, or Loom interleaving failure as a repository bug, not as backlog.
  9. If a lint or hardening check forces an exception, document it in code or command-line reason at the narrowest scope instead of adding a new workspace-wide allow.