Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ __pycache__/
# Untracked cache of the estate-wide en-GB-oxendict dictionary.
.typos-oxendict-base.json
.typos-oxendict-base.toml
.grepai/
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The template requires **Copier 9.0** or later to avoid incompatibilities.
enabled【F:template/Cargo.toml†L1-L9】.
- **Pinned toolchain** file specifying a configurable nightly release
【F:template/rust-toolchain.toml.jinja†L1-L3】.
- **Optional Polonius support**, recommended and enabled by default for
applications, with coherent Cargo, Makefile, coverage, release, and agent
guidance that can be disabled for wider library compiler compatibility.
- **Project metadata prompts** for repository URL, homepage, crates.io keywords,
crates.io categories, nightly date, and optional Linux development target.
- **Fast generated tooling** including Cranelift debug code generation, Linux
Expand Down Expand Up @@ -115,6 +118,7 @@ flowchart LR
Additional details are in [`docs/testing.md`](docs/testing.md).

User-facing generated-project behaviour is documented in
[`docs/users-guide.md`](docs/users-guide.md). Parent-template development
requirements are documented in
[`docs/users-guide.md`](docs/users-guide.md), with upgrade guidance in the
[`0.2.0 migration guide`](docs/migrations/0.2.0.md). Parent-template
development requirements are documented in
[`docs/developers-guide.md`](docs/developers-guide.md).
12 changes: 12 additions & 0 deletions copier.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ flavour:
default: lib
help: 'What type of project? (lib = reusable library, app = executable binary)'

enable_polonius:
type: bool
default: "{{ flavour == 'app' }}"
help: >-
Enable the nightly Polonius borrow checker (-Zpolonius=next)? Recommended
for applications; libraries may disable it to retain wider compiler
compatibility.

codescene_project_id:
type: str
default: ''
Expand Down Expand Up @@ -105,6 +113,10 @@ rust_nightly_date:
default: '2025-06-10'
help: 'Rust nightly toolchain date (YYYY-MM-DD)'
placeholder: 'e.g. 2025-06-10'
validator: >-
{% if not (rust_nightly_date | regex_search('^[0-9]{4}-[0-9]{2}-[0-9]{2}\\Z')) %}
Rust nightly date must use YYYY-MM-DD.
{% endif %}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

dev_target:
type: str
Expand Down
52 changes: 52 additions & 0 deletions docs/adr-004-optional-polonius-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# ADR-004: Make Polonius support optional

## Status

Accepted.

## Context

Polonius permits borrow-centric internal APIs that the stable non-lexical
lifetime analysis rejects, but its alpha analysis still requires a dated
nightly toolchain and `-Zpolonius=next`. Applications can usually accept that
binding, while reusable libraries often need wider compiler compatibility.
Cargo build flags are also replaced whenever tooling supplies `RUSTFLAGS`, so
an enabled project must preserve the Polonius flag across every such build
surface.

## Decision

An `enable_polonius` Copier choice defaults to enabled for applications and
disabled for libraries, with either default available for explicit override.
Enabled projects propagate `-Zpolonius=next` through Cargo, Make, coverage,
Continuous Integration (CI) and release builds. This consistent propagation
establishes a single, explicit toolchain contract across every supported build
surface. Enabled projects require a dated nightly toolchain, and every explicit
`RUSTFLAGS` value must remain synchronized with the configured Polonius flag.

## Progress and outcome

The template now renders the option, pinned toolchain, compiler flags, policy
guidance, CI and release configuration for the selected state. Parent-template
contract and compilation tests cover enabled and disabled applications and
libraries. The implementation and its validation are complete; future
generated projects retain an explicit opt-out or opt-in as their compatibility
requirements change.

## Risks

- A pinned nightly can become unavailable or acquire regressions and therefore
requires deliberate upgrades.
- A new build path that overrides `RUSTFLAGS` can silently omit Polonius unless
its rendered contract is extended and tested.
- Enabling borrow-centric APIs can make source builds fail under stable Rust or
plain nightly without `-Zpolonius=next`.

## Consequences

