Skip to content

HTTP API: convergence

Whether every unit of work in this repository is somewhere another worker could find it. A holding is anything that can hold work — a work tree with uncommitted files, a branch with commits, a stash — and each carries a disposition read from git: integrated (the trunk reaches it), published (a remote-tracking ref reaches it), local_only (committed on this disk and nowhere else) or uncommitted. The last two are at risk: they are invisible to every other worker, and a worker that stops is not a rollback. Measured offline, from this checkout alone; whether a pull request exists for a branch is a question for the forge and is not asked here.

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.

convergence

Whether every unit of work in this repository is somewhere another worker could find it. A holding is anything that can hold work — a work tree with uncommitted files, a branch with commits, a stash — and each carries a disposition read from git: integrated (the trunk reaches it), published (a remote-tracking ref reaches it), local_only (committed on this disk and nowhere else) or uncommitted. The last two are at risk: they are invisible to every other worker, and a worker that stops is not a rollback. Measured offline, from this checkout alone; whether a pull request exists for a branch is a question for the forge and is not asked here.

GET /api/v1/convergence convergence.report

Is any work held where it can be lost?

Every holding of this repository with its disposition, the evidence read for it and the command that would move it out of danger, the tallies per disposition, how many holdings exist on one disk only, and the one verdict over them. Read from git on every call: a commit, a push or an editor's save changes the answer between two calls.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_convergence
  • MCP resource {"name": "convergence", "uri": "majordomus://convergence"}
  • $ convergence

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/convergence"

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 ConvergenceReport Is any work held where it can be lost?: 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.

ConvergenceReport

object
The repository's convergence: every holding, and the one verdict over them.

use majordomus_cli::convergence::{ConvergenceReport, SCHEMA};

let empty = ConvergenceReport {
    schema: SCHEMA.into(),
    repository: "/tmp/r".into(),
    trunk: Some("master".into()),
    holdings: vec![],
    tallies: Default::default(),
    at_risk: 0,
    converged: true,
};
// the verdict is the one word a gate reads; the count is what a person reads next to it
assert!(empty.converged && empty.at_risk == 0);
assert_eq!(serde_json::to_value(&empty).unwrap()["schema"], "convergence/v1");
propertytyperequireddescription
schema string yes SCHEMA.
repository string yes The primary checkout this was measured from.
trunk string | null no The trunk the containment was decided against, when there is one.
holdings array of Holding → Holding yes Every holding, at-risk ones first, then by kind and identity.
tallies map of integer (uint) yes How many holdings carry each disposition, by its word.
at_risk integer (uint) yes The holdings that exist on one disk only.
converged boolean yes Nothing is held where it can be lost.

Used by convergence.report

Disposition

"integrated" | "published" | "local_only" | "uncommitted"
Where the work a holding carries can be reached from.

The order is the order of safety: everything above Disposition::LocalOnly is reachable by somebody who is not the worker that made it.

use majordomus_cli::convergence::Disposition;

// declared in the order of safety, so the derived order says which is worse
assert!(Disposition::Integrated < Disposition::Published);
assert!(Disposition::Published < Disposition::LocalOnly);
assert_eq!(serde_json::to_string(&Disposition::LocalOnly).unwrap(), "\"local_only\"");
  • integratedThe trunk reaches it: it is in the repository's own history.
  • publishedA remote reaches it: another checkout can fetch it, and it survives this disk.
  • local_onlyCommitted here and nowhere else. One disk away from being lost.
  • uncommittedNot committed at all: files in a work tree, or a stash entry.

Used by Holding

Holding

object
One thing holding work, and what is known about where that work can be reached from.

use majordomus_cli::convergence::{Disposition, Holding, HoldingKind};

let branch = Holding {
    kind: HoldingKind::Branch,
    identity: "feature/x".into(),
    disposition: Disposition::LocalOnly,
    evidence: "abc123 is on no remote-tracking ref of this checkout".into(),
    remedy: "git push -u origin feature/x".into(),
    at_risk: true,
};
// an at-risk holding always carries the command that would move it out of danger
assert!(branch.at_risk && !branch.remedy.is_empty());
// and a reachable one carries none, so the field is left out of the document
let safe = Holding { disposition: Disposition::Published, remedy: String::new(), at_risk: false, ..branch };
assert!(!serde_json::to_string(&safe).unwrap().contains("remedy"));
propertytyperequireddescription
kind HoldingKind → HoldingKind yes What kind of thing holds it.
identity string yes How it is named: a branch name, an absolute worktree path, a stash ref.
disposition Disposition → Disposition yes Where the work can be reached from.
evidence string yes What was actually read — never a restatement of the disposition.
remedy string no The command that would move it out of danger. Empty when it is not at risk.
at_risk boolean yes This holding's work exists on one disk only.

Used by ConvergenceReport

HoldingKind

"worktree" | "branch" | "stash"
What kind of thing is holding the work.

use majordomus_cli::convergence::HoldingKind;

// the word a report and a persisted record use; renaming a variant must not rename it
assert_eq!(HoldingKind::Worktree.as_str(), "worktree");
assert_eq!(serde_json::to_string(&HoldingKind::Stash).unwrap(), "\"stash\"");
  • worktreeA registered work tree, holding files that are not committed.
  • branchA local branch, holding commits.
  • stashAn entry of git stash.

Used by Holding

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