Skip to content

HTTP API: release

What this project has shipped and what it would ship next, derived rather than maintained: the changelog composes the layer's release records, the decisions dated inside each release's window and the conventional commits in its range; the version report reads the one place the version is authored and the projection the shell tool prints, and says what the public contract requires it to become — or that it cannot be measured, and so decides nothing — with what the commits since the last release imply beside it as evidence.

Rendered from docs/generated/openapi.json, which the executable generates from its capability registry and CI refuses stale. The raw document is at /openapi.json; a running server serves the same document at its own /openapi.json and Swagger UI at /swagger.

release

What this project has shipped and what it would ship next, derived rather than maintained: the changelog composes the layer's release records, the decisions dated inside each release's window and the conventional commits in its range; the version report reads the one place the version is authored and the projection the shell tool prints, and says what the public contract requires it to become — or that it cannot be measured, and so decides nothing — with what the commits since the last release imply beside it as evidence.

GET /api/v1/changelog release.changelog

The changelog

Every release the layer records, newest first, with the work that has not been released leading. A section's decisions are the ADRs dated inside that release's window, its changes the conventional commits in its range, its artifacts the record's own evidence. Nothing in it is authored, and a section that could not be read says so rather than appearing empty.

  • query
  • implemented
  • benchmark required
  • cache disabled
  • MCP tool majordomus_changelog
  • MCP resource {"name": "changelog", "uri": "majordomus://changelog"}

Query parameters

nametyperequireddescriptionexamples
version string no One version, or `unreleased`; every section when absent. one-version: "0.10.0"

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/changelog?version=0.10.0"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 ReleaseChangelog The changelog: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/release/analysis release.analysis

What the public contract did, and the smallest version it allows

The public capability surface of this tree against the one the last release published, every movement of it named with the reason it counts for what it does, and the smallest version this tree may therefore declare. The compatibility level is measured from the contract rather than read off the commit subjects: a capability, route, MCP tool, command, or a required field of either schema that a caller could hold and cannot any more is breaking whatever the commit that removed it called itself. What the commits said about themselves is carried beside the verdict as evidence, and the answer says when the two disagree.

  • query
  • implemented
  • benchmark waived: published_history
  • cache disabled
  • MCP tool majordomus_release_analysis

Query parameters

nametyperequireddescriptionexamples
since string no A ref to compare with instead of the last release: `v0.4.0`, or any commit that carries a committed registry.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/release/analysis"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 ReleaseVersionPlan What the public contract did, and the smallest version it allows: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/release/next release.next

The version a landing allocates, and why

What the integration executor raises the trunk's version to when it lands a head (ADR 0134): the version the trunk declares, how far the head moves the public contract measured against the trunk's committed registry, what that movement requires under the version's mode, the repository's floor from `release.floor` in the policy, the exemption class that lowered the floor if every changed path falls in one, the larger of the requirement and the floor, and the trunk's version raised by it — with one sentence per step. Everything is read from git at the two refs, so the answer does not depend on which executable runs or on the branch's own version, which is why a retried landing cannot raise twice. It decides and writes nothing; an input that cannot be read is refused rather than guessed.

  • query
  • implemented
  • benchmark waived: published_history
  • cache disabled
  • MCP tool majordomus_release_next

Query parameters

nametyperequireddescriptionexamples
base string no The trunk the landing raises from; `origin/master` when absent.
head string no What lands; `HEAD` when absent.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/release/next"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 LandingDecision The version a landing allocates, and why: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/release/version release.version

The version, and the one the contract requires next

