Skip to content

HTTP API: shared types

The schemas more than one tag's operations use, or that no operation reaches, and any operation carrying more than one tag.

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.

Schemas

The components more than one tag's operations use, or none does. Each row is the type's own field, its doc comment the description; a type another page owns links there.

ActiveTask

object
The active task of this checkout, when there is one.
propertytyperequireddescription
id string yes The task id.
task string yes What is being worked on.
profile string yes The execution profile it runs under.
outcome string yes Its typed outcome so far: active, handed_over, or a finished one.
scope array of string no The paths it claims.
requires array of string no The obligations it owes before the outcome completed is available.

Beside scope and not inside it, because the two are different promises: scope is containment — where a worker may write — and this is delivery. A change can sit entirely inside its scope and still be uncommitted on a laptop (ADR 0030). super::obligations is what judges each of these against its evidence; here it is reported as declared.

started_at string no When it started.
head string no The commit it started at.
issue string no The issue the worker said this task executes (start --issue). A name, never state: what it serves is answered by intents.binding on every read (ADR 0111).
intent string no The intent the worker said this task serves (start --intent).
exemption string no The exemption class the worker gave instead (start --exempt).
exemption_because string no The reason given with the exemption.
binding string no The standing intents.binding answered when the task started — what start was told then, not where the work stands now.
plan_revision string no The pin of what the work was for when the task started.
evidence_standing string no The pin of where the served criteria's evidence stood when the task started.

Used by Closure Completion Continuity

AnswerObservation

object
When the state behind an answer was read: the freshness contract of a capability answer.

Declared once and carried in the output type of every answer that is a picture of state, so that the output schema, OpenAPI, the MCP tool schemas and the command line's JSON all carry it without a transport of their own. It is set where the state is read — the index build, the git read — never by the executor, which knows when a handler ran and not when the state it read was taken.

use std::time::{Duration, UNIX_EPOCH};
use majordomus_cli::index::AnswerObservation;
let at = UNIX_EPOCH + Duration::from_secs(1_788_000_000);
let o = AnswerObservation::taken("git read", at, Some(Duration::from_secs(120)));
assert_eq!(o.observed_at, "2026-08-29T10:40:00Z");
assert_eq!(o.stale_after.as_deref(), Some("2026-08-29T10:42:00Z"));
assert!(!o.is_stale_at(at + Duration::from_secs(120)));
assert!(o.is_stale_at(at + Duration::from_secs(121)));
propertytyperequireddescription
observed_at string yes RFC 3339 UTC, whole seconds: when the state behind the answer was read. Empty only in an answer from an index that was never built (a hand-made one in a test).
source string yes What was read at that time: index build or git read.
stale_after string | null no RFC 3339 UTC: after this the answer is not to be trusted as current even though nothing announced a change. Absent when there is no known blind spot.

Used by Health RepositoryObservations

CapabilityId

string
A stable, globally meaningful identity: a namespace, a dot, and a local part. repository.info and objects.get for executables; <kind>.<identity> for a declarative object (rule.majordomus.scope-integrity@1, document.docs/CLI.md, policy..ai/repo/policy.yaml).

Grammar: the namespace matches [a-z][a-z0-9_-]*; the local part is non-empty and carries no whitespace or control character, any other Unicode included, because it is opaque: a path, a versioned identity, or a name, as the kind's identity rule produced it.

Used by Capability CapabilitySummary CommandProvenance

CapabilityKind

"query" | "command" | "resource"
What a capability is. Three kinds exist because three semantics exist: something that is executed and changes nothing, something that is executed and changes this process's own memory, and something that is read. A command may also write the repository's own tracked records, which its kind does not say and its execution policy does (ExecutionPolicy::writes_repository).

How long a call takes is not a kind. A read that walks every file of the layer is still a read, and the thing that makes it worth watching — that it reports as it goes and stops when it is asked to — is one property of its handler, declared with crate::capability::Executable::cancellable and carried on ExecutionPolicy.

  • queryExecutable and read-only: a typed handler, an input schema, an output schema.
  • commandExecutable and changing something: a typed handler, bound to POST over HTTP, and announced to MCP clients as not read-only. Most reach no further than this process's own memory (a peer announcing itself); one that writes the repository's tracked records says so on its execution policy, which is where a projection reads it.
  • resourceDeclarative content the repository holds: read as it is, never executed.

Used by AnswerView Capability CapabilitySummary

CapabilityProvenance

one of 2 variants
Where a capability came from. Never an absolute path.
variant Written in Rust, in the named module of this executable. Written in Rust, composed in builtin.rs.
modulestringyesThe Rust module the descriptor was composed in.
source"builtin"yes
variant Read from the repository's layer.
pathstringyesRepository-relative path.
directorystringyesThe directory the path sits in, repository-relative; . for the root.
source_classstringyesThe sources.yaml class that discovered the file.
sectionstring | nullnoThe manifest section the path falls under, when it falls under one.
media_typestringyesIANA media type of the object's content.
memberstring | nullnoFor one member of a collection file, its key path in the file (claims.3).
source"declarative"yes

Used by AnswerView Capability CapabilitySummary

Checkouts

"repository" | "this"
Which checkouts a reading of the server status covers.

The two answers cost differently and that is the whole reason the choice exists: the repository's checkouts are read from git's registry of them and each one costs a lease read and a probe, while this checkout alone costs one of each and enumerates nothing. The word each variant serialises to is the word the query parameter, the tool input and the command-line flag all take.