- Generated applications adopt Polonius by default; generated libraries keep
wider compiler compatibility by default.
- Opted-in projects document their nightly requirement and preserve the flag
across supported build paths.
- Maintainers must treat the Copier answer, dated channel, and explicit flag
propagation as one toolchain policy when updating generated projects.
8 changes: 5 additions & 3 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ so Python test dependencies must be added to that invocation before tests
import them. Keep long runs logged through `tee` into `/tmp`, following the
example in `AGENTS.md`.

The tests render both library and application projects, run generated public
gates such as `make all`, validate generated Makefiles with `mbake`, and parse
generated `Cargo.toml` files as TOML.
The tests render both library and application projects with Polonius enabled
and disabled, run generated public gates such as `make all`, validate generated
Makefiles with `mbake`, and parse generated Cargo and workflow configuration.
The Polonius contract checks every `RUSTFLAGS` override, including Linux mold
linking, LLVM coverage, and cross-platform application releases.

## Formatting, Linting, and Type Checking

Expand Down
23 changes: 23 additions & 0 deletions docs/execplans/rust-project-enhancements.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ Pinned GitHub Action SHAs drift over time. The mitigation is to resolve the
current default-branch commit for each action repository during implementation
and record the resulting pins in the plan and pull request validation notes.

Polonius remains a nightly-only alpha analysis, and explicit `RUSTFLAGS`
replace Cargo's build-level flags. Generated projects that opt in therefore
depend on a dated nightly, and every build path that overrides `RUSTFLAGS` must
re-state `-Zpolonius=next`. Contract tests cover the supported override paths;
new paths must extend those tests.

## Progress

- [x] 2026-05-23: Confirmed branch `rust-project-enhancements` and clean
Expand Down Expand Up @@ -107,6 +113,11 @@ and record the resulting pins in the plan and pull request validation notes.
was 9 passed in 18.54 seconds.
- [x] 2026-05-23: Pushed `rust-project-enhancements` and created draft pull
request <https://github.com/leynos/agent-template-rust/pull/32>.
- [x] 2026-08-02: Extended the completed tooling import with an optional
`enable_polonius` choice, application-recommended and library-compatible
defaults, flag propagation across Cargo, Make, coverage, CI, and release
builds, generated policy guidance, and enabled/disabled contract and
compilation coverage. Recorded the extension in ADR-004.

## Surprises & Discoveries

Expand Down Expand Up @@ -140,6 +151,12 @@ Validate generated projects through pytest-copier by invoking the generated
public `make all` target rather than stitching together private commands from
the parent repository.

Expose Polonius as a Copier choice that defaults on for applications and off
for libraries. When enabled, use the dated channel from `rust-toolchain.toml`
and preserve `-Zpolonius=next` wherever `RUSTFLAGS` are replaced; this permits
borrow-centric application internals without imposing nightly compatibility on
generated libraries. ADR-004 records the alternatives and accepted risks.

## Outcomes & Retrospective

The template now renders projects with Cranelift debug codegen, Linux mold
Expand All @@ -148,3 +165,9 @@ cargo-binstall metadata for app projects, Whitaker linting with CI caching,
SHA-pinned CI actions, and pytest-copier coverage that runs generated
`make all` gates. The branch was pushed and draft pull request
<https://github.com/leynos/agent-template-rust/pull/32> was opened for review.

The later Polonius extension is also complete. Generated applications now opt
in by default, libraries retain wider compiler compatibility by default, and
all supported flag-override paths preserve the selected compiler contract.
Rendered-project tests exercise both choices, and generated documentation
explains the nightly dependency and borrow-centric design policy.
27 changes: 27 additions & 0 deletions docs/migrations/0.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Migrating to 0.2.0

Version 0.2.0 adds the `enable_polonius` Copier prompt. The prompt controls
whether generated projects use the nightly Polonius borrow-checking analysis
through `-Zpolonius=next`.

## Update an existing project

1. Commit or otherwise preserve local changes before updating.
2. Run `copier update` using the same answers file and template source as the
existing project.
3. Review the new `enable_polonius` answer before accepting the rendered
changes. Applications default to enabled; libraries default to disabled.
Either value can be overridden explicitly.
4. Review changes to the Rust toolchain, Cargo configuration, Makefile,
coverage and release workflows as one toolchain-policy update.
5. Run the generated project's full quality gates after resolving Copier
conflicts.

