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
gates.model
The CI model
What it is
Every validation gate this repository declares — the job that runs it, the command it runs, what it proves, whether every plan selects it and whether its runner can be had on demand at all — with the path classes that decide which change selects which gate, and, derived from those classes, the pathspecs each gate's evidence is taken over.
Descriptor
- Canonical id
gates.model- Module
gates- Kind
- query
- Stability
- behaviorallyverified
- Provenance
- declaredcached
- Source
apps/majordomus-cli/src/capability/builtin/gates.rs- Cache
process, 4 entries, 5s- Benchmark
required- Tags
- gatescicompletionintrospection
Run
queryA query. It reads and changes nothing.
Projections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_gates | - |
| MCP resource | majordomus://gates | - |
| HTTP | GET /api/v1/gates | Swagger |
| Command line | not exposed | - |
Schemas
Input · Empty
{
"type": "object",
"additionalProperties": false,
"description": "No input.",
"title": "Empty"
}Output · GateModelReport
{
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "The schema version the file states."
},
"source": {
"type": "string",
"description": "Where it was read from, repository-relative."
},
"count": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "How many gates it declares; measured, not written down."
},
"on_demand": {
"type": "array",
"items": {
"type": "string"
},
"description": "The gates whose runner cannot be had on demand, so no routine plan selects them.\nA gate that never runs protects nothing, and this is where that is said out loud."
},
"gates": {
"type": "array",
"items": {
"$ref": "#/$defs/GateModelEntry"
},
"description": "Every gate, in declaration order, with the pathspecs its evidence is taken over."
},
"classes": {
"type": "array",
"items": {
"$ref": "#/$defs/GateClass"
},
"description": "Every path class, in declaration order."
},
"unreadable": {
"type": [
"string",
"null"
],
"description": "Why there is no model to report, when there is none. A repository of the layer does\nnot have to declare a CI model, and a reader is entitled to the difference between\n\"this repository declares no gates\" and \"this call failed\"."
}
},
"required": [
"version",
"source",
"count",
"on_demand",
"gates",
"classes"
],
"description": "The CI model this repository declares: what may refuse a change, and what selects each.\n\nTwo things about this report are worth knowing before reading one. `count` is measured\nfrom `gates` rather than written down, so the two can never disagree the way a\nhand-maintained total does. And `unreadable` is how a reader tells \"this repository\ndeclares no CI model\" from \"the call failed\": a repository of the layer is not obliged\nto declare gates, so the empty model is an answer, and it says why it is empty.\n\n```\nuse majordomus_cli::capability::builtin::gates::GateModelReport;\nuse serde_json::json;\n\n// the shape every surface serves: `unreadable` is skipped when the model was read\nlet r: GateModelReport = serde_json::from_value(json!({\n \"version\": 1,\n \"source\": \".ai/repo/ci/gates.yaml\",\n \"count\": 0,\n \"on_demand\": [],\n \"gates\": [],\n \"classes\": [],\n})).unwrap();\nassert_eq!(r.count, r.gates.len(), \"the total is measured, never declared\");\nassert!(r.unreadable.is_none(), \"read, and it declares nothing\");\n\n// and the other empty answer, which a reader must not confuse with that one\nlet absent: GateModelReport = serde_json::from_value(json!({\n \"version\": 0,\n \"source\": \".ai/repo/ci/gates.yaml\",\n \"count\": 0,\n \"on_demand\": [],\n \"gates\": [],\n \"classes\": [],\n \"unreadable\": \"no such file\",\n})).unwrap();\nassert_eq!(absent.unreadable.as_deref(), Some(\"no such file\"));\n```",
"title": "GateModelReport",
"$defs": {
"GateModelEntry": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The gate's identity, unique in the model."
},
"job": {
"type": "string",
"description": "The CI job that runs it."
},
"runs": {
"type": "string",
"description": "The command it runs."
},
"summary": {
"type": "string",
"description": "One line saying what it proves.",
"default": ""
},
"always": {
"type": "boolean",
"description": "True when every plan selects it, whatever changed.",
"default": false
},
"on-demand": {
"type": "boolean",
"description": "True when only a plan that asks for it selects it: its runner cannot be had on\ndemand, so planning it on every push is planning a verdict that never arrives.",
"default": false
},
"at-finish": {
"type": "boolean",
"description": "True when the gate measures the deployment rather than the tree, so no change to\nany file can select it and the path classes can never reach it. `finish` asks it\ninstead: the question \"is what the public is being served still a projection of\nthe trunk\" has no answer a commit can give, and its moment is when a session\nclaims to be done.",
"default": false
},
"implies": {
"type": "array",
"items": {
"type": "string"
},
"description": "Gates that are selected whenever this one is.",
"default": []
},
"requires": {
"type": "array",
"items": {
"type": "string"
},
"description": "Gates this one cannot run without.",
"default": []
},
"inputs": {
"type": "array",
"items": {
"type": "string"
},
"description": "The pathspecs a run of this gate is evidence over, derived from the classes that\nselect it. A change to any of them makes a recorded run stale."
}
},
"required": [
"id",
"job",
"runs",
"inputs"
],
"description": "```\nuse majordomus_cli::capability::builtin::gates::GateModelEntry;\nuse majordomus_cli::gates::GateDecl;\n\nlet decl: GateDecl = serde_json::from_value(serde_json::json!({\n \"id\": \"site-check\",\n \"job\": \"site\",\n \"runs\": \"scripts/site-check\",\n \"summary\": \"the site is whole\"\n}))\n.unwrap();\nlet entry = GateModelEntry { declared: decl, inputs: vec![\"site/**\".into()] };\n// the declaration as the file carries it, plus the paths its evidence is taken over —\n// derived from the classes that select it, never written down a second time\nassert_eq!(entry.declared.id, \"site-check\");\nassert_eq!(entry.inputs, [\"site/**\"]);\n```\nOne gate of the model, with what the model implies about it."
},
"GateClass": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The class's identity."
},
"paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "The pathspecs it covers, in the subset the model's header defines.",
"default": []
},
"except": {
"type": "array",
"items": {
"type": "string"
},
"description": "Single paths its patterns cover and the class does not: a data file under an\nescalating directory, read by named gates only. Each one is a path, never a\npattern, so a file that appears under the directory tomorrow still falls in it.",
"default": []
},
"gates": {
"anyOf": [
{
"$ref": "#/$defs/GateClassSelects"
},
{
"type": "null"
}
],
"description": "`full`, or the gates it names.",
"default": null
}
},
"required": [
"id"
],
"description": "```\nuse majordomus_cli::gates::GateClass;\n\n// one path class as the CI model declares it: which paths select which gates\nlet class: GateClass = serde_json::from_value(serde_json::json!({\n \"id\": \"docs\",\n \"paths\": [\"docs/**\"],\n \"gates\": [\"reference-check\"]\n}))\n.unwrap();\nassert_eq!(class.id, \"docs\");\nassert_eq!(class.paths, [\"docs/**\"]);\n```\nOne path class: the paths it covers and the gates a change to them selects."
},
"GateClassSelects": {
"anyOf": [
{
"type": "string",
"description": "`gates: full` — any change in this class escalates the whole plan."
},
{
"type": "array",
"items": {
"type": "string"
},
"description": "`gates: [a, b]` — exactly these, and `gates: []` — nothing."
}
],
"description": "What a class selects: the word `full`, or the gates it names."
}
}
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
default
{}