devtask.milestone
One milestone as an executable dependency graph
What it is
Every issue of the milestone as a node with its readiness, its wave and what waits on it; every dependency edge with at least one end inside, the crossing ones marked; the issues partitioned into ready, blocked, waiting, active, review, completion-blocked, complete and cancelled; the critical blockers ordered by how much unfinished work each holds back; the startable work partitioned into subsets that may genuinely run at the same time — same wave, each parallel-safe, no two sharing a scope path — with each serialisation naming the path that caused it; every dependency cycle as its strongly connected component; and every finding about the milestone or its issues. A pure function of the canonical records: no git, no clock, no network, every list in canonical order, so two runs on two machines produce the same bytes. A work surface reading this derives nothing itself.
Descriptor
- Canonical id
devtask.milestone- Module
devtask- Kind
- query
- Stability
- behaviorallyverified
- Provenance
- declared
- Source
apps/majordomus-cli/src/capability/builtin/devtask.rs- Cache
disabled- Benchmark
required- Tags
- devtaskplanmilestonegraphreadinessprovenance
Run
queryProjections
| Interface | As | Where |
|---|---|---|
| MCP tool | majordomus_devtask_milestone | - |
| MCP resource | not exposed | - |
| HTTP | GET /api/v1/devtask/milestone | Swagger |
| Command line | majordomus devtask milestone | - |
Schemas
Input · DevMilestoneInput
{
"type": "object",
"additionalProperties": false,
"properties": {
"milestone": {
"type": "string",
"description": "The milestone id, as the canonical model spells it. An id the model does not declare\nis answered with an empty graph and every field `unknown`, not refused."
}
},
"required": [
"milestone"
],
"description": "Which milestone to read as a dependency graph.",
"title": "DevMilestoneInput"
}Output · MilestoneGraph
{
"type": "object",
"properties": {
"milestone": {
"type": "string",
"description": "The milestone id, as asked for."
},
"declared": {
"type": "boolean",
"description": "Whether the canonical model declares it. False is answered, not refused."
},
"record": {
"type": [
"string",
"null"
],
"description": "Repository-relative path of the canonical record, when there is one."
},
"title": {
"$ref": "#/$defs/AttestedText",
"description": "One line naming the outcome, as authored."
},
"outcome": {
"$ref": "#/$defs/AttestedText",
"description": "What is true once it is real, as authored."
},
"status": {
"$ref": "#/$defs/AttestedText",
"description": "The derived milestone status."
},
"rank": {
"$ref": "#/$defs/AttestedCount",
"description": "Its layer in the milestone graph: what orders the roadmap."
},
"depends_on": {
"$ref": "#/$defs/AttestedList",
"description": "The milestones it requires, as authored."
},
"blocked_by": {
"$ref": "#/$defs/AttestedList",
"description": "Those of them that are not `DONE`. Non-empty means every issue here is held back\nwhatever its own dependencies say."
},
"dependents": {
"$ref": "#/$defs/AttestedList",
"description": "The milestones that require it."
},
"nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/MilestoneNode"
},
"description": "Every issue of the milestone, in canonical order."
},
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/MilestoneEdge"
},
"description": "Every dependency edge with at least one end in this milestone, sorted."
},
"ready": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues a worker may pick up now."
},
"blocked": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues waiting on a peer."
},
"waiting": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues waiting only on the milestone gate."
},
"active": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues somebody has started."
},
"review": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues awaiting confirmation."
},
"completion_blocked": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues declared complete whose evidence does not back that up."
},
"complete": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues finished on their own terms."
},
"cancelled": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues withdrawn."
},
"critical_blockers": {
"type": "array",
"items": {
"$ref": "#/$defs/CriticalBlocker"
},
"description": "What to unblock first, most downstream work first."
},
"parallelizable": {
"type": "array",
"items": {
"$ref": "#/$defs/ParallelSet"
},
"description": "The startable work partitioned into subsets that may run at the same time."
},
"cycles": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
},
"description": "Every dependency cycle with an issue of this milestone in it, each as its strongly\nconnected component in canonical order."
},
"diagnostics": {
"type": "array",
"items": {
"$ref": "#/$defs/DevTaskDiagnostic"
},
"description": "Everything wrong with the records of this milestone or of its issues."
},
"counts": {
"$ref": "#/$defs/MilestoneCounts",
"description": "The issues counted by readiness."
}
},
"required": [
"milestone",
"declared",
"title",
"outcome",
"status",
"rank",
"depends_on",
"blocked_by",
"dependents",
"nodes",
"edges",
"ready",
"blocked",
"waiting",
"active",
"review",
"completion_blocked",
"complete",
"cancelled",
"critical_blockers",
"parallelizable",
"cycles",
"diagnostics",
"counts"
],
"description": "One milestone as an executable dependency graph.",
"title": "MilestoneGraph",
"$defs": {
"AttestedText": {
"type": "object",
"properties": {
"value": {
"type": [
"string",
"null"
],
"description": "The value, absent exactly when the provenance is `unknown`."
},
"provenance": {
"$ref": "#/$defs/FieldProvenance",
"description": "Where it came from."
},
"source": {
"type": "string",
"description": "The one place a reader goes to check it: a repository-relative path with the key it\nwas read from, a capability id, or a command. Never a machine path."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Why the provenance is what it is, when that is worth a sentence: always for\n`unknown` and for `inferred`, and omitted otherwise."
}
},
"required": [
"provenance",
"source"
],
"description": "One scalar with its provenance: the unit every field of a development task is made of.\n\nThe invariant the constructors enforce, and `AttestedText::is_consistent` states, is\nthat a value is present exactly when the provenance is not `FieldProvenance::Unknown`.\nWithout it the shape can express \"unknown, and here is the value\", which is the defect\nthis whole module exists to prevent."
},
"FieldProvenance": {
"oneOf": [
{
"type": "string",
"const": "explicit",
"description": "A person authored it, in the canonical record, under this key. The strongest thing\nthat can be said about a value, and the only one that survives a rewrite of every\nderivation in this repository."
},
{
"type": "string",
"const": "derived",
"description": "A machine worked it out from records or from git, by a rule that cannot be wrong\nabout a repository it can read: an issue's dependents are the issues that name it.\nRe-derived on every call and stored nowhere."
},
{
"type": "string",
"const": "inferred",
"description": "A machine worked it out by a rule that *can* be wrong, because the relation it reads\nwas never declared: a session belongs to an issue because its branch name contains\nthe issue's id. Useful, and never to be mistaken for a declaration."
},
{
"type": "string",
"const": "unknown",
"description": "Not available. Either the record does not carry the key, or the fact lives outside\nwhat this process may read — a live GitHub state, for one — and the field says which\nin its `reason`. An unknown field never carries a value."
}
],
"description": "Where a value came from, and therefore how much weight a reader may put on it.\n\nThe four words are not degrees of confidence, they are four different relations to the\nrepository, and the distinction that matters most is the first from the last two: a\nperson wrote it, or a machine worked it out.\n\n```\nuse majordomus_cli::devtask::{AttestedText, FieldProvenance};\n\n// a value the record itself carried\nlet title = AttestedText::explicit(\"Ship it\", \".ai/repo/project/issues/I0001.yaml#title\");\nassert_eq!(title.provenance, FieldProvenance::Explicit);\n\n// a key the record does not have is unknown, and unknown carries no value\nlet missing = AttestedText::unknown(\n \".ai/repo/project/issues/I0001.yaml#objective\",\n \"the record declares no `objective`\",\n);\nassert_eq!(missing.provenance, FieldProvenance::Unknown);\nassert!(missing.value.is_none());\n```"
},
"AttestedCount": {
"type": "object",
"properties": {
"value": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0,
"description": "The number, absent exactly when the provenance is `unknown`."
},
"provenance": {
"$ref": "#/$defs/FieldProvenance",
"description": "Where it came from."
},
"source": {
"type": "string",
"description": "The one place a reader goes to check it."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Why the provenance is what it is, when that is worth a sentence."
}
},
"required": [
"provenance",
"source"
],
"description": "A count with its provenance. Separate from `AttestedText` because a wave, a commit\ntally and an evidence count are numbers a caller does arithmetic on, and a number\nrendered as a string is a number every client has to parse back."
},
"AttestedList": {
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "The items, in the order the record or the derivation produced them. Empty for an\nauthored empty list and for an unknown field alike; `provenance` tells them apart."
},
"provenance": {
"$ref": "#/$defs/FieldProvenance",
"description": "Where they came from."
},
"source": {
"type": "string",
"description": "The one place a reader goes to check them."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Why the provenance is what it is, when that is worth a sentence."
}
},
"required": [
"values",
"provenance",
"source"
],
"description": "A list with its provenance. The distinction a bare `Vec` cannot make is the one that\nmatters here: `depends_on: []` in the record is an authored empty list, and a record with\nno `depends_on` key is unknown. Both are empty; only one is a statement."
},
"MilestoneNode": {
"type": "object",
"properties": {
"issue": {
"type": "string",
"description": "The issue id."
},
"title": {
"type": "string",
"description": "One line naming the outcome."
},
"readiness": {
"$ref": "#/$defs/TaskReadiness",
"description": "Where it stands as work."
},
"canonical_status": {
"type": "string",
"description": "The canonical status it refines."
},
"wave": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0,
"description": "The execution wave, absent when a cycle prevents it from having one."
},
"priority": {
"type": "string",
"description": "`p0` … `p3`."
},
"parallel_safe": {
"type": "boolean",
"description": "Whether it may run beside another issue of its wave."
},
"blocked_by": {
"type": "array",
"items": {
"type": "string"
},
"description": "What is not `DONE` yet, plus the milestone gate when it applies."
},
"dependents": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues that wait on it directly."
},
"transitive_dependents": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "How many issues wait on it directly or through another."
},
"scope": {
"type": "array",
"items": {
"type": "string"
},
"description": "The paths it touches."
}
},
"required": [
"issue",
"title",
"readiness",
"canonical_status",
"priority",
"parallel_safe",
"blocked_by",
"dependents",
"transitive_dependents",
"scope"
],
"description": "One issue of a milestone, as a node of its graph."
},
"TaskReadiness": {
"oneOf": [
{
"type": "string",
"const": "ready",
"description": "Nothing holds it back and nobody has started it. Canonical `READY`."
},
{
"type": "string",
"const": "blocked",
"description": "At least one issue it depends on is not `DONE`. Canonical `BLOCKED`."
},
{
"type": "string",
"const": "waiting",
"description": "Every issue dependency is satisfied; the milestone gate holds it. Canonical\n`BLOCKED`, and a distinct situation because it is resolved in another milestone."
},
{
"type": "string",
"const": "in_progress",
"description": "Execution began: the record carries `started_at`. Canonical `ACTIVE`."
},
{
"type": "string",
"const": "review",
"description": "Implementation is declared finished and completion has not been recorded: the record\ncarries `verified_at`. Canonical `VERIFY`."
},
{
"type": "string",
"const": "completion_blocked",
"description": "Completion *was* recorded and the evidence the record itself requires is not all\nthere, so the plan refuses to call it `DONE`. Canonical `VERIFY`, and the case the\n`evidence_missing` finding is about."
},
{
"type": "string",
"const": "complete",
"description": "Finished on its own terms: not cancelled, completion recorded, every required\nevidence token present. Canonical `DONE`."
},
{
"type": "string",
"const": "cancelled",
"description": "Withdrawn. Canonical `CANCELLED`."
},
{
"type": "string",
"const": "undeclared",
"description": "The canonical model does not declare this id. Not a status: the absence of a record."
}
],
"description": "Where a development task stands, as work.\n\n```\nuse majordomus_cli::devtask::TaskReadiness;\n\n// every state names the canonical status it refines, and refines only one\nassert_eq!(TaskReadiness::Ready.canonical(), \"READY\");\nassert_eq!(TaskReadiness::Waiting.canonical(), \"BLOCKED\");\nassert_eq!(TaskReadiness::Blocked.canonical(), \"BLOCKED\");\nassert_eq!(TaskReadiness::Review.canonical(), \"VERIFY\");\nassert_eq!(TaskReadiness::CompletionBlocked.canonical(), \"VERIFY\");\n\n// and one state is the absence of a record rather than a status of one\nassert!(TaskReadiness::Undeclared.canonical().is_empty());\n```"
},
"MilestoneEdge": {
"type": "object",
"properties": {
"from": {
"type": "string",
"description": "The issue that must be real first."
},
"to": {
"type": "string",
"description": "The issue that waits for it."
},
"external": {
"type": "boolean",
"description": "True when one end is outside this milestone. Such an edge is why a milestone can be\nentirely blocked with nothing wrong inside it, so it is marked rather than dropped."
}
},
"required": [
"from",
"to",
"external"
],
"description": "One dependency edge of the milestone's graph, `from` before `to`."
},
"CriticalBlocker": {
"type": "object",
"properties": {
"issue": {
"type": "string",
"description": "The issue."
},
"readiness": {
"$ref": "#/$defs/TaskReadiness",
"description": "Where it stands, which is what says whether unblocking it is work or a decision."
},
"blocks": {
"type": "array",
"items": {
"type": "string"
},
"description": "The unfinished issues that wait on it, directly or through another, in canonical\norder."
},
"weight": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "How many that is. The number the list is ordered by."
}
},
"required": [
"issue",
"readiness",
"blocks",
"weight"
],
"description": "One unfinished issue that holds back downstream work, and how much."
},
"ParallelSet": {
"type": "object",
"properties": {
"wave": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "The wave every issue in it belongs to."
},
"issues": {
"type": "array",
"items": {
"type": "string"
},
"description": "The issues, in canonical order."
},
"serialised": {
"type": "array",
"items": {
"$ref": "#/$defs/ScopeConflict"
},
"description": "Why the set is not larger: the candidates of the same wave that had to be serialised\nagainst something already in it."
}
},
"required": [
"wave",
"issues",
"serialised"
],
"description": "One subset of the startable work that may genuinely run at the same time."
},
"ScopeConflict": {
"type": "object",
"properties": {
"held": {
"type": "string",
"description": "The issue already in the set."
},
"excluded": {
"type": "string",
"description": "The issue that could not join it."
},
"path": {
"type": "string",
"description": "The scope path they share, as one of them declares it."
}
},
"required": [
"held",
"excluded",
"path"
],
"description": "Two issues of one wave that cannot run together, and the path that stops them."
},
"DevTaskDiagnostic": {
"type": "object",
"properties": {
"level": {
"type": "string",
"description": "`FAIL` or `WARN`, as the plan's own finding levels."
},
"code": {
"type": "string",
"description": "The stable code a reader greps for."
},
"message": {
"type": "string",
"description": "What is wrong, in one line."
},
"reproduce": {
"type": "string",
"description": "The one command that shows it again."
}
},
"required": [
"level",
"code",
"message",
"reproduce"
],
"description": "One thing wrong with this task's records, in the shape `project.finding-carries-reproduce`\nasks for."
},
"MilestoneCounts": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "Every issue naming this milestone."
},
"required": {
"type": "integer",
"format": "uint32",
"minimum": 0,
"description": "Total less cancelled: the denominator a progress figure uses."
},
"by_readiness": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "uint32",
"minimum": 0
},
"description": "One entry per readiness state that has at least one issue, keyed by its word."
}
},
"required": [
"total",
"required",
"by_readiness"
],
"description": "One milestone's issues counted by readiness, plus the two denominators."
}
}
}Examples
These are the capability's own benchmark cases: the same inputs the benchmark runs and the OpenAPI document shows.
first-milestone
{
"milestone": "M000"
}