The version the crate manifest declares — the one place it is authored — the version the shell tool prints from its projection `share/version.txt`, and whether that projection is current — the question `generate --check` refuses and `scripts/release-version --check` gates on. Then the next version, which is the public contract's answer: the version `release analyze` requires against the last release — the declared version when it already satisfies the contract, otherwise the smallest one it allows, and the smallest release above the last when the contract requires none over it — with `decided_by` naming who answered. It is the one release selection `release bump` also raises to when no target is named, so the report and the writer cannot disagree. When the contract cannot be measured — no published baseline, a version that is not three numbers, or any other error that makes the plan unsound, the plan `release bump` refuses to write from — the baseline is refused, not guessed: `next` is absent, `decided_by` is `undecided` and `contract_unreadable` carries every error, one per line. The bump the conventional commits since the last release imply, the version it would produce and the commits themselves are carried beside it as evidence, and never answer `next` in the contract's place.

  • query
  • implemented
  • benchmark required
  • cache checkout (max_entries=2)
  • MCP tool majordomus_release_version

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/release/version"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 ReleaseVersionReport The version, and the one the contract requires next: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.

Schemas

The components only this tag's operations use. Each row is the type's own field, its doc comment the description; a type another page owns links there.

AppliedExemption

object
The exemption a decision applied, by class.

use majordomus_cli::release::landing::AppliedExemption;
let e = AppliedExemption { id: "plan-records".into(), reason: "plan records".into() };
assert_eq!(e.id, "plan-records");
propertytyperequireddescription
id string yes The class's id.
reason string yes The class's description, which is the reason the floor did not apply.

Used by LandingDecision

LandingDecision

object
What a landing allocates, and why, step by step.

use majordomus_cli::release::compat::Impact;
use majordomus_cli::release::landing::{decide, LandingDecision, ReleasePolicy, DECISION_SCHEMA};
use majordomus_cli::release::version::Version;
let d: LandingDecision = decide(Version::parse("1.2.3").unwrap(), Impact::None, &ReleasePolicy::default(), &[]);
assert_eq!(d.schema, DECISION_SCHEMA);
assert_eq!(d.next_version, "1.2.3", "nothing moved and nothing is owed");
propertytyperequireddescription
schema string yes DECISION_SCHEMA.
trunk_version string yes What the trunk declares: the number the next one is raised from.
observed ReleaseImpact → ReleaseImpact yes How far the change moves the public contract, measured (ADR 0051).
required_by_compatibility ReleaseImpact → ReleaseImpact yes What that movement requires under the version's mode.
floor ReleaseImpact → ReleaseImpact yes The policy's floor, or none when an exemption applied.
exemption AppliedExemption | null → AppliedExemption no The exemption that lowered the floor, if one did.
selected ReleaseImpact → ReleaseImpact yes The larger of the requirement and the floor.
next_version string yes The trunk's version raised by selected.
allocation ReleaseAllocation → ReleaseAllocation yes Whether the policy allocates at landing; the decision is computed either way.
mode ReleasePolicyMode → ReleasePolicyMode yes The compatibility mode the trunk's version puts in force.
explanation array of string yes One sentence per step, in the order the decision was made.

Used by release.next

ReleaseAllocation

"off" | "landing"
Whether the integration executor allocates the version when it lands a pull request.

use majordomus_cli::release::landing::Allocation;
// a policy that says nothing changes nothing: a pull request raises its own version
assert_eq!(Allocation::default(), Allocation::Off);
let a: Allocation = serde_json::from_str("\"landing\"").unwrap();
assert_eq!(a.as_str(), "landing");
  • offThe version is raised by whoever changes the contract, as before ADR 0134.
  • landingThe integration executor raises it at landing; a pull request does not.

Used by LandingDecision

ReleaseBaseline

object
The release this tree is measured against, and how it was found.

use majordomus_cli::release::compat::Baseline;
let baseline = Baseline {
    version: "0.5.0".into(),
    reference: "v0.5.0".into(),
    read_at: "3a032c1e033b".into(),
    commit: "3a032c1e033b".into(),
    recorded: true,
    atoms: 300,
    fingerprint: "sha256:…".into(),
};
// `recorded` is the half that matters: a baseline taken from a tag alone is reported.
assert!(baseline.recorded);
assert_eq!(baseline.reference, "v0.5.0");
propertytyperequireddescription
version string yes 0.5.0.
reference string yes v0.5.0: the release, as a reader recognises it.
read_at string yes The ref the surface was actually read from — the commit the release record names, which a clone always has, rather than a tag it may not.
commit string yes The commit that ref resolved to.
recorded boolean yes Whether the layer holds a release record for it, which is what makes it canonical rather than the newest thing that looked like a tag.
atoms integer (uint) yes How many public atoms it published.
fingerprint string yes The fingerprint of the surface it published.

