Skip to content

HTTP API: commit

The commit as a value: the scope vocabulary this repository's own history yields, what the working tree would commit and how it divides, and the verdict on one message against the repository's commit policy.

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.

commit

The commit as a value: the scope vocabulary this repository's own history yields, what the working tree would commit and how it divides, and the verdict on one message against the repository's commit policy.

GET /api/v1/commit/history commit.history

Judge a range of history

Every commit in a git range against the commit policy, in one pass: how many were read, how many git composed and are exempt, how many carry an error, and every commit that has a finding with what it is. What the gate reads to refuse new debt, and what answers `is the history of this branch clean?` without a process per commit.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_commit_history
  • $ commit history

Query parameters

nametyperequireddescriptionexamples
range string no Anything `git log` accepts: `origin/master..HEAD`, `v0.6.0..`, a bare `HEAD`. Default `HEAD`, which is the whole history reachable from here. whole-history: "HEAD"

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/commit/history?range=HEAD"

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 HistoryReport Judge a range of history: 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/commit/plan commit.plan

What the working tree would commit

The working tree as git reports it — branch, upstream, divergence, every staged, unstaged and untracked path, and any merge or rebase in progress — divided into the commits the history's own scoping supports, each with the evidence for it, under a fingerprint of the repository, the worktree, HEAD and the exact change set, so that a plan acted on later can be refused rather than applied to a tree it was not made for.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_commit_plan
  • MCP resource {"name": "commit-plan", "uri": "majordomus://commit/plan"}
  • $ commit plan

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/commit/plan"

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 CommitPlan What the working tree would commit: 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/commit/scopes commit.scopes

The scope vocabulary

Every scope this repository's commit history uses, how often, and the directories each one is written about — learned from the history rather than declared in a table, so that a subsystem committed today is in the vocabulary today and one nobody has touched falls to the bottom on its own.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_commit_scopes
  • MCP resource {"name": "commit-scopes", "uri": "majordomus://commit/scopes"}
  • $ commit scopes

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/commit/scopes"

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 ScopesReport The scope vocabulary: 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/commit/validate commit.validate

Judge one commit message

One message against this repository's commit policy: the grammar, the subject width, whether the scope is one the history uses, whether every record the message names exists, whether a fix carries a test, and whether a breaking change explains itself — as typed findings with severities, or an exemption for a subject git composed. The same verdict the commit-msg hook refuses with and the gate reads the history through.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_commit_validate
  • $ commit validate

Query parameters

nametyperequireddescriptionexamples
message string yes The commit message, as it would be stored. Comment lines are ignored, as git ignores them, so what is judged is what would be committed. conventional: "feat(commit): the rule is decided by a command"merge: "Merge pull request #249 from korczis/fix"not-conventional: "update stuff"
paths array of string no The paths the commit would contain. Omit them and the judgements that need them — whether a fix carries a test — are not made rather than guessed.

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/commit/validate?message=feat%28commit%29%3A%20the%20rule%20is%20decided%20by%20a%20command"

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 CommitVerdict Judge one commit message: 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.

ChangeStage

"staged" | "unstaged" | "untracked"
Where one path stands between the index and the working tree.

use majordomus_cli::commit::ChangeStage;
// ordered so that what would be in the next commit sorts first
assert!(ChangeStage::Staged < ChangeStage::Unstaged);
assert!(ChangeStage::Unstaged < ChangeStage::Untracked);
  • stagedDifferent in the index from HEAD: it would be in the next commit.
  • unstagedDifferent in the working tree from the index: it would not.
  • untrackedNot tracked at all.

Used by PathChange

CommitExemption

"git_authored"
Why a commit was not judged.

use majordomus_cli::commit::{judge, CommitExemption, CommitPolicy, CommitSubject};
let v = judge(&CommitSubject::of("Merge pull request #1 from a/b"), &CommitPolicy::default());
assert_eq!(v.exempt, Some(CommitExemption::GitAuthored));
  • git_authoredgit composed the subject: a merge, a revert, a fixup.

Used by CommitVerdict JudgedCommit

CommitGroup

object
One commit a plan proposes. One commit a plan proposes: its message, its paths, and why those paths are one commit.

