Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
verctl
It reads .changeset/*.md, bumps declared version files, writes
changelogs, and opens or updates one prepare-release PR.
It is not @changesets/cli, not Knope, and not a forkctl verb.
Setup
= "2026.7.7"
[]
= true
= true
[]
= [
"git::https://github.com/victor-software-house/verctl.git//tasks/ver?ref=<tag>",
]
Until a GitHub Release exists, build from this repo:
Output
Every command returns one typed report. Pretty, --color never, and
--format json render the same data. JSON and human errors share that report
contract; JSON errors use stdout, while human errors use stderr. --quiet
suppresses only successful human output.
Fragments
Same file shape as Changesets. YAML fence. Quoted or unquoted keys.
forkctl: patch
"@scope/pkg": patch
Restore mise.toml when later patches unapply.
Allowed bumps: major, minor, patch, none.
An optional verctl:group key, added, changed, or fixed, files the
entry under that heading. A section where no fragment names a group stays a
flat list; once one does, the section gets ### Added, ### Changed, and
### Fixed headings, and an ungrouped fragment goes under ### Changed.
A new package starts at 0.0.0, and its first fragment is a patch, so its
first release is 0.0.1. Moving to 0.1.0 or 1.0.0 is a separate operator
decision. prepare and check refuse a minor or major fragment on a
package at 0.0.0, including its pre-releases and builds; to open at
0.1.0 on purpose, set first_minor: true on the package in .ctl/ver.yaml.
Changelog
Rendered with minijinja 2.x from these defaults (override either path):
Default release line:
PR - Summary ([#12](https://github.com/org/repo/pull/12)).
Outside + PR - Summary ([#12](https://github.com/org/repo/pull/12) by [@ext](https://github.com/ext)).
Commit only - Summary ([`96fb0bc`](https://github.com/org/repo/commit/96fb0bc)).
No link - Summary.
An entry ends with a single period: a summary that already ends with one
loses it before the reference, so Summary. with a pull request renders
- Summary ([#12](…))..
No extra thanks line. If a PR exists, the SHA is omitted.
The pull request is the trailing (#N) in the subject of the commit
that added the fragment, which GitHub's squash merge writes; without one
the entry links that commit. Release notes credit no maintainer: list
maintainers and the Apps that open pull requests in
changelog.internal_authors. With a token, a byline needs a login
outside that list, a person rather than a bot, and a pull request from a
fork.
Version files
verctl prepare applies fragment bumps through drivers.
Local writes are the default. prepare --pr is the Version PR
(Action happy path; the token is GITHUB_TOKEN, not the gh account).
prepare --dry-run / --preview prints the plan and writes nothing.
prepare --pr --preview also shows consumed fragments and open vs update.
Cargo and npm are stock drivers, not a separate code path.
A repo's declarations live in one file, .ctl/ver.yaml. .ctl/ is the
directory every ctl CLI shares, so a repo root gains one entry however many of
them it uses.
drivers:
cargo:
format: toml
keys:
# after is optional. If omitted, verctl looks at lockfiles /
# packageManager (bun, pnpm, yarn, npm) and Cargo.lock.
npm:
format: json
keys:
# after: mise run install # overrides detection
packages:
- name: verctl
path: Cargo.toml
# driver: cargo # inferred from the file name
- name: other
path: VERSION
read: ver-read-version # mise run ver-read-version
write:
A string is a mise task. A list is execvp (no shell).
Stdin is the file. Write drivers also get VERCTL_VERSION.
Stdout is the version (read) or the new file (write).
after is printed, not run.
0.x rejects a major fragment.
Runners
Which machines run the work is configuration. A machine is declared once
under runners; the jobs that run on it name it.
# One machine. `labels` is how GitHub finds it — every label at once, so this
# is a single machine carrying three labels, not three machines.
runners:
big:
labels:
# One validation job per entry. Naming two machines gives two checks,
# `verify (big)` and `verify (ns)`. Omit `ci` for one `verify` on ubuntu-latest.
ci:
verify:
runners:
# One build target per entry: the name is the job, the tarball, and the
# platform. One tarball is one machine, so `runner` is not a list.
assets:
darwin-arm64:
linux-x64:
runner: big
| section | one entry is | its name is | with no runners / runner |
|---|---|---|---|
runners |
one machine | how jobs refer to it | — |
ci |
a PR / push validation job | the check name | ubuntu-latest |
assets |
a release build job and its tarball | the check name, the --build argument, part of the filename |
the built-in record for that name |
Two sections take runners and no others, because of what config is for here:
verctl decides how many jobs there are. Your workflow file decides where a fixed job runs.
A static YAML file cannot declare N jobs without knowing N, and only the repo
knows N. That fan-out is what ci and assets buy; the machine rides along
on it. Every other job verctl ships — plan, crate, pin, prepare — is
always exactly one job, so nothing decides its count and nothing needs to
configure it. Its runs-on is one literal in a workflow you own and already
copied from examples/:
plan:
runs-on: # yours to edit; no declaration needed
So a repo with no hosted runners at all is already served: edit those literals
and declare ci / assets. plan could not read its machine from config
anyway — runs-on resolves before the job exists, and plan is the job that
reads the declarations.
Names sort alphabetically, not in file order, so a verify job written above
an audit job still plans audit first. Checks are independent, so the order is
display only.
Only labels reaches GitHub. A runner name is a name in the file: it
resolves against runners or fails there, listing what is declared, so a
label can never pass itself off as a machine and verctl never invents a label.
A label GitHub does not know queues the way any wrong label queues — verctl
cannot tell which machines carry which labels, so it passes them through.
Built-in target records, so a public repo needs no config to stay on free hosted minutes:
| name | labels | os |
arch |
triple |
|---|---|---|---|---|
darwin-arm64 |
["macos-latest"] |
darwin |
arm64 |
aarch64-apple-darwin |
linux-x64 |
["ubuntu-latest"] |
linux |
x64 |
x86_64-unknown-linux-gnu |
Each field is a default the repo overrides one at a time: naming a runner
moves the machine and leaves the platform alone. A name outside that set
describes a platform verctl knows nothing about, so it must describe all of it
— runner, os, arch, and triple are all required, and a partial record
is an error rather than a build against an empty target triple. os: darwin
renders as macos in the filename; that is the only rename. triple stays
its own field because the machine and what it builds need not match: an x64
linux runner can build aarch64-unknown-linux-gnu.
ci is exactly as trusted as .github/workflows/ci.yml: for a
pull_request event both come from the pull request's own tree, so a fork PR
that can declare a label could equally have written that label into the
workflow file. Neither is a reason to expose a self-hosted runner group to a
public repository — keep allows_public_repositories=false there and keep
fork-PR approval on, and read declared labels as untrusted input in a repo
where that is not true.
verctl ci and verctl assets print what resolved, so a default is visible
output rather than an assumption. Both also write a matrix for
$GITHUB_OUTPUT: GitHub needs runs-on before a job exists, so a small
plan job emits the labels and the real jobs consume them
(examples/workflows/ci.yml).
Actions
These replace changesets/action. They are GitHub Actions, not a
GitHub App. The runner already has git and GITHUB_TOKEN. PRs are
opened with octocrab. Commits and push are git2 with that token,
not the git/gh CLIs. There is no local token setup.
| Action | Role |
|---|---|
actions/version-pr |
Open or update the Version PR |
actions/publish |
Run after that PR merges |
actions/asset |
Build and attach one native target |
Example consumer workflow: examples/workflows/version-pr.yml.
actions/version-pr takes auto-merge: true to merge the Version PR once the
base branch's required checks pass. It needs a ruleset that requires a status check
on that branch, and a GitHub App token in token.
Mise owns tools. Publish uses Actions OIDC, not a PAT.
A package with publisher: none is released on GitHub only: publish
creates its tag and Release, and assets its tarballs, with no registry
upload. Use it for private binaries, such as a Cargo package with
publish = false.
Operator documents
Clap remains the command grammar. ctl-core Surface renders the shared version,
mounted invocation/no---, and visible command-inventory fragments. Verctl's
Jinja templates retain domain prose, and operator_docs byte-compares their
renders with skills/verctl/SKILL.md and src/instructions.md.
License
MIT.