From 9608b02ac54be87ad7d69ceced82b6e2b4e864ad Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 24 May 2026 16:22:17 +0100 Subject: [PATCH 01/10] Render generated AGENTS guidance by flavour Add the requested testing and documentation requirements to the generated AGENTS.md, including app and library-specific users-guide wording. --- template/{AGENTS.md => AGENTS.md.jinja} | 36 +++++++++++- tests/test_template.py | 76 +++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) rename template/{AGENTS.md => AGENTS.md.jinja} (89%) diff --git a/template/AGENTS.md b/template/AGENTS.md.jinja similarity index 89% rename from template/AGENTS.md rename to template/AGENTS.md.jinja index d156dd0..5398564 100644 --- a/template/AGENTS.md +++ b/template/AGENTS.md.jinja @@ -151,8 +151,21 @@ project: meaningfully named structs. - Where a function is returning a large error, consider using `Arc` to reduce the amount of data returned. -- Write unit and behavioural tests for new functionality. Run both before and - after making any change. +- Ensure that new features are validated with unit tests using `rstest` and + behavioural tests using `rstest-bdd` where applicable. Cover happy paths, + unhappy paths, and relevant edge cases. +- Add end-to-end tests where a change affects externally observable workflows, + integration contracts, persistence, command-line behaviour, network + boundaries, UI flows, or other system-level behaviour. +- Use property tests with `proptest` or a bounded model checker with `kani` + when a change introduces an invariant over a range of inputs, states, + orderings, or transitions. Use judgement to choose the appropriate level of + rigour. +- Use an exhaustive proof with `verus` for introduced lemmas or contractual + business logic. Proofs must be substantive, rigorous, and well-founded, not + merely a restatement of the assumed property. +- Run relevant unit, behavioural, property, model-checking, proof, and + end-to-end suites before and after making any change. - Every module **must** begin with a module level (`//!`) comment explaining the module's purpose and utility. - Document public APIs using Rustdoc comments (`///`) so documentation can be @@ -262,6 +275,25 @@ project: - Use GitHub-flavoured Markdown footnotes (`[^1]`) for references and footnotes. +## Project Documentation + +Record design decisions in the design document. Where a decision is +substantive, record it in an ADR document following the documentation style +guide, then reference that ADR from the design document. + +{% if flavour == 'app' -%} +Update `docs/users-guide.md` for any change to application behaviour or user +interface that a user should know about. Document internally facing interfaces +or practices in the relevant component architecture document. Document +internally facing conventions or practices in `docs/developers-guide.md`. +{% else -%} +Update `docs/users-guide.md` for any change to behaviour or public API that a +consumer of the library should know about. Document internally facing +interfaces or practices in the relevant component architecture document. +Document internally facing conventions or practices in +`docs/developers-guide.md`. +{% endif %} + ## Additional tooling The following tooling is available in this environment: diff --git a/tests/test_template.py b/tests/test_template.py index b50cc7d..db4ce1c 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -297,7 +297,83 @@ def test_generated_tooling_contracts( release_workflow=release_workflow, ) +@pytest.mark.parametrize( + ("flavour", "expected_user_docs", "unexpected_user_docs"), + [ + ( + APP, + "application behaviour or user\ninterface", + "behaviour or public API that a\nconsumer of the library", + ), + ( + LIB, + "behaviour or public API that a\nconsumer of the library", + "application behaviour or user\ninterface", + ), + ], +) +def test_generated_agent_instructions_include_quality_contracts( + tmp_path: Path, + copier: CopierFixture, + flavour: str, + expected_user_docs: str, + unexpected_user_docs: str, +) -> None: + """Generated AGENTS.md captures testing and documentation expectations.""" + project = render_project( + tmp_path, + copier, + project_name="AgentDocsExample", + package_name="agent_docs_example", + flavour=flavour, + ) + agents = (project / "AGENTS.md").read_text(encoding="utf-8") + assert "unit tests using `rstest`" in agents, ( + "expected AGENTS.md to require rstest unit coverage" + ) + assert "behavioural tests using `rstest-bdd`" in agents, ( + "expected AGENTS.md to require rstest-bdd behavioural coverage" + ) + assert "Cover happy paths" in agents, ( + "expected AGENTS.md to require happy-path coverage" + ) + assert "unhappy paths, and relevant edge cases" in agents, ( + "expected AGENTS.md to require happy, unhappy, and edge-case coverage" + ) + assert "externally observable workflows" in agents, ( + "expected AGENTS.md to describe when end-to-end tests are required" + ) + assert "property tests with `proptest`" in agents, ( + "expected AGENTS.md to require proptest where invariants apply" + ) + assert "bounded model checker with `kani`" in agents, ( + "expected AGENTS.md to require kani where invariants apply" + ) + assert "exhaustive proof with `verus`" in agents, ( + "expected AGENTS.md to require Verus for contractual logic" + ) + assert "substantive, rigorous, and well-founded" in agents, ( + "expected AGENTS.md to reject vacuous proof obligations" + ) + assert "Record design decisions in the design document" in agents, ( + "expected AGENTS.md to require design-decision documentation" + ) + assert "ADR document following the documentation style\nguide" in agents, ( + "expected AGENTS.md to require substantive ADRs" + ) + assert expected_user_docs in agents, ( + "expected AGENTS.md to render flavour-specific users-guide wording" + ) + assert unexpected_user_docs not in agents, ( + "expected AGENTS.md to omit the other flavour's users-guide wording" + ) + assert "relevant component architecture document" in agents, ( + "expected AGENTS.md to require component architecture documentation" + ) + assert "docs/developers-guide.md" in agents, ( + "expected AGENTS.md to require developers-guide convention updates" + ) def test_generated_structured_file_snapshots( tmp_path: Path, copier: CopierFixture, snapshot: SnapshotAssertion ) -> None: From 06d72e0454800e028323947069667f881904483f Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 24 May 2026 16:41:53 +0100 Subject: [PATCH 02/10] Refresh shared template documentation Fold reusable improvements from sibling project docs into the shared template copies while leaving project-specific policy and title-case drift behind. Keep the Markdown style guide aligned with repository-layout guidance, fix malformed doctest references, refresh dependency-injection examples, and apply stable wrapping and table formatting. --- ...antipatterns-and-refactoring-strategies.md | 6 +-- template/docs/documentation-style-guide.md | 35 +++++++------ ...esting-in-rust-via-dependency-injection.md | 27 +++++----- template/docs/rust-doctest-dry-guide.md | 9 ++-- .../docs/rust-testing-with-rstest-fixtures.md | 49 +++++++++---------- 5 files changed, 67 insertions(+), 59 deletions(-) diff --git a/template/docs/complexity-antipatterns-and-refactoring-strategies.md b/template/docs/complexity-antipatterns-and-refactoring-strategies.md index 5a7e722..503a4f5 100644 --- a/template/docs/complexity-antipatterns-and-refactoring-strategies.md +++ b/template/docs/complexity-antipatterns-and-refactoring-strategies.md @@ -85,9 +85,9 @@ Core Principles of Calculation: Cognitive Complexity is incremented based on three main rules[^8]: 1. **Breaks in Linear Flow:** Each time the code breaks the normal linear - reading flow (e.g., loops, conditionals like - `if`/`else`/`switch`, `try-catch` blocks, jumps to labels, and sequences of - logical operators like `&&` and `||`), a penalty is applied. + reading flow (e.g., loops, conditionals like `if`/`else`/`switch`, + `try-catch` blocks, jumps to labels, and sequences of logical operators like + `&&` and `||`), a penalty is applied. 2. **Nesting:** Each level of nesting of these flow-breaking structures adds an additional penalty. This is because deeper nesting makes it harder to keep diff --git a/template/docs/documentation-style-guide.md b/template/docs/documentation-style-guide.md index 4d33791..d4334c3 100644 --- a/template/docs/documentation-style-guide.md +++ b/template/docs/documentation-style-guide.md @@ -73,9 +73,10 @@ material, the user's guide explains how to use the project, the developer's guide explains how to work on the project, the design document explains why the system is shaped the way it is, and the repository layout document explains where important things live. For discoverability, use canonical filenames -unless a stronger repository-specific constraint applies: `docs/contents.md`, -`docs/users-guide.md`, `docs/developers-guide.md`, `docs/repository-layout.md`, -and a primary design document with an explicit product or topic name such as +unless a stronger repository-specific constraint applies. A minimal canonical +set looks like +`docs/{contents,users-guide,developers-guide,repository-layout}.md` plus a +primary design document under `docs/*-design.md`, for example `docs/theoremc-design.md` or `docs/query-planner-design.md`. ### Contents file @@ -135,8 +136,9 @@ the existing system, not as the place for the project's primary design document. - Open with one short paragraph that states the audience and the operational scope of the guide. -- Link early to the design document, accepted decision records, and other - normative references that explain architecture or rationale in depth. +- Link early to the design document, repository layout document, accepted + decision records, and other normative references that explain architecture or + rationale in depth. - Put maintainer-facing implementation guidance here, for example build, test, lint, release, debugging, extension, and contribution workflows. - Use numbered sections for long-form technical documents to improve @@ -148,6 +150,9 @@ the existing system, not as the place for the project's primary design document. - Keep subsystem descriptions focused on current responsibilities, integration points, and operational expectations. Put design rationale, major trade-offs, and proposed architecture in design documents instead. +- Do not embed repository-layout guidance here. The canonical location for + file-tree and path-responsibility documentation is + `docs/repository-layout.md`. - Keep the document synchronized with decision records, roadmap items, and the codebase. A stale developer's guide is worse than a shorter one. @@ -175,10 +180,10 @@ decision, and an RFC to propose a change. ### Design document Use a dedicated design document, conventionally named -`docs/-design.md`, to explain the architecture, -constraints, rationale, and intended evolution of a system or subsystem. This -document is the correct location for design intent; that material must not be -buried in the user's guide or developer's guide. +`docs/-design.md`, to explain the architecture, constraints, +rationale, and intended evolution of a system or subsystem. This document is +the correct location for design intent; that material must not be buried in the +user's guide or developer's guide. - Start with a concise front matter section that states status, scope, primary audience, and the decision records or other documents that take precedence. @@ -408,7 +413,7 @@ Include these sections as appropriate to the decision's complexity: ### ADR template -```markdown +```plaintext # Architectural decision record (ADR) NNN: ## Status @@ -488,9 +493,9 @@ implementation is required.> ### Repository layout document Use a repository layout document, canonically `docs/repository-layout.md`, to -explain the shape of the tree and the responsibilities of its major paths. This -may be a standalone document or a clearly labelled section within the -developer's guide, provided readers can find it easily from the contents file. +explain the shape of the tree and the responsibilities of its major paths. Use +this standalone document as the canonical location for repository-layout +guidance rather than embedding that material in the developer's guide. - Document the top-level directories and any critical subdirectories that a new contributor must understand quickly. @@ -504,6 +509,8 @@ developer's guide, provided readers can find it easily from the contents file. long-lived reference documents belong. - Call out any directories with unusual constraints, such as generated output, fixtures, snapshots, or capability-restricted paths. +- Ensure the contents file links directly to `docs/repository-layout.md` so + readers can find it without scanning the developer's guide. - Update the layout document when the repository structure changes enough that a contributor could otherwise follow outdated guidance. @@ -634,7 +641,7 @@ This hierarchy should align with the GIST framework: ### Roadmap example -```markdown +```plaintext ## 1. Core infrastructure ### 1.1. Logging subsystem diff --git a/template/docs/reliable-testing-in-rust-via-dependency-injection.md b/template/docs/reliable-testing-in-rust-via-dependency-injection.md index b418875..60a8c6f 100644 --- a/template/docs/reliable-testing-in-rust-via-dependency-injection.md +++ b/template/docs/reliable-testing-in-rust-via-dependency-injection.md @@ -2,8 +2,8 @@ Writing robust, reliable, and parallelizable tests requires an intentional approach to handling external dependencies such as environment variables, the -filesystem, or the system clock. Functions that directly call `std::env::var` -or `SystemTime::now()` are difficult to test because they depend on global, +filesystem, or the system clock. Functions that directly call `std::env::var` or +`SystemTime::now()` are difficult to test because they depend on global, non-deterministic state. This leads to several problems: @@ -18,9 +18,10 @@ This leads to several problems: The solution is a classic software design pattern: **Dependency Injection (DI)**. Instead of a function reaching out to the global state, its -dependencies are provided as arguments. The `mockable` crate offers a -convenient set of traits (`Env`, `Clock`, etc.) to implement this pattern for -common system interactions in Rust. +dependencies are provided as arguments. The +[mockable](https://docs.rs/mockable/latest/mockable/) crate offers a convenient +set of traits (`Env`, `Clock`, etc.) to implement this pattern for common +system interactions in Rust. ______________________________________________________________________ @@ -32,13 +33,12 @@ First, add the crate to development dependencies in `Cargo.toml`. ```toml [dev-dependencies] -mockable = { version = "0.1.4", default-features = false, features = ["clock", "mock"] } +mockable = "0.3" ``` ### 2. The untestable code (before) -Directly calling `std::env` makes it difficult to test all logic paths -exhaustively. +Directly calling `std::env` makes it hard to test all logic paths. ```rust,no_run pub fn get_api_key() -> Option<String> { @@ -127,8 +127,8 @@ ______________________________________________________________________ ## 🔩 Handling other non-deterministic dependencies This dependency injection pattern also applies to other non-deterministic -dependencies, such as the system clock. The `mockable` crate provides a `Clock` -trait for this purpose. +dependencies, such as the system clock. `mockable` provides a `Clock` trait for +this purpose. ### Untestable code @@ -193,7 +193,7 @@ ______________________________________________________________________ ## 📌 Key takeaways - **The Problem is Non-Determinism:** Directly accessing global state like - `std::env` or `SystemTime::now` makes code difficult to test exhaustively. + `std::env` or `SystemTime::now` makes code hard to test. - **The Solution is Dependency Injection:** Pass dependencies into functions as arguments. - **Use** `mockable` **Traits:** Abstract dependencies behind traits such as @@ -204,5 +204,6 @@ ______________________________________________________________________ to interact with the actual system. - **`RealEnv` is NOT a Scope Guard:** `RealEnv` directly mutates the global process environment without automatic cleanup. For integration tests that - require modifying the live environment, consider a crate such as `temp_env`. - For unit tests, `MockEnv` is preferable. + require modifying the live environment, consider a crate such as + [temp_env](https://crates.io/crates/temp-env). For unit tests, `MockEnv` is + preferable. diff --git a/template/docs/rust-doctest-dry-guide.md b/template/docs/rust-doctest-dry-guide.md index e882814..db79dec 100644 --- a/template/docs/rust-doctest-dry-guide.md +++ b/template/docs/rust-doctest-dry-guide.md @@ -641,10 +641,11 @@ July 15, 2025, <https://doc.rust-lang.org/rustdoc/documentation-tests.html> Language Forum, accessed on July 15, 2025, <https://users.rust-lang.org/t/best-practice-for-doc-testing-readme/114862> [^11]: Compile_fail doc test ignored in cfg(test) - help - The Rust Programming -Language Forum, accessed on July 15, 2025, -<https://users.rust-lang.org/t/compile-fail-doc-test-ignored-in-cfg-test/124927> -Accessed on July 15, 2025, -<https://users.rust-lang.org/t/test-setup-for-doctests/50426> + Language Forum, accessed on July 15, 2025, + <https://users.rust-lang.org/t/compile-fail-doc-test-ignored-in-cfg-test/124927>; + Test setup for doctests - The Rust Programming Language Forum, accessed + on July 15, 2025, + <https://users.rust-lang.org/t/test-setup-for-doctests/50426> [^12]: quote_doctest - Rust - [Docs.rs](http://Docs.rs), accessed on July 15, 2025, <https://docs.rs/quote-doctest> [^13]: Advanced features - The rustdoc boOK - Rust Documentation, accessed on diff --git a/template/docs/rust-testing-with-rstest-fixtures.md b/template/docs/rust-testing-with-rstest-fixtures.md index 2e686e5..a8146ce 100644 --- a/template/docs/rust-testing-with-rstest-fixtures.md +++ b/template/docs/rust-testing-with-rstest-fixtures.md @@ -317,12 +317,11 @@ another. If fixtures were shared by default, a mutation to a fixture's state in one test could lead to unpredictable behaviour or failures in subsequent tests that use the same fixture. Such dependencies would make tests order-dependent, significantly harder to debug, and less trustworthy. By providing a fresh -instance for each test (unless explicitly specified otherwise using -`#[once]`), `rstest` upholds this cornerstone of reliable testing, ensuring -each test operates on a known, independent baseline. The `#[once]` attribute, -discussed later, provides an explicit mechanism to opt into shared fixture -state when isolation is not a concern, or when the cost of fixture creation is -prohibitive. +instance for each test (unless explicitly specified otherwise using `#[once]`), +`rstest` upholds this cornerstone of reliable testing, ensuring each test +operates on a known, independent baseline. The `#[once]` attribute, discussed +later, provides an explicit mechanism to opt into shared fixture state when +isolation is not a concern, or when the cost of fixture creation is prohibitive. ## IV. Parameterized tests with `rstest` @@ -716,16 +715,16 @@ async fn async_data_fetcher() -> String { ``` The example above uses `async_std::task::sleep` purely as a convenient -stand-in; the fixture may call into whichever runtime the project adopts -because `rstest` simply awaits the returned future. +stand-in; the fixture may call into whichever runtime the project adopts because + `rstest` simply awaits the returned future. ### B. Writing asynchronous tests (`async fn` with `#[rstest]`) Test functions themselves can also be `async fn`. `rstest` polls the future the test returns but does not install or default to an async runtime. Annotate the -test with the runtime's attribute (for example, -`#[tokio::test]`, `#[async_std::test]`, or `#[actix_rt::test]`) alongside -`#[rstest]` so the runtime drives execution. +test with the runtime's attribute (for example, `#[tokio::test]`, +`#[async_std::test]`, or `#[actix_rt::test]`) alongside `#[rstest]` so the +runtime drives execution. ```rust,no_run use rstest::*; @@ -1340,20 +1339,20 @@ provided by `rstest`: **Table 2:** Key `rstest` attributes quick reference -| Attribute | Core Purpose | -| ---------------------------- | -------------------------------------------------------------------------------------------- | -| #[rstest] | Marks a function as an rstest test; enables fixture injection and parameterization. | -| #[fixture] | Defines a function that provides a test fixture (setup data or services). | -| #[case(…)] | Defines a single parameterized test case with specific input values. | -| #[values(…)] | Defines a list of values for an argument, generating tests for each value or combination. | -| #[once] | Marks a fixture to be initialized only once and shared (as a static reference) across tests. | -| #[future] | Simplifies async argument types by removing impl Future boilerplate. | -| #[awt] | (Function or argument level) Automatically .awaits future arguments in async tests. | -| #[from(original_name)] | Allows renaming an injected fixture argument in the test function. | -| #[with(…)] | Overrides default arguments of a fixture for a specific test. | -| #[default(…)] | Provides default values for arguments within a fixture function. | -| #[timeout(…)] | Sets a timeout for an asynchronous test. | -| #[files("glob_pattern",…)] | Injects file paths (or contents, with mode=) matching a glob pattern as test arguments. | +| Attribute | Core Purpose | +| -------------------------- | -------------------------------------------------------------------------------------------- | +| #[rstest] | Marks a function as an rstest test; enables fixture injection and parameterization. | +| #[fixture] | Defines a function that provides a test fixture (setup data or services). | +| #[case(…)] | Defines a single parameterized test case with specific input values. | +| #[values(…)] | Defines a list of values for an argument, generating tests for each value or combination. | +| #[once] | Marks a fixture to be initialized only once and shared (as a static reference) across tests. | +| #[future] | Simplifies async argument types by removing impl Future boilerplate. | +| #[awt] | (Function or argument level) Automatically .awaits future arguments in async tests. | +| #[from(original_name)] | Allows renaming an injected fixture argument in the test function. | +| #[with(…)] | Overrides default arguments of a fixture for a specific test. | +| #[default(…)] | Provides default values for arguments within a fixture function. | +| #[timeout(…)] | Sets a timeout for an asynchronous test. | +| #[files("glob_pattern",…)] | Injects file paths (or contents, with mode=) matching a glob pattern as test arguments. | By mastering `rstest`, Rust developers can significantly elevate the quality and efficiency of their testing practices, leading to more reliable, From fe38ec73e276159c278b20b0976d4ca58a710121 Mon Sep 17 00:00:00 2001 From: Payton McIntosh <pmcintosh@df12.net> Date: Sun, 24 May 2026 16:50:14 +0100 Subject: [PATCH 03/10] Add generated documentation navigation Add in-template documentation contents and repository layout references for generated Rust projects. Render the layout by project flavour so app projects include the release workflow and executable entrypoint while library projects document the library crate root. Cover the generated documents in the pytest-copier contract tests. --- template/docs/contents.md.jinja | 31 +++++++ template/docs/repository-layout.md.jinja | 107 +++++++++++++++++++++++ tests/test_template.py | 61 +++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 template/docs/contents.md.jinja create mode 100644 template/docs/repository-layout.md.jinja diff --git a/template/docs/contents.md.jinja b/template/docs/contents.md.jinja new file mode 100644 index 0000000..1117e31 --- /dev/null +++ b/template/docs/contents.md.jinja @@ -0,0 +1,31 @@ +# Documentation contents + +[Documentation contents](contents.md) is the index for {{ project_name }}'s +documentation set. + +## Project guides + +- [Repository layout](repository-layout.md) explains the generated project's + top-level files, directories, and ownership boundaries. +- [Documentation style guide](documentation-style-guide.md) defines the + spelling, structure, Markdown, Architecture Decision Record (ADR), Request + for Comments (RFC), and roadmap conventions used by this documentation set. + +## Rust reference material + +- [Reliable testing in Rust via dependency injection](reliable-testing-in-rust-via-dependency-injection.md) + explains how to keep tests deterministic by injecting environment, clock, + filesystem, and other external dependencies. +- [Rust doctest Don't Repeat Yourself guide](rust-doctest-dry-guide.md) + explains how to write maintainable, executable Rust documentation examples. +- [Rust testing with `rstest` fixtures](rust-testing-with-rstest-fixtures.md) + explains fixture-based, parameterized, and asynchronous testing with `rstest`. + +## Engineering practice + +- [Complexity antipatterns and refactoring strategies](complexity-antipatterns-and-refactoring-strategies.md) + explains cognitive complexity, the bumpy-road antipattern, and refactoring + approaches for maintainable code. +- [Scripting standards](scripting-standards.md) explains the preferred Python + scripting stack, command execution patterns, and test expectations for helper + scripts. diff --git a/template/docs/repository-layout.md.jinja b/template/docs/repository-layout.md.jinja new file mode 100644 index 0000000..0b9da84 --- /dev/null +++ b/template/docs/repository-layout.md.jinja @@ -0,0 +1,107 @@ +# Repository layout + +This document describes the generated {{ project_name }} repository layout. It +is the canonical reference for where source code, tests, configuration, +automation, and long-lived documentation belong. + +## Top-level tree + +The tree below shows the generated repository structure. It is intentionally +compact and omits build output such as `target/`. + +```plaintext +. +├── .cargo/ +│ └── config.toml +├── .github/ +│ ├── dependabot.yml +│ └── workflows/ +│ ├── ci.yml +{% if flavour == 'app' %} +│ └── release.yml +{% endif %} +├── docs/ +│ ├── contents.md +│ ├── repository-layout.md +│ └── ... +├── src/ +{% if flavour == 'app' %} +│ └── main.rs +{% else %} +│ └── lib.rs +{% endif %} +├── tests/ +│ └── stub.rs +├── AGENTS.md +├── Cargo.toml +├── LICENSE +├── Makefile +├── README.md +├── clippy.toml +├── codecov.yml +└── rust-toolchain.toml +``` + +## Path responsibilities + +- `.cargo/config.toml`: Configures Cargo defaults for local development, + including Linux linker and code-generation settings. +- `.github/dependabot.yml`: Configures automated dependency update checks. +- `.github/workflows/ci.yml`: Runs the generated project's continuous + integration checks. +{% if flavour == 'app' %} +- `.github/workflows/release.yml`: Builds and publishes binary release + artefacts for the application flavour. +{% endif %} +- `docs/`: Holds long-lived reference documentation, guides, style rules, and + design material. +- `docs/contents.md`: Indexes the documentation set and should be updated when + documentation files are added, renamed, or removed. +- `docs/repository-layout.md`: Documents the repository tree and path + responsibilities. +{% if flavour == 'app' %} +- `src/main.rs`: Contains the application entrypoint and top-level executable + wiring. +{% else %} +- `src/lib.rs`: Contains the library crate root and exported public API + surface. +{% endif %} +- `tests/`: Holds integration and behavioural tests that exercise public + behaviour. +- `tests/stub.rs`: Keeps the generated test directory valid until real tests + replace it. +- `AGENTS.md`: Provides repository-specific working instructions for agents and + contributors. +- `Cargo.toml`: Defines package metadata, dependencies, lint policy, and Cargo + configuration. +- `LICENSE`: Records the project licence text. +- `Makefile`: Provides the public build, lint, test, coverage, and + documentation validation commands. +- `README.md`: Introduces the project and gives the shortest useful + getting-started path. +- `clippy.toml`: Configures Clippy lint behaviour that is not expressed + directly in `Cargo.toml`. +- `codecov.yml`: Configures coverage reporting behaviour. +- `rust-toolchain.toml`: Pins the Rust toolchain channel and required + components. + +## Ownership boundaries + +- Keep generated source code under `src/`. Add modules below `src/` when a + feature grows beyond a small entrypoint or crate root. +- Keep black-box integration tests and externally observable workflow tests + under `tests/`. +- Keep reusable documentation under `docs/`. Update `docs/contents.md` whenever + a documentation file is added, renamed, or removed. +- Keep build and validation entrypoints in `Makefile`; prefer adding or + extending a Make target over documenting an ad hoc command. +- Keep continuous integration workflow changes under `.github/workflows/` and + dependency-update policy under `.github/dependabot.yml`. +- Do not commit generated build output such as `target/`, coverage artefacts, + or local editor state. + +## Updating this document + +Update this document when the repository gains a new top-level directory, a new +long-lived documentation category, a new workflow file, or a changed ownership +boundary that would otherwise make the tree misleading. diff --git a/tests/test_template.py b/tests/test_template.py index db4ce1c..d4cb5af 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -272,6 +272,8 @@ def test_generated_tooling_contracts( makefile = read_generated_text(project / "Makefile") cargo_config = read_generated_text(project / ".cargo/config.toml") ci_workflow = read_generated_text(project / ".github/workflows/ci.yml") + docs_contents = read_generated_text(project / "docs/contents.md") + repository_layout = read_generated_text(project / "docs/repository-layout.md") readme = read_generated_text(project / "README.md") rust_toolchain = read_generated_text(project / "rust-toolchain.toml") test_stub = read_generated_text(project / "tests/stub.rs") @@ -292,11 +294,14 @@ def test_generated_tooling_contracts( rust_toolchain=rust_toolchain, parsed_ci_workflow=parsed_ci_workflow, ci_workflow=ci_workflow, + docs_contents=docs_contents, + repository_layout=repository_layout, readme=readme, test_stub=test_stub, release_workflow=release_workflow, ) + @pytest.mark.parametrize( ("flavour", "expected_user_docs", "unexpected_user_docs"), [ @@ -453,7 +458,58 @@ def require_optional_mapping( pytest.fail(f"expected {label} key {key!r} to be a mapping when present") return value +def assert_documentation_navigation_contracts( + docs_contents: str, repository_layout: str, flavour: str +) -> None: + """Assert generated documentation navigation and layout contracts.""" + assert "[Documentation contents](contents.md)" in docs_contents, ( + "expected generated contents file to link to itself" + ) + assert "[Repository layout](repository-layout.md)" in docs_contents, ( + "expected generated contents file to link to the layout reference" + ) + assert "[Documentation style guide](documentation-style-guide.md)" in ( + docs_contents + ), "expected generated contents file to link to the style guide" + assert "docs/contents.md" in repository_layout, ( + "expected generated layout to document the contents file" + ) + assert "docs/repository-layout.md" in repository_layout, ( + "expected generated layout to document itself" + ) + assert "Cargo.toml" in repository_layout, ( + "expected generated layout to document Cargo metadata" + ) + assert "Makefile" in repository_layout, ( + "expected generated layout to document public command entrypoints" + ) + assert "checks. - `" not in repository_layout, ( + "expected generated layout bullets to render on separate lines" + ) + assert "responsibilities. - `" not in repository_layout, ( + "expected generated layout flavour bullets to render on separate lines" + ) + if flavour == APP: + assert ".github/workflows/release.yml" in repository_layout, ( + "expected app layout to document the release workflow" + ) + assert "src/main.rs" in repository_layout, ( + "expected app layout to document the executable entrypoint" + ) + assert "src/lib.rs" not in repository_layout, ( + "expected app layout to omit the library crate root" + ) + else: + assert ".github/workflows/release.yml" not in repository_layout, ( + "expected lib layout to omit the app release workflow" + ) + assert "src/lib.rs" in repository_layout, ( + "expected lib layout to document the library crate root" + ) + assert "src/main.rs" not in repository_layout, ( + "expected lib layout to omit the executable entrypoint" + ) def assert_generated_tooling_contracts( *, package: dict[str, Any], @@ -465,6 +521,8 @@ def assert_generated_tooling_contracts( rust_toolchain: str, parsed_ci_workflow: dict[str, Any], ci_workflow: str, + docs_contents: str, + repository_layout: str, readme: str, test_stub: str, release_workflow: str | None, @@ -474,6 +532,9 @@ def assert_generated_tooling_contracts( _assert_makefile_contracts(makefile) _assert_cargo_config_contracts(cargo_config, dev_target, rust_toolchain) _assert_ci_workflow_contracts(parsed_ci_workflow, ci_workflow, test_stub) + assert_documentation_navigation_contracts( + docs_contents, repository_layout, flavour + ) assert "Development builds use `mold` on Linux" in readme, ( "expected generated README to document mold for development builds" ) From ce3453b0cec66cf38ef57179b8d2cdf710bdfa7b Mon Sep 17 00:00:00 2001 From: Payton McIntosh <pmcintosh@df12.net> Date: Sun, 24 May 2026 17:46:36 +0100 Subject: [PATCH 04/10] Address template documentation review concerns Filter blank package metadata answers before rendering Cargo.toml, pin cross to an immutable revision in the generated app release workflow, and repair the rstest guide grammar issue. Split the generated tooling contract test into focused assertion helpers and add a regression case for blank keywords and categories. --- ...ur == 'app' %}release.yml{% endif %}.jinja | 4 +- template/Cargo.toml.jinja | 6 +- .../docs/rust-testing-with-rstest-fixtures.md | 2 +- tests/test_template.py | 88 ++----------------- 4 files changed, 16 insertions(+), 84 deletions(-) diff --git a/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja b/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja index a8626d0..5f0578b 100644 --- a/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja +++ b/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja @@ -8,7 +8,7 @@ on: env: BIN_NAME: {% endraw %}{{ package_name }}{% raw %} - CROSS_REVISION: v0.2.5 + CROSS_REVISION: 88f49ff79e777bef6d3564531636ee4d3cc2f8d2 jobs: build: @@ -63,7 +63,7 @@ jobs: if [ -x "$HOME/.cargo/bin/cross" ]; then exit 0 fi - cargo install cross --git https://github.com/cross-rs/cross --tag "${CROSS_REVISION}" + cargo install cross --git https://github.com/cross-rs/cross --rev "${CROSS_REVISION}" - name: Cache cargo registry uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae with: diff --git a/template/Cargo.toml.jinja b/template/Cargo.toml.jinja index 2280795..69f4e91 100644 --- a/template/Cargo.toml.jinja +++ b/template/Cargo.toml.jinja @@ -1,3 +1,5 @@ +{% set package_keyword_values = package_keywords.split(',') | map('trim') | reject('equalto', '') | list -%} +{% set package_category_values = package_categories.split(',') | map('trim') | reject('equalto', '') | list -%} [package] name = "{{ package_name }}" version = "0.1.0" @@ -7,8 +9,8 @@ license = "ISC" repository = "{{ repository_url }}" homepage = "{{ homepage_url }}" readme = "README.md" -keywords = [{% for keyword in package_keywords.split(',') %}"{{ keyword.strip() }}"{% if not loop.last %}, {% endif %}{% endfor %}] -categories = [{% for category in package_categories.split(',') %}"{{ category.strip() }}"{% if not loop.last %}, {% endif %}{% endfor %}] +keywords = [{% for keyword in package_keyword_values %}"{{ keyword }}"{% if not loop.last %}, {% endif %}{% endfor %}] +categories = [{% for category in package_category_values %}"{{ category }}"{% if not loop.last %}, {% endif %}{% endfor %}] [dependencies] diff --git a/template/docs/rust-testing-with-rstest-fixtures.md b/template/docs/rust-testing-with-rstest-fixtures.md index a8146ce..d657da1 100644 --- a/template/docs/rust-testing-with-rstest-fixtures.md +++ b/template/docs/rust-testing-with-rstest-fixtures.md @@ -716,7 +716,7 @@ async fn async_data_fetcher() -> String { The example above uses `async_std::task::sleep` purely as a convenient stand-in; the fixture may call into whichever runtime the project adopts because - `rstest` simply awaits the returned future. +`rstest` simply awaits the returned future. ### B. Writing asynchronous tests (`async fn` with `#[rstest]`) diff --git a/tests/test_template.py b/tests/test_template.py index d4cb5af..44b8f24 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -302,83 +302,6 @@ def test_generated_tooling_contracts( ) -@pytest.mark.parametrize( - ("flavour", "expected_user_docs", "unexpected_user_docs"), - [ - ( - APP, - "application behaviour or user\ninterface", - "behaviour or public API that a\nconsumer of the library", - ), - ( - LIB, - "behaviour or public API that a\nconsumer of the library", - "application behaviour or user\ninterface", - ), - ], -) -def test_generated_agent_instructions_include_quality_contracts( - tmp_path: Path, - copier: CopierFixture, - flavour: str, - expected_user_docs: str, - unexpected_user_docs: str, -) -> None: - """Generated AGENTS.md captures testing and documentation expectations.""" - project = render_project( - tmp_path, - copier, - project_name="AgentDocsExample", - package_name="agent_docs_example", - flavour=flavour, - ) - agents = (project / "AGENTS.md").read_text(encoding="utf-8") - - assert "unit tests using `rstest`" in agents, ( - "expected AGENTS.md to require rstest unit coverage" - ) - assert "behavioural tests using `rstest-bdd`" in agents, ( - "expected AGENTS.md to require rstest-bdd behavioural coverage" - ) - assert "Cover happy paths" in agents, ( - "expected AGENTS.md to require happy-path coverage" - ) - assert "unhappy paths, and relevant edge cases" in agents, ( - "expected AGENTS.md to require happy, unhappy, and edge-case coverage" - ) - assert "externally observable workflows" in agents, ( - "expected AGENTS.md to describe when end-to-end tests are required" - ) - assert "property tests with `proptest`" in agents, ( - "expected AGENTS.md to require proptest where invariants apply" - ) - assert "bounded model checker with `kani`" in agents, ( - "expected AGENTS.md to require kani where invariants apply" - ) - assert "exhaustive proof with `verus`" in agents, ( - "expected AGENTS.md to require Verus for contractual logic" - ) - assert "substantive, rigorous, and well-founded" in agents, ( - "expected AGENTS.md to reject vacuous proof obligations" - ) - assert "Record design decisions in the design document" in agents, ( - "expected AGENTS.md to require design-decision documentation" - ) - assert "ADR document following the documentation style\nguide" in agents, ( - "expected AGENTS.md to require substantive ADRs" - ) - assert expected_user_docs in agents, ( - "expected AGENTS.md to render flavour-specific users-guide wording" - ) - assert unexpected_user_docs not in agents, ( - "expected AGENTS.md to omit the other flavour's users-guide wording" - ) - assert "relevant component architecture document" in agents, ( - "expected AGENTS.md to require component architecture documentation" - ) - assert "docs/developers-guide.md" in agents, ( - "expected AGENTS.md to require developers-guide convention updates" - ) def test_generated_structured_file_snapshots( tmp_path: Path, copier: CopierFixture, snapshot: SnapshotAssertion ) -> None: @@ -726,8 +649,15 @@ def _assert_release_workflow_contracts(release_workflow: str) -> None: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" in release_workflow ), "expected app release workflow to use pinned upload-artifact action" - assert "CROSS_REVISION: v0.2.5" in release_workflow, ( - "expected app release workflow to pin cross revision" + cross_revision = "88f49ff79e777bef6d3564531636ee4d3cc2f8d2" + assert f"CROSS_REVISION: {cross_revision}" in release_workflow, ( + "expected app release workflow to pin cross to an immutable revision" + ) + assert 'cargo install cross --git https://github.com/cross-rs/cross --rev "$' in ( + release_workflow + ), "expected app release workflow to install cross by immutable revision" + assert "--tag" not in release_workflow, ( + "expected app release workflow not to install cross by mutable tag" ) assert "aarch64-pc-windows-gnullvm" not in release_workflow, ( "expected app release workflow to omit unsupported cross targets" From 6d9a974f1be5eae7ee13b1112cde4385c235cd49 Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Sun, 24 May 2026 19:47:04 +0200 Subject: [PATCH 05/10] Clear release build rustflags Set an empty `RUSTFLAGS` environment for generated release artifact builds so `cross build` does not inherit repository-local mold linker flags from `.cargo/config.toml`. --- .../{% if flavour == 'app' %}release.yml{% endif %}.jinja | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja b/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja index 5f0578b..d69ba9d 100644 --- a/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja +++ b/template/.github/workflows/{% if flavour == 'app' %}release.yml{% endif %}.jinja @@ -76,6 +76,10 @@ jobs: - name: Build release binary # Use +stable to override rust-toolchain.toml (which specifies nightly # with Cranelift for development) and ensure release builds use stable. + env: + # Build release artifacts without repository-local linker flags, + # including mold rustflags from .cargo/config.toml. + RUSTFLAGS: "" run: cross +stable build --release --target ${{ matrix.target }} - name: Prepare artifact run: | From 89d6ea2d6ea28156f5c5a9c7ee610fc3cd50023b Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Sun, 24 May 2026 19:58:16 +0200 Subject: [PATCH 06/10] Add observability guidance to generated AGENTS Document `tracing` as the default logging and diagnostics facade and require `metrics` for usage, uptake, failure, and mitigation metric emission in generated project instructions. --- template/AGENTS.md.jinja | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/template/AGENTS.md.jinja b/template/AGENTS.md.jinja index 5398564..9a334d9 100644 --- a/template/AGENTS.md.jinja +++ b/template/AGENTS.md.jinja @@ -262,6 +262,30 @@ project: - Consume fallible fixtures in `rstest` by **making the test return `Result`** and applying `?` to the fixture. +### Observability + +- Use `tracing` for logging and diagnostics. Prefer structured + `tracing::{trace, debug, info, warn, error}` events and spans over + `println!`, `eprintln!`, or direct `log` macros. Add fields for identifiers, + state, and error context so downstream subscribers can filter and correlate + events without parsing message text. +- Use `#[tracing::instrument]` or explicit spans around request handling, + command execution, retries, background jobs, and other meaningful units of + work. Do not hold `Span::enter()` guards across `.await`; use + `Instrument::instrument` or scoped synchronous spans instead. +- Use the `metrics` crate for metric emission where usage, uptake, failure, + or mitigation metrics are required. Prefer `counter!` for cumulative events, + `gauge!` for values that rise and fall, and `histogram!` for distributions + such as latency or payload size. +- Describe emitted metrics with `describe_counter!`, `describe_gauge!`, or + `describe_histogram!` where the unit or purpose is not obvious from the + metric name. Keep metric names stable and labels low-cardinality; do not put + user input, request IDs, paths with unbounded parameters, or raw error + strings into labels. +- Libraries may emit `metrics` and `tracing` instrumentation, but must not + install global recorders or subscribers. Applications should initialise + exporters/subscribers once, as early as practical in startup. + ## Markdown Guidance - Validate Markdown files using `make markdownlint`. From 058cb909cc05fc86cbb3147efa640718e7eeae4b Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Sun, 24 May 2026 20:11:36 +0200 Subject: [PATCH 07/10] Document generated project tooling Add rendered user and developer guides that describe public Makefile targets, cargo-nextest fallback behaviour, Whitaker linting, and Cranelift debug code generation. --- template/README.md.jinja | 12 +++++-- template/docs/contents.md.jinja | 4 +++ template/docs/developers-guide.md.jinja | 21 ++++++++++++ template/docs/repository-layout.md.jinja | 6 ++++ .../docs/rust-testing-with-rstest-fixtures.md | 2 +- template/docs/users-guide.md.jinja | 34 +++++++++++++++++++ 6 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 template/docs/developers-guide.md.jinja create mode 100644 template/docs/users-guide.md.jinja diff --git a/template/README.md.jinja b/template/README.md.jinja index 9cb13cc..33529e6 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -2,11 +2,19 @@ This is a generated project using [Copier](https://copier.readthedocs.io/). +## Project Guides + +- [User guide](docs/users-guide.md) documents public Makefile targets, the + `cargo-nextest` fallback, and release/build behaviour. +- [Developer guide](docs/developers-guide.md) documents contributor tooling, + including Whitaker linting and Cranelift debug code generation. + ## Build Linkers Development builds use `mold` on Linux through `.cargo/config.toml` so local -debug builds link quickly. Coverage generation uses `lld` instead because the -LLVM coverage tools expect LLVM-compatible linker behaviour. +debug builds link quickly. Debug code generation uses Cranelift for faster +local checks. Coverage generation uses `lld` instead because the LLVM coverage +tools expect LLVM-compatible linker behaviour. Install both linkers before running the full workflow locally: diff --git a/template/docs/contents.md.jinja b/template/docs/contents.md.jinja index 1117e31..6e036bc 100644 --- a/template/docs/contents.md.jinja +++ b/template/docs/contents.md.jinja @@ -5,6 +5,10 @@ documentation set. ## Project guides +- [User guide](users-guide.md) explains how to use the generated project and + its public build and test commands. +- [Developer guide](developers-guide.md) explains the local workflow and + implementation tooling for contributors. - [Repository layout](repository-layout.md) explains the generated project's top-level files, directories, and ownership boundaries. - [Documentation style guide](documentation-style-guide.md) defines the diff --git a/template/docs/developers-guide.md.jinja b/template/docs/developers-guide.md.jinja new file mode 100644 index 0000000..67aa297 --- /dev/null +++ b/template/docs/developers-guide.md.jinja @@ -0,0 +1,21 @@ +# Developer Guide + +This guide explains the contributor workflow for the generated +{{ project_name }} project. + +## Local Workflow + +Use `make all` as the public entrypoint for formatting, linting, and tests. +`make lint` runs rustdoc, Clippy, and Whitaker. `make test` prefers +`cargo nextest run` and falls back to `cargo test` when cargo-nextest is not +available. `make coverage` uses `cargo llvm-cov` with `lld`. + +## Tooling + +Development builds use Cranelift for debug code generation. On Linux targets, +`.cargo/config.toml` configures clang to link with `mold` so debug builds link +quickly. Coverage generation uses `lld` because LLVM coverage tooling expects +LLVM-compatible linker behaviour. + +Install `clang`, `lld`, and `mold` before running the full generated workflow +locally on Linux. diff --git a/template/docs/repository-layout.md.jinja b/template/docs/repository-layout.md.jinja index 0b9da84..b8b4021 100644 --- a/template/docs/repository-layout.md.jinja +++ b/template/docs/repository-layout.md.jinja @@ -22,7 +22,9 @@ compact and omits build output such as `target/`. {% endif %} ├── docs/ │ ├── contents.md +│ ├── developers-guide.md │ ├── repository-layout.md +│ ├── users-guide.md │ └── ... ├── src/ {% if flavour == 'app' %} @@ -57,6 +59,10 @@ compact and omits build output such as `target/`. design material. - `docs/contents.md`: Indexes the documentation set and should be updated when documentation files are added, renamed, or removed. +- `docs/users-guide.md`: Explains how to use the generated project and its + public build and test commands. +- `docs/developers-guide.md`: Explains the contributor workflow and local + tooling used to work on the generated project. - `docs/repository-layout.md`: Documents the repository tree and path responsibilities. {% if flavour == 'app' %} diff --git a/template/docs/rust-testing-with-rstest-fixtures.md b/template/docs/rust-testing-with-rstest-fixtures.md index d657da1..166e69d 100644 --- a/template/docs/rust-testing-with-rstest-fixtures.md +++ b/template/docs/rust-testing-with-rstest-fixtures.md @@ -1341,7 +1341,7 @@ provided by `rstest`: | Attribute | Core Purpose | | -------------------------- | -------------------------------------------------------------------------------------------- | -| #[rstest] | Marks a function as an rstest test; enables fixture injection and parameterization. | +| #[rstest] | Marks a function as an `rstest` test; enables fixture injection and parameterization. | | #[fixture] | Defines a function that provides a test fixture (setup data or services). | | #[case(…)] | Defines a single parameterized test case with specific input values. | | #[values(…)] | Defines a list of values for an argument, generating tests for each value or combination. | diff --git a/template/docs/users-guide.md.jinja b/template/docs/users-guide.md.jinja new file mode 100644 index 0000000..c23bbac --- /dev/null +++ b/template/docs/users-guide.md.jinja @@ -0,0 +1,34 @@ +# User Guide + +This guide explains how to use the generated {{ project_name }} project after +rendering it from the template. + +## Generated Tooling + +Generated projects use Rust 2024, a pinned nightly toolchain, strict lint +settings, and documented starter code. Library projects render `src/lib.rs`. +Application projects render `src/main.rs`, release automation, and +`[package.metadata.binstall]` metadata for binary installation. + +Development builds use Cranelift for debug code generation. On Linux targets, +`.cargo/config.toml` configures clang to link with `mold` so local debug builds +link quickly. Coverage generation uses `lld` instead because LLVM coverage +tools expect LLVM-compatible linker behaviour. + +## Makefile Targets + +The generated `Makefile` exposes these public targets: + +- `make all` runs formatting checks, linting, and tests. +- `make check-fmt` verifies Rust formatting. +- `make lint` runs rustdoc, Clippy, and Whitaker with warnings denied. +- `make test` runs `cargo nextest run` when cargo-nextest is installed and + falls back to `cargo test` otherwise. Library projects also run doctests. +- `make build` builds the debug target. +- `make release` builds the release target. +- `make coverage` writes `lcov.info` using `cargo llvm-cov` and `lld`. +- `make markdownlint` checks Markdown files. +- `make nixie` validates Mermaid diagrams. + +Install `clang`, `lld`, and `mold` before running the full generated workflow +locally on Linux. From bc51dfe301720210832aa9e713427c4e79af6df9 Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Sun, 24 May 2026 20:15:33 +0200 Subject: [PATCH 08/10] Fix rstest table article --- template/docs/rust-testing-with-rstest-fixtures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/docs/rust-testing-with-rstest-fixtures.md b/template/docs/rust-testing-with-rstest-fixtures.md index 166e69d..3a04b89 100644 --- a/template/docs/rust-testing-with-rstest-fixtures.md +++ b/template/docs/rust-testing-with-rstest-fixtures.md @@ -1341,7 +1341,7 @@ provided by `rstest`: | Attribute | Core Purpose | | -------------------------- | -------------------------------------------------------------------------------------------- | -| #[rstest] | Marks a function as an `rstest` test; enables fixture injection and parameterization. | +| #[rstest] | Marks a function as a `rstest` test; enables fixture injection and parameterization. | | #[fixture] | Defines a function that provides a test fixture (setup data or services). | | #[case(…)] | Defines a single parameterized test case with specific input values. | | #[values(…)] | Defines a list of values for an argument, generating tests for each value or combination. | From 1025275d54e11f8f2310aab7e17768ff92838889 Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Sun, 24 May 2026 20:26:20 +0200 Subject: [PATCH 09/10] Minimise generated README documentation links Keep the templated README focused on documentation entry points and move technical tooling detail out of its contract. --- template/README.md.jinja | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/template/README.md.jinja b/template/README.md.jinja index 33529e6..d3b637a 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -2,28 +2,8 @@ This is a generated project using [Copier](https://copier.readthedocs.io/). -## Project Guides +## Documentation -- [User guide](docs/users-guide.md) documents public Makefile targets, the - `cargo-nextest` fallback, and release/build behaviour. -- [Developer guide](docs/developers-guide.md) documents contributor tooling, - including Whitaker linting and Cranelift debug code generation. - -## Build Linkers - -Development builds use `mold` on Linux through `.cargo/config.toml` so local -debug builds link quickly. Debug code generation uses Cranelift for faster -local checks. Coverage generation uses `lld` instead because the LLVM coverage -tools expect LLVM-compatible linker behaviour. - -Install both linkers before running the full workflow locally: - -```sh -sudo apt-get install clang lld mold -``` - -Run coverage with: - -```sh -make coverage -``` +- [Documentation contents](docs/contents.md) +- [User guide](docs/users-guide.md) +- [Developer guide](docs/developers-guide.md) From ce6ca40be656ff74c33dd9d26abcb9334d66244b Mon Sep 17 00:00:00 2001 From: leynos <leynos@troubledskies.net> Date: Mon, 25 May 2026 01:25:16 +0200 Subject: [PATCH 10/10] Align template tests after rebase Update the generated README contract and structured workflow snapshot to match the rebased template output. --- tests/__snapshots__/test_template.ambr | 7 +++++-- tests/test_template.py | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/__snapshots__/test_template.ambr b/tests/__snapshots__/test_template.ambr index ceb8699..b6d571d 100644 --- a/tests/__snapshots__/test_template.ambr +++ b/tests/__snapshots__/test_template.ambr @@ -237,7 +237,7 @@ 'release_workflow': dict({ 'env': dict({ 'BIN_NAME': 'snapshot_example', - 'CROSS_REVISION': 'v0.2.5', + 'CROSS_REVISION': '88f49ff79e777bef6d3564531636ee4d3cc2f8d2', }), 'jobs': dict({ 'build': dict({ @@ -273,7 +273,7 @@ if [ -x "$HOME/.cargo/bin/cross" ]; then exit 0 fi - cargo install cross --git https://github.com/cross-rs/cross --tag "${CROSS_REVISION}" + cargo install cross --git https://github.com/cross-rs/cross --rev "${CROSS_REVISION}" ''', }), @@ -294,6 +294,9 @@ }), }), dict({ + 'env': dict({ + 'RUSTFLAGS': '', + }), 'name': 'Build release binary', 'run': 'cross +stable build --release --target ${{ matrix.target }}', }), diff --git a/tests/test_template.py b/tests/test_template.py index 44b8f24..bbfad17 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -458,11 +458,14 @@ def assert_generated_tooling_contracts( assert_documentation_navigation_contracts( docs_contents, repository_layout, flavour ) - assert "Development builds use `mold` on Linux" in readme, ( - "expected generated README to document mold for development builds" + assert "[Documentation contents](docs/contents.md)" in readme, ( + "expected generated README to link to the documentation contents" ) - assert "Coverage generation uses `lld`" in readme, ( - "expected generated README to document lld for coverage" + assert "[User guide](docs/users-guide.md)" in readme, ( + "expected generated README to link to the user guide" + ) + assert "[Developer guide](docs/developers-guide.md)" in readme, ( + "expected generated README to link to the developer guide" ) if release_workflow is not None: _assert_release_workflow_contracts(release_workflow)