integration.trains
The trains of stacked pull requests
What it is
Every train of open pull requests — a group joined by confirmed dependencies: a base branch that is another open pull request's head, or a `Stacked on #N` / `Depends on #N` line in the body — in landing order. Per link: number, title, author, head and base branch, head commit, the forge's page and checks, disposition, lane, reasons, next action, required-check state and every check on the head, relation to master with how far behind it is and whether it carries master, the prepared head under refs/prep/<n> with whether it is the head the forge serves (pushed) or not yet (pending), its position and depth, whether its turn has come, what it stands on and what it carries, its queue rank and whether a cycle catches it. Per train: roots, tops, the next link the order allows, the edges (STACKED_ON or DEPENDS_ON, satisfied when the link below landed), and the pull requests beneath it that are no longer open. Beside them the prepared heads no train holds, the tallies and the queue's diagnostics. Read from the recorded observation, the queue `majordomus prs status` prints and the local refs: it fetches, pushes and moves nothing. `observed: false` with the reason when this checkout has recorded no observation.
Descriptor
- Canonical id
integration.trains- Module
integration- Kind
- query
- Stability
- experimental
- Provenance
- declared
- Source
apps/majordomus-cli/src/capability/builtin/integration.rs- Cache
disabled- Benchmark
required- Tags
- integrationpull-requeststrainsgraph
Run
queryProjections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_pull_request_trains | - |
| MCP resource | not exposed | - |
| HTTP | GET /api/v1/pull-requests/trains | Swagger |
| Command line | majordomus prs trains | - |
Schemas
Input · Empty
{
"type": "object",
"additionalProperties": false,
"description": "No input.",
"title": "Empty"
}Output · TrainsView
{
"type": "object",
"properties": {
"observed": {
"type": "boolean",
"description": "Whether a forge observation is recorded in this checkout."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Why there are no trains to show, when nothing is observed."
},
"repository": {
"type": [
"string",
"null"
],
"description": "`owner/name`, when observed."
},
"base": {
"type": [
"string",
"null"
],
"description": "The integration base."
},
"master_sha": {
"type": [
"string",
"null"
],
"description": "The master commit every link's relation was decided against."
},
"observed_at": {
"type": [
"string",
"null"
],
"description": "When the forge was observed."
},
"trains": {
"type": "array",
"items": {
"$ref": "#/$defs/Train"
},
"description": "Every train, the one whose next link ranks highest in the queue first."
},
"unattached_prep": {
"type": "array",
"items": {
"$ref": "#/$defs/UnattachedPrep"
},
"description": "Prepared heads under `refs/prep/` whose pull request is no link of any train: open\nand stacked on nothing, or not open at all (landed, closed)."
},
"tallies": {
"$ref": "#/$defs/TrainTallies",
"description": "Counts over every train."
},
"diagnostics": {
"type": "array",
"items": {
"type": "string"
},
"description": "What makes this answer less than a full one: the queue's own diagnostics (a stale\nobservation first), and a dependency cycle when one is found."
}
},
"required": [
"observed",
"trains",
"unattached_prep",
"tallies",
"diagnostics"
],
"description": "Every train of the observed queue, and why there is none when there is none.",
"title": "TrainsView",
"$defs": {
"Train": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "`train-<n>`, after the first root in turn order: stable while that link is open."
},
"roots": {
"type": "array",
"items": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"description": "The links that land first: no open link of the train is below them."
},
"tops": {
"type": "array",
"items": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"description": "The links at the top: no link of the train is stacked on them."
},
"next": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0,
"description": "The first link, in turn order, whose every dependency has landed: the train's next\nmerge as far as order is concerned (its disposition says whether it can merge now)."
},
"links": {
"type": "array",
"items": {
"$ref": "#/$defs/TrainLink"
},
"description": "Every open link, in turn order — `TrainLink::position` 1 first."
},
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/TrainEdge"
},
"description": "Every confirmed dependency of a link, upper link first."
},
"beneath": {
"type": "array",
"items": {
"$ref": "#/$defs/BeneathLink"
},
"description": "The pull requests below the train that are no longer open — landed, closed without a\nmerge, or unread — which a link still declares."
},
"by_disposition": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "uint",
"minimum": 0
},
"description": "Links by disposition word."
}
},
"required": [
"id",
"roots",
"tops",
"links",
"edges",
"beneath",
"by_disposition"
],
"description": "One train: open pull requests joined by confirmed dependencies, in the order they land."
},
"TrainLink": {
"type": "object",
"properties": {
"number": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "The pull request number."
},
"title": {
"type": "string",
"description": "Its title."
},
"author": {
"type": "string",
"description": "Its author's login."
},
"head_ref": {
"type": "string",
"description": "Its head branch."
},
"base_ref": {
"type": "string",
"description": "The branch it targets: the integration base, or the head of the link below."
},
"head_sha": {
"type": "string",
"description": "The head commit it was assessed at, as the forge served it."
},
"url": {
"type": [
"string",
"null"
],
"description": "The pull request on the forge, when the repository is known."
},
"checks_url": {
"type": [
"string",
"null"
],
"description": "Its checks on the forge, when the repository is known."
},
"disposition": {
"$ref": "#/$defs/PullRequestDisposition",
"description": "The executor's classification."
},
"lane": {
"$ref": "#/$defs/IntegrationLane",
"description": "The lane that disposition belongs to."
},
"reasons": {
"type": "array",
"items": {
"type": "string"
},
"description": "The machine-readable reasons, as every surface spells them (`stacked_on:#<number>`)."
},
"next_action": {
"type": [
"string",
"null"
],
"description": "What happens next, in words, when anything does."
},
"required_checks": {
"$ref": "#/$defs/RequiredCheckState",
"description": "The required checks on its head."
},
"checks": {
"type": "array",
"items": {
"$ref": "#/$defs/LinkCheck"
},
"description": "Every check the forge reported on its head, required or not."
},
"relation": {
"$ref": "#/$defs/RelationToMaster",
"description": "What its head is to master, decided by git."
},
"relation_kind": {
"type": "string",
"description": "That relation's word (`behind`, `up_to_date`, `carries_master`, ...)."
},
"behind": {
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0,
"description": "How many master commits its head lacks, when the relation counts them."
},
"carries_master": {
"type": "boolean",
"description": "Whether its head carries master by a merge of its own (ADR 0129) — read from the\nrelation's word, so an executable that does not know that relation says `false`."
},
"prep": {
"anyOf": [
{
"$ref": "#/$defs/PreparedHead"
},
{
"type": "null"
}
],
"description": "The head the executor prepared for it, when `refs/prep/<n>` exists."
},
"position": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "Its turn in the train, 1-based: every link below it comes earlier."
},
"depth": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "How many links are below it on its longest path to master; a root is 0."
},
"at_turn": {
"type": "boolean",
"description": "Every dependency it declares has landed: it is the link the order allows next."
},
"on": {
"type": "array",
"items": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"description": "The pull requests it stands on, open links and those beneath alike."
},
"carries": {
"type": "array",
"items": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"description": "The links stacked on it."
},
"rank": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "Its rank in the queue, 1-based."
},
"in_cycle": {
"type": "boolean",
"description": "Caught in a cycle of confirmed dependencies: no link of the cycle can land first."
}
},
"required": [
"number",
"title",
"author",
"head_ref",
"base_ref",
"head_sha",
"disposition",
"lane",
"reasons",
"required_checks",
"checks",
"relation",
"relation_kind",
"carries_master",
"position",
"depth",
"at_turn",
"on",
"carries",
"rank",
"in_cycle"
],
"description": "One open pull request as a link of a train."
},
"PullRequestDisposition": {
"oneOf": [
{
"type": "string",
"const": "ready",
"description": "Contains the current master, every required check passed on its head, nothing\nblocks it: the executor may merge it."
},
{
"type": "string",
"const": "needs_refresh",
"description": "Merges cleanly but does not contain master: the executor may bring master in (a\nmerge commit on the branch, never a rewrite), after which CI must run again."
},
{
"type": "string",
"const": "waiting_for_checks",
"description": "Contains master; a required check has not finished on this head."
},
{
"type": "string",
"const": "waiting_for_review",
"description": "A required review is missing or changes were requested."
},
{
"type": "string",
"const": "waiting_for_dependency",
"description": "A declared dependency has not landed."
},
{
"type": "string",
"const": "draft",
"description": "A draft."
},
{
"type": "string",
"const": "needs_repair",
"description": "A required check failed on this head."
},
{
"type": "string",
"const": "conflicting",
"description": "Conflicts on authored paths: a person resolves them."
},
{
"type": "string",
"const": "blocked",
"description": "A label whose policy is to hold holds it, or the repository allows no merge commit,\nwhich is the only way the executor merges."
},
{
"type": "string",
"const": "unsafe",
"description": "The forge has auto-merge armed on it: the forge would merge it on its own, outside the\nexecutor and against whatever master is then. Held until a person disarms it."
},
{
"type": "string",
"const": "redundant",
"description": "Its head already landed, its merge changes nothing, or every one of its commits is on\nmaster as an equal patch: strong evidence from git, eligible for closure under policy.\n(Before 0.13 this was the word `superseded`.)"
},
{
"type": "string",
"const": "superseded",
"description": "A successor was declared for it by someone the repository lets declare one — an owner,\nmember or collaborator, in a pull request of this repository — and that successor\nlanded: git finds its head or its merge commit in master. The successor is\n`PullRequestAssessment::superseded_by`. Eligible for closure under policy."
},
{
"type": "string",
"const": "possibly_redundant",
"description": "Weak evidence: only derived artifacts would change, or its declared successor was merged\nsomewhere master does not contain. Surfaced for a person; never closed automatically."
},
{
"type": "string",
"const": "other_base",
"description": "Targets a branch other than the integration base."
},
{
"type": "string",
"const": "unknown",
"description": "Something needed to decide could not be observed."
},
{
"type": "string",
"const": "obsolete",
"description": "A person marked it obsolete with a label the label policy declares (owner decision\nD3): the only evidence that reaches this word. Listed for a person by cleanup and\nnever closed automatically; age, shared paths or a similar title never make one."
}
],
"description": "The one classification of an open pull request. Every value carries a machine-readable\nreason in `PullRequestAssessment::reasons`; there is no `mergeable: bool`."
},
"IntegrationLane": {
"oneOf": [
{
"type": "string",
"const": "ready",
"description": "The executor may merge it now."
},
{
"type": "string",
"const": "waiting",
"description": "Something will happen without a person: a refresh, a check, a dependency."
},
{
"type": "string",
"const": "repair",
"description": "A person must change the branch."
},
{
"type": "string",
"const": "cleanup",
"description": "Its work is on master already, or a successor that landed replaced it."
},
{
"type": "string",
"const": "held",
"description": "Not asking to be merged, held by a label or a setting, unsafe for the executor, or\nundecidable."
}
],
"description": "The queues the Cockpit and the status table group pull requests into."
},
"RequiredCheckState": {
"oneOf": [
{
"type": "string",
"const": "passed",
"description": "Every required check passed on this head."
},
{
"type": "string",
"const": "skipped",
"description": "Every required check passed, at least one of them by a skip the policy permits for\nthat context. A skip the policy does not permit is not a pass: it is `missing`."
},
{
"type": "string",
"const": "pending",
"description": "A required check is queued or running."
},
{
"type": "string",
"const": "failed",
"description": "A required check failed."
},
{
"type": "string",
"const": "missing",
"description": "A required check has not reported on this head at all."
},
{
"type": "string",
"const": "unknown",
"description": "The repository's required checks could not be read."
}
],
"description": "The state of the repository's *required* checks on one head, which is the only check\nstate a merge decision reads. A visible green check that is not required proves nothing,\nand a required check that is absent is not a pass."
},
"LinkCheck": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The check's name."
},
"state": {
"$ref": "#/$defs/CheckRunState",
"description": "Where it stands."
}
},
"required": [
"name",
"state"
],
"description": "One check run on a link's head."
},
"CheckRunState": {
"oneOf": [
{
"type": "string",
"const": "passed",
"description": "Completed and passed."
},
{
"type": "string",
"const": "failed",
"description": "Completed and failed (failure, timed out, cancelled, action required)."
},
{
"type": "string",
"const": "pending",
"description": "Queued or running; also what a report that says nothing is taken to be, so a\ndefault is never a pass."
},
{
"type": "string",
"const": "skipped",
"description": "Completed as skipped or neutral."
}
],
"description": "Where one check run stands."
},
"RelationToMaster": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "contained"
}
},
"required": [
"kind"
],
"description": "The head is an ancestor of master: every commit already landed."
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "superseded"
}
},
"required": [
"kind"
],
"description": "Merging the head into master changes nothing: its patch is already fully there."
},
{
"type": "object",
"properties": {
"commits": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "How many commits, every one of them already on master."
},
"kind": {
"type": "string",
"const": "patch_ids_upstream"
}
},
"required": [
"kind",
"commits"
],
"description": "The merge would conflict or change only derived output, but every commit the head has\nand master lacks is on master already as an equal patch (`git cherry` marks each `-`):\nthe change landed, and master moved on past it. Never a partial match, and never a\nhead that carries a merge commit of its own, whose resolution has no patch to compare."
},
{
"type": "object",
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "The derived paths that would change."
},
"kind": {
"type": "string",
"const": "derived_only"
}
},
"required": [
"kind",
"paths"
],
"description": "Merging changes only derived artifacts: the authored change is already on master,\nand what differs is output the generators rewrite."
},
{
"type": "object",
"properties": {
"authored": {
"type": "array",
"items": {
"type": "string"
},
"description": "Authored (non-derived) paths the merge changes."
},
"owes_version": {
"type": [
"string",
"null"
],
"description": "The version a landing of this head allocates, when master allocates at landing\n(ADR 0134) and the head does not carry it: the head owes a refresh even though it\ncontains master. Absent when the policy does not allocate, or the head carries it."
},
"kind": {
"type": "string",
"const": "up_to_date"
}
},
"required": [
"kind",
"authored"
],
"description": "The head already contains master and merges cleanly."
},
{
"type": "object",
"properties": {
"behind": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "Commits on master the head does not have."
},
"authored": {
"type": "array",
"items": {
"type": "string"
},
"description": "Authored (non-derived) paths the merge changes."
},
"kind": {
"type": "string",
"const": "carries_master"
}
},
"required": [
"kind",
"behind",
"authored"
],
"description": "The head does not contain master, yet master holds nothing the head lacks: merging it\nwrites the head's own tree, and every commit master has beyond it is a merge commit.\nThat is a head stacked on a pull request that has since landed. A verdict on the head\nis then a verdict on what master becomes, tree and listed history alike, exactly as\nwhen the head contains master; and no path is merged at all, so the forge needs no\ndriver for it."
},
{
"type": "object",
"properties": {
"behind": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "Commits on master the head does not have."
},
"authored": {
"type": "array",
"items": {
"type": "string"
},
"description": "Authored paths the merge changes."
},
"kind": {
"type": "string",
"const": "behind"
}
},
"required": [
"kind",
"behind",
"authored"
],
"description": "The head does not contain master, but the merge is clean with the derived driver."
},
{
"type": "object",
"properties": {
"paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "The conflicting authored paths."
},
"kind": {
"type": "string",
"const": "conflicting"
}
},
"required": [
"kind",
"paths"
],
"description": "Merging conflicts on authored paths: a person must resolve it."
},
{
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Why."
},
"kind": {
"type": "string",
"const": "unknown"
}
},
"required": [
"kind",
"reason"
],
"description": "Git could not answer (the head is not fetched, or git failed); the reason."
}
],
"description": "What the pull request's head is to the current master, decided locally by git with\nthis repository's own merge drivers — never by the forge's `mergeable`, which cannot\nrun the derived-file driver and calls nearly every pull request here conflicting."
},
"PreparedHead": {
"type": "object",
"properties": {
"reference": {
"type": "string",
"description": "The ref."
},
"sha": {
"type": "string",
"description": "The commit it names."
},
"state": {
"$ref": "#/$defs/PrepState",
"description": "Whether that commit is the head the forge serves."
}
},
"required": [
"reference",
"sha",
"state"
],
"description": "The head the executor prepared for a link under `refs/prep/<n>`."
},
"PrepState": {
"oneOf": [
{
"type": "string",
"const": "pushed",
"description": "The prepared head is the pull request's head: it was pushed."
},
{
"type": "string",
"const": "pending",
"description": "The prepared head is another commit: it waits for its turn to be pushed."
}
],
"description": "Whether a prepared head has reached the forge."
},
"TrainEdge": {
"type": "object",
"properties": {
"from": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "The upper link: the one that depends."
},
"to": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "The pull request it stands on."
},
"kind": {
"$ref": "#/$defs/TrainEdgeKind",
"description": "How the dependency is declared."
},
"satisfied": {
"type": "boolean",
"description": "Whether it is satisfied: the pull request below landed."
}
},
"required": [
"from",
"to",
"kind",
"satisfied"
],
"description": "A confirmed dependency between two pull requests of a train."
},
"TrainEdgeKind": {
"oneOf": [
{
"type": "string",
"const": "stacked_on",
"description": "Its base branch is the other's head, or its body says `Stacked on #N`."
},
{
"type": "string",
"const": "depends_on",
"description": "Its body says `Depends on #N`, `Requires #N` or `Land after #N`."
}
],
"description": "How a dependency between two links is declared."
},
"BeneathLink": {
"type": "object",
"properties": {
"number": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "Its number."
},
"state": {
"$ref": "#/$defs/DependencyState",
"description": "What the forge says became of it."
},
"url": {
"type": [
"string",
"null"
],
"description": "The pull request on the forge, when the repository is known."
}
},
"required": [
"number",
"state"
],
"description": "A pull request below a train that is not open any more."
},
"DependencyState": {
"oneOf": [
{
"type": "string",
"const": "open",
"description": "Still open."
},
{
"type": "string",
"const": "merged",
"description": "Merged: the only state that satisfies it."
},
{
"type": "string",
"const": "closed_unmerged",
"description": "Closed without a merge: its work never landed, so a person decides."
},
{
"type": "string",
"const": "unread",
"description": "Not open, and the forge could not say what became of it — refused, or not a pull\nrequest at all: unknown, never satisfied."
}
],
"description": "What became of a pull request another one depends on."
},
"UnattachedPrep": {
"type": "object",
"properties": {
"number": {
"type": "integer",
"format": "uint64",
"minimum": 0,
"description": "The pull request the ref is named for."
},
"sha": {
"type": "string",
"description": "The commit it names."
},
"open": {
"type": "boolean",
"description": "Whether that pull request is open."
}
},
"required": [
"number",
"sha",
"open"
],
"description": "A prepared head whose pull request is no link of any train."
},
"TrainTallies": {
"type": "object",
"properties": {
"trains": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "Trains."
},
"links": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "Open links across every train."
},
"pending_prep": {
"type": "integer",
"format": "uint",
"minimum": 0,
"description": "Links whose prepared head is not the head the forge serves."
},
"by_disposition": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "uint",
"minimum": 0
},
"description": "Links by disposition word."
}
},
"required": [
"trains",
"links",
"pending_prep",
"by_disposition"
],
"description": "Counts over every train."
}
}
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
default
{}