Skip to content

plan.drift

Every contradiction between an issue's declared status and its delivery (plan.delivery), each with the objects that prove it: `merged_not_recorded` — READY, BLOCKED or ACTIVE, with a merge on the trunk naming it (pull request, merge commit, release) and no open pull request naming it; `active_without_work` — ACTIVE since before the window, with no branch, pull request, merge or live mesh claim naming it; `done_without_trace` — DONE with no merge naming it, advisory because record-only issues exist. Each finding carries its age, whether it is past the window `.ai/repo/ci/plan.yaml` declares (`drift.window`, seconds), the sources that were not read and could have dissolved it, and the command that settles it (`majordomus plan verify|done <id>`). With `gate`, judged as a gate: on a `head` every merged_not_recorded is pending; on the `trunk` one merged longer than the window ago is refused and a younger one pending; the other classes never refuse; a missing or unreadable window refuses (exit 12); refusals fail the gate (exit 10) only under `drift.enforcement: blocking`, and are reported with exit 0 under `advisory`. Observed live, or `at` a commit as plan.delivery is. Inferred on every call; it never writes a stamp.

Every contradiction between an issue's declared status and its delivery (plan.delivery), each with the objects that prove it: `merged_not_recorded` — READY, BLOCKED or ACTIVE, with a merge on the trunk naming it (pull request, merge commit, release) and no open pull request naming it; `active_without_work` — ACTIVE since before the window, with no branch, pull request, merge or live mesh claim naming it; `done_without_trace` — DONE with no merge naming it, advisory because record-only issues exist. Each finding carries its age, whether it is past the window `.ai/repo/ci/plan.yaml` declares (`drift.window`, seconds), the sources that were not read and could have dissolved it, and the command that settles it (`majordomus plan verify|done <id>`). With `gate`, judged as a gate: on a `head` every merged_not_recorded is pending; on the `trunk` one merged longer than the window ago is refused and a younger one pending; the other classes never refuse; a missing or unreadable window refuses (exit 12); refusals fail the gate (exit 10) only under `drift.enforcement: blocking`, and are reported with exit 0 under `advisory`. Observed live, or `at` a commit as plan.delivery is. Inferred on every call; it never writes a stamp.

  • query
  • experimental
  • module plan
  • #plan
  • #project
  • #delivery
  • #drift
  • #gate

Exposure

Where this one definition is reachable. Absence is explicit: a surface not listed does not carry it.

surfaceas
MCPtool majordomus_plan_drift
HTTPGET /api/v1/plan/drift operationId plan.drift
command linemajordomus plan drift

Schemas

The canonical input and output, from the Rust types; the MCP tool schema and the OpenAPI parameters and responses are derived from these.

input · PlanDriftInput

Which issues, as of when, and judged as which gate: the input of plan.drift.

use majordomus_cli::capability::builtin::plan::{DriftGate, PlanDriftInput};