use majordomus_cli::capability::builtin::server::Checkouts;
assert_eq!(serde_json::to_value(Checkouts::This).unwrap(), "this");
assert_eq!(serde_json::to_value(Checkouts::Repository).unwrap(), "repository");
assert_eq!(Checkouts::default(), Checkouts::Repository, "the wide answer is what a caller who says nothing has always got");
  • repositoryEvery checkout git registers for the repository, the primary first — a lease read and a probe each. The default, because it is the answer this capability gave before there was anything to ask.
  • thisThis checkout alone. No other checkout is enumerated, no other lease is read and no other server is probed; servers holds the one entry, and it is this one.

Used by brief.session peers.list server.status

CoverageStrength

"uncovered" | "observed" | "weak" | "covered"
How well a criterion is carried by the plan: whether work exists for it, whether that work is gated by evidence, and whether the recorded gap already saw it true.

use majordomus_cli::intent_plan::CoverageStrength;
// the wire spelling every surface answers with
assert_eq!(serde_json::to_string(&CoverageStrength::Weak).unwrap(), "\"weak\"");
// the order is the one a reader ranks by: uncovered is the worst
assert!(CoverageStrength::Uncovered < CoverageStrength::Covered);
  • uncoveredNo live issue serves it.
  • observedNo live issue serves it, and none needs to: the recorded gap observed it already true. Whether it is met is still the evidence's question, never this one's.
  • weakEvery live issue serving it requires no evidence, so nothing gates its completion.
  • coveredAt least one live issue serving it requires evidence before it is DONE.

Used by CriterionCoverage

CoveringIssue

object
One issue as coverage sees it: which milestone carries it, the status the plan derived for it, and how much evidence it owes before it may be DONE.

use majordomus_cli::intent_plan::CoveringIssue;
let issue = CoveringIssue {
    id: "I1901".into(),
    milestone: "intent-planning".into(),
    status: "READY".into(),
    evidence_need: 2,
};
// an issue that owes no evidence covers a criterion only weakly
assert!(issue.evidence_need > 0);
propertytyperequireddescription
id string yes The issue id.
milestone string yes Its milestone.
status string yes Its derived status.
evidence_need integer (uint32) yes How many evidence tokens it requires before it may be DONE.

Used by CriterionCoverage

CriterionCoverage

object
One criterion of one intent, and the work that carries it: the live issues that serve it, the milestones those issues sit under, and the strength derived from them.

use majordomus_cli::intent_plan::{CoverageStrength, CriterionCoverage};
let c = CriterionCoverage {
    intent: "intent-planning".into(),
    criterion: "gap-is-recorded".into(),
    strength: CoverageStrength::Uncovered,
    issues: vec![],
    milestones: vec![],
};
// nothing serves it, so nothing in the plan will make it true
assert_eq!(c.strength, CoverageStrength::Uncovered);
assert!(c.issues.is_empty());
propertytyperequireddescription
intent string yes The intent.
criterion string yes The criterion id inside it.
strength CoverageStrength → CoverageStrength yes The strength derived from the serving issues.
issues array of CoveringIssue → CoveringIssue yes Every live issue that serves it, in id order.
milestones array of string yes The milestones those issues belong to, sorted and unique.

Used by IntentCoverage IntentExplanation

Diagnostic

object
One finding about the declarative state, named by a stable code, tied to a path where there is one, and carrying the command that reproduces it where there is one.
propertytyperequireddescription
severity Severity → Severity yes How bad.
code string yes A stable machine-readable code, e.g. unknown_key, duplicate_identity.
path string | null no Repository-relative path of the file concerned, when there is one.
message string yes What is wrong, for a person; the code is for a program.

Used by CommitPlan CommitVerdict CompiledContext JudgedCommit LayerSummary Records RepositoryEnvironment RepositoryReport Status Synced

Divergence

"exact" | "advanced" | "diverged" | "different_context" | "unknown"
How far a record's recorded commit is from the commit this checkout is on.

The four words are the shell tool's, deliberately. A reader that met advanced from one surface and stale from another would have to learn the same four facts twice.

  • exactWritten at this commit. Trust it.
  • advancedGit has moved forward since. Trust it, and expect some of it to be done.
  • divergedThe recorded commit is not an ancestor: history was rewritten. Trust git, not this.
  • different_contextAnother branch or another worktree. This record is not about your work.
  • unknownGit could not answer, so this process says so rather than guessing exact.

Used by ObligationClosure Record

ErrorBody

object
The body of every error response.
propertytyperequireddescription
error ErrorDetail → ErrorDetail yes The one error.