Used by ReleaseVersionPlan

ReleaseChange

object
One change, from one commit.
propertytyperequireddescription
kind ReleaseChangeKind → ReleaseChangeKind yes What it did.
scope string | null no The area it did it in, when the subject named one: commands, ci, site.
subject string yes The subject, without the type and scope that prefixed it.
breaking boolean yes Whether the commit marked itself breaking, with ! or a BREAKING CHANGE: trailer.
commit string yes The abbreviated commit.
url string | null no Where that commit can be read, when the repository's own URL is known. Derived from about::REPOSITORY, never written beside each entry.
references array of ReleaseReference → ReleaseReference no The records of the layer this commit names — issues, milestones — resolved against what the layer actually holds.

Used by ReleaseChangeGroup ReleaseVersionReport

ReleaseChangeDirection

"added" | "removed" | "changed"
Whether a change added, removed or altered part of the contract.

use majordomus_cli::release::compat::Direction;
assert_eq!(Direction::Removed.sign(), '-');
  • addedIt was not there before.
  • removedIt was there before and is not now.
  • changedIt is there in both, and differs.

Used by ReleaseSurfaceChange

ReleaseChangeGroup

object
The changes of one kind, with the heading and the rank that decide where they are shown.

The grouping is in the document rather than in each renderer. It was in the Markdown renderer alone, and the site — which cannot see a Rust function — grouped alphabetically instead, so the same changelog read in two orders depending on which surface showed it. A presentation order stated once and carried is the only kind that survives a projection.

propertytyperequireddescription
kind ReleaseChangeKind → ReleaseChangeKind yes What the changes in it did.
heading string yes The heading it is shown under.
rank integer (uint8) yes Where it sorts among the other groups; ascending.
changes array of ReleaseChange → ReleaseChange yes The changes, in the order the commits were read.

Used by ReleaseSection

ReleaseChangelog

object
The whole changelog.
propertytyperequireddescription
schema string yes The schema this document satisfies.
current string yes The version the tree currently declares.
sections array of ReleaseSection → ReleaseSection yes Newest first, the unreleased section leading when there is one.
diagnostics array of string yes What could not be read, said rather than hidden: a repository with no git history, a release record that names no commit, a tag that is not in this clone.
produced_by ReleaseProducedBy | null → ReleaseProducedBy no Which capability answered, and where else the same value can be had.

Used by release.changelog

ReleaseChangelogArtifact

object
One published artifact, from the release record's own evidence.
propertytyperequireddescription
target string yes The platform triple the record names.
name string yes The file name.
sha256 string yes Its SHA-256, as the record read it off the file.

Used by ReleaseSection

ReleaseCommitEvidence

object
What the commit subjects since the baseline said about themselves.

Evidence, never authority. It is here so that a person can see the human account and the measured one side by side, and so that VersionPlan::understated can say when the two disagree — which is the case this whole subsystem exists to catch.

use majordomus_cli::release::compat::{CommitEvidence, Impact};
let commits = CommitEvidence {
    commits: 24,
    implied: Impact::Patch,
    breaking: Vec::new(),
};
// The contract moved by a minor and the subjects claim a patch: the plan reports that
// disagreement rather than deferring to the words.
assert!(commits.implied < Impact::Minor);
propertytyperequireddescription
commits integer (uint) yes How many commits since the baseline.
implied ReleaseImpact → ReleaseImpact yes What their conventional-commit types alone would have implied.
breaking array of string no The subjects of the ones that claimed to be breaking.

Used by ReleaseVersionPlan

ReleaseDecision

object
One decision, as the layer's own ADR object states it.
propertytyperequireddescription
id string yes adr-0027.
title string yes The decision, in its own words.
status string yes proposed, accepted, superseded.
date string yes The date the record carries.
url string | null no Where the decision itself can be read: the file in the repository. The site has no per-ADR page, so this is the honest destination rather than an invented route.
added string | null no The commit that added the file, which is what decides the release it belongs to. The front-matter date says when the decision was made; those are different questions.

