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