Used by artifacts.list brief.session capabilities.list capabilities.projections capabilities.describe release.changelog commands.get commands.list commands.graph commit.history commit.plan commit.scopes commit.validate continuity.state continuity.device continuity.plan continuity.publish continuity.records continuity.resume continuity.status continuity.sync convergence.report dashboard.overview delivery.report delivery.feature deploy.get deploy.list deploy.check deploy.observed design.system design.contrast design.explain design.tokens devcontext.compile devcontext.explain devcontext.policy devtask.issue devtask.milestone directories.list distribution.model distribution.artifact distribution.build distribution.releases distribution.status economics.summary economics.check economics.explain economics.runs editors.status entity.show entity.kinds environment.status environment.explain environment.preflight episodes.list episodes.attach episodes.detach evidence.report evidence.claim evidence.test executions.list executions.cancel executions.demonstrate executions.events executions.get executions.protocol executions.report executions.start gates.model gates.completion graph.get graph.list guard.edit health.report intents.list intents.binding intents.coverage intent_realization.explain intents.preflight intents.propose intent_realization.work intents.record intents.validate knowledge_base.candidates knowledge_base.record knowledge_base.status lifecycle.closed lifecycle.episodes lifecycle.providers lifecycle.recovery lifecycle.runtime health.live mcp.projection mesh.status mesh.claim mesh.release mesh.cooperation mesh.doctor mesh.events mesh.firewall mesh.handover.publish mesh.handover.consume mesh.identity mesh.link.hello mesh.link.sync mesh.nodes mesh.peer mesh.peers mesh.register mesh.review.request mesh.review.answer mesh.review.withdraw mesh.session.open mesh.session.close mesh.state mesh.verify models.list models.route monitor.stream objects.get objects.list objects.verify obligations.vocabulary obligations.closure observatory.capture observatory.events observatory.replay pack.plan pack.verify peers.list peers.announce perf.counters plan.model plan.issues plan.next plan.record plan.roadmap plan.status plan.transition plan.validate plan.waves product.domains product.feature product.features product.matrix product.providers product.validate integration.queue integration.cleanup integration.events integration.explain integration.prove_dry_run integration.timings integration.trains quality.report quality.rustdoc health.ready reasoning.status reasoning.advisors reasoning.check reasoning.explain reasoning.plan reasoning.record recover.orphans release.analysis release.next release.version repository.info rules.report rules.proves rules.show repository.scope repository.scope_classify objects.search served.show server.status session_domain.identity session_domain.machine shell.check skills.status skills.explain skills.verify trace.report trace.commit trace.issue web.surfaces why.list why.areas why.audiences why.diagnose why.moment why.validate worktree.topology worktree.inspect worktree.migration_plan worktree.status worktree.uncommitted

ErrorDetail

object
What went wrong, as the HTTP projection names it.
propertytyperequireddescription
code string yes What kind of failure it is: invalid_input, not_found, method_not_allowed, refused, forbidden, too_large, unavailable (a surface whose producer has not run) or internal.
message string yes The reason, for a person.

Used by ErrorBody

EvidenceExecution

object
One recorded run of one test: the whole of what this repository durably remembers about it.

Every field is provenance. A result with no commit is an anonymous green, which is the thing a badge must never be derived from.

use majordomus_cli::evidence::{digest_of, Execution, Origin, Outcome, Runner, TestId};

let id = TestId::of("apps/majordomus-cli/tests/why.rs").unwrap();
let run = Execution {
    test: id.as_string(),
    runner: Runner::Crate,
    source: id.source(),
    outcome: Outcome::Pass,
    seconds: 12,
    commit: "0".repeat(40),
    working_tree: "clean".into(),
    digest: digest_of(b"fn main() {}"),
    at: "2026-09-11T00:00:00Z".into(),
    origin: Origin::Ci,
    command: id.reproduce(),
    run: None,
};

// it is a value in a tracked JSON file, so it has to survive the file unchanged
let back: Execution = serde_json::from_str(&serde_json::to_string(&run).unwrap()).unwrap();
assert_eq!(back, run);
assert_eq!(back.test, "crate:why");
assert_eq!(back.command, "cargo test --test why");

Example

One recorded run, with the provenance the runner did not carry: which commit, which tree state, the digest of the test's own source, when, and how to run it again.

use majordomus_cli::evidence::{Execution, Origin, Outcome, Runner};
let e = Execution {
    test: "suite:07_scope".into(),
    runner: Runner::Suite,
    source: "test/cases/07_scope.sh".into(),
    outcome: Outcome::Pass,
    seconds: 3,
    commit: "a04b65c9".into(),
    working_tree: "clean".into(),
    digest: "sha256:00".into(),
    at: "2026-09-11T00:00:00Z".into(),
    origin: Origin::Local,
    command: "bash test/run.sh 07_scope".into(),
    run: None,
};
assert!(e.outcome.proves());
propertytyperequireddescription
test string yes The test, in TestId::as_string form.
runner EvidenceRunner → EvidenceRunner yes Which runner produced it.
source string yes The test's own source, repository-relative.
outcome EvidenceOutcome → EvidenceOutcome yes What it said.
seconds integer (uint64) yes How long it took, in whole seconds.
commit string yes The full commit id the run was made against.
working_tree string yes clean, dirty or unknown: whether the tree the run measured was the commit.
digest string yes sha256:<hex> of the test's own source as it was when the run was recorded. An execution whose test no longer hashes to this did not run the test that is there now.
at string yes When it was recorded, RFC 3339, UTC.
origin EvidenceOrigin → EvidenceOrigin yes Where the run happened.
command string yes The exact command that runs this one test again.
run EvidenceRun | null → EvidenceRun no The continuous-integration run the execution was recorded in, when it was recorded in one that could name itself. Absent for a local run, and absent from every row recorded before runs were named, which is why the ledger's version did not change.

Used by ClaimEvidence EvidenceClaimProof RuleTestProof TestEvidence

EvidenceOrigin

"local" | "ci" | "release"
Where a run happened. The canonical model is provider-neutral: a CI adapter records Ci, and nothing here knows or cares which CI it was.

use majordomus_cli::evidence::Origin;

