When I first wrote about my Rust 2026 GitHub starter template, the main idea was simple: stop rebuilding the same Rust project infrastructure for every new repository.
That article described an early version of the template. Since then, the project has changed substantially. The most important work has not been adding more tools or more workflow jobs. It has been removing duplicated work, making defaults safer, and ensuring that the generated project contains only the pieces it actually needs.
This update covers v0.4.1 (November 9, 2026). It is a follow-up to the original article, not a replacement for it: the original explains where the project started; this one explains what the template has become.
- Repository: github.com/d-oit/rust-2026-template
- Original article: GitHub rust-2026-template — my Rust starter in 2026
- Full template changelog: CHANGELOG-TEMPLATE.md
- Getting started: QUICKSTART.md
What changed since the original article?
The biggest changes are architectural:
- One canonical owner for blocking quality checks, instead of several jobs repeating the same work.
- Validated project profiles that generate a smaller, purpose-built repository.
- Agent skills selected according to the project profile rather than copied wholesale into every project.
- A fail-closed CI aggregate and a least-privilege workflow permission model.
- A separate, explicit security policy for the excluded fuzz workspace.
- More reliable release and maintenance automation, including action-runtime updates and deterministic architecture diagrams.
Let's look at the reasoning behind those changes.
1. CI: one owner per quality check
A CI pipeline can become slower and harder to understand simply by accumulating jobs. If formatting, Clippy, tests, dependency audits, and documentation tests run in multiple jobs, you pay for the same work more than once. Worse, different jobs can quietly drift and enforce different versions of the rules.
The template now consolidates the blocking quality checks under the Rust-based xtask runner. The Quality Gate workflow invokes a lifecycle-specific tier from config/xtask.json.
The central idea is one blocking owner per check and event:
| Event | Canonical owner | Purpose |
|---|---|---|
| Pull request | ci.yml → quality-gate → xtask pull-request tier | Fast, required verification before merge |
| Protected-branch push | ci.yml → quality-gate → xtask protected-branch tier | Broader checks, including unused-dependency validation |
| Scheduled/manual security scan | security-scan.yml | Independent scheduled or on-demand audit and dependency-policy checks |
| Fuzz dependency scan | fuzz.yml | Validate the separate fuzz dependency graph |
| Release | Release workflow's configured xtask tier | Verify the release candidate |
Standalone jobs no longer rerun the blocking fmt, Clippy, test, audit, and deny checks. Coverage is generated during the same canonical test execution and uploaded from that run, rather than being produced by a second test pass.
CI should fail closed
The CI Success aggregate is intended to be the stable required merge-gate context. It does not treat every skipped job as harmless.
A skipped job is accepted only when the pull-request path-filter contract proves that the job was not required. Missing results, unexpected skips, cancelled jobs, timeouts, and unknown results fail the aggregate. On push and manual events, required jobs are expected to run.
This distinction matters: a green aggregate should mean the required checks actually ran, not merely that GitHub did not report a failure.
Path-aware execution
The workflow uses changed paths to avoid compiling and testing Rust when a change only affects unrelated files. Markdown-only changes can still trigger Markdown validation without needlessly running the full Rust test suite.
The lifecycle tiers and path-to-check mappings live in config/xtask.json, rather than being scattered across workflow YAML. This makes the policy easier to inspect and adapt in projects generated from the template.
2. Project profiles: start with the project you are building
A reusable template should not force every project to inherit the full reference repository.
The template now provides validated profiles under config/template-profiles/. A profile controls which crates, workflows, skills, and verification defaults survive initialization.
| Profile | Intended use | What it keeps |
|---|---|---|
minimal | Small application | App starter, renamed library crate, xtask, core workflows and skills |
library | Reusable Rust library | Library crate, xtask, workspace tests, library lockfile policy |
service | Long-running service | Service-oriented actor, storage, and registry patterns |
performance-sensitive | Latency- or throughput-sensitive code | Service foundations plus performance-oriented tooling |
ai-agent | Agent-centric application | App foundations and agent development/authoring capabilities |
workspace | Full reference workspace | The broad set of crates, benchmarks, workflows, and maintainer capabilities |
The old --profile cli spelling remains as a compatibility alias for minimal; new projects should use minimal.
For example:
./scripts/init-template.sh \ --profile minimal \ --name my-app \ --description "My Rust application" \ --author "Your Name" \ --repo YOUR_USER/YOUR_REPO You can inspect or validate a profile through xtask as well:
cargo run -p xtask --bin xtask -- template inspect --profile service cargo run -p xtask --bin xtask -- template validate-profile \ --profile config/template-profiles/library.toml See the project profiles guide for the full matrix and behavior.
Lockfiles are profile-specific
The template distinguishes libraries from applications:
- Application-oriented profiles track
Cargo.lockby default, making the dependency graph reproducible. - The
libraryprofile keepsCargo.lockignored, following the usual Cargo library convention.
This is applied during template initialization. It avoids asking every adopter to remember a policy decision that should be made deliberately at project creation.
3. AI agents: reusable skills, not just more instruction files
The original article introduced the template's agent support: a canonical AGENTS.md, skill definitions, tool-specific adapters, and generated LLM context files.
That foundation remains, but the newer design focuses on matching the agent capabilities to the project profile.
The core documentation model is still:
| Layer | Location | Responsibility |
|---|---|---|
| Human onboarding | README.md, QUICKSTART.md | Explain the project to developers |
| Agent contract | AGENTS.md | Canonical rules and constraints |
| Reusable procedures | .agents/skills/ | Task-specific, repeatable workflows |
| Tool adapters | .claude/, .gemini/, .opencode/, .qwen/, etc. | Tool-specific integration details |
A small library does not need every maintainer, DORA, skill-authoring, or agent-evaluation capability. Minimal and library profiles keep the core Rust development skills. Service and performance-sensitive profiles add relevant advanced development skills. The ai-agent profile includes skill authoring and evaluation; workspace retains the broader maintainer set.
Initialization regenerates .agents/SKILLS.md and removes stale skill references from AGENTS.md when a profile prunes capabilities. That keeps the index and the canonical agent contract aligned with the files that actually exist.
The template continues to include:
-
AGENTS.mdas the canonical project contract. -
.agents/skills/as reusable task knowledge. - Tool adapters for supported coding environments.
-
llms.txtand generatedllms-full.txtfor machine-readable project context. - Validation and evaluation for skills, rather than treating every Markdown file as a useful skill automatically.
The goal is not to make an agent autonomous by adding more prompts. It is to make the project's rules, boundaries, and repeatable procedures explicit and testable.
4. Quality tooling: remove checks that do not earn their keep
A good starter template should be strict about useful engineering properties and skeptical of ceremony.
Version 0.4.0 removed the Roast Scorer and its associated always-running CI job, helper script, example, Cargo alias, and aggregate dependency. It duplicated expensive checks such as Clippy, coverage, audit, deny, MSRV, builds, and benchmarks, then applied a heuristic score that did not gate merges meaningfully.
The ineffective CiStatusArtifact quality check was removed too. It could pass when the file it was meant to inspect was absent, which made the check weaker than its name suggested.
The replacement direction is straightforward:
- Keep deterministic checks that have clear pass/fail semantics.
- Give each blocking check one owner.
- Make skipped work explainable through path-selection contracts.
- Publish summaries and artifacts without writing generated CI state back to the product branch.
- Ensure quality checks verify that useful work actually happened, not just that a command returned successfully.
The local entry point remains:
bash scripts/quality-gates.sh It delegates to the same xtask quality orchestration used by CI. CI enables coverage collection for its canonical test run; local runs do not need to pay that cost by default.
5. Security and supply-chain boundaries
Security is most effective when it is part of the normal workflow and its ownership is clear.
The template retains dependency auditing, license and source policy checks, secret scanning, privacy checks, and workflow validation. It also tightens how those checks treat lockfiles.
Root workspace and fuzz workspace are different dependency graphs
The fuzz project is excluded from the normal Cargo workspace. That is useful for keeping fuzz-specific tooling and dependencies out of ordinary builds, but exclusion also means the root workspace's dependency checks do not automatically validate the fuzz graph.
Version 0.4.0 adds a dedicated fuzz/Cargo.lock and scripts/check-fuzz-deps.sh. The Fuzz workflow validates the excluded workspace with locked metadata, cargo audit, and cargo deny. It checks the committed lockfile without silently regenerating it; a missing or stale lockfile should be fixed explicitly.
Least privilege in CI
The default CI workflow no longer needs repository write permission. It uses read access for source checkout, publishes summaries to $GITHUB_STEP_SUMMARY, and uploads generated status or coverage data as workflow artifacts rather than committing them to main.
Write permissions are scoped to workflows that genuinely need to publish releases or perform a maintenance action. This reduces the impact of a compromised action or unexpected workflow behavior.
GitHub Actions and Node.js runtimes
Version 0.4.1 updates actions that were behind and producing Node.js runtime deprecation warnings. The template also unifies the Rust cache action and Rust toolchain action references so composite setup and workflow jobs do not accidentally use different revisions and cache identities.
The verify-actions skill's SHA reference table is now regenerated from the live workflow definitions, reducing the chance that its documentation drifts away from what CI actually runs.
6. Release engineering: preflight before publish
The release workflow has also received important correctness fixes.
One historical issue was that the template's VERSION file intentionally remains 0.0.0 as a starter value, while the template itself has its own release history in .template/CHANGELOG-TEMPLATE.md. A preflight check that compared every template tag only with VERSION could therefore reject a valid template release.
The v0.4.1 preflight now accepts a tag that matches either the starter VERSION or the latest template changelog release, while still failing closed for an unexpected tag. The post-release checkout permissions were corrected as well.
The wider release setup includes cargo-dist, git-cliff, release provenance, SBOM generation, and verification guidance. The important point is that release metadata and verification should be explicit, not inferred from a file that serves a different purpose.
For maintainers, the detailed implementation history is recorded in the template changelog.
7. Architecture diagrams that do not create noisy pull requests
Generated documentation should be deterministic. If an unchanged input produces different output on different machines, automation can create meaningless diffs.
The overview diagram is now derived from config/xtask.json and cargo metadata. A contract test checks that the committed asset matches the repository's current structure and quality-gate configuration.
The workflow no longer commits a PNG export. Rasterization depends on the local font stack and produced different bytes for the same diagram, which caused unnecessary update pull requests. SVG and Excalidraw outputs are stable and are used by the README and documentation.
The workflow's earlier failure mode was also fixed: it previously tried pushing to protected main, retried a rejected push, then attempted PR creation without the required token while swallowing the error. The updated approach reports failures honestly and can create a pull request when direct updates are not allowed.
The diagram is meant to describe the real project, not to become another source of truth that drifts away from it.
8. Optional capabilities stay optional
The template includes several patterns and guides that not every project needs. One example is Rust-to-WASM support.
Version 0.4.0 added an opt-in WASM guide covering target selection, a pinned wasm-bindgen-cli, test runners, a guard against accidentally running zero tests, and release-only wasm-opt size budgets.
It does not add a WASM target, dependency, or CI job to the default template. That is intentional. Documentation and a proven path are useful; imposing another toolchain and CI workload on every project is not.
The same principle applies to the other patterns: start with a profile, add the capability when the project needs it, and keep the default path maintainable.
9. Getting started today
The repository is a GitHub template. The shortest path for a new application is:
- Create a repository using rust-2026-template.
- Clone it and run the initializer with the profile that matches your project.
- Review the generated
AGENTS.md, project metadata, and security contact details. - Run the local quality gate.
- Push and inspect the CI summary.
Example:
git clone https://github.com/YOUR_USER/YOUR_REPO.git cd YOUR_REPO ./scripts/init-template.sh \ --profile minimal \ --name my-app \ --description "My Rust application" \ --author "Your Name" \ --repo YOUR_USER/YOUR_REPO bash scripts/quality-gates.sh For a library, use --profile library. For a service, consider service; for an agent-centric application, use ai-agent. The full instructions and prerequisites are in QUICKSTART.md.
What I would emphasize now
The first version of this article focused on the collection of tools included in the template: Rust formatting, Clippy, tests, supply-chain checks, benchmarks, fuzzing, agent instructions, and release automation.
Those capabilities still matter, but the most valuable improvements in the current version are about coherence:
- CI has one owner for each blocking check.
- Required checks cannot silently disappear behind an unexpected skip.
- Generated projects can be small without manually deleting half the template.
- Agent capabilities match the profile instead of being copied indiscriminately.
- Security checks cover dependency graphs that are outside the main workspace.
- Workflow permissions and generated outputs are deliberately constrained.
- Release and architecture automation fail visibly rather than reporting success after doing nothing.
I prefer a starter that helps a project grow without forcing it to carry the entire template forever. That is the direction of v0.4.x: fewer duplicated checks, clearer ownership, safer defaults, and optional complexity where it belongs.
Try it: d-oit/rust-2026-template
Read the original article: GitHub rust-2026-template — my Rust starter in 2026
See every release change: CHANGELOG-TEMPLATE.md
*Version note: this article describes the repository state and template changelog available on November 9, 2026 (v0.4.1).