use majordomus_cli::commit::{plan::group, CommitGroup, ScopeVocabulary};
let mut v = ScopeVocabulary::default();
v.learn("commit", "src/commit", 40);
let g: Vec<CommitGroup> = group(&["src/commit/a.rs".into()], &v, &[]);
// the subject is empty on purpose: what a change did is the one thing no evidence in
// the tree can state
assert_eq!(g[0].message.header.subject, "");
assert_eq!(g[0].message.header.scope.as_deref(), Some("commit"));
assert!(g[0].rationale.contains("40 prior commit"), "{}", g[0].rationale);
propertytyperequireddescription
message CommitMessage → CommitMessage yes The message as proposed. The summary is the one field a person is expected to replace: nothing here can know why a change was made, and a planner that wrote a confident sentence about it would be writing fiction into the history.
paths array of string yes The paths, in order.
rationale string yes Why these paths are one commit, in evidence a reader can check.
scope_from ScopeSuggestion | null → ScopeSuggestion no The scope suggestion and what it was derived from, when there was one.

Used by CommitPlan

CommitHeader

object
The header of a commit message: its first line, taken apart.

word is the type as the author wrote it and kind is what that word names; they differ exactly when the word is not one of the eleven the convention defines, and keeping both is what lets a reader see wip(site): halfway through as its author typed it while a judge still reports that wip is not a type.

use majordomus_cli::commit::CommitHeader;
use majordomus_cli::release::ChangeKind;
let h = CommitHeader::parse("fix(plan): the fingerprint names what moved");
assert_eq!(h.word, "fix");
assert_eq!(h.kind, ChangeKind::Fix);
assert_eq!(h.scope.as_deref(), Some("plan"));
assert!(!h.breaking);
assert_eq!(h.subject, "the fingerprint names what moved");
propertytyperequireddescription
word string yes The type as written: feat, fix, wip, or the empty string when the subject carries no type: head at all.
kind ReleaseChangeKind → ReleaseChangeKind yes What Self::word names; ChangeKind::Other for a word the convention does not define, and for a subject with no head.
scope string | null no The parenthesised scope, when there is one.
breaking boolean yes Whether the head carried the ! that marks a breaking change. The BREAKING CHANGE: trailer is a property of the body and is read by crate::commit::CommitMessage.
subject string yes What the commit says it did. For a header that does not parse this is the whole first line, unchanged, and for an unknown type word it keeps the word: nothing that the author wrote is dropped on the way through.

Used by CommitMessage

CommitMessage

object
A commit message taken apart: header, body, trailers.

use majordomus_cli::commit::CommitMessage;
let m = CommitMessage::parse("feat(a): a thing\n\nThe body.\n\nRefs: I1305\n");
assert_eq!(m.header.subject, "a thing");
assert_eq!(m.body, "The body.");
assert_eq!(m.trailers.len(), 1);
propertytyperequireddescription
header CommitHeader → CommitHeader yes The first line, parsed.
body string yes Everything between the header and the trailer block, with surrounding blank lines removed. Empty when the message is a subject alone.
trailers array of CommitTrailer → CommitTrailer yes The Key: value lines of the final paragraph, in the order they were written.

Used by CommitGroup CommitVerdict

CommitPlan

object
A plan over a working tree.

use majordomus_cli::commit::{CommitPlan, PlanFingerprint, WorkingTreeState};
let plan = CommitPlan {
    fingerprint: PlanFingerprint::of("/r/.git", "/r", &WorkingTreeState::default()),
    tree: WorkingTreeState::default(),
    groups: Vec::new(),
    diagnostics: Vec::new(),
};
assert!(plan.groups.is_empty(), "a clean tree proposes nothing");
propertytyperequireddescription
fingerprint PlanFingerprint → PlanFingerprint yes What the plan was derived from, and what makes it stale.
tree WorkingTreeState → WorkingTreeState yes The tree as it stood.
groups array of CommitGroup → CommitGroup yes The commits proposed, in the order they should be made.
diagnostics array of Diagnostic → Diagnostic yes What a person should know before acting on it.

Used by commit.plan

CommitTrailer