assert_eq!(Origin::parse("CI"), Some(Origin::Ci));
assert_eq!(Origin::parse("local"), Some(Origin::Local));
// naming the provider is the adapter's business; the ledger records the kind of run,
// so a repository that changes CI does not change what its old evidence says
assert!(Origin::parse("github-actions").is_none());

Example

use majordomus_cli::evidence::Origin;
assert_eq!(Origin::parse("ci"), Some(Origin::Ci));
assert_eq!(Origin::parse("nowhere"), None);
  • localSomeone ran it on their own machine.
  • ciA continuous-integration run.
  • releaseA release run.

Used by EvidenceExecution

EvidenceOutcome

"pass" | "fail" | "skip" | "timeout" | "error"
What a run said about one test.

NotRun is deliberately absent: it is the absence of an execution, not an outcome one had. Encoding it here would let a recorder write "this did not run" and have it counted among the things that did.

use majordomus_cli::evidence::Outcome;

assert!(Outcome::parse("ok").proves());
assert!(!Outcome::parse("timeout").proves());
// exactly one of the five is evidence of anything; the other four are reasons there
// is none, kept apart because "it declined" and "the harness broke" are different bugs
assert_eq!(Outcome::parse("skipped"), Outcome::Skip);
assert_eq!(Outcome::parse("nonsense"), Outcome::Error);

Example

use majordomus_cli::evidence::Outcome;
assert!(Outcome::parse("ok").proves());
assert_eq!(Outcome::parse("wat"), Outcome::Error);
  • passIt passed.
  • failIt failed.
  • skipIt declined to run and said so (an unmet precondition the case reports itself).
  • timeoutThe bound fired before it finished.
  • errorThe harness could not run it at all.

Used by EvidenceExecution

EvidenceRun

object
The continuous-integration run an execution was recorded in: enough to navigate back to the run that produced it.

Origin says what kind of run it was and deliberately names no provider. This names the one run, in the words the provider addressed it with when it happened — its identifier, its attempt, the workflow and job, and the address a reader follows — because a link built later from a template would point wherever the template's idea of the provider had drifted to. Only an adapter constructs it; nothing in the model reads the provider's name.

use majordomus_cli::evidence::RunRef;

let actions = |k: &str| {
    let v = match k {
        "GITHUB_ACTIONS" => "true",
        "GITHUB_SERVER_URL" => "https://github.com",
        "GITHUB_REPOSITORY" => "owner/repo",
        "GITHUB_RUN_ID" => "42",
        "GITHUB_RUN_ATTEMPT" => "2",
        "GITHUB_WORKFLOW" => "validate",
        "GITHUB_JOB" => "evidence",
        _ => return None,
    };
    Some(v.to_string())
};
let run = RunRef::from_env(actions).unwrap();
assert_eq!(run.id, "42");
assert_eq!(run.attempt, 2);
assert_eq!(run.url, "https://github.com/owner/repo/actions/runs/42/attempts/2");
// a run that names neither its pull request's head nor its event still names itself
assert_eq!((run.head_sha, run.event), (None, None));

let head = "0123456789abcdef0123456789abcdef01234567";
let pull = |k: &str| match k {
    "MJ_RUN_HEAD_SHA" => Some(head.to_string()),
    "GITHUB_EVENT_NAME" => Some("pull_request".to_string()),
    _ => actions(k),
};
let run = RunRef::from_env(pull).unwrap();
assert_eq!(run.head_sha.as_deref(), Some(head));
assert_eq!(run.event.as_deref(), Some("pull_request"));

// outside a run there is nothing to name, and a half-described run is not a run
assert!(RunRef::from_env(|_| None).is_none());
assert!(RunRef::from_env(|k| (k == "GITHUB_ACTIONS").then(|| "true".to_string())).is_none());
propertytyperequireddescription
provider string yes The provider that ran it, as the adapter that recorded it names it.
id string yes The provider's identifier of the run.
attempt integer (uint32) yes Which attempt of the run, counted from 1.
workflow string yes The workflow the run belongs to.
job string yes The job within the run that recorded the execution.
url string yes Where a reader finds the run, as the provider addressed it at the time.
head_sha string | null no The pull request's head commit when the run was a pull-request run (its commit is GitHub's merge commit, the tree that ran). Carried beside the commit, never substituted for it.
event string | null no The event that started the run: push, pull_request, schedule, workflow_dispatch.

Used by EvidenceExecution

EvidenceRunner

"suite" | "crate"
Which runner produced a result, and therefore how the test is named and re-run.

The repository has exactly two that a claim may name today. A third would be a variant here and a branch in TestId::of; a claim naming something neither runner produces is reported as ProofState::Unrunnable rather than silently counted as covered.

use majordomus_cli::evidence::{Runner, TestId};

// which runner owns a test is read off where the test lives, never declared
assert_eq!(TestId::of("test/cases/07_scope.sh").unwrap().runner, Runner::Suite);
assert_eq!(
    TestId::of("apps/majordomus-cli/tests/why.rs").unwrap().runner,
    Runner::Crate
);
// and a path under neither directory belongs to no runner at all
assert!(TestId::of("test/lib.sh").is_none());

Example

use majordomus_cli::evidence::Runner;
assert_eq!(Runner::Suite.prefix(), "suite");
assert_eq!(Runner::Crate.prefix(), "crate");
  • suiteA behavioural case under test/cases/, run by test/run.sh.
  • crateAn integration test binary under apps/majordomus-cli/tests/, run by cargo test.

Used by EvidenceExecution TestEvidence

ExecutionEffect