Enabling Polonius binds the generated project to its configured dated nightly
toolchain. Every build path that sets `RUSTFLAGS` explicitly must include
`-Zpolonius=next` because an explicit value replaces Cargo's build-level
flags. Disabling Polonius retains the wider compiler compatibility expected by
libraries, although the template still uses its configured nightly toolchain.

The rationale and accepted constraints are recorded in
[ADR-004](../adr-004-optional-polonius-support.md).
13 changes: 13 additions & 0 deletions docs/users-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ metadata used in the generated `Cargo.toml`:

- `flavour` selects `lib` or `app` and determines the generated structure and
release metadata.
- `enable_polonius` enables the nightly Polonius borrow checker
(`-Zpolonius=next`). It defaults to enabled for applications, where
borrow-centric internal APIs can evolve with the project, and disabled for
libraries, which commonly need wider compiler compatibility. Either default
can be overridden. Existing projects should follow the
[0.2.0 migration guide](migrations/0.2.0.md) when adopting this prompt.
- `package_description` becomes `[package].description`.
- `repository_url` becomes `[package].repository` and is used by generated
app projects for cargo-binstall release URLs.
Expand All @@ -35,6 +41,13 @@ settings, and documented starter code. Library projects render `src/lib.rs`.
Application projects render `src/main.rs`, `src/lib.rs`, release automation, and
`[package.metadata.binstall]` metadata for binary installation.

When Polonius is enabled, the generated Cargo configuration, Makefile, coverage
workflows, and application release workflow preserve `-Zpolonius=next`. The
Linux target-specific `mold` flags repeat it because Cargo selects target
rustflags instead of merging them with `[build].rustflags`. The generated
project also includes `docs/polonius.md` and matching `AGENTS.md` guidance for
borrow-centric APIs.

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
Expand Down
15 changes: 14 additions & 1 deletion template/.cargo/config.toml.jinja
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
{% if enable_polonius -%}
# Enable the Polonius alpha borrow-checking analysis by default so Cargo,
# rust-analyzer, and verification tools agree about what borrows are legal.
# An inherited RUSTFLAGS value overrides this table; the generated Makefile and
# workflows therefore re-state -Zpolonius=next whenever they set RUSTFLAGS.
[build]
rustflags = ["-Zpolonius=next"]
rustdocflags = ["-Zpolonius=next"]
{% endif -%}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
[unstable]
codegen-backend = true

Expand All @@ -7,7 +16,11 @@ codegen-backend = "cranelift"
{% if dev_target and 'linux' in dev_target -%}
[target.{{ dev_target }}]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
{% if enable_polonius -%}
# Cargo selects target rustflags instead of merging them with build.rustflags,
# so this target-specific mold configuration must repeat the Polonius flag.
{% endif -%}
rustflags = [{% if enable_polonius %}"-Zpolonius=next", {% endif %}"-C", "link-arg=-fuse-ld=mold"]
{% elif dev_target -%}
# mold is Linux-only. For faster linking on {{ dev_target }}, add a
# platform-specific block here. Common options include lld via clang on macOS
Expand Down
4 changes: 2 additions & 2 deletions template/.github/workflows/ci.yml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ jobs:
- name: Log coverage linker configuration
run: |
echo "Coverage linker: clang"
echo "Coverage RUSTFLAGS: -C link-arg=-fuse-ld=lld"
echo "Coverage RUSTFLAGS:{% endraw %}{% if enable_polonius %} -Zpolonius=next{% endif %}{% raw %} -C link-arg=-fuse-ld=lld"
echo "Coverage CFLAGS: -fuse-ld=lld"
echo "Coverage LDFLAGS: -fuse-ld=lld"
- name: Test and Measure Coverage
uses: leynos/shared-actions/.github/actions/generate-coverage@18bed1ca49a6de3d8882bd72635a32ae3f023d57
env:
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER: clang
RUSTFLAGS: -C link-arg=-fuse-ld=lld
RUSTFLAGS:{% endraw %}{% if enable_polonius %} -Zpolonius=next{% endif %}{% raw %} -C link-arg=-fuse-ld=lld
CFLAGS: -fuse-ld=lld
LDFLAGS: -fuse-ld=lld
with:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Coverage (main)
{% raw %}name: Coverage (main)

