diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 7996890..daa4449 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -12,7 +12,7 @@ name: Deploy Documentation paths: - 'docs/**' - 'site/**' - - 'CLAUDE.md' + - 'AGENTS.md' - 'Cargo.toml' workflow_dispatch: diff --git a/.github/workflows/docs-freshness.md b/.github/workflows/docs-freshness.md index 479faaa..922df37 100644 --- a/.github/workflows/docs-freshness.md +++ b/.github/workflows/docs-freshness.md @@ -46,7 +46,7 @@ This repository has an Astro Starlight documentation site in `site/` that genera 1. **Docs pages** — markdown files in `docs/` are converted to Starlight MDX pages via `site/scripts/generate-docs-pages.mjs` 2. **Workflow reference pages** — `.github/workflows/*.yml` files are parsed into reference pages via `site/scripts/generate-workflow-pages.mjs` -3. **Reference pages** — sections from `CLAUDE.md` are extracted into individual reference pages via `site/scripts/generate-reference-pages.mjs` +3. **Reference pages** — sections from `AGENTS.md` are extracted into individual reference pages via `site/scripts/generate-reference-pages.mjs` The generated content lives in `site/src/content/docs/` and must stay in sync with these sources. diff --git a/.gitignore b/.gitignore index c1aca80..895df82 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ junit.xml # Project specific *.local + +# Claude Code session-local memory (see AGENTS.md for the committed docs) +CLAUDE.local.md diff --git a/AGENTS.md b/AGENTS.md index ad7a33e..df80d87 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,147 +1,416 @@ # AGENTS.md -Instructions for AI coding agents working on this Rust project. +This file provides guidance to AI coding agents (Claude Code and others) when working with code in this repository. -## Project Context +## Project Overview -- **Language**: Rust (edition 2024, MSRV 1.92) -- **Build System**: Cargo -- **Linting**: clippy with pedantic and nursery lints -- **Formatting**: rustfmt (100-char lines, 4-space indent) -- **Error Handling**: `thiserror` for custom error types -- **Testing**: Built-in test framework + proptest for property-based testing -- **Supply Chain**: cargo-deny for dependency auditing +This is a **GitHub template repository** for Rust crates. The crate name is `rust_template` (Rust edition 2024, MSRV 1.92). It ships both a library (`crates/lib.rs`) and a binary (`crates/main.rs`). Source lives in `crates/`, not the standard `src/` directory. -## File Structure +--- -``` -crates/ - lib.rs # Library entry point and public API - main.rs # Binary entry point (optional) -tests/ # Integration tests -benches/ # Benchmarks (criterion) -examples/ # Example programs +## Documentation Standard: Diátaxis + +All documentation in this project follows the [Diátaxis framework](https://diataxis.fr/). When adding or updating documentation — including this file, `docs/`, doc comments, and README files — classify content into one of four modes: + +| Mode | Purpose | Prompt | Example | +|---|---|---|---| +| **How-to** | Task-oriented steps | "How do I…?" | Adding a new error variant, running tests | +| **Reference** | Precise, factual lookup | "What is…?" | Lint tables, cargo profiles, API signatures | +| **Explanation** | Design rationale | "Why does…?" | Why `thiserror`, why `panic = "abort"` | +| **Tutorial** | Learning-oriented walkthrough | "Teach me…" | (Not used in AGENTS.md; use `docs/` for tutorials) | + +**Rules for contributors (human and AI):** + +- Before writing documentation, decide which Diátaxis mode it belongs to. Do not mix modes in a single section. +- **How-to** sections use numbered steps and end with a verification command. +- **Reference** sections use tables or structured lists. No rationale — just facts. +- **Explanation** sections use "Why X" headings and focus on trade-offs and decisions. +- New `docs/` files must declare their Diátaxis mode in a frontmatter comment or heading. +- When extending this AGENTS.md, place new content under the correct Diátaxis heading below. + +--- + + + +## How-to Guides + +### Build and Run + +[`just`](https://github.com/casey/just) is the local task runner. Run `just` to list all recipes. + +```bash +just # List all recipes +just check # Full CI check (fmt + clippy + test + doc + deny) +just build # Debug build +just build-release # Release build +just run # Run the binary +just template-sync # Sync shared tooling from rust-template upstream ``` -## Build and Test Commands +
+Raw cargo equivalents ```bash -cargo build # Build -cargo test --all-features # Run all tests -cargo clippy --all-targets --all-features -- -D warnings # Lint -cargo fmt -- --check # Check formatting -cargo doc --no-deps # Build docs -cargo deny check # Supply chain audit +cargo build # Build +cargo test --all-features # Run all tests +cargo test test_name # Run specific test +cargo test -- --nocapture # Run tests with stdout +cargo clippy --all-targets --all-features -- -D warnings # Lint (CI uses -D warnings) +cargo fmt # Format +cargo fmt -- --check # Check formatting +cargo deny check # Supply chain audit +cargo doc --no-deps --all-features # Build docs +cargo +nightly miri test # UB detection + +# Full CI check (run before pushing) +cargo fmt -- --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test && cargo doc --no-deps && cargo deny check ``` -## Code Rules +
+ +### Run Tests -### Never Panic in Library Code +```bash +just test # All tests (unit + integration + doc) +just test-verbose # Tests with stdout visible +just test-single NAME # Single test by name +just coverage # LCOV coverage report +just coverage-html # HTML coverage report +just msrv # Check against MSRV 1.92 +just miri # Miri undefined behavior detection +just mutants # Mutation testing +``` -Do not use `unwrap()`, `expect()`, or `panic!()`. Always return `Result`: +### Lint and Format -```rust -pub fn parse(input: &str) -> Result { - input.parse().map_err(Error::Parse) -} +```bash +just fmt # Format code +just fmt-check # Check formatting (no modify) +just lint # Clippy with CI-equivalent flags +just lint-fix # Clippy auto-fix +just deny # Supply chain audit +just audit # Advisory database check ``` -### Use thiserror for Errors +### Add a New Public Function + +1. Add the function in `crates/lib.rs` (or a module under `crates/`). +2. Annotate with `#[must_use]` if it returns a value without side effects. +3. Use `const fn` if the body permits. +4. Write a doc comment with `# Arguments`, `# Returns`, `# Errors` (if fallible), and `# Examples`. +5. Add a unit test in the `#[cfg(test)] mod tests` block within the same file. +6. Add an integration test in `tests/integration_test.rs`. +7. Run `just check` before committing. + +### Add a New Error Variant + +1. Add the variant to the `Error` enum in `crates/lib.rs`. +2. Include a `#[error("...")]` format string with meaningful context. +3. Prefer structured variants (named fields) over tuple variants when there are multiple pieces of context. +4. Add a display test in the `test_error_display` test. + +### Add a Builder Field to Config + +1. Add the field to the `Config` struct with a doc comment. +2. Set a sensible default in `Config::new()`. +3. Add a `with_(mut self, value: T) -> Self` method marked `#[must_use]` and `const fn`. +4. Add a test case in `test_config_builder` and `test_config_default`. + +--- + + + +## Reference + +### Source Layout + +| Path | Purpose | +|---|---| +| `crates/lib.rs` | Library root: `Error` (thiserror), `Result`, `Config` (builder), `add()`, `divide()` | +| `crates/main.rs` | Binary entry point: `main() -> ExitCode`, delegates to `run() -> Result` | +| `tests/integration_test.rs` | Integration tests including property-based tests (proptest) | +| `clippy.toml` | Clippy thresholds and test-mode exemptions | +| `rustfmt.toml` | Formatter settings (stable options active, nightly options commented) | +| `deny.toml` | Supply chain policy: licenses, bans, source restrictions | +| `justfile` | Local task runner recipes (CI parity) | + +### Error Handling + +- **Crate error type**: `Error` enum derived with `thiserror::Error`. +- **Result alias**: `pub type Result = std::result::Result`. +- **Propagation**: use `?` operator. Never `unwrap()`, `expect()`, or `panic!()` in library code. +- **Binary**: `main()` returns `ExitCode`; delegates to `run() -> Result`. On `Err`, the binary renders the error to stderr in the format selected by `--format` / TTY (below). + +#### Dual-Consumer Error Output (RFC 9457) + +The crate emits errors for two consumers from one `Error` value: the human (the `thiserror` `Display`, unchanged) and the LLM agent (a serializable `application/problem+json` envelope). The envelope type is `ProblemDetails` in `crates/problem.rs`, re-exported from the crate root alongside `Applicability`, `CodeAction`, `SuggestedFix`, and `OutputFormat`. Map any error with `Error::to_problem()`; render for a format with `Error::render(OutputFormat)`. + +**Envelope members** (`ProblemDetails`): + +| Field | RFC 9457 role | Notes | +|---|---|---| +| `type` | standard | Stable, version-embedded URI (`.../v1`). | +| `title` | standard | Short summary, stable per `type`. | +| `status` | standard | Numeric status class. | +| `detail` | standard | This-occurrence text; equals the `Display` string. | +| `instance` | standard | `urn:` occurrence reference. | +| `retry_after` | agent extension | Delta-seconds, or `null` (serialized) on non-transient errors. | +| `suggested_fix` | agent extension | `{ description, applicability }`, or `null`. | +| `code_actions` | agent extension | Array of LSP-`CodeAction`-shaped `{ title, kind, applicability }`. | +| `exit_code` | optional extension | Process exit code; omitted from JSON when absent. | + +**Applicability markers** (on every `suggested_fix` and `code_action`): `machine_applicable` (auto-apply), `maybe_incorrect` (escalate to human), `has_placeholders` (fill slots first), `unspecified` (default; treat as `maybe_incorrect`). + +**Type URIs** (one per variant, distinct, versioned): `InvalidInput` → `https://attested-delivery.github.io/rust-template/errors/invalid-input/v1`; `OperationFailed` → `https://attested-delivery.github.io/rust-template/errors/operation-failed/v1`. A breaking change ships a new version rather than redefining the existing one. The base is a single configurable constant, `ERROR_TYPE_BASE_URI` (derived URI = `{base}/{slug}/{version}`); adopters point it at their own docs host. Each URI is dereferenceable — it resolves to a per-type reference page under `docs/reference/errors/` (the canonical source). The `instance` URN namespace tracks `CARGO_PKG_NAME`. + +**Format selection** (`OutputFormat::select(explicit, is_terminal)`): JSON when `--format=json` or (no flag and stderr is not a TTY); pretty otherwise. Pretty output is byte-identical to the historical `Error: {e}` line. + +For the rationale, see the **Dual-Consumer Error Output** explanation doc (`docs/explanation/error-architecture.md`). + +### Ownership and Borrowing + +- Prefer `&str` over `String` in function parameters. +- Prefer `&[T]` over `Vec` in function parameters. +- Use `Cow<'_, str>` when a function may or may not allocate. +- Pass large structs by reference; pass `Copy` types by value. +- Avoid unnecessary `.clone()` — if you need ownership, take owned types in the signature. + +### Type Design + +- Use newtypes to enforce domain invariants (e.g., `struct Port(u16)` over bare `u16`). +- Derive `Debug` on all types. Derive `Clone`, `PartialEq`, `Eq`, `Hash` when semantically correct. +- Use `#[non_exhaustive]` on public enums and structs that may grow. +- Prefer `enum` for closed sets, `trait` for open extension. + +### Builder Pattern + +This project uses consuming-self builders with `const fn`: ```rust -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("invalid input: {0}")] - InvalidInput(String), - #[error(transparent)] - Io(#[from] std::io::Error), +#[must_use] +pub const fn with_field(mut self, value: T) -> Self { + self.field = value; + self } ``` -### Document All Public Items +- `Config::new()` is `const fn` and `#[must_use]`. +- `Default` impl delegates to `new()`. +- Every builder method is `const fn` and `#[must_use]`. + +### Const and Must-Use Annotations + +- `#[must_use]` on all pure functions that return a value. +- `const fn` wherever the compiler allows it. +- Both annotations on builder methods. + +### Lint Configuration + +Clippy runs with **pedantic + nursery + cargo** lint groups. All are set to `warn` with priority -1. + +**Denied lints** (hard errors): + +| Lint | Reason | +|---|---| +| `unwrap_used` | Use `?` or explicit match | +| `expect_used` | Use `?` or explicit match | +| `panic` | Return errors instead | +| `todo` | No placeholder code | +| `unimplemented` | No placeholder code | +| `dbg_macro` | No debug prints in production | +| `print_stdout` | Use logging; binary exempts itself with `#[allow]` | +| `print_stderr` | Use logging; binary exempts itself with `#[allow]` | + +**Allowed lints**: + +| Lint | Reason | +|---|---| +| `missing_errors_doc` | Opt-in documentation | +| `missing_panics_doc` | Opt-in documentation | +| `module_name_repetitions` | Common in Rust API design | +| `must_use_candidate` | Applied manually where meaningful | +| `redundant_pub_crate` | Allow `pub(crate)` for clarity | + +**Clippy thresholds** (from `clippy.toml`): + +| Threshold | Value | +|---|---| +| `too-many-lines-threshold` | 100 | +| `too-many-arguments-threshold` | 7 | +| `cognitive-complexity-threshold` | 25 | +| `excessive-nesting-threshold` | 4 | +| `max-struct-bools` | 3 | +| `max-fn-params-bools` | 3 | +| `pass-by-value-size-limit` | 256 bytes | +| `type-complexity-threshold` | 250 | + +**Test exemptions**: `allow-unwrap-in-tests`, `allow-expect-in-tests`, `allow-dbg-in-tests`, `allow-print-in-tests` are all `true`. -Include `# Examples` and `# Errors` sections: +### Formatting + +Configured in `rustfmt.toml` (stable options active): + +| Setting | Value | +|---|---| +| `max_width` | 100 | +| `edition` | 2024 | +| `tab_spaces` | 4 | +| `hard_tabs` | false | +| `use_field_init_shorthand` | true | +| `reorder_imports` | true | +| `reorder_modules` | true | +| `newline_style` | Unix | +| `match_block_trailing_comma` | true | + +Nightly-only options (`imports_granularity`, `group_imports`, `trailing_comma`, `brace_style`, etc.) are commented out but documented for when nightly is used. + +### Import Ordering + +Group imports in this order, separated by blank lines: + +1. `std` / `core` / `alloc` +2. External crates +3. `crate` / `super` / `self` + +Within each group, alphabetical order (enforced by `reorder_imports = true`). + +### Doc Comments + +All public items require doc comments. Structure: ```rust -/// Processes the input data. +/// Brief one-line summary. +/// +/// Extended description (optional, for complex items). +/// +/// # Arguments +/// +/// * `param` - Description. +/// +/// # Returns +/// +/// What this function returns. /// /// # Errors /// -/// Returns [`Error::InvalidInput`] if the input is empty. +/// When and why this function returns an error (required for fallible functions). /// /// # Examples /// /// ```rust -/// use rust_template::process; -/// let result = process("data")?; -/// # Ok::<(), rust_template::Error>(()) +/// use rust_template::my_function; +/// +/// let result = my_function(42); +/// assert_eq!(result, 42); /// ``` -pub fn process(input: &str) -> Result { - // implementation -} ``` -### Prefer Borrowing Over Ownership +- Doc examples must compile (`cargo test` runs them as doctests). +- Use `#![doc = include_str!("../README.md")]` at the crate root to pull in README as crate docs. -```rust -// Preferred -pub fn process(data: &[u8]) -> Result, Error> { ... } +### Unsafe Code -// Avoid -pub fn process(data: Vec) -> Result, Error> { ... } -``` +`unsafe` code is **forbidden** (`unsafe_code = "forbid"` in `[lints.rust]`). No exceptions. -### Use const fn Where Possible +### Supply Chain Security -```rust -#[must_use] -pub const fn new() -> Self { - Self { value: 0 } -} -``` +`deny.toml` enforces: + +- **Licenses**: only permissive (MIT, Apache-2.0, BSD-2/3, ISC, Zlib, MPL-2.0, Unicode, CC0, BSL-1.0, 0BSD). +- **Sources**: crates.io only; unknown registries and git sources denied. +- **Bans**: `openssl` (use `rustls`), `atty` (use `std::io::IsTerminal`). +- **Advisories**: all advisory types (vulnerability, unmaintained, unsound, notice, yanked) denied. +- **Wildcards**: wildcard version requirements denied. + +### Testing + +| Test type | Location | Crate | +|---|---|---| +| Unit tests | `#[cfg(test)] mod tests` inside source files | — | +| Integration tests | `tests/integration_test.rs` | — | +| Property tests | `tests/integration_test.rs::property_tests` | `proptest` | +| Parameterized tests | anywhere, via `#[test_case]` | `test-case` | +| Doc tests | `///` examples on public items | — | -## Testing Patterns +**Code coverage requirement**: 90% minimum. Run `just coverage` to generate an LCOV report and verify. CI enforces this threshold via Codecov. -### Unit Tests +**Property test pattern** (proptest): ```rust -#[cfg(test)] -mod tests { +mod property_tests { use super::*; + use proptest::prelude::*; - #[test] - fn test_success() { - let result = function(valid_input); - assert_eq!(result, expected); - } - - #[test] - fn test_error() { - let result = function(invalid_input); - assert!(matches!(result, Err(Error::InvalidInput(_)))); + proptest! { + #[test] + fn my_property(input in any::()) { + prop_assert!(some_invariant(input)); + } } } ``` -### Property-Based Tests +### CI/CD -```rust -use proptest::prelude::*; +CI and the container chain run through `pipeline.yml`; releases run through flat, independent tag-triggered workflows (`release.yml`, `publish.yml`, `package-homebrew.yml`) — the same architecture as `attested-delivery/rlm-rs`, the verified reference. -proptest! { - #[test] - fn roundtrip(input in any::()) { - let encoded = encode(input); - prop_assert_eq!(decode(&encoded)?, input); - } -} -``` +**Project specificity is var-driven**: the release workflows resolve the crate name, binary name, version, description, and license from `cargo metadata` at runtime, and owner/repo from the GitHub context. Instantiating the template requires editing only `Cargo.toml` (plus optional repo variable `HOMEBREW_TAP_REPO`, default `homebrew-tap`, and optional secret `HOMEBREW_TAP_TOKEN`). Nothing in the workflow files is renamed. + +**Publication is disabled in the template**: `publish = false` in Cargo.toml gates the container build, crates.io publishing, GitHub Release creation, and Homebrew tap updates (the workflows read it via `cargo metadata`; cargo itself also refuses `cargo publish`). In template state the `pipeline.yml` `gate` job resolves `publishable=false`, so the Docker build → sign → verify chain is **skipped** rather than built — a template ships no container. Deleting that one line in a downstream project arms all four channels. + +**CI stage** (`ci-checks.yml`): fmt, clippy, test (Linux/macOS/Windows), doc build, cargo-deny, MSRV check (1.92), `all-checks-pass` gate. Runs in parallel with `ci-coverage.yml` (LCOV/Codecov), `ci-test-matrix.yml` (12-combo matrix, PR only), and `pin-check` (central `attested-delivery/.github` workflow asserting every `uses:` is pinned to a full commit SHA). + +**Docker** (`release-docker.yml`): multi-platform build after CI passes, **gated on `publish` (skipped in template state)**. PR = build-only; push on main/tags. Pushed images flow through `docker-sign` (centralized `attested-delivery/.github` `sign-and-attest.yml`, pinned by full SHA — under SLSA Build L3 the signing identity is the central workflow, not this repo) and `docker-verify` (fail-closed attestation verification). + +**Release** (`release.yml`, tags + dispatch dry-run): resolve metadata → 5-platform build matrix with per-binary SLSA provenance attested at build time (`{bin}-{version}-{platform}` naming) → test + cargo-audit gates (tags are untrusted input) → CycloneDX SBOM generated and attested over every binary → **fail-closed `gh attestation verify` before the release exists** → tag-gated GitHub Release with checksums. A tag publishes nothing unattested. + +**Publish** (`publish.yml`, tags + dispatch dry-run): pre-publish gauntlet → crates.io **Trusted Publishing** (OIDC, no long-lived token; one-time crates.io setup: workflow `publish.yml`, environment `copilot`) → download the registry-served `.crate`, byte-compare against the local package, attest the registry bytes. + +**Homebrew** (`package-homebrew.yml`): `workflow_run` on Release completion (bot-authored release events don't trigger workflows) → source formula generated from Cargo.toml metadata into `{owner}/homebrew-tap`. + +Releases are orchestrated by the `/release` skill (`.claude/skills/release/`). Artifact verification commands live in `SECURITY.md` § Verifying Release Artifacts. + +See `docs/template/CI-WORKFLOWS.md` for the full reference. + +### Cargo Profiles + +| Profile | Optimization | LTO | Codegen Units | Panic | Strip | Debug | +|---|---|---|---|---|---|---| +| `dev` | 0 | off | default | unwind | no | 1 (line tables) | +| `release` | 3 | thin | 1 | abort | yes | no | +| `release-debug` | 3 | thin | 1 | abort | no | full | + +--- + + + +## Explanation + +### Why `crates/` Instead of `src/` + +This template uses `crates/` as the source directory to distinguish it from the common `src/` layout. This is a template convention — downstream projects may restructure. The `[lib]` and `[[bin]]` paths in `Cargo.toml` point to `crates/lib.rs` and `crates/main.rs`. + +### Why `thiserror` for Errors + +`thiserror` provides derive macros for `std::error::Error` with zero runtime overhead. It generates `Display` and `From` implementations from attributes, keeping error definitions concise and consistent. The crate-level `Result` alias reduces boilerplate across the API. + +### Why Consuming-Self Builders + +The builder pattern uses `fn with_field(mut self, ...) -> Self` instead of `&mut self`. This enables: + +- **Const evaluation**: `const fn` is compatible with owned self, not `&mut self`. +- **Chaining**: `Config::new().with_a(1).with_b(2)` reads naturally. +- **Move semantics**: no hidden shared state; the builder is consumed on each call. + +### Why Pedantic Clippy + +Enabling `pedantic`, `nursery`, and `cargo` lint groups catches subtle issues early: missing docs, inefficient patterns, cargo metadata problems. The strict deny list (`unwrap_used`, `panic`, etc.) enforces that library code handles all errors explicitly, pushing failures to the API boundary where callers can make decisions. + +### Why `panic = "abort"` in Release + +Release builds use `panic = "abort"` to eliminate unwinding tables, reducing binary size. Combined with `strip = true` and `lto = "thin"`, this produces small, fast binaries. The `release-debug` profile inherits these optimizations but preserves debug symbols for profiling. -## Forbidden Patterns +### Why Ban `openssl` and `atty` -- `unsafe` blocks (unless explicitly justified) -- `unwrap()`, `expect()`, `panic!()` in library code -- `todo!()`, `unimplemented!()` -- `dbg!()`, `print!()`, `println!()`, `eprint!()`, `eprintln!()` +- **`openssl`**: links to a system C library with complex build requirements and CVE history. `rustls` is a pure-Rust TLS implementation with smaller attack surface. +- **`atty`**: unmaintained and unnecessary since Rust 1.70 added `std::io::IsTerminal` to the standard library. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index c687af0..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,416 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is a **GitHub template repository** for Rust crates. The crate name is `rust_template` (Rust edition 2024, MSRV 1.92). It ships both a library (`crates/lib.rs`) and a binary (`crates/main.rs`). Source lives in `crates/`, not the standard `src/` directory. - ---- - -## Documentation Standard: Diátaxis - -All documentation in this project follows the [Diátaxis framework](https://diataxis.fr/). When adding or updating documentation — including this file, `docs/`, doc comments, and README files — classify content into one of four modes: - -| Mode | Purpose | Prompt | Example | -|---|---|---|---| -| **How-to** | Task-oriented steps | "How do I…?" | Adding a new error variant, running tests | -| **Reference** | Precise, factual lookup | "What is…?" | Lint tables, cargo profiles, API signatures | -| **Explanation** | Design rationale | "Why does…?" | Why `thiserror`, why `panic = "abort"` | -| **Tutorial** | Learning-oriented walkthrough | "Teach me…" | (Not used in CLAUDE.md; use `docs/` for tutorials) | - -**Rules for contributors (human and AI):** - -- Before writing documentation, decide which Diátaxis mode it belongs to. Do not mix modes in a single section. -- **How-to** sections use numbered steps and end with a verification command. -- **Reference** sections use tables or structured lists. No rationale — just facts. -- **Explanation** sections use "Why X" headings and focus on trade-offs and decisions. -- New `docs/` files must declare their Diátaxis mode in a frontmatter comment or heading. -- When extending this CLAUDE.md, place new content under the correct Diátaxis heading below. - ---- - - - -## How-to Guides - -### Build and Run - -[`just`](https://github.com/casey/just) is the local task runner. Run `just` to list all recipes. - -```bash -just # List all recipes -just check # Full CI check (fmt + clippy + test + doc + deny) -just build # Debug build -just build-release # Release build -just run # Run the binary -just template-sync # Sync shared tooling from rust-template upstream -``` - -
-Raw cargo equivalents - -```bash -cargo build # Build -cargo test --all-features # Run all tests -cargo test test_name # Run specific test -cargo test -- --nocapture # Run tests with stdout -cargo clippy --all-targets --all-features -- -D warnings # Lint (CI uses -D warnings) -cargo fmt # Format -cargo fmt -- --check # Check formatting -cargo deny check # Supply chain audit -cargo doc --no-deps --all-features # Build docs -cargo +nightly miri test # UB detection - -# Full CI check (run before pushing) -cargo fmt -- --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test && cargo doc --no-deps && cargo deny check -``` - -
- -### Run Tests - -```bash -just test # All tests (unit + integration + doc) -just test-verbose # Tests with stdout visible -just test-single NAME # Single test by name -just coverage # LCOV coverage report -just coverage-html # HTML coverage report -just msrv # Check against MSRV 1.92 -just miri # Miri undefined behavior detection -just mutants # Mutation testing -``` - -### Lint and Format - -```bash -just fmt # Format code -just fmt-check # Check formatting (no modify) -just lint # Clippy with CI-equivalent flags -just lint-fix # Clippy auto-fix -just deny # Supply chain audit -just audit # Advisory database check -``` - -### Add a New Public Function - -1. Add the function in `crates/lib.rs` (or a module under `crates/`). -2. Annotate with `#[must_use]` if it returns a value without side effects. -3. Use `const fn` if the body permits. -4. Write a doc comment with `# Arguments`, `# Returns`, `# Errors` (if fallible), and `# Examples`. -5. Add a unit test in the `#[cfg(test)] mod tests` block within the same file. -6. Add an integration test in `tests/integration_test.rs`. -7. Run `just check` before committing. - -### Add a New Error Variant - -1. Add the variant to the `Error` enum in `crates/lib.rs`. -2. Include a `#[error("...")]` format string with meaningful context. -3. Prefer structured variants (named fields) over tuple variants when there are multiple pieces of context. -4. Add a display test in the `test_error_display` test. - -### Add a Builder Field to Config - -1. Add the field to the `Config` struct with a doc comment. -2. Set a sensible default in `Config::new()`. -3. Add a `with_(mut self, value: T) -> Self` method marked `#[must_use]` and `const fn`. -4. Add a test case in `test_config_builder` and `test_config_default`. - ---- - - - -## Reference - -### Source Layout - -| Path | Purpose | -|---|---| -| `crates/lib.rs` | Library root: `Error` (thiserror), `Result`, `Config` (builder), `add()`, `divide()` | -| `crates/main.rs` | Binary entry point: `main() -> ExitCode`, delegates to `run() -> Result` | -| `tests/integration_test.rs` | Integration tests including property-based tests (proptest) | -| `clippy.toml` | Clippy thresholds and test-mode exemptions | -| `rustfmt.toml` | Formatter settings (stable options active, nightly options commented) | -| `deny.toml` | Supply chain policy: licenses, bans, source restrictions | -| `justfile` | Local task runner recipes (CI parity) | - -### Error Handling - -- **Crate error type**: `Error` enum derived with `thiserror::Error`. -- **Result alias**: `pub type Result = std::result::Result`. -- **Propagation**: use `?` operator. Never `unwrap()`, `expect()`, or `panic!()` in library code. -- **Binary**: `main()` returns `ExitCode`; delegates to `run() -> Result`. On `Err`, the binary renders the error to stderr in the format selected by `--format` / TTY (below). - -#### Dual-Consumer Error Output (RFC 9457) - -The crate emits errors for two consumers from one `Error` value: the human (the `thiserror` `Display`, unchanged) and the LLM agent (a serializable `application/problem+json` envelope). The envelope type is `ProblemDetails` in `crates/problem.rs`, re-exported from the crate root alongside `Applicability`, `CodeAction`, `SuggestedFix`, and `OutputFormat`. Map any error with `Error::to_problem()`; render for a format with `Error::render(OutputFormat)`. - -**Envelope members** (`ProblemDetails`): - -| Field | RFC 9457 role | Notes | -|---|---|---| -| `type` | standard | Stable, version-embedded URI (`.../v1`). | -| `title` | standard | Short summary, stable per `type`. | -| `status` | standard | Numeric status class. | -| `detail` | standard | This-occurrence text; equals the `Display` string. | -| `instance` | standard | `urn:` occurrence reference. | -| `retry_after` | agent extension | Delta-seconds, or `null` (serialized) on non-transient errors. | -| `suggested_fix` | agent extension | `{ description, applicability }`, or `null`. | -| `code_actions` | agent extension | Array of LSP-`CodeAction`-shaped `{ title, kind, applicability }`. | -| `exit_code` | optional extension | Process exit code; omitted from JSON when absent. | - -**Applicability markers** (on every `suggested_fix` and `code_action`): `machine_applicable` (auto-apply), `maybe_incorrect` (escalate to human), `has_placeholders` (fill slots first), `unspecified` (default; treat as `maybe_incorrect`). - -**Type URIs** (one per variant, distinct, versioned): `InvalidInput` → `https://attested-delivery.github.io/rust-template/errors/invalid-input/v1`; `OperationFailed` → `https://attested-delivery.github.io/rust-template/errors/operation-failed/v1`. A breaking change ships a new version rather than redefining the existing one. The base is a single configurable constant, `ERROR_TYPE_BASE_URI` (derived URI = `{base}/{slug}/{version}`); adopters point it at their own docs host. Each URI is dereferenceable — it resolves to a per-type reference page under `docs/reference/errors/` (the canonical source). The `instance` URN namespace tracks `CARGO_PKG_NAME`. - -**Format selection** (`OutputFormat::select(explicit, is_terminal)`): JSON when `--format=json` or (no flag and stderr is not a TTY); pretty otherwise. Pretty output is byte-identical to the historical `Error: {e}` line. - -For the rationale, see the **Dual-Consumer Error Output** explanation doc (`docs/explanation/error-architecture.md`). - -### Ownership and Borrowing - -- Prefer `&str` over `String` in function parameters. -- Prefer `&[T]` over `Vec` in function parameters. -- Use `Cow<'_, str>` when a function may or may not allocate. -- Pass large structs by reference; pass `Copy` types by value. -- Avoid unnecessary `.clone()` — if you need ownership, take owned types in the signature. - -### Type Design - -- Use newtypes to enforce domain invariants (e.g., `struct Port(u16)` over bare `u16`). -- Derive `Debug` on all types. Derive `Clone`, `PartialEq`, `Eq`, `Hash` when semantically correct. -- Use `#[non_exhaustive]` on public enums and structs that may grow. -- Prefer `enum` for closed sets, `trait` for open extension. - -### Builder Pattern - -This project uses consuming-self builders with `const fn`: - -```rust -#[must_use] -pub const fn with_field(mut self, value: T) -> Self { - self.field = value; - self -} -``` - -- `Config::new()` is `const fn` and `#[must_use]`. -- `Default` impl delegates to `new()`. -- Every builder method is `const fn` and `#[must_use]`. - -### Const and Must-Use Annotations - -- `#[must_use]` on all pure functions that return a value. -- `const fn` wherever the compiler allows it. -- Both annotations on builder methods. - -### Lint Configuration - -Clippy runs with **pedantic + nursery + cargo** lint groups. All are set to `warn` with priority -1. - -**Denied lints** (hard errors): - -| Lint | Reason | -|---|---| -| `unwrap_used` | Use `?` or explicit match | -| `expect_used` | Use `?` or explicit match | -| `panic` | Return errors instead | -| `todo` | No placeholder code | -| `unimplemented` | No placeholder code | -| `dbg_macro` | No debug prints in production | -| `print_stdout` | Use logging; binary exempts itself with `#[allow]` | -| `print_stderr` | Use logging; binary exempts itself with `#[allow]` | - -**Allowed lints**: - -| Lint | Reason | -|---|---| -| `missing_errors_doc` | Opt-in documentation | -| `missing_panics_doc` | Opt-in documentation | -| `module_name_repetitions` | Common in Rust API design | -| `must_use_candidate` | Applied manually where meaningful | -| `redundant_pub_crate` | Allow `pub(crate)` for clarity | - -**Clippy thresholds** (from `clippy.toml`): - -| Threshold | Value | -|---|---| -| `too-many-lines-threshold` | 100 | -| `too-many-arguments-threshold` | 7 | -| `cognitive-complexity-threshold` | 25 | -| `excessive-nesting-threshold` | 4 | -| `max-struct-bools` | 3 | -| `max-fn-params-bools` | 3 | -| `pass-by-value-size-limit` | 256 bytes | -| `type-complexity-threshold` | 250 | - -**Test exemptions**: `allow-unwrap-in-tests`, `allow-expect-in-tests`, `allow-dbg-in-tests`, `allow-print-in-tests` are all `true`. - -### Formatting - -Configured in `rustfmt.toml` (stable options active): - -| Setting | Value | -|---|---| -| `max_width` | 100 | -| `edition` | 2024 | -| `tab_spaces` | 4 | -| `hard_tabs` | false | -| `use_field_init_shorthand` | true | -| `reorder_imports` | true | -| `reorder_modules` | true | -| `newline_style` | Unix | -| `match_block_trailing_comma` | true | - -Nightly-only options (`imports_granularity`, `group_imports`, `trailing_comma`, `brace_style`, etc.) are commented out but documented for when nightly is used. - -### Import Ordering - -Group imports in this order, separated by blank lines: - -1. `std` / `core` / `alloc` -2. External crates -3. `crate` / `super` / `self` - -Within each group, alphabetical order (enforced by `reorder_imports = true`). - -### Doc Comments - -All public items require doc comments. Structure: - -```rust -/// Brief one-line summary. -/// -/// Extended description (optional, for complex items). -/// -/// # Arguments -/// -/// * `param` - Description. -/// -/// # Returns -/// -/// What this function returns. -/// -/// # Errors -/// -/// When and why this function returns an error (required for fallible functions). -/// -/// # Examples -/// -/// ```rust -/// use rust_template::my_function; -/// -/// let result = my_function(42); -/// assert_eq!(result, 42); -/// ``` -``` - -- Doc examples must compile (`cargo test` runs them as doctests). -- Use `#![doc = include_str!("../README.md")]` at the crate root to pull in README as crate docs. - -### Unsafe Code - -`unsafe` code is **forbidden** (`unsafe_code = "forbid"` in `[lints.rust]`). No exceptions. - -### Supply Chain Security - -`deny.toml` enforces: - -- **Licenses**: only permissive (MIT, Apache-2.0, BSD-2/3, ISC, Zlib, MPL-2.0, Unicode, CC0, BSL-1.0, 0BSD). -- **Sources**: crates.io only; unknown registries and git sources denied. -- **Bans**: `openssl` (use `rustls`), `atty` (use `std::io::IsTerminal`). -- **Advisories**: all advisory types (vulnerability, unmaintained, unsound, notice, yanked) denied. -- **Wildcards**: wildcard version requirements denied. - -### Testing - -| Test type | Location | Crate | -|---|---|---| -| Unit tests | `#[cfg(test)] mod tests` inside source files | — | -| Integration tests | `tests/integration_test.rs` | — | -| Property tests | `tests/integration_test.rs::property_tests` | `proptest` | -| Parameterized tests | anywhere, via `#[test_case]` | `test-case` | -| Doc tests | `///` examples on public items | — | - -**Code coverage requirement**: 90% minimum. Run `just coverage` to generate an LCOV report and verify. CI enforces this threshold via Codecov. - -**Property test pattern** (proptest): - -```rust -mod property_tests { - use super::*; - use proptest::prelude::*; - - proptest! { - #[test] - fn my_property(input in any::()) { - prop_assert!(some_invariant(input)); - } - } -} -``` - -### CI/CD - -CI and the container chain run through `pipeline.yml`; releases run through flat, independent tag-triggered workflows (`release.yml`, `publish.yml`, `package-homebrew.yml`) — the same architecture as `attested-delivery/rlm-rs`, the verified reference. - -**Project specificity is var-driven**: the release workflows resolve the crate name, binary name, version, description, and license from `cargo metadata` at runtime, and owner/repo from the GitHub context. Instantiating the template requires editing only `Cargo.toml` (plus optional repo variable `HOMEBREW_TAP_REPO`, default `homebrew-tap`, and optional secret `HOMEBREW_TAP_TOKEN`). Nothing in the workflow files is renamed. - -**Publication is disabled in the template**: `publish = false` in Cargo.toml gates the container build, crates.io publishing, GitHub Release creation, and Homebrew tap updates (the workflows read it via `cargo metadata`; cargo itself also refuses `cargo publish`). In template state the `pipeline.yml` `gate` job resolves `publishable=false`, so the Docker build → sign → verify chain is **skipped** rather than built — a template ships no container. Deleting that one line in a downstream project arms all four channels. - -**CI stage** (`ci-checks.yml`): fmt, clippy, test (Linux/macOS/Windows), doc build, cargo-deny, MSRV check (1.92), `all-checks-pass` gate. Runs in parallel with `ci-coverage.yml` (LCOV/Codecov), `ci-test-matrix.yml` (12-combo matrix, PR only), and `pin-check` (central `attested-delivery/.github` workflow asserting every `uses:` is pinned to a full commit SHA). - -**Docker** (`release-docker.yml`): multi-platform build after CI passes, **gated on `publish` (skipped in template state)**. PR = build-only; push on main/tags. Pushed images flow through `docker-sign` (centralized `attested-delivery/.github` `sign-and-attest.yml`, pinned by full SHA — under SLSA Build L3 the signing identity is the central workflow, not this repo) and `docker-verify` (fail-closed attestation verification). - -**Release** (`release.yml`, tags + dispatch dry-run): resolve metadata → 5-platform build matrix with per-binary SLSA provenance attested at build time (`{bin}-{version}-{platform}` naming) → test + cargo-audit gates (tags are untrusted input) → CycloneDX SBOM generated and attested over every binary → **fail-closed `gh attestation verify` before the release exists** → tag-gated GitHub Release with checksums. A tag publishes nothing unattested. - -**Publish** (`publish.yml`, tags + dispatch dry-run): pre-publish gauntlet → crates.io **Trusted Publishing** (OIDC, no long-lived token; one-time crates.io setup: workflow `publish.yml`, environment `copilot`) → download the registry-served `.crate`, byte-compare against the local package, attest the registry bytes. - -**Homebrew** (`package-homebrew.yml`): `workflow_run` on Release completion (bot-authored release events don't trigger workflows) → source formula generated from Cargo.toml metadata into `{owner}/homebrew-tap`. - -Releases are orchestrated by the `/release` skill (`.claude/skills/release/`). Artifact verification commands live in `SECURITY.md` § Verifying Release Artifacts. - -See `docs/template/CI-WORKFLOWS.md` for the full reference. - -### Cargo Profiles - -| Profile | Optimization | LTO | Codegen Units | Panic | Strip | Debug | -|---|---|---|---|---|---|---| -| `dev` | 0 | off | default | unwind | no | 1 (line tables) | -| `release` | 3 | thin | 1 | abort | yes | no | -| `release-debug` | 3 | thin | 1 | abort | no | full | - ---- - - - -## Explanation - -### Why `crates/` Instead of `src/` - -This template uses `crates/` as the source directory to distinguish it from the common `src/` layout. This is a template convention — downstream projects may restructure. The `[lib]` and `[[bin]]` paths in `Cargo.toml` point to `crates/lib.rs` and `crates/main.rs`. - -### Why `thiserror` for Errors - -`thiserror` provides derive macros for `std::error::Error` with zero runtime overhead. It generates `Display` and `From` implementations from attributes, keeping error definitions concise and consistent. The crate-level `Result` alias reduces boilerplate across the API. - -### Why Consuming-Self Builders - -The builder pattern uses `fn with_field(mut self, ...) -> Self` instead of `&mut self`. This enables: - -- **Const evaluation**: `const fn` is compatible with owned self, not `&mut self`. -- **Chaining**: `Config::new().with_a(1).with_b(2)` reads naturally. -- **Move semantics**: no hidden shared state; the builder is consumed on each call. - -### Why Pedantic Clippy - -Enabling `pedantic`, `nursery`, and `cargo` lint groups catches subtle issues early: missing docs, inefficient patterns, cargo metadata problems. The strict deny list (`unwrap_used`, `panic`, etc.) enforces that library code handles all errors explicitly, pushing failures to the API boundary where callers can make decisions. - -### Why `panic = "abort"` in Release - -Release builds use `panic = "abort"` to eliminate unwinding tables, reducing binary size. Combined with `strip = true` and `lto = "thin"`, this produces small, fast binaries. The `release-debug` profile inherits these optimizations but preserves debug symbols for profiling. - -### Why Ban `openssl` and `atty` - -- **`openssl`**: links to a system C library with complex build requirements and CVE history. `rustls` is a pure-Rust TLS implementation with smaller attack surface. -- **`atty`**: unmaintained and unnecessary since Rust 1.70 added `std::io::IsTerminal` to the standard library. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05afe7d..46f3898 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,7 +109,7 @@ lint groups. Key rules: - **Use `const fn`** where possible - **Line length**: 100 characters maximum -See [CLAUDE.md](CLAUDE.md) for detailed coding patterns and examples. +See [AGENTS.md](AGENTS.md) for detailed coding patterns and examples. ## Operational Runbooks @@ -124,5 +124,5 @@ For ongoing project operations, see the runbooks in [`docs/runbooks/`](docs/runb AI-assisted contributions are welcome. If using AI tools, please ensure generated code follows the same quality standards as hand-written code. -See `CLAUDE.md` and `.github/copilot-instructions.md` for AI-specific +See `AGENTS.md` and `.github/copilot-instructions.md` for AI-specific guidelines. diff --git a/README.md b/README.md index 1db4cfd..6bede6d 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,6 @@ Cargo.toml # Project manifest clippy.toml # Clippy configuration rustfmt.toml # Formatter configuration deny.toml # cargo-deny configuration -CLAUDE.md # AI assistant instructions AGENTS.md # AI coding agent instructions .editorconfig # Cross-editor defaults .devcontainer/ # Codespaces / dev container config @@ -229,7 +228,7 @@ Verification commands for every artifact type live in [SECURITY.md](SECURITY.md# ### AI Coding Agent - **Copilot Setup** (`.github/workflows/copilot-setup-steps.yml`) - Environment for GitHub Copilot coding agent -- **Agent Instructions**: `AGENTS.md`, `.github/copilot-instructions.md`, `CLAUDE.md` +- **Agent Instructions**: `AGENTS.md`, `.github/copilot-instructions.md` - **Path-Specific Instructions**: `.github/instructions/` for Rust code and test patterns - **Reusable Prompts**: `.github/prompts/` for common development tasks diff --git a/SUPPORT.md b/SUPPORT.md index fdaeb6e..5b68eb3 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -11,7 +11,7 @@ - **API Docs**: Run `cargo doc --open` locally - **Examples**: See the `examples/` directory -- **CLAUDE.md**: AI-assisted development guidelines +- **AGENTS.md**: AI-assisted development guidelines ## Before Opening an Issue diff --git a/docs/explanation/architecture.md b/docs/explanation/architecture.md index 1745ee2..9c4db1f 100644 --- a/docs/explanation/architecture.md +++ b/docs/explanation/architecture.md @@ -174,4 +174,4 @@ are almost always used once, so that cost rarely bites. - [ADR-0002 — Documentation Directory Structure](../adr/0002-documentation-directory-structure.md) - [Signed Releases & SLSA Provenance](../security/SIGNED-RELEASES.md) — the attestation chain in depth - [Deployment Guide](../DEPLOYMENT.md) — how to actually cut a release -- The project `CLAUDE.md` Reference and Explanation sections — the authoritative source for lint tables, cargo profiles, and conventions +- The project `AGENTS.md` Reference and Explanation sections — the authoritative source for lint tables, cargo profiles, and conventions diff --git a/docs/template/CI-WORKFLOWS.md b/docs/template/CI-WORKFLOWS.md index 07ae91d..df8247b 100644 --- a/docs/template/CI-WORKFLOWS.md +++ b/docs/template/CI-WORKFLOWS.md @@ -81,7 +81,7 @@ package-homebrew.yml | Contributor Recognition | `contributors.yml` | manual | -- | Opt-in | | Template Init | `template-init.yml` | push to main, manual | -- | Active | | Nightly Builds | `nightly.yml` | manual | -- | Opt-in | -| Deploy Documentation | `docs-deploy.yml` | push to main (docs/site/CLAUDE.md/Cargo.toml paths), manual | -- | Active | +| Deploy Documentation | `docs-deploy.yml` | push to main (docs/site/AGENTS.md/Cargo.toml paths), manual | -- | Active | | ADR Validation | `adr-validation.yml` | push, PR (docs/adr paths), manual | -- | Active | | ADR Viewer | `adr-viewer.yml` | push (docs/adr paths), manual | -- | Active | | Docker Hub Multi-Registry | `docker-hub.yml` | manual | `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` | Opt-in | diff --git a/docs/template/CONFIGURATION.md b/docs/template/CONFIGURATION.md index 245511e..4d1d83a 100644 --- a/docs/template/CONFIGURATION.md +++ b/docs/template/CONFIGURATION.md @@ -357,20 +357,14 @@ To customize the dev container, edit `.devcontainer/devcontainer.json`. Common c The template includes configuration files for multiple AI coding assistants. -### CLAUDE.md +### AGENTS.md -Instructions for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Located at the repo root. +Instructions for [GitHub Copilot coding agent](https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent) and [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Located at the repo root. - Build commands, project structure, and code style rules - Error handling patterns and documentation requirements - Testing conventions and CI/CD pipeline details - -### AGENTS.md - -Instructions for [GitHub Copilot coding agent](https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent). Located at the repo root. - -- Read by the Copilot coding agent when it works on issues and PRs -- Shares the same project conventions as `CLAUDE.md` +- Read by the Copilot coding agent when it works on issues and PRs, and by Claude Code ### .github/copilot-instructions.md @@ -400,7 +394,7 @@ Instructions for [GitHub Copilot Chat](https://docs.github.com/en/copilot/custom ### Customizing AI Instructions -- Edit `CLAUDE.md` and `AGENTS.md` at the repo root to update project-wide conventions. +- Edit `AGENTS.md` at the repo root to update project-wide conventions. - Add new `.instructions.md` files under `.github/instructions/` for path-specific rules. - Add new `.prompt.md` files under `.github/prompts/` for reusable task prompts. - All AI instruction files are regular Markdown -- placeholder replacement applies to them automatically. diff --git a/docs/template/GITHUB-TEMPLATE-FEATURES.md b/docs/template/GITHUB-TEMPLATE-FEATURES.md index b95b328..54055cc 100644 --- a/docs/template/GITHUB-TEMPLATE-FEATURES.md +++ b/docs/template/GITHUB-TEMPLATE-FEATURES.md @@ -125,8 +125,7 @@ This reference covers every category of GitHub repository configuration, whether | Repository custom instructions | Yes | `.github/copilot-instructions.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Path-specific instructions | Yes | `.github/instructions/*.instructions.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Reusable prompts | Yes | `.github/prompts/*.prompt.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | -| Agent instructions | Yes | `AGENTS.md` (anywhere in repo) | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | -| Claude Code instructions | Yes | `CLAUDE.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | +| Agent instructions | Yes | `AGENTS.md` (repo root; also read by Claude Code) | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Copilot setup steps | Yes | `.github/workflows/copilot-setup-steps.yml` | [Customize agent environment](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment) | | "Jumpstart with Copilot" prompt | **No** | UI feature during creation only | [Creating templates](https://docs.github.com/en/copilot/tutorials/copilot-chat-cookbook/communicate-effectively/creating-templates) | diff --git a/docs/tutorials/first-project.md b/docs/tutorials/first-project.md index d06a72b..427c694 100644 --- a/docs/tutorials/first-project.md +++ b/docs/tutorials/first-project.md @@ -256,7 +256,7 @@ the same style as `add` — copying a known-good shape is the safest way to make change that passes every gate on the first try. > Want the full design rationale (why `thiserror`, why consuming builders, why -> the strict lints)? It lives in the project's `CLAUDE.md` under +> the strict lints)? It lives in the project's `AGENTS.md` under > **Explanation**. For now, you know enough to make your first change. --- @@ -506,6 +506,6 @@ You now have everything you need to build something real. | See what copies when you "Use this template" | [GitHub Template Features](../template/GITHUB-TEMPLATE-FEATURES.md) (reference) | | Fix a failing CI check | [CI Troubleshooting](../runbooks/CI-TROUBLESHOOTING.md) (how-to) | | Cut and verify a release | [Releasing](../runbooks/RELEASING.md) (how-to) | -| Understand the design rationale (why `thiserror`, `crates/`, strict lints) | `CLAUDE.md` → Explanation | +| Understand the design rationale (why `thiserror`, `crates/`, strict lints) | `AGENTS.md` → Explanation | Happy building. 🦀 diff --git a/site/scripts/generate-reference-pages.mjs b/site/scripts/generate-reference-pages.mjs index b4b949d..b9e06a3 100644 --- a/site/scripts/generate-reference-pages.mjs +++ b/site/scripts/generate-reference-pages.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Extracts reference sections from the project root CLAUDE.md + * Extracts reference sections from the project root AGENTS.md * and generates individual Starlight MDX pages. */ @@ -14,7 +14,7 @@ const projectRoot = resolve(__dirname, "..", ".."); const siteRoot = resolve(__dirname, ".."); /** - * Sections to extract from CLAUDE.md. + * Sections to extract from AGENTS.md. * heading: the ### heading text to match * output: output filename under reference/ * title: page title @@ -107,13 +107,13 @@ function extractSection(content, heading) { } /** - * Generate reference pages from CLAUDE.md sections. + * Generate reference pages from AGENTS.md sections. * @param {string} [outputBase] - Override output base directory * @returns {{ generated: string[], skipped: string[] }} */ export function generateReferencePages(outputBase) { - const claudeMdPath = join(projectRoot, "CLAUDE.md"); - const content = readFileSync(claudeMdPath, "utf-8"); + const agentsMdPath = join(projectRoot, "AGENTS.md"); + const content = readFileSync(agentsMdPath, "utf-8"); const outDir = outputBase || join(siteRoot, "src", "content", "docs"); const generated = []; const skipped = []; @@ -146,7 +146,7 @@ export function generateReferencePages(outputBase) { // Run directly if (process.argv[1] === fileURLToPath(import.meta.url)) { - console.log("Generating reference pages from CLAUDE.md..."); + console.log("Generating reference pages from AGENTS.md..."); const { generated, skipped } = generateReferencePages(); console.log(`\nDone: ${generated.length} generated, ${skipped.length} skipped.`); } diff --git a/site/src/content/docs/explanation/architecture.mdx b/site/src/content/docs/explanation/architecture.mdx index 4bbce60..a8a25ff 100644 --- a/site/src/content/docs/explanation/architecture.mdx +++ b/site/src/content/docs/explanation/architecture.mdx @@ -174,4 +174,4 @@ are almost always used once, so that cost rarely bites. - [ADR-0002 — Documentation Directory Structure](../adr/0002-documentation-directory-structure.md) - [Signed Releases & SLSA Provenance](/rust-template/security/signed-releases/) — the attestation chain in depth - [Deployment Guide](../DEPLOYMENT.md) — how to actually cut a release -- The project `CLAUDE.md` Reference and Explanation sections — the authoritative source for lint tables, cargo profiles, and conventions +- The project `AGENTS.md` Reference and Explanation sections — the authoritative source for lint tables, cargo profiles, and conventions diff --git a/site/src/content/docs/getting-started/configuration.mdx b/site/src/content/docs/getting-started/configuration.mdx index d609337..92b8282 100644 --- a/site/src/content/docs/getting-started/configuration.mdx +++ b/site/src/content/docs/getting-started/configuration.mdx @@ -358,20 +358,14 @@ To customize the dev container, edit `.devcontainer/devcontainer.json`. Common c The template includes configuration files for multiple AI coding assistants. -### CLAUDE.md +### AGENTS.md -Instructions for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Located at the repo root. +Instructions for [GitHub Copilot coding agent](https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent) and [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Located at the repo root. - Build commands, project structure, and code style rules - Error handling patterns and documentation requirements - Testing conventions and CI/CD pipeline details - -### AGENTS.md - -Instructions for [GitHub Copilot coding agent](https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent). Located at the repo root. - -- Read by the Copilot coding agent when it works on issues and PRs -- Shares the same project conventions as `CLAUDE.md` +- Read by the Copilot coding agent when it works on issues and PRs, and by Claude Code ### .github/copilot-instructions.md @@ -401,7 +395,7 @@ Instructions for [GitHub Copilot Chat](https://docs.github.com/en/copilot/custom ### Customizing AI Instructions -- Edit `CLAUDE.md` and `AGENTS.md` at the repo root to update project-wide conventions. +- Edit `AGENTS.md` at the repo root to update project-wide conventions. - Add new `.instructions.md` files under `.github/instructions/` for path-specific rules. - Add new `.prompt.md` files under `.github/prompts/` for reusable task prompts. - All AI instruction files are regular Markdown -- placeholder replacement applies to them automatically. diff --git a/site/src/content/docs/getting-started/github-template-features.mdx b/site/src/content/docs/getting-started/github-template-features.mdx index fd47335..eb7b7bd 100644 --- a/site/src/content/docs/getting-started/github-template-features.mdx +++ b/site/src/content/docs/getting-started/github-template-features.mdx @@ -126,8 +126,7 @@ This reference covers every category of GitHub repository configuration, whether | Repository custom instructions | Yes | `.github/copilot-instructions.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Path-specific instructions | Yes | `.github/instructions/*.instructions.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Reusable prompts | Yes | `.github/prompts/*.prompt.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | -| Agent instructions | Yes | `AGENTS.md` (anywhere in repo) | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | -| Claude Code instructions | Yes | `CLAUDE.md` | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | +| Agent instructions | Yes | `AGENTS.md` (repo root; also read by Claude Code) | [Custom instructions for Copilot](https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | | Copilot setup steps | Yes | `.github/workflows/copilot-setup-steps.yml` | [Customize agent environment](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment) | | "Jumpstart with Copilot" prompt | **No** | UI feature during creation only | [Creating templates](https://docs.github.com/en/copilot/tutorials/copilot-chat-cookbook/communicate-effectively/creating-templates) | diff --git a/site/src/content/docs/tutorials/first-project.mdx b/site/src/content/docs/tutorials/first-project.mdx index 98c34bd..a59fdeb 100644 --- a/site/src/content/docs/tutorials/first-project.mdx +++ b/site/src/content/docs/tutorials/first-project.mdx @@ -257,7 +257,7 @@ the same style as `add` — copying a known-good shape is the safest way to make change that passes every gate on the first try. > Want the full design rationale (why `thiserror`, why consuming builders, why -> the strict lints)? It lives in the project's `CLAUDE.md` under +> the strict lints)? It lives in the project's `AGENTS.md` under > **Explanation**. For now, you know enough to make your first change. --- @@ -507,6 +507,6 @@ You now have everything you need to build something real. | See what copies when you "Use this template" | [GitHub Template Features](/rust-template/getting-started/github-template-features/) (reference) | | Fix a failing CI check | [CI Troubleshooting](/rust-template/runbooks/ci-troubleshooting/) (how-to) | | Cut and verify a release | [Releasing](/rust-template/runbooks/releasing/) (how-to) | -| Understand the design rationale (why `thiserror`, `crates/`, strict lints) | `CLAUDE.md` → Explanation | +| Understand the design rationale (why `thiserror`, `crates/`, strict lints) | `AGENTS.md` → Explanation | Happy building. 🦀 diff --git a/site/src/content/docs/workflows/ci-workflows.mdx b/site/src/content/docs/workflows/ci-workflows.mdx index 071d19a..77123b6 100644 --- a/site/src/content/docs/workflows/ci-workflows.mdx +++ b/site/src/content/docs/workflows/ci-workflows.mdx @@ -82,7 +82,7 @@ package-homebrew.yml | Contributor Recognition | `contributors.yml` | manual | -- | Opt-in | | Template Init | `template-init.yml` | push to main, manual | -- | Active | | Nightly Builds | `nightly.yml` | manual | -- | Opt-in | -| Deploy Documentation | `docs-deploy.yml` | push to main (docs/site/CLAUDE.md/Cargo.toml paths), manual | -- | Active | +| Deploy Documentation | `docs-deploy.yml` | push to main (docs/site/AGENTS.md/Cargo.toml paths), manual | -- | Active | | ADR Validation | `adr-validation.yml` | push, PR (docs/adr paths), manual | -- | Active | | ADR Viewer | `adr-viewer.yml` | push (docs/adr paths), manual | -- | Active | | Docker Hub Multi-Registry | `docker-hub.yml` | manual | `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` | Opt-in |