"read" | "process_state" | "repository_mutation"
What running a capability changes outside the caller.

Classified, never declared: it follows from the kind, which is the field a declaration already carries. A projection reads this to decide whether to ask before running something — the Cockpit's confirmation is derived from it — instead of naming capabilities it must treat carefully, which is a list that goes stale the day after it is written.

  • readNothing changes. Every query and every resource of this executable.
  • process_stateThis process's own memory changes, and nothing outside it.
  • repository_mutationThe repository changes.

    For most of this executable's life nothing classified to this, because no capability of any kind wrote to the repository. That day arrived with ADR 0040: development semantics are capabilities of this runtime, and the first of them — plan.transition — stamps a field into an issue's own tracked record. The variant was put on the model in advance so that when one did, it would say so here, where a projection already reads it and a client already asks before running it, rather than in whichever page happens to render its button.

    It is not classified from the kind, because the kind cannot know it: a command that announces a peer and a command that writes a tracked file are the same kind. A handler that writes the repository says so with ExecutionPolicy::writes_repository, the way one that stops says so with ExecutionPolicy::stoppable.

Used by mcp.projection ExecutionPolicy McpEffectCount McpToolEntry Row

Freshness

"fresh" | "aging" | "stale" | "unknown" | "invalid"
How old a record is, judged against session.freshness in the policy.

This is the second of two independent judgements every record carries, and it exists because the first one cannot answer this question. Divergence says where a record's commit sits relative to HEAD; advanced means that commit is an ancestor of this one, which sounds like agreement and is identically true on the day the record is written and a month later. Between 2026-09-05 and 2026-09-11 this repository served a handover that was advanced and six days dead, and every surface that read this model — MCP, the HTTP API, the Cockpit — presented its Next Action as the thing to do (ADR 0052).

The thresholds are policy and are read, never restated here: a constant in this file would be a second source of truth for a number the policy owns, and a repository whose policy predates the key is reported as Freshness::Unknown naming the missing key rather than judged against a default nobody declared.

The five words divide into two groups, and the division is the one a caller acts on: fresh and aging are current and may be presented as the instruction, stale and invalid must be shown as history, and unknown is neither — a record nobody can date is not thereby an old record, and treating it as one would quietly discard a briefing that is perfectly good. Freshness::history is that division, and it is the only place it is decided.

use majordomus_cli::capability::builtin::continuity::Freshness;

// the two renderings are one value: the serialised word is the word `as_str` gives,
// so a reader who met `stale` from MCP and from the command line met one fact
let stale = serde_json::to_value(Freshness::Stale).unwrap();
assert_eq!(stale, serde_json::json!("stale"));
assert_eq!(stale.as_str(), Some(Freshness::Stale.as_str()));

// and the division a caller acts on: absence of a judgement is not a bad judgement
assert!(Freshness::Stale.history() && Freshness::Invalid.history());
assert!(!Freshness::Unknown.history(), "undated is not old");
assert!(!Freshness::Fresh.history() && !Freshness::Aging.history());
  • freshInside session.freshness.fresh_minutes. Current; act on it.
  • agingBetween the two thresholds. Still current, but its age is worth stating.
  • staleAt or beyond session.freshness.stale_minutes. History, never current.
  • unknownNothing to judge it by: no timestamp, or no thresholds declared.
  • invalidA timestamp that does not parse, or one in the future.

Used by KnowledgeCandidate KnowledgeStatus Record

HealthStatus

"ok" | "warn" | "fail" | "unknown"
Where one dimension of the system stands. Ordered by severity, so the worst check decides the whole.
  • okThe engine that decides this dimension is satisfied.
  • warnNothing is broken and something is worth looking at.
  • failThe engine that decides this dimension is not satisfied.
  • unknownThis process cannot decide it, and says so rather than reporting ok.

Used by DashboardCard DashboardOverview DashboardQuestion Health HealthCheck Readiness

IntentCriterion

object
One criterion with the evidence behind it: what the record declared, what the ledger says about it, whether that meets it, and the command that would produce the evidence again.

use majordomus_cli::evidence::ProofState;
use majordomus_cli::intent::{IntentCriterion, IntentEvidenceState};
let c = IntentCriterion {
    id: "stage-is-derived".into(),
    criterion: "the stage follows the plan".into(),
    evidence: "test".into(),
    reference: "apps/majordomus-cli/tests/intent.rs".into(),
    state: IntentEvidenceState::NotRun,
    met: false,
    proof: Some(ProofState::NotRun),
    reproduce: None,
};
// nothing recorded is not a pass
assert!(!c.met);
propertytyperequireddescription
id string yes Unique within the intent.
criterion string yes What is observably true when it is met.
evidence string yes test, claim, command or deployment.
ref string yes What the evidence names, as authored.
state IntentEvidenceState → IntentEvidenceState yes Derived: what the evidence says.
met boolean yes Derived: the state is current.
proof ProofState | null → ProofState no Derived: the evidence module's own verdict behind state, for a test or claim criterion whose reference resolves. A met criterion rests on proven (a pass at this revision) or inputs_unchanged (a pass whose named inputs have not moved since, which is not proof at this revision), and every surface shows which, so met is never the one tick for both.
reproduce string | null no The command that produces the evidence again, when one is known.

Used by IntentPreflightIntent IntentView

IntentEvidenceState

