A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live
release.changelog
The changelog
What it is
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.
Descriptor
- Canonical id
release.changelog- Module
release- Kind
- query
- Stability
- implemented
- Provenance
- declared
- Source
apps/majordomus-cli/src/capability/builtin/release.rs- Cache
disabled- Benchmark
required- Tags
- releasechangelog
Run
queryA query. It reads and changes nothing.
Projections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_changelog | - |
| MCP resource | majordomus://changelog | - |
| HTTP | GET /api/v1/changelog | Swagger |
| Command line | not exposed | - |
Schemas
Input · ReleaseChangelogInput
{
"type": "object",
"additionalProperties": false,
"properties": {
"version": {
"type": [
"string",
"null"
],
"description": "One version, or `unreleased`; every section when absent."
}
},
"description": "Which part of the changelog to answer with.",
"title": "ReleaseChangelogInput"
}Output · ReleaseChangelog
{
"type": "object",
"properties": {
"schema": {
"type": "string",
"description": "The schema this document satisfies."
},
"current": {
"type": "string",
"description": "The version the tree currently declares."
},
"sections": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseSection"
},
"description": "Newest first, the unreleased section leading when there is one."
},
"diagnostics": {
"type": "array",
"items": {
"type": "string"
},
"description": "What could not be read, said rather than hidden: a repository with no git history,\na release record that names no commit, a tag that is not in this clone."
},
"produced_by": {
"anyOf": [
{
"$ref": "#/$defs/ReleaseProducedBy"
},
{
"type": "null"
}
],
"description": "Which capability answered, and where else the same value can be had."
}
},
"required": [
"schema",
"current",
"sections",
"diagnostics"
],
"description": "The whole changelog.",
"title": "ReleaseChangelog",
"$defs": {
"ReleaseSection": {
"type": "object",
"properties": {
"version": {
"type": "string",
"description": "The version, or `unreleased`."
},
"tag": {
"type": [
"string",
"null"
],
"description": "The tag, when one was published."
},
"date": {
"type": [
"string",
"null"
],
"description": "When it was published, from the record."
},
"commit": {
"type": [
"string",
"null"
],
"description": "The commit it was published from."
},
"unreleased": {
"type": "boolean",
"description": "Whether this section is the work that has not been released."
},
"notes_url": {
"type": [
"string",
"null"
],
"description": "The release notes the record names — the published release itself."
},
"compare_url": {
"type": [
"string",
"null"
],
"description": "Every commit between the previous release and this one, as the forge renders it."
},
"tree_url": {
"type": [
"string",
"null"
],
"description": "The tree at this release."
},
"decisions": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseDecision"
},
"description": "The decisions dated inside this release's window."
},
"groups": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseChangeGroup"
},
"description": "The changes, from the commits in this release's range, grouped by what they did and\nordered by the rank each group carries. The flat list is the concatenation of the\ngroups' own; nothing holds it twice."
},
"artifacts": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseChangelogArtifact"
},
"description": "What was published, when this section is a release."
}
},
"required": [
"version",
"unreleased",
"decisions",
"groups",
"artifacts"
],
"description": "One version's worth of changelog."
},
"ReleaseDecision": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "`adr-0027`."
},
"title": {
"type": "string",
"description": "The decision, in its own words."
},
"status": {
"type": "string",
"description": "`proposed`, `accepted`, `superseded`."
},
"date": {
"type": "string",
"description": "The date the record carries."
},
"url": {
"type": [
"string",
"null"
],
"description": "Where the decision itself can be read: the file in the repository. The site has no\nper-ADR page, so this is the honest destination rather than an invented route."
},
"added": {
"type": [
"string",
"null"
],
"description": "The commit that added the file, which is what decides the release it belongs to. The\nfront-matter date says when the decision was made; those are different questions."
}
},
"required": [
"id",
"title",
"status",
"date"
],
"description": "One decision, as the layer's own ADR object states it."
},
"ReleaseChangeGroup": {
"type": "object",
"properties": {
"kind": {
"$ref": "#/$defs/ReleaseChangeKind",
"description": "What the changes in it did."
},
"heading": {
"type": "string",
"description": "The heading it is shown under."
},
"rank": {
"type": "integer",
"format": "uint8",
"minimum": 0,
"maximum": 255,
"description": "Where it sorts among the other groups; ascending."
},
"changes": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseChange"
},
"description": "The changes, in the order the commits were read."
}
},
"required": [
"kind",
"heading",
"rank",
"changes"
],
"description": "The changes of one kind, with the heading and the rank that decide where they are shown.\n\nThe grouping is in the document rather than in each renderer. It was in the Markdown\nrenderer alone, and the site — which cannot see a Rust function — grouped alphabetically\ninstead, so the same changelog read in two orders depending on which surface showed it.\nA presentation order stated once and carried is the only kind that survives a projection."
},
"ReleaseChangeKind": {
"oneOf": [
{
"type": "string",
"const": "feat",
"description": "A capability a person did not have before."
},
{
"type": "string",
"const": "fix",
"description": "Behaviour that was wrong and is not any more."
},
{
"type": "string",
"const": "perf",
"description": "Performance, with the behaviour unchanged."
},
{
"type": "string",
"const": "refactor",
"description": "Structure, with the behaviour unchanged."
},
{
"type": "string",
"const": "docs",
"description": "Documentation."
},
{
"type": "string",
"const": "test",
"description": "Tests."
},
{
"type": "string",
"const": "chore",
"description": "The build, the pipeline, the tooling."
},
{
"type": "string",
"const": "ci",
"description": "Continuous integration."
},
{
"type": "string",
"const": "style",
"description": "Formatting, with the behaviour unchanged."
},
{
"type": "string",
"const": "build",
"description": "The build system or its dependencies."
},
{
"type": "string",
"const": "revert",
"description": "A change that undoes another."
},
{
"type": "string",
"const": "merge",
"description": "An integration: one line of work brought into another.\n\nNot in the conventional-commit specification, and in this repository's history 38\ntimes and counting, because integration here is a deliberate act with a subject\nsomebody writes — `merge: bring origin/master into feature/x` — and not the sentence\n`git merge` composes. Declaring it is what lets the validator hold those subjects to\nthe same width and the same references as every other commit, instead of either\nrefusing a convention this project practises or exempting it from being checked at\nall. The changelog never renders it: `commits::in_range` reads the log with\n`--no-merges`, so a merge commit is not a release entry however its subject is\nspelled."
},
{
"type": "string",
"const": "other",
"description": "A commit whose subject does not parse as a conventional commit."
}
],
"description": "What a conventional commit says it did.\n\nThe set is the one the repository's own commit convention uses; a commit whose subject\ndoes not parse is `ChangeKind::Other` and still appears, because a changelog that\nsilently drops what it cannot classify is a changelog that lies by omission."
},
"ReleaseChange": {
"type": "object",
"properties": {
"kind": {
"$ref": "#/$defs/ReleaseChangeKind",
"description": "What it did."
},
"scope": {
"type": [
"string",
"null"
],
"description": "The area it did it in, when the subject named one: `commands`, `ci`, `site`."
},
"subject": {
"type": "string",
"description": "The subject, without the type and scope that prefixed it."
},
"breaking": {
"type": "boolean",
"description": "Whether the commit marked itself breaking, with `!` or a `BREAKING CHANGE:` trailer."
},
"commit": {
"type": "string",
"description": "The abbreviated commit."
},
"url": {
"type": [
"string",
"null"
],
"description": "Where that commit can be read, when the repository's own URL is known. Derived from\n`about::REPOSITORY`, never written beside each entry."
},
"references": {
"type": "array",
"items": {
"$ref": "#/$defs/ReleaseReference"
},
"description": "The records of the layer this commit names — issues, milestones — resolved against\nwhat the layer actually holds."
}
},
"required": [
"kind",
"subject",
"breaking",
"commit"
],
"description": "One change, from one commit."
},
"ReleaseReference": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "`issue` or `milestone`."
},
"id": {
"type": "string",
"description": "`I1305`, `M000`."
},
"title": {
"type": "string",
"description": "What it is, from the record itself."
},
"route": {
"type": [
"string",
"null"
],
"description": "Where it is published on this site, when it has a page."
}
},
"required": [
"kind",
"id",
"title"
],
"description": "A record of the layer that a commit names in its own text.\n\nInferred, never declared beside the commit: an issue id or a milestone id appearing in a\nsubject or a body is a reference, and the layer already holds the object it refers to. A\nreference to something the layer does not have is not carried — a link to a record that\ndoes not exist is worse than no link, because the reader cannot tell until they follow it.\n\n```\nuse majordomus_cli::release::model::Reference;\nlet r = Reference {\n kind: \"issue\".into(),\n id: \"I1305\".into(),\n title: \"An observed contract has a fingerprint\".into(),\n route: Some(\"/plan/i1305/\".into()),\n};\nlet json = serde_json::to_value(&r).unwrap();\nassert_eq!(json[\"id\"], \"I1305\");\nassert_eq!(json[\"route\"], \"/plan/i1305/\");\n// a reference the site has no page for carries no route rather than an invented one\nlet bare = Reference { route: None, ..r };\nassert!(serde_json::to_value(&bare).unwrap().get(\"route\").is_none());\n```"
},
"ReleaseChangelogArtifact": {
"type": "object",
"properties": {
"target": {
"type": "string",
"description": "The platform triple the record names."
},
"name": {
"type": "string",
"description": "The file name."
},
"sha256": {
"type": "string",
"description": "Its SHA-256, as the record read it off the file."
}
},
"required": [
"target",
"name",
"sha256"
],
"description": "One published artifact, from the release record's own evidence."
},
"ReleaseProducedBy": {
"type": "object",
"properties": {
"capability": {
"type": "string",
"description": "The capability id."
},
"cli": {
"type": [
"string",
"null"
],
"description": "The command line that renders it."
},
"http": {
"type": [
"string",
"null"
],
"description": "The HTTP route that answers with it."
},
"mcp_tool": {
"type": [
"string",
"null"
],
"description": "The MCP tool."
},
"mcp_resource": {
"type": [
"string",
"null"
],
"description": "The MCP resource URI."
}
},
"required": [
"capability"
],
"description": "Which capability answered with this document, and where that capability is projected.\n\nThe document carries its own provenance so that a reader — a person on the site, a client\nover MCP — can find the same value elsewhere without anything having to enumerate the\nroutes. A page that listed them would be declaring them a second time, which is the\nfailure `site-check`'s `registry` and `cli` assertions exist to catch; it caught this one.\n\n```\nuse majordomus_cli::release::model::ProducedBy;\nlet by = ProducedBy {\n capability: \"release.changelog\".into(),\n cli: Some(\"majordomus release changelog\".into()),\n http: Some(\"/api/v1/changelog\".into()),\n mcp_tool: None,\n mcp_resource: Some(\"majordomus://changelog\".into()),\n};\nlet json = serde_json::to_value(&by).unwrap();\nassert_eq!(json[\"capability\"], \"release.changelog\");\n// a surface the capability is not projected on is absent, not null\nassert!(json.get(\"mcp_tool\").is_none());\nassert_eq!(ProducedBy::default().capability, \"\");\n```"
}
}
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
all
{}one-version
{
"version": "0.10.0"
}