Used by ReleaseSection

ReleaseDiagnostic

object
Something about the release state that a person should be told, whatever the verdict.

use majordomus_cli::release::compat::{Diagnostic, Severity};
let d = Diagnostic {
    id: "tag-without-record".into(),
    severity: Severity::Warning,
    message: "v0.1.0 is tagged and the layer holds no release record for it".into(),
};
assert_eq!(d.severity, Severity::Warning, "a warning states the plan still stands");
propertytyperequireddescription
id string yes tag-without-record, tag-predates-records, record-without-tag, tag-commit-mismatch.
severity ReleaseSeverity → ReleaseSeverity yes Whether it stops a release, only warns, or only informs.
message string yes What is wrong, in one sentence.

Used by ReleaseVersionPlan

ReleaseImpact

"none" | "patch" | "minor" | "major"
How much a change moves the contract.

Ordered, so that "the declared bump covers what is required" is a comparison rather than a table:

use majordomus_cli::release::compat::Impact;
assert!(Impact::Major > Impact::Minor);
assert!(Impact::Minor > Impact::Patch);
assert!(Impact::Patch > Impact::None);
  • noneThe contract did not move.
  • patchIt moved behind the boundary: a caller cannot tell.
  • minorSomething arrived; every existing caller still works.
  • majorSomething a caller could hold is gone or changed under it.

Used by LandingDecision ReleaseCommitEvidence ReleaseSurfaceChange ReleaseVersionPlan

ReleasePolicy

object
The compatibility policy, named and versioned.

use majordomus_cli::release::compat::{Impact, Policy, POLICY_SCHEMA};
use majordomus_cli::release::version::Version;
let policy = Policy::for_version(Version::parse("0.5.0").unwrap());
assert_eq!(policy.schema, POLICY_SCHEMA, "a verdict carries the rules it was reached under");
assert_eq!(policy.required_of(Impact::Major), Impact::Minor);
propertytyperequireddescription
schema string yes majordomus/version-policy/v1.
mode ReleasePolicyMode → ReleasePolicyMode yes Which rule is in force, decided by the baseline version alone.

Used by ReleaseVersionPlan

ReleasePolicyMode

"pre1-0-strict" | "semver"
How this project turns a measured impact into the version it owes.

Semantic versioning hands 0.y.z a blanket exemption — "anything MAY change at any time" — and Elm refuses the exemption by starting every package at 1.0.0, because a rule with an escape hatch is a rule about when to use the hatch. This repository is 0.x and has not earned 1.0.0, so neither answer is available: demanding major would demand 1.0.0 for a single removal, and granting the exemption would let a breaking change ship as a patch.

So below 1.0.0 the floor is a minor. Any movement of the surface, gone or new, is at least a minor release; only an unchanged surface owes nothing. That is the strongest signal 0.x has, and a removal is still named as breaking whatever the verdict, so the evidence does not quietly become a minor along with the number.

At 1.0.0 the shift ends: Mode::Semver is the ordinary rule, and the transition is one line of Policy::required_of.

use majordomus_cli::release::compat::{Mode, Policy};
use majordomus_cli::release::version::Version;
assert_eq!(Policy::for_version(Version::parse("0.9.0").unwrap()).mode, Mode::Pre1_0Strict);
assert_eq!(Policy::for_version(Version::parse("1.0.0").unwrap()).mode, Mode::Semver);
  • pre1-0-strictBelow 1.0.0: breaking changes are allowed, and cost a minor rather than nothing.
  • semverAt or above 1.0.0: the impact is the requirement.

Used by LandingDecision ReleasePolicy

ReleaseProducedBy

object
Which capability answered with this document, and where that capability is projected.

The document carries its own provenance so that a reader — a person on the site, a client over MCP — can find the same value elsewhere without anything having to enumerate the routes. A page that listed them would be declaring them a second time, which is the failure site-check's registry and cli assertions exist to catch; it caught this one.