"current" | "stale" | "failing" | "not_run" | "not_derivable" | "unresolved"
What the evidence behind one criterion says, as a word. Only Current meets a criterion: a stale, failing, unrecorded or underivable reference never does, and neither does one that resolves to nothing.

Whether a recorded run is current is not decided here. A test or claim criterion is judged by the evidence module's one truth table (evidence::freshness()) at the working tree and read through evidence::current, the same judgement the claim report and the subject pages make; IntentEvidenceState::judged is the whole of the translation. So the state follows the evidence both ways: a criterion met today reads stale or failing the moment the ledger or the tree says so, and met again once a current pass is recorded.

use majordomus_cli::intent::IntentEvidenceState;
assert_eq!(serde_json::to_string(&IntentEvidenceState::NotRun).unwrap(), "\"not_run\"");
assert_ne!(IntentEvidenceState::Stale, IntentEvidenceState::Current);
  • currentA passing run that is current evidence at the working tree: its commit is in this history, it measured a tree that was its commit, the test still hashes to what ran, and nothing the criterion names has changed since — for a test, its source and the source and implementation of every claim that test proves; for a claim, what the claim names. A change the criterion does not name, the evidence ledger's own included, leaves it current. A test that proves no claim naming an implementation names no code under test, so for it nothing but the ledger may have changed since the run. The only state that meets a criterion; the criterion's proof says whether it rests on proven or on inputs_unchanged.
  • staleA pass that is not current evidence: something the criterion names has changed since it, it names no code under test and anything but the ledger and the plan's own records has changed since, the test no longer hashes to what ran, the run's commit is not in this history (or git could not compare it), or the run measured a tree that was not its commit — a run on a dirty tree, which no later tree can be matched against.
  • failingThe latest recorded run failed, timed out or errored.
  • not_runThe reference resolves and no run counts for it: none was recorded, the latest run declined to run (a skip), or the claim names no test a runner drives.
  • not_derivableThe reference resolves, and this kind of evidence (command, deployment) is not derivable from the ledger, so it never meets a criterion by itself.
  • unresolvedThe reference is empty or names nothing this repository holds.

Used by IntentCriterion IntentUnmetCriterion IntentVerdictReason

IntentFinding

object
One validation finding: its level, a stable code a reader greps for, the intent or milestone it is about, one line of what is wrong, and the command that shows it again.

use majordomus_cli::intent::{IntentFinding, FAIL};
let f = IntentFinding {
    level: FAIL.into(),
    code: "unknown_milestone".into(),
    subject: "intent-lifecycle".into(),
    message: "`no-such` is not a milestone".into(),
    reproduce: "majordomus intent validate".into(),
};
assert_eq!(f.level, "FAIL");
assert_eq!(f.reproduce, "majordomus intent validate");
propertytyperequireddescription
level string yes FAIL or WARN. A failure means the intent model is invalid.
code string yes The stable code a reader greps for.
subject string yes The intent or milestone the finding is about.
message string yes What is wrong, in one line.
reproduce string yes The command that shows it again.

Used by IntentCoverage IntentRealization IntentRealizationView IntentValidation

IntentMilestone

object
One milestone an intent names, as the plan derives it. A milestone that does not resolve is carried with resolved: false rather than dropped, because a name that points at nothing is a finding and not an absence.

use majordomus_cli::intent::IntentMilestone;
let named = IntentMilestone { id: "no-such".into(), resolved: false, status: None };
assert!(!named.resolved);
assert!(named.status.is_none());
propertytyperequireddescription
id string yes The id the intent names.
resolved boolean yes Whether the plan holds a milestone with that id.
status string | null no The status the plan derives for it; absent when it does not resolve.

Used by IntentView

IntentStage

"declared" | "planned" | "executing" | "verifying" | "satisfied" | "cancelled" | "superseded"
Where an intent stands, derived on every read and stored nowhere.

use majordomus_cli::intent::IntentStage;
// one word every surface prints, and an order that follows the work
assert_eq!(IntentStage::Executing.as_str(), "executing");
assert!(IntentStage::Declared < IntentStage::Satisfied);
  • declaredA named milestone does not resolve, or none is named.
  • plannedEvery milestone resolves and none has started.
  • executingA milestone is under way.
  • verifyingEvery milestone that counts is DONE; satisfaction is not yet evidenced.
  • satisfiedVerifying, and every criterion has current evidence.
  • cancelledThe record says it was abandoned.
  • supersededThe record names the intent that replaced it.

Used by IntentLink IntentPreflightIntent IntentPreflightMatch IntentRealizationView IntentView

IntentVerdict

object
Whether the evidence alone settles an intent, and which criteria hold it back: every unmet criterion, in the order the record declares them. Empty when the verdict is satisfied, and when it is unknown because the intent declares no criterion.

use majordomus_cli::intent::{verdict, IntentVerdict, IntentVerdictState};
// an intent that declares no criterion cannot be settled by evidence
let v: IntentVerdict = verdict(&[]);
assert_eq!(v.state, IntentVerdictState::Unknown);
assert!(v.reasons.is_empty());
propertytyperequireddescription
state IntentVerdictState → IntentVerdictState yes Derived: what the evidence says.
reasons array of IntentVerdictReason → IntentVerdictReason yes Every criterion that is not met, with the state of its evidence.

Used by IntentView

IntentVerdictReason

object
One criterion that holds a verdict back from satisfied: which one, the kind of evidence it names, and the state that evidence is in — the criterion's own words, not new ones.