let every: PlanDriftInput = serde_json::from_str("{}").unwrap();
assert!(every.issue.is_none() && every.at.is_none() && every.gate.is_none());
let gate: PlanDriftInput = serde_json::from_value(serde_json::json!({ "gate": "trunk" })).unwrap();
assert_eq!(gate.gate, Some(DriftGate::Trunk));
assert!(serde_json::from_str::<PlanDriftInput>(r#"{"gate":"main"}"#).is_err());
assert!(serde_json::from_str::<PlanDriftInput>(r#"{"window":60}"#).is_err());
propertytyperequireddescription
at string | null no Observe as of this commit, as plan.delivery does. Default: live.
gate one of 2 no Judge as a gate on a head or on the trunk. Default: report only, exit 0.
issue string | null no One issue id. Default: every issue of the plan.
JSON Schema
{
  "$defs": {
    "DriftGate": {
      "description": "Which tree a `plan.drift` gate judges.\n\n```\nuse majordomus_cli::capability::builtin::plan::DriftGate;\nassert_eq!(serde_json::to_value(DriftGate::Trunk).unwrap(), \"trunk\");\nassert_eq!(serde_json::from_str::<DriftGate>(r#\"\"head\"\"#).unwrap(), DriftGate::Head);\n```",
      "oneOf": [
        {
          "const": "head",
          "description": "A head: not the shared plan, so every merged_not_recorded is pending.",
          "type": "string"
        },
        {
          "const": "trunk",
          "description": "The trunk: merged work unrecorded past the window is refused.",
          "type": "string"
        }
      ]
    }
  },
  "additionalProperties": false,
  "description": "Which issues, as of when, and judged as which gate: the input of `plan.drift`.\n\n```\nuse majordomus_cli::capability::builtin::plan::{DriftGate, PlanDriftInput};\n\nlet every: PlanDriftInput = serde_json::from_str(\"{}\").unwrap();\nassert!(every.issue.is_none() && every.at.is_none() && every.gate.is_none());\nlet gate: PlanDriftInput = serde_json::from_value(serde_json::json!({ \"gate\": \"trunk\" })).unwrap();\nassert_eq!(gate.gate, Some(DriftGate::Trunk));\nassert!(serde_json::from_str::<PlanDriftInput>(r#\"{\"gate\":\"main\"}\"#).is_err());\nassert!(serde_json::from_str::<PlanDriftInput>(r#\"{\"window\":60}\"#).is_err());\n```",
  "properties": {
    "at": {
      "description": "Observe as of this commit, as `plan.delivery` does. Default: live.",
      "type": [
        "string",
        "null"
      ]
    },
    "gate": {
      "anyOf": [
        {
          "$ref": "#/$defs/DriftGate"
        },
        {
          "type": "null"
        }
      ],
      "description": "Judge as a gate on a `head` or on the `trunk`. Default: report only, exit 0."
    },
    "issue": {
      "description": "One issue id. Default: every issue of the plan.",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "title": "PlanDriftInput",
  "type": "object"
}

output · DriftReport

Every contradiction between the plan's stamps and its delivery, and, when a gate was asked, its verdict.
propertytyperequireddescription
classes array yes The class vocabulary, in order.
enforced boolean yes Whether a refusal fails the gate (drift.enforcement: blocking).
exit_code integer yes The exit code the gate ends with: 0, EXIT_REFUSED or EXIT_UNJUDGED.
findings array yes The findings, in issue order, then class order.
gate one of 2 no The tree a gate judged, when one was asked.
observed reference yes Where and when the delivery was observed.
pending integer yes How many it reported as pending.
refused integer yes How many findings the gate refused.
schema integer yes The version of this shape.
sources array yes Every source, read or not: the delivery's, and the mesh claims.
tallies object yes How many findings of each class, keyed by every class.
verdict string yes report (no gate), clean, pending, advisory (refusals not enforced), refused, or unjudged (no window).
window reference yes The declared window.
JSON Schema
{
  "$defs": {
    "DeliveryObservation": {
      "description": "Where and when the answer was observed.",
      "properties": {
        "commit": {
          "description": "The commit that history ends at.",
          "type": "string"
        },
        "mode": {
          "description": "`live`, or `at` a named commit.",
          "type": "string"
        },
        "observed_at": {
          "description": "When: the clock in live mode, the commit's committer time at a commit.",
          "type": "string"
        },
        "trunk": {
          "description": "The history read: the trunk ref in live mode, the commit asked for otherwise.",
          "type": "string"
        }
      },
      "required": [
        "mode",
        "trunk",
        "commit",
        "observed_at"
      ],
      "type": "object"
    },
    "DeliveryReading": {
      "description": "Whether one source of the answer was read.",
      "oneOf": [
        {
          "const": "read",
          "description": "It was read; what it holds is in the answer.",
          "type": "string"
        },
        {
          "const": "unread",
          "description": "It was not read, and `reason` says why. Nothing it could have proved is claimed or\ndenied.",
          "type": "string"
        }
      ]
    },
    "DeliverySource": {
      "description": "One source the answer is built from, and whether it was read.",
      "properties": {
        "detail": {
          "description": "What was read, or why nothing was: the ref, the file, the command that would record\nit.",
          "type": "string"
        },
        "name": {
          "description": "`branches`, `forge`, `history`, `releases` or `publication`.",
          "type": "string"
        },
        "observed_at": {
          "description": "When what was read was observed, RFC 3339, when it was read.",
          "type": [
            "string",
            "null"
          ]
        },
        "state": {
          "$ref": "#/$defs/DeliveryReading",
          "description": "Read or unread."
        }
      },
      "required": [
        "name",
        "state",
        "detail"
      ],
      "type": "object"
    },
    "DeliveryStage": {
      "description": "How far one issue's work has travelled, in order: each stage implies every one before it\nhas been passed through, so the furthest one proved is the issue's stage.",
      "oneOf": [
        {
          "const": "none",
          "description": "Nothing that was read names the issue.",
          "type": "string"
        },
        {
          "const": "branch",
          "description": "A branch names it.",
          "type": "string"
        },
        {
          "const": "pr_open",
          "description": "An open pull request names it.",
          "type": "string"
        },
        {
          "const": "merged",
          "description": "A merge in the trunk's history names it.",
          "type": "string"
        },
        {
          "const": "released",
          "description": "A `v*` release contains its newest merge.",
          "type": "string"
        },
        {
          "const": "deployed",
          "description": "The published `latest.json` names that release or a later one.",
          "type": "string"
        }
      ]
    },
    "DriftClass": {
      "description": "One kind of contradiction between the stamps and the work.",
      "oneOf": [
        {
          "const": "merged_not_recorded",
          "description": "READY, BLOCKED or ACTIVE, with merged work and no open pull request naming it.",
          "type": "string"
        },
        {
          "const": "active_without_work",
          "description": "ACTIVE since before the window, with no branch, pull request, merge or mesh claim.",
          "type": "string"
        },
        {
          "const": "done_without_trace",
          "description": "DONE with no merge naming it. Advisory: record-only issues exist.",
          "type": "string"
        }
      ]
    },
    "DriftFinding": {
      "description": "One contradiction between an issue's stamps and its delivery.",
      "properties": {
        "advisory": {
          "description": "True for a class that never refuses.",
          "type": "boolean"
        },
        "age_seconds": {
          "description": "Seconds from `since` to the observation.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "class": {
          "$ref": "#/$defs/DriftClass",
          "description": "The contradiction."
        },
        "issue": {
          "description": "The issue id.",
          "type": "string"
        },
        "milestone": {
          "description": "Its milestone.",
          "type": "string"
        },
        "past_window": {
          "description": "Whether that is longer than the window; absent when either is unknown.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "proof": {
          "$ref": "#/$defs/DriftProof",
          "description": "What proves it."
        },
        "remedy": {
          "description": "The command that settles it.",
          "type": "string"
        },
        "settled": {
          "description": "True when no unread source could have dissolved it.",
          "type": "boolean"
        },
        "since": {
          "description": "Since when the contradiction holds: the merge for `merged_not_recorded`, the start\nstamp for `active_without_work`.",
          "type": [
            "string",
            "null"
          ]
        },
        "stage": {
          "$ref": "#/$defs/DeliveryStage",
          "description": "Its inferred delivery stage."
        },
        "status": {
          "description": "Its declared status.",
          "type": "string"
        },
        "title": {
          "description": "Its title.",
          "type": "string"
        },
        "unread": {
          "description": "The sources that were not read and could have dissolved it.",
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        "verdict": {
          "anyOf": [
            {
              "$ref": "#/$defs/DriftVerdict"
            },
            {
              "type": "null"
            }
          ],
          "description": "What the gate made of it, when a gate was asked."
        }
      },
      "required": [
        "issue",
        "title",
        "milestone",
        "status",
        "stage",
        "class",
        "advisory",
        "proof",
        "settled",
        "unread",
        "remedy"
      ],
      "type": "object"
    },
    "DriftGate": {
      "description": "Which tree a `plan.drift` gate judges.\n\n```\nuse majordomus_cli::capability::builtin::plan::DriftGate;\nassert_eq!(serde_json::to_value(DriftGate::Trunk).unwrap(), \"trunk\");\nassert_eq!(serde_json::from_str::<DriftGate>(r#\"\"head\"\"#).unwrap(), DriftGate::Head);\n```",
      "oneOf": [
        {
          "const": "head",
          "description": "A head: not the shared plan, so every merged_not_recorded is pending.",
          "type": "string"
        },
        {
          "const": "trunk",
          "description": "The trunk: merged work unrecorded past the window is refused.",
          "type": "string"
        }
      ]
    },
    "DriftProof": {
      "description": "The objects that prove a finding.",
      "properties": {
        "merge_commit": {
          "description": "The newest commit in the trunk's history that names the issue.",
          "type": [
            "string",
            "null"
          ]
        },
        "merged_at": {
          "description": "When that commit was made.",
          "type": [
            "string",
            "null"
          ]
        },
        "pull_request": {
          "description": "The pull request the newest merge naming the issue merged.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "release": {
          "description": "The first release that contains it.",
          "type": [
            "string",
            "null"
          ]
        },
        "started_at": {
          "description": "When execution was recorded to have begun, for `active_without_work`.",
          "type": [
            "string",
            "null"
          ]
        },
        "via": {
          "description": "How it names the issue (`title_or_branch`, `slug`, `subject`, `forge_title`).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "DriftVerdict": {
      "description": "What a gate made of one finding.",
      "oneOf": [
        {
          "const": "refused",
          "description": "Refused: merged work the trunk has carried unrecorded past the window.",
          "type": "string"
        },
        {
          "const": "pending",
          "description": "Reported and not refused: on a head, or inside the window.",
          "type": "string"
        },
        {
          "const": "reported",
          "description": "Reported; this class never refuses.",
          "type": "string"
        },
        {
          "const": "advisory",
          "description": "Advisory; this class never refuses (`done_without_trace`).",
          "type": "string"
        }
      ]
    },
    "DriftWindow": {
      "description": "The declared window, or why there is none.",
      "properties": {
        "detail": {
          "description": "What was read, or why nothing was.",
          "type": "string"
        },
        "enforcement": {
          "description": "`advisory` (refusals are reported, the gate passes) or `blocking`.",
          "type": [
            "string",
            "null"
          ]
        },
        "model": {
          "description": "The file it is declared in.",
          "type": "string"
        },
        "seconds": {
          "description": "The seconds the trunk may carry merged work the plan has not recorded.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "state": {
          "$ref": "#/$defs/DeliveryReading",
          "description": "Read, or unread with `detail` saying why."
        }
      },
      "required": [
        "model",
        "state",
        "detail"
      ],
      "type": "object"
    }
  },
  "description": "Every contradiction between the plan's stamps and its delivery, and, when a gate was\nasked, its verdict.",
  "properties": {
    "classes": {
      "description": "The class vocabulary, in order.",
      "items": {
        "$ref": "#/$defs/DriftClass"
      },
      "type": "array"
    },
    "enforced": {
      "description": "Whether a refusal fails the gate (`drift.enforcement: blocking`).",
      "type": "boolean"
    },
    "exit_code": {
      "description": "The exit code the gate ends with: 0, `EXIT_REFUSED` or `EXIT_UNJUDGED`.",
      "format": "uint8",
      "maximum": 255,
      "minimum": 0,
      "type": "integer"
    },
    "findings": {
      "description": "The findings, in issue order, then class order.",
      "items": {
        "$ref": "#/$defs/DriftFinding"
      },
      "type": "array"
    },
    "gate": {
      "anyOf": [
        {
          "$ref": "#/$defs/DriftGate"
        },
        {
          "type": "null"
        }
      ],
      "description": "The tree a gate judged, when one was asked."
    },
    "observed": {
      "$ref": "#/$defs/DeliveryObservation",
      "description": "Where and when the delivery was observed."
    },
    "pending": {
      "description": "How many it reported as pending.",
      "format": "uint",
      "minimum": 0,
      "type": "integer"
    },
    "refused": {
      "description": "How many findings the gate refused.",
      "format": "uint",
      "minimum": 0,
      "type": "integer"
    },
    "schema": {
      "description": "The version of this shape.",
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "sources": {
      "description": "Every source, read or not: the delivery's, and the mesh claims.",
      "items": {
        "$ref": "#/$defs/DeliverySource"
      },
      "type": "array"
    },
    "tallies": {
      "additionalProperties": {
        "format": "uint",
        "minimum": 0,
        "type": "integer"
      },
      "description": "How many findings of each class, keyed by every class.",
      "type": "object"
    },
    "verdict": {
      "description": "`report` (no gate), `clean`, `pending`, `advisory` (refusals not enforced),\n`refused`, or `unjudged` (no window).",
      "type": "string"
    },
    "window": {
      "$ref": "#/$defs/DriftWindow",
      "description": "The declared window."
    }
  },
  "required": [
    "schema",
    "observed",
    "window",
    "sources",
    "classes",
    "tallies",
    "findings",
    "refused",
    "pending",
    "enforced",
    "verdict",
    "exit_code"
  ],
  "title": "DriftReport",
  "type": "object"
}

Policies

benchmark
required — a target on every transport the exposure declares; the cases are the input type's
cache
disabled — every call runs the handler

Benchmark targets

Derived from the registry for this repository: one requirement per transport, and the cases the input type provides. The whole matrix is on the benchmarks page.

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