use majordomus_cli::release::model::ProducedBy;
let by = ProducedBy {
    capability: "release.changelog".into(),
    cli: Some("majordomus release changelog".into()),
    http: Some("/api/v1/changelog".into()),
    mcp_tool: None,
    mcp_resource: Some("majordomus://changelog".into()),
};
let json = serde_json::to_value(&by).unwrap();
assert_eq!(json["capability"], "release.changelog");
// a surface the capability is not projected on is absent, not null
assert!(json.get("mcp_tool").is_none());
assert_eq!(ProducedBy::default().capability, "");
propertytyperequireddescription
capability string yes The capability id.
cli string | null no The command line that renders it.
http string | null no The HTTP route that answers with it.
mcp_tool string | null no The MCP tool.
mcp_resource string | null no The MCP resource URI.

Used by ReleaseChangelog

ReleaseReference

object
A record of the layer that a commit names in its own text.

Inferred, never declared beside the commit: an issue id or a milestone id appearing in a subject or a body is a reference, and the layer already holds the object it refers to. A reference to something the layer does not have is not carried — a link to a record that does not exist is worse than no link, because the reader cannot tell until they follow it.

use majordomus_cli::release::model::Reference;
let r = Reference {
    kind: "issue".into(),
    id: "I1305".into(),
    title: "An observed contract has a fingerprint".into(),
    route: Some("/plan/i1305/".into()),
};
let json = serde_json::to_value(&r).unwrap();
assert_eq!(json["id"], "I1305");
assert_eq!(json["route"], "/plan/i1305/");
// a reference the site has no page for carries no route rather than an invented one
let bare = Reference { route: None, ..r };
assert!(serde_json::to_value(&bare).unwrap().get("route").is_none());
propertytyperequireddescription
kind string yes issue or milestone.
id string yes I1305, M000.
title string yes What it is, from the record itself.
route string | null no Where it is published on this site, when it has a page.

Used by ReleaseChange

ReleaseSection

object
One version's worth of changelog.
propertytyperequireddescription
version string yes The version, or unreleased.
tag string | null no The tag, when one was published.
date string | null no When it was published, from the record.
commit string | null no The commit it was published from.
unreleased boolean yes Whether this section is the work that has not been released.
notes_url string | null no The release notes the record names — the published release itself.
compare_url string | null no Every commit between the previous release and this one, as the forge renders it.
tree_url string | null no The tree at this release.
decisions array of ReleaseDecision → ReleaseDecision yes The decisions dated inside this release's window.
groups array of ReleaseChangeGroup → ReleaseChangeGroup yes The changes, from the commits in this release's range, grouped by what they did and ordered by the rank each group carries. The flat list is the concatenation of the groups' own; nothing holds it twice.
artifacts array of ReleaseChangelogArtifact → ReleaseChangelogArtifact yes What was published, when this section is a release.

Used by ReleaseChangelog

ReleaseSeverity

"error" | "warning" | "note"
How much a diagnostic matters.

An error says the plan itself cannot be trusted and blocks the verdict; a warning says something about the release state is wrong and the verdict stands anyway; a note says something true and not wrong, so that the warnings keep meaning what they say.

use majordomus_cli::release::compat::Severity;
assert_ne!(Severity::Error, Severity::Warning);
assert_ne!(Severity::Warning, Severity::Note);
  • errorThe plan cannot be trusted; the verdict is blocked.
  • warningThe plan stands, and something is still wrong.
  • noteNothing is wrong; the reader is told something about the release state anyway.

Used by ReleaseDiagnostic

ReleaseStatus

"ok" | "blocked"
Whether the tree may land or ship as it stands.

use majordomus_cli::release::compat::Status;
assert_ne!(Status::Ok, Status::Blocked);
  • okThe declared version covers what the contract did.
  • blockedIt does not, and the plan says by how much.

Used by ReleaseVersionPlan

ReleaseSurfaceChange

object
One movement of the public contract, with the reason it counts for what it does.

use majordomus_cli::release::compat::{Direction, Impact, SurfaceChange, SurfaceKind};
let change = SurfaceChange {
    impact: Impact::Major,
    direction: Direction::Removed,
    surface: SurfaceKind::Http,
    capability: "alpha.list".into(),
    id: "GET /api/v1/alpha".into(),
    detail: "the capability survives; this way of reaching it does not".into(),
};
assert_eq!(change.to_string(), "- route GET /api/v1/alpha");
propertytyperequireddescription
impact ReleaseImpact → ReleaseImpact yes How much it moves the contract on its own.
direction ReleaseChangeDirection → ReleaseChangeDirection yes Added, removed, or altered.
surface ReleaseSurfaceKind → ReleaseSurfaceKind yes Which promise it is.
capability string yes The capability it belongs to.
id string yes The thing itself: GET /api/v1/foo, majordomus_release_analysis, release.analysis.
detail string yes Why this impact and not a smaller one, in one clause.

Used by ReleaseVersionPlan

ReleaseSurfaceKind

"capability" | "kind" | "mcp_tool" | "mcp_resource" | "http" | "cli" | "input" | "output"
Which projection of the contract a change is in.

Not where it was found — everything here is found in the registry — but which promise it is, so that a reader sees "a route is gone" rather than a JSON pointer.

use majordomus_cli::release::compat::SurfaceKind;
assert_eq!(SurfaceKind::Http.as_str(), "route");
  • capabilityThe capability itself.
  • kindWhether calling it changes anything.
  • mcp_toolAn MCP tool name.
  • mcp_resourceAn MCP resource URI.
  • httpAn HTTP method and path.
  • cliA command-line path.
  • inputWhat a caller may send.
  • outputWhat a caller is promised back.

Used by ReleaseSurfaceChange

ReleaseVersionDecidedBy

"contract" | "contract_and_commits" | "undecided"
Who answered a version report's next: the public contract, the contract with the commits — or no one, because the contract could not be measured.

ADR 0051 makes the contract the authority and demotes conventional commits to evidence, so DecidedBy::Contract is the normal answer. An unmeasurable contract is refused, not guessed: DecidedBy::Undecided leaves next absent and carries the reason, and the commit inference stays what it always is, evidence — never the answer in its place.

use majordomus_cli::release::model::DecidedBy;
assert_eq!(serde_json::to_value(DecidedBy::Contract).unwrap(), "contract");
assert_eq!(DecidedBy::Undecided.as_str(), "undecided");
assert_eq!(DecidedBy::default(), DecidedBy::Undecided);
  • contractrelease analyze measured the public contract against the last release, and next is the version it requires: the declared version when that already satisfies the contract, otherwise the smallest one it allows.
  • contract_and_commitsThe contract was measured and requires no release over the last one — only behaviour behind the public boundary changed — while the commits carry changes: next is the smallest release above the last, a patch, however much the commit subjects claim.
  • undecidedThe contract could not be measured — no published baseline carries a registry, or a version is not three numbers — so nothing decides next: it is absent, the reason is in contract_unreadable, and release bump without an explicit target refuses.

Used by ReleaseVersionReport

ReleaseVersionPlan

object
The whole answer: what moved, what that requires, and what the tree declares.

One value, and every surface renders it — the command line, /api/v1/release/analysis, the MCP tool, the Cockpit panel, the generated document and the CI gate. There is no second computation anywhere, which is the property that makes the number mean something.

use majordomus_cli::release::compat::{Status, VersionPlan};
#### use majordomus_cli::release::compat::{Baseline, CommitEvidence, Impact, Policy};
#### use majordomus_cli::release::version::Version;
let plan = VersionPlan {
    policy: Policy::for_version(Version::parse("0.5.0").unwrap()),
    baseline: Baseline {
        version: "0.5.0".into(), reference: "v0.5.0".into(), read_at: "3a032c1e".into(),
        commit: "3a032c1e".into(), recorded: true, atoms: 300, fingerprint: "sha256:a".into(),
    },
    declared_version: "0.5.0".into(),
    tool_version: "0.5.0".into(),
    writers_agree: true,
    atoms: 333,
    fingerprint: "sha256:b".into(),
    implied: Impact::Minor,
    required: Impact::Minor,
    declared: Impact::None,
    required_version: "0.6.0".into(),
    status: Status::Blocked,
    breaking: false,
    changes: Vec::new(),
    commits: CommitEvidence { commits: 24, implied: Impact::Patch, breaking: Vec::new() },
    understated: true,
    diagnostics: Vec::new(),
};
// The contract requires a minor and the version declares none: this tree may not land.
assert_eq!(plan.status, Status::Blocked);
assert!(plan.declared < plan.required);
assert!(plan.understated, "the commit subjects claim less than the contract did");
propertytyperequireddescription
policy ReleasePolicy → ReleasePolicy yes The policy the verdict was reached under.
baseline ReleaseBaseline → ReleaseBaseline yes The release this was measured against.
declared_version string yes The version the crate manifest declares — the one place it is authored.
tool_version string yes The version bin/majordomus prints: what its projection, share/version.txt, states.
writers_agree boolean yes Whether that projection is current — states what the manifest declares. The name is kept from when the version had two writers; the manifest is the only one now.
atoms integer (uint) yes How many public atoms this tree carries.
fingerprint string yes The fingerprint of this tree's surface.
implied ReleaseImpact → ReleaseImpact yes What the surface movement amounts to on its own.
required ReleaseImpact → ReleaseImpact yes What the policy requires of it.
declared ReleaseImpact → ReleaseImpact yes What the two version numbers already represent.
required_version string yes The smallest version this tree may declare.
status ReleaseStatus → ReleaseStatus yes Whether it does.
breaking boolean yes Whether anything a caller could hold is gone.
changes array of ReleaseSurfaceChange → ReleaseSurfaceChange yes Every movement of the contract, worst first.
commits ReleaseCommitEvidence → ReleaseCommitEvidence yes What the commits said about themselves.
understated boolean yes Whether the commit subjects understate what the contract actually did.
diagnostics array of ReleaseDiagnostic → ReleaseDiagnostic no Anything incoherent about the release state, whatever the verdict.

Used by release.analysis

ReleaseVersionReport

object
What the version is, what the public contract requires it to become, and what the commits since the last release say about themselves.

next is the contract's answer (ADR 0051): the analysis' required_version, or the smallest release above the last when that requirement is the last release itself. The commit inference is evidence — bump and commits_imply — and never the answer: when the contract cannot be measured next is absent, decided_by is undecided and contract_unreadable says why. The selection is crate::release::version::select, the one function release bump also takes its default target from.

propertytyperequireddescription
declared string yes The version the crate manifest declares — the one place it is authored.
tool string yes The version bin/majordomus prints: what its projection, share/version.txt, states.
agree boolean yes Whether that projection is current. generate --check refuses a stale one and scripts/release-version --check proves the tool prints it; this is the same question asked by the executable, so every surface can show the answer.
last_release string | null no The last release: the highest version the layer records (the newest version tag when it records none) — the baseline release analyze measures the contract from, so the contract and the commit evidence describe one window.
bump string yes What the commit subjects since it imply: major, minor, patch, or none. Evidence only — the contract decides next; release analyze reports where the two disagree.
commits_imply string | null no The version that bump would produce from the last release (from the declared version when nothing was released yet). Evidence only, even when the contract cannot answer.
next string | null no The next version. When the contract could be measured, the version it requires: the declared version when that already satisfies it, otherwise the smallest one it allows — and, when that is the last release itself, the smallest release above it. Absent when nothing would be released, and when the contract could not be measured (decided_by: undecided): an unmeasurable baseline is refused, not guessed.
decided_by ReleaseVersionDecidedBy → ReleaseVersionDecidedBy no Who answered next.
contract_unreadable string | null no Why the contract could not answer, when decided_by is undecided.
changes array of ReleaseChange → ReleaseChange yes How many commits since the last release, and of what kind — the evidence for the bump, so that a surprising answer can be checked rather than believed.

Used by release.version

Searches the titles and descriptions of every page this site publishes.