use majordomus_cli::intent::{IntentEvidenceState, IntentVerdictReason};
let r = IntentVerdictReason {
    criterion: "probe-passes".into(),
    evidence: "test".into(),
    state: IntentEvidenceState::NotRun,
};
assert_eq!(r.state, IntentEvidenceState::NotRun);
propertytyperequireddescription
criterion string yes The criterion's id.
evidence string yes Its kind of evidence: test, claim, command or deployment.
state IntentEvidenceState → IntentEvidenceState yes The state of that evidence, as the criterion reports it.

Used by IntentVerdict

IntentVerdictState

"satisfied" | "unsatisfied" | "unknown"
What the evidence alone says about an intent, as a word: derived from its criteria and never from the plan, so it can read satisfied while a milestone is still open (ADR 0107).

use majordomus_cli::intent::IntentVerdictState;
let word = serde_json::to_string(&IntentVerdictState::Unsatisfied).unwrap();
assert_eq!(word, "\"unsatisfied\"");
assert_eq!(IntentVerdictState::Satisfied.as_str(), "satisfied");
  • satisfiedEvery criterion has current evidence.
  • unsatisfiedA criterion the ledger or the claim join can settle (test, claim) is not met.
  • unknownEvery unmet criterion is of a kind the ledger cannot yet settle (command, deployment), or the intent declares no criterion: the evidence cannot answer.

Used by IntentVerdict

IntentView

object
One intent, as its record declares it and as the plan and the ledger derive it: the statement and invariants as authored, each milestone with its derived status, each criterion with the state of its evidence, the stage that follows from both, and the verdict that follows from the evidence alone.

use majordomus_cli::intent::{verdict, IntentStage, IntentVerdictState, IntentView};
let view = IntentView {
    id: "intent-lifecycle".into(),
    title: "Work is traceable to the intent it serves".into(),
    statement: "A reader can ask what work is for.".into(),
    invariants: vec!["no intent status is stored".into()],
    stage: IntentStage::Declared,
    milestones: vec![],
    satisfaction: vec![],
    met: 0,
    verdict: verdict(&[]),
    governance: vec!["adr:adr-0070".into()],
    non_goals: vec![],
    superseded_by: None,
    source: ".ai/repo/project/intents/intent-lifecycle.yaml".into(),
};
// a record naming no milestone that resolves has not been planned
assert_eq!(view.stage, IntentStage::Declared);
// and one declaring no criterion cannot be settled by evidence
assert_eq!(view.verdict.state, IntentVerdictState::Unknown);
propertytyperequireddescription
id string yes The identity, which is also the file name.
title string yes One line.
statement string yes What must become true.
invariants array of string yes What must stay true.
stage IntentStage → IntentStage yes Derived: the stage.
milestones array of IntentMilestone → IntentMilestone yes The milestones, each with its derived status.
satisfaction array of IntentCriterion → IntentCriterion yes The criteria, each with its evidence state.
met integer (uint) yes Derived: how many criteria are met.
verdict IntentVerdict → IntentVerdict yes Derived from the criteria alone, never from the plan: whether the evidence settles the intent, and which criteria hold it back.
governance array of string yes The governance a worker on this intent loads.
non_goals array of string yes What is deliberately not required.
superseded_by string | null no The replacement, when the record names one.
source string yes Repository-relative path of the record.

Used by intents.record IntentExplanation IntentList

ObjectProvenance

object
Where an object came from. Every field is computed from the repository, never authored.
propertytyperequireddescription
path string yes Repository-relative path, forward slashes, as the version-control index names it.
directory string yes The directory the path sits in, repository-relative; . for the root. This is the hierarchy position a client orders by; no merge semantics are implied.
source_class string yes The sources.yaml class that discovered the file (rule, readme, ...).
section string | null no The manifest section the path falls under (rules, prompts, ...), when it falls under one; a root README.md falls under none.
bytes integer (uint64) yes Size of the file in bytes.
member string | null no For an object that is one member of a collection file: the member's key path in the file (claims.3).

Used by EntityView ObjectView

ObjectSummary

object
One object, summarised for a listing.
propertytyperequireddescription
uri string yes majordomus://<kind>/<identity>.
id string yes The capability id, <kind>.<identity>.
kind string yes The kind the object was read as.
identity string yes The identity within the kind.
title string | null no The title, when the kind's title rule found one.
description string | null no The one-line description, when the kind holds one.
path string yes Repository-relative source path.

Used by KindEntry ObjectList

ProofState

"proven" | "inputs_unchanged" | "stale" | "failing" | "not_run" | "unrunnable" | "no_test"
What the repository can say about one claim's proof, right now.

Ordered from strongest to weakest, so a summary that sorts by this reads as a ranking.

use majordomus_cli::evidence::ProofState;

let mut states = vec![ProofState::NotRun, ProofState::Stale, ProofState::Proven];
states.sort();
assert_eq!(
    states,
    vec![ProofState::Proven, ProofState::Stale, ProofState::NotRun]
);

// the two strongest are deliberately not one state: `inputs unchanged` is the absence
// of a known invalidation, and collapsing it into `proven` is the unaccountable badge
assert_ne!(ProofState::Proven, ProofState::InputsUnchanged);
assert!(ProofState::Proven < ProofState::InputsUnchanged);
assert!(ProofState::InputsUnchanged.passing());

Example

