plan.drift
Každý rozpor mezi deklarovaným stavem úkolu a jeho dodáním (plan.delivery), každý s objekty, které ho dokládají: `merged_not_recorded` — READY, BLOCKED nebo ACTIVE, s merge na trunku, který ho jmenuje (pull request, merge commit, release), a bez otevřeného pull requestu, který ho jmenuje; `active_without_work` — ACTIVE od doby před oknem, bez branche, pull requestu, merge nebo živého mesh claimu, který by ho jmenoval; `done_without_trace` — DONE bez merge, který by ho jmenoval, pouze poradní, protože existují úkoly jen se záznamem. Každé zjištění nese svůj věk, zda je za oknem, které deklaruje `.ai/repo/ci/plan.yaml` (`drift.window`, sekundy), zdroje, které nebyly přečteny a mohly by ho rozpustit, a příkaz, který ho vyřeší (`majordomus plan verify|done <id>`). S `gate` posuzováno jako kontrola: na `head` je každý merged_not_recorded pending; na `trunk` je ten sloučený před delší dobou než okno odmítnut a mladší pending; ostatní třídy nikdy neodmítají; chybějící nebo nečitelné okno odmítá (kód 12); odmítnutí shodí kontrolu (kód 10) jen pod `drift.enforcement: blocking` a pod `advisory` se hlásí s kódem 0. Pozorováno živě, nebo `at` commitu jako plan.delivery. Odvozuje se při každém volání; nikdy nezapisuje razítko.
Každý rozpor mezi deklarovaným stavem úkolu a jeho dodáním (plan.delivery), každý s objekty, které ho dokládají: `merged_not_recorded` — READY, BLOCKED nebo ACTIVE, s merge na trunku, který ho jmenuje (pull request, merge commit, release), a bez otevřeného pull requestu, který ho jmenuje; `active_without_work` — ACTIVE od doby před oknem, bez branche, pull requestu, merge nebo živého mesh claimu, který by ho jmenoval; `done_without_trace` — DONE bez merge, který by ho jmenoval, pouze poradní, protože existují úkoly jen se záznamem. Každé zjištění nese svůj věk, zda je za oknem, které deklaruje `.ai/repo/ci/plan.yaml` (`drift.window`, sekundy), zdroje, které nebyly přečteny a mohly by ho rozpustit, a příkaz, který ho vyřeší (`majordomus plan verify|done <id>`). S `gate` posuzováno jako kontrola: na `head` je každý merged_not_recorded pending; na `trunk` je ten sloučený před delší dobou než okno odmítnut a mladší pending; ostatní třídy nikdy neodmítají; chybějící nebo nečitelné okno odmítá (kód 12); odmítnutí shodí kontrolu (kód 10) jen pod `drift.enforcement: blocking` a pod `advisory` se hlásí s kódem 0. Pozorováno živě, nebo `at` commitu jako plan.delivery. Odvozuje se při každém volání; nikdy nezapisuje razítko.
- query
- experimentální
- modul plan
- #plan
- #project
- #delivery
- #drift
- #gate
Zpřístupnění
Kde je tato jedna definice dosažitelná. Absence je explicitní: povrch, který není uveden, ji nenese.
| povrch | jako |
|---|---|
| MCP | nástroj majordomus_plan_drift |
| HTTP | GET /api/v1/plan/drift operationId plan.drift |
| příkazová řádka | majordomus plan drift |
Schémata
Kanonický vstup a výstup z typů Rustu; schéma nástroje MCP a parametry a odpovědi OpenAPI se odvozují z nich.
vstup · PlanDriftInput
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());| vlastnost | typ | povinné | popis |
|---|---|---|---|
| v | string | null | ne | Pozorovat k tomuto commitu, jako to dělá plan.delivery. Výchozí: živě. |
| kontrola | jedno ze 2 | ne | Posoudit jako kontrolu na head nebo na trunk. Výchozí: jen hlásit, exit 0. |
| issue | string | null | ne | Jedno id úkolu. Výchozí: každý úkol plánu. |
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"
}výstup · DriftReport
| vlastnost | typ | povinné | popis |
|---|---|---|---|
| třídy | array | ano | Slovník tříd, v pořadí. |
| vynuceno | boolean | ano | Zda odmítnutí způsobí selhání kontroly (drift.enforcement: blocking). |
| exit_code | integer | ano | Návratový kód, kterým kontrola končí: 0, EXIT_REFUSED nebo EXIT_UNJUDGED. |
| zjištění | array | ano | Nálezy, v pořadí úkolů, pak v pořadí tříd. |
| kontrola | jedno ze 2 | ne | Strom, který kontrola posoudila, když o to byla požádána. |
| pozorováno | reference | ano | Kde a kdy bylo doručení pozorováno. |
| čeká | integer | ano | Kolik jich ohlásil jako čekající. |
| odmítnuto | integer | ano | Kolik nálezů kontrola odmítla. |
| schema | integer | ano | Verze tohoto tvaru. |
| zdroje | array | ano | Každý zdroj, přečtený i nepřečtený: zdroj dodávky a mesh claims. |
| součty | object | ano | Kolik nálezů každé třídy, klíčováno každou třídou. |
| verdikt | string | ano | report (bez kontroly), clean, pending, advisory (odmítnutí se nevynucují), refused nebo unjudged (bez okna). |
| okno | reference | ano | Deklarované okno. |
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"
}Politiky
- benchmark
- povinné — cíl na každém transportu, který expozice deklaruje; případy jsou ty ze vstupního typu
- cache
- vypnuto — každé volání spustí handler
Cíle benchmarku
Odvozeno z registru pro tento repozitář: jeden požadavek na transport a případy, které poskytuje typ vstupu. Celá matice je na stránce měření výkonu.
| transport | state | případy | targets |
|---|---|---|---|
| direct | pokryto | 2 | every-issue one-issue-at-head-as-trunk-gate |
| mcp | pokryto | 2 | every-issue one-issue-at-head-as-trunk-gate |
| http | pokryto | 2 | every-issue one-issue-at-head-as-trunk-gate |