From 3434c426a88368d65b6261f32951e3ff7d497660 Mon Sep 17 00:00:00 2001 From: Robert Allen Date: Wed, 15 Jul 2026 12:51:45 -0400 Subject: [PATCH 1/2] Migrate committed CLAUDE.md to AGENTS.md + gitignored CLAUDE.local.md CLAUDE.md's content is now AGENTS.md's content (AGENTS.md is natively read by Claude Code, so no CLAUDE.md stub is needed for tooling discovery). Updates every consumer that referenced CLAUDE.md by name so nothing silently breaks: the Starlight reference-page generator (site/scripts/generate-reference-pages.mjs), the docs-deploy trigger path, the docs-freshness workflow's own description, and the prose mentions across README/CONTRIBUTING/SUPPORT/docs. CHANGELOG.md is left untouched as a historical record. Adds CLAUDE.local.md to .gitignore per the new convention. Closes #26 --- .github/workflows/docs-deploy.yml | 2 +- .github/workflows/docs-freshness.md | 2 +- .gitignore | 3 + AGENTS.md | 457 +++++++++++++++++----- CLAUDE.md | 416 -------------------- CONTRIBUTING.md | 4 +- README.md | 3 +- SUPPORT.md | 2 +- docs/explanation/architecture.md | 2 +- docs/template/CI-WORKFLOWS.md | 2 +- docs/template/CONFIGURATION.md | 14 +- docs/template/GITHUB-TEMPLATE-FEATURES.md | 3 +- docs/tutorials/first-project.md | 4 +- site/scripts/generate-reference-pages.mjs | 12 +- 14 files changed, 387 insertions(+), 539 deletions(-) delete mode 100644 CLAUDE.md 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..515abe9 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` (anywhere in repo; 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.`); } From 71ff393a3745d065692396f10a63e8fc96dc99a8 Mon Sep 17 00:00:00 2001 From: Robert Allen Date: Wed, 15 Jul 2026 13:00:31 -0400 Subject: [PATCH 2/2] Address Copilot review: regenerate stale site content, fix AGENTS.md location wording The generated site/src/content/docs/*.mdx pages were committed stale after the CLAUDE.md -> AGENTS.md source migration. Regenerate them via site/scripts/generate-all.mjs so they match the updated sources. Also aligns the GITHUB-TEMPLATE-FEATURES.md "Agent instructions" row wording with the rest of the docs, which consistently describe AGENTS.md as a repo-root file. --- docs/template/GITHUB-TEMPLATE-FEATURES.md | 2 +- site/src/content/docs/explanation/architecture.mdx | 2 +- .../content/docs/getting-started/configuration.mdx | 14 ++++---------- .../getting-started/github-template-features.mdx | 3 +-- site/src/content/docs/tutorials/first-project.mdx | 4 ++-- site/src/content/docs/workflows/ci-workflows.mdx | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/docs/template/GITHUB-TEMPLATE-FEATURES.md b/docs/template/GITHUB-TEMPLATE-FEATURES.md index 515abe9..54055cc 100644 --- a/docs/template/GITHUB-TEMPLATE-FEATURES.md +++ b/docs/template/GITHUB-TEMPLATE-FEATURES.md @@ -125,7 +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; also read by Claude Code) | [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/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 |