use majordomus_cli::evidence::ProofState;
assert!(ProofState::Proven.passing());
assert!(!ProofState::NotRun.passing());
assert!(!ProofState::InputsUnchanged.meaning().is_empty());
  • provenA passing run, and nothing has changed since it — the diff against the execution's own commit is empty but for the ledger. Proof of the tree in front of you.
  • inputs_unchangedA passing run, and nothing this claim names has changed since. Not proof at HEAD: something the claim does not name may have broken it.
  • staleA passing run, but something this claim names has changed since. The proof is older than its subject.
  • failingThe latest run of this claim's test did not pass.
  • not_runThe claim names a test a runner owns, and no run of it has ever been recorded.
  • unrunnableThe claim names a path no runner in this repository drives. Nothing can record it, so nothing can prove it this way.
  • no_testThe claim names no test at all. Correct for a planned or rejected claim, and a defect for any other.

Used by evidence.report ClaimEvidence EvidenceClaimProof EvidenceFinding IntentCriterion RuleTestProof SkillTest

ReleaseChangeKind

"feat" | "fix" | "perf" | "refactor" | "docs" | "test" | "chore" | "ci" | "style" | "build" | "revert" | "merge" | "other"
What a conventional commit says it did.

The set is the one the repository's own commit convention uses; a commit whose subject does not parse is ChangeKind::Other and still appears, because a changelog that silently drops what it cannot classify is a changelog that lies by omission.

  • featA capability a person did not have before.
  • fixBehaviour that was wrong and is not any more.
  • perfPerformance, with the behaviour unchanged.
  • refactorStructure, with the behaviour unchanged.
  • docsDocumentation.
  • testTests.
  • choreThe build, the pipeline, the tooling.
  • ciContinuous integration.
  • styleFormatting, with the behaviour unchanged.
  • buildThe build system or its dependencies.
  • revertA change that undoes another.
  • mergeAn integration: one line of work brought into another.

    Not in the conventional-commit specification, and in this repository's history 38 times and counting, because integration here is a deliberate act with a subject somebody writes — merge: bring origin/master into feature/x — and not the sentence git merge composes. Declaring it is what lets the validator hold those subjects to the same width and the same references as every other commit, instead of either refusing a convention this project practises or exempting it from being checked at all. The changelog never renders it: commits::in_range reads the log with --no-merges, so a merge commit is not a release entry however its subject is spelled.

  • otherA commit whose subject does not parse as a conventional commit.

Used by CommitHeader ReleaseChange ReleaseChangeGroup

ServerStanding

"absent" | "starting" | "ready" | "outdated" | "stale"
Where a checkout's server stands, decided from its lease, whether the server the lease names answers for that checkout, and whether what answers is this executable's code.

use majordomus_cli::capability::builtin::server::ServerStanding;
let all = [ServerStanding::Absent, ServerStanding::Starting, ServerStanding::Ready,
           ServerStanding::Outdated, ServerStanding::Stale];
let words: Vec<&str> = all.iter().map(|s| s.as_str()).collect();
assert_eq!(words, ["absent", "starting", "ready", "outdated", "stale"]);
assert_eq!(serde_json::from_str::<ServerStanding>("\"stale\"").unwrap(), ServerStanding::Stale);
  • absentNo lease: nothing serves this checkout.
  • startingA lease without an address, young enough that its owner is still binding.
  • readyThe server the lease names answers for this checkout, from the code on disk, at this executable's version.
  • outdatedThe server answers, but from older code than this executable, or from a file that has been replaced since it started: everything it says is yesterday's.
  • staleThe lease names a server that does not answer, or is not a lease at all.

Used by BoardView ServerStatus ServerView

Severity

"info" | "warning" | "error"
How bad a diagnostic is. Error excludes the file it concerns from the index and puts the index into the degraded state; Warning and Info do neither.
  • infoWorth knowing; changes nothing.
  • warningSomething to look at; the object is still served.
  • errorThe file is excluded and the index is degraded.

Used by Diagnostic EditorDiagnostic ExecutionDiagnostic Finding ProductFinding TopologyDiagnostic

Stability

"implemented" | "behaviorally_verified" | "experimental" | "planned" | "unsupported"
Where a capability stands, in the repository's own vocabulary for claims. A capability that is Planned or Unsupported may be listed but is never executable through any projection; the registry refuses to build otherwise.
  • implementedImplemented, and no behavioural test names it yet.
  • behaviorally_verifiedImplemented and proved by a behavioural test.
  • experimentalImplemented, executable, and expected to change.
  • plannedSpecified and not implemented: listed, never executable.
  • unsupportedConsidered and refused: listed with the reason, never executable.

Used by Capability CapabilitySummary CommandNode

Summary

object
The registry counted: by source, kind, stability and projection.
propertytyperequireddescription
total integer (uint) yes Every capability.
builtin integer (uint) yes Composed in Rust.
declarative integer (uint) yes Read from the layer.
by_kind map of integer (uint) yes By kind (query, resource).
by_stability map of integer (uint) yes By stability.
mcp_tools integer (uint) yes With an MCP tool exposure.
mcp_resources integer (uint) yes With an MCP resource exposure.
http_routes integer (uint) yes With an HTTP exposure.
cli_commands integer (uint) yes With a CLI exposure.
modules integer (uint) yes Modules: composed, derived, and one per declarative kind.
benchmark_required integer (uint) yes Executables whose benchmark policy is required.
benchmark_waived integer (uint) yes Executables waived from benchmarking for a typed reason.
cached integer (uint) yes Executables the executor caches.

Used by CapabilityList RepositoryReport

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