# CodeScene accepts `cs-coverage upload` only for analysed branches, so
# main-branch coverage is uploaded here on push; pull requests generate
Expand Down Expand Up @@ -48,14 +48,14 @@ jobs:
- name: Log coverage linker configuration
run: |
echo "Coverage linker: clang"
echo "Coverage RUSTFLAGS: -C link-arg=-fuse-ld=lld"
echo "Coverage RUSTFLAGS:{% endraw %}{% if enable_polonius %} -Zpolonius=next{% endif %}{% raw %} -C link-arg=-fuse-ld=lld"
echo "Coverage CFLAGS: -fuse-ld=lld"
echo "Coverage LDFLAGS: -fuse-ld=lld"
- name: Test and Measure Coverage
uses: leynos/shared-actions/.github/actions/generate-coverage@18bed1ca49a6de3d8882bd72635a32ae3f023d57
env:
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER: clang
RUSTFLAGS: -C link-arg=-fuse-ld=lld
RUSTFLAGS:{% endraw %}{% if enable_polonius %} -Zpolonius=next{% endif %}{% raw %} -C link-arg=-fuse-ld=lld
CFLAGS: -fuse-ld=lld
LDFLAGS: -fuse-ld=lld
with:
Expand All @@ -71,3 +71,4 @@ jobs:
format: lcov
access-token: ${{ env.CS_ACCESS_TOKEN }}
installer-checksum: ${{ vars.CODESCENE_CLI_SHA256 }}
{% endraw %}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
persist-credentials: false
- uses: leynos/shared-actions/.github/actions/setup-rust@18bed1ca49a6de3d8882bd72635a32ae3f023d57
with:
toolchain: stable
toolchain: {% endraw %}{% if enable_polonius %}nightly-{{ rust_nightly_date }}{% else %}stable{% endif %}{% raw %}
- name: Cache cross binary
id: cache-cross
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
Expand All @@ -74,13 +74,11 @@ jobs:
restore-keys: |
${{ runner.os }}-cargo-${{ matrix.target }}-
- name: Build release binary
# Use +stable to override rust-toolchain.toml (which specifies nightly
# with Cranelift for development) and ensure release builds use stable.
# Select the generated project's supported compiler explicitly and
# omit repository-local linker and Cranelift configuration.
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 }}
RUSTFLAGS: {% endraw %}{% if enable_polonius %}-Zpolonius=next{% else %}""{% endif %}{% raw %}
run: cross +{% endraw %}{% if enable_polonius %}nightly-{{ rust_nightly_date }}{% else %}stable{% endif %}{% raw %} build --release --target ${{ matrix.target }}
- name: Prepare artifact
run: |
mkdir -p artifacts/${{ matrix.os }}-${{ matrix.arch }}
Expand Down
19 changes: 19 additions & 0 deletions template/AGENTS.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,25 @@ This repository is written in Rust and uses Cargo for building and dependency
management. Contributors should follow these best practices when working on the
project:

{% if enable_polonius -%}
### Borrow checker: Polonius, not NLL

This repository compiles with the Polonius alpha analysis
(`-Zpolonius=next`) on the dated nightly pinned in `rust-toolchain.toml`.
Internal APIs should be borrow-centric: lookups and get-or-create accessors
return references, keys are cloned only on insertion, and error context is
built lazily. See `docs/polonius.md` for the full contract.

- Never rewrite a site tagged `POLONIUS(...)` into a double lookup, an
`entry(key.clone())` call, id or index indirection, or eager error context.
- Do not add defensive clones merely to end a borrow without first compiling
the natural borrow-returning form under the project toolchain.
- Respect `POLONIUS-REFUSED(...)` tags. Polonius does not relax aliasing,
suspension-point, lock-boundary, or thread-boundary constraints.
- Verify new borrow-sensitive code with the project toolchain and classify it
with and without `-Zpolonius=next` as described in `docs/polonius.md`.

{% endif -%}
- Run `make check-fmt`, `make lint`, and `make test` before committing. These
targets wrap the following commands, so contributors understand the exact
behaviour and policy enforced:
Expand Down
Loading
Loading