object
One Key: value line in the trailer block at the end of a commit message.

use majordomus_cli::commit::CommitMessage;
use majordomus_cli::commit::CommitTrailer;
let m = CommitMessage::parse("fix(x): a thing\n\nWhy.\n\nRefs: I1305\n");
let t: &CommitTrailer = &m.trailers[0];
assert_eq!(t.key, "Refs");
assert_eq!(t.value, "I1305");
propertytyperequireddescription
key string yes The key, as written: Refs, Co-Authored-By, BREAKING CHANGE.
value string yes Everything after the colon and one space.

Used by CommitMessage

CommitVerdict

object
What the judge concluded: the message as it was parsed, whether anything of error severity was found, the exemption when git composed the subject rather than a person, and every finding in CommitRule::ALL order — which is the order the evidence for them appears in the message, so a person reads them in the order they would fix them.

use majordomus_cli::commit::{judge, CommitPolicy, CommitSubject, CommitVerdict};
let v: CommitVerdict = judge(&CommitSubject::of("update stuff"), &CommitPolicy::default());
assert!(!v.passed);
assert_eq!(v.findings[0].code, "commit.not_conventional");
assert_eq!(v.message.header.subject, "update stuff", "what was judged, not what was typed");
propertytyperequireddescription
message CommitMessage → CommitMessage yes The message as parsed; what was judged, not what was typed.
passed boolean yes Whether nothing of error severity was found.
exempt CommitExemption | null → CommitExemption no Why the commit was not judged, when it was not.
findings array of Diagnostic → Diagnostic yes Every finding, in CommitRule::ALL order.

Used by commit.validate

HistoryReport

object
A range of history, judged. A range of history, judged in one pass.

use majordomus_cli::commit::verdict::HistoryReport;
let r = HistoryReport {
    range: "HEAD".into(),
    total: 1755,
    exempt: 561,
    failing: 54,
    commits: Vec::new(),
};
// a commit with nothing to say is counted and not listed: a report of a thousand
// passing commits is a report nobody reads
assert!(r.commits.is_empty());
assert_eq!(r.total - r.exempt, 1194);
propertytyperequireddescription
range string yes The range as asked for.
total integer (uint) yes How many commits were read.
exempt integer (uint) yes How many were exempt because git composed their subject.
failing integer (uint) yes How many carry a finding of error severity.
commits array of JudgedCommit → JudgedCommit yes Every commit that carries a finding, newest first. A commit with nothing to say is counted and not listed: a report that printed a thousand passing commits would be a report nobody reads.

Used by commit.history

JudgedCommit

object
One commit of the history, judged.

use majordomus_cli::commit::verdict::JudgedCommit;
// what a history report lists: only the commits that have something to say
let row = JudgedCommit {
    commit: "a1b2c3d4e".into(),
    subject: "update stuff".into(),
    passed: false,
    exempt: None,
    findings: Vec::new(),
};
assert!(!row.passed);
propertytyperequireddescription
commit string yes The commit name, as git gave it.
subject string yes Its subject line.
passed boolean yes Whether it passed.
exempt CommitExemption | null → CommitExemption no Why it was not judged, when it was not.
findings array of Diagnostic → Diagnostic yes Every finding.

Used by HistoryReport

PathChange

object
One path, and where it stands.

use majordomus_cli::commit::ChangeStage;
use majordomus_cli::commit::plan::PathChange;
// staged and modified again since: the commit would carry the staged half only
let c = PathChange {
    path: "src/a.rs".into(), stage: ChangeStage::Staged, status: "MM".into(), partial: true,
};
assert!(c.partial);
propertytyperequireddescription
path string yes Repository-relative, forward slashes.
stage ChangeStage → ChangeStage yes Whether it is staged, unstaged or untracked. A path modified both in the index and in the working tree appears once, as ChangeStage::Staged, with partial set.
status string yes The two-letter status git reported, verbatim.
partial boolean no Set when the path is staged and modified again since: committing it commits the staged half only, which is a thing a person should be told rather than discover.

Used by WorkingTreeState

PlanFingerprint

object
What a plan was derived from.

Compared, never trusted: a plan whose fingerprint differs from the tree in front of it describes a tree that no longer exists.

use majordomus_cli::commit::{PlanFingerprint, WorkingTreeState};
let f = PlanFingerprint::of("/repo/.git", "/repo", &WorkingTreeState::default());
assert_eq!(f.head, "unborn", "a repository with no commits says so");
propertytyperequireddescription
repository string yes The repository: git's common directory, which every worktree of one repository shares and no two repositories do.
worktree string yes The worktree: this checkout's own path. Two worktrees of one repository have the same repository and different worktrees, which is what keeps their plans apart.
head string yes HEAD when the plan was derived, or unborn.
changes string yes SHA-256 over every change, its stage and its status, in order. Restaging one file changes it; touching a file the plan does not contain does not.

Used by CommitPlan

ScopeSuggestion

object
Which scope a set of paths belongs to, and the evidence for it.

use majordomus_cli::commit::{ScopeSuggestion, ScopeVocabulary};
let mut v = ScopeVocabulary::default();
// two scopes with equal claim to one directory: reported, never broken silently
v.learn("a", "src", 7);
v.learn("b", "src", 7);
let s: ScopeSuggestion = v.suggest(&["src/x.rs".into()]).expect("a tie");
assert!(s.ambiguous);
propertytyperequireddescription
scope string yes The scope.
commits integer (uint) yes How many prior commits associate this scope with directories these paths are in.
directory string yes The directory prefix the association was strongest at.
ambiguous boolean yes Whether another scope was as strongly associated. A tie is reported rather than broken silently: a planner that guessed would be inventing the one field of a commit message that a reader uses to navigate.

Used by CommitGroup

ScopeUse

object
One scope the history uses, and how much.

use majordomus_cli::commit::scopes::ScopeUse;
let u = ScopeUse { scope: "commit".into(), commits: 40, directories: vec!["src/commit".into()] };
assert_eq!(u.commits, 40);
propertytyperequireddescription
scope string yes The scope word, as commits spell it.
commits integer (uint) yes How many of the sampled commits used it.
directories array of string yes The directory prefixes those commits touched, most often first, at most five.

Used by ScopeVocabulary

ScopeVocabulary

object
The vocabulary as observed, with what was observed.

use majordomus_cli::commit::ScopeVocabulary;
// an empty vocabulary knows nothing and says so, rather than guessing
let v = ScopeVocabulary::default();
assert!(!v.knows("anything"));
assert!(v.words().is_empty());
propertytyperequireddescription
scopes array of ScopeUse → ScopeUse yes Every scope, by how often it is used, then by name. The order is the answer to "which scopes does this repository use", so it is the order a person reads.
sampled integer (uint) yes How many commits were read to learn it.
unavailable string | null no Set when git could not be asked; the vocabulary is then empty rather than wrong.

Used by ScopesReport

ScopesReport

object
The vocabulary, with the policy that reads it.

use majordomus_cli::capability::builtin::commit::ScopesReport;
use majordomus_cli::commit::ScopeVocabulary;
let r = ScopesReport { vocabulary: ScopeVocabulary::default(), sample: 1500 };
assert_eq!(r.sample, majordomus_cli::commit::scopes::SAMPLE);
propertytyperequireddescription
vocabulary ScopeVocabulary → ScopeVocabulary yes Every scope the history uses, most used first, with the directories it is used about.
sample integer (uint) yes How many commits back the vocabulary is learned from.

Used by commit.scopes

WorkingTreeState

object
What git says about the working tree right now.

use majordomus_cli::commit::WorkingTreeState;
let clean = WorkingTreeState::default();
assert!(clean.is_clean());
assert!(clean.staged().is_empty());
propertytyperequireddescription
branch string | null no The branch, or None when HEAD is detached.
head string | null no HEAD's full name, or None in a repository with no commits.
upstream string | null no The upstream ref, when the branch has one.
ahead integer (uint) yes Commits this branch has that its upstream does not.
behind integer (uint) yes Commits the upstream has that this branch does not.
in_progress string | null no A merge, rebase, cherry-pick or bisect in progress, when there is one.
changes array of PathChange → PathChange yes Every change, ordered by stage then path.

Used by CommitPlan

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