Plan
milestones are outcome specifications and issues are execution contracts; status, execution waves and the next ready issue are derived from the dependency graph, never stored
milestones are outcome specifications and issues are execution contracts; status, execution waves and the next ready issue are derived from the dependency graph, never stored
Where it lives
State it owns
What implements it
Specified in
The command that acts on it
majordomus plan
The milestone and issue model — validate it, read it, and move one issue through it.
- state-mutating
- --json
majordomus plan <subcommand> [options]
See it refuse, and accept
plan reads a canonical project model and moves one issue through it. Status is never stored — it is derived from what was recorded about an issue and from the state of its dependencies. Every scenario here is executed against the real binary by test/cases/34_command_fixtures.sh.
Given this repository
# Installed, with a canonical project model carrying one milestone and two issues, the # second depending on the first. The helpers come from test/lib.sh, which the fixture # runner has already sourced. . "$FIXTURE_SETUP/installed.sh" pj_init pj_milestone M000 pj_issue I0001 M000 pj_issue I0002 M000 I0001
Run
$ majordomus plan validate
Output contains
- milestone
- issue
- 0 failure
exit
0
Schemas, references, the dependency graph and status consistency, in one pass. A key nobody reads is an error here for the same reason it is an error in the policy: an unread field is a belief nothing checks.
Given this repository
# A repository with Majordomus installed and projections generated, and one commit of work. "$MJ" init >/dev/null "$MJ" update >/dev/null mkdir -p lib docs echo a > lib/a echo d > docs/d git add . && git commit -qm base
Run
$ majordomus plan validate
Output contains
- no canonical project model
exit
12
Exit 12 is MISSING_ARTIFACT: the repository is not set up for a project model, which is a different fact from a model that fails to parse.
Given this repository
# Installed, with a canonical project model carrying one milestone and two issues, the # second depending on the first. The helpers come from test/lib.sh, which the fixture # runner has already sourced. . "$FIXTURE_SETUP/installed.sh" pj_init pj_milestone M000 pj_issue I0001 M000 pj_issue I0002 M000 I0001
Run
$ majordomus plan ready
Output contains
- I0001
exit
0
I0002 depends on I0001, so only I0001 is ready. Readiness is derived from the graph rather than labelled on the issue, so it cannot disagree with the dependencies.
Given this repository
# Installed, with a canonical project model carrying one milestone and two issues, the # second depending on the first. The helpers come from test/lib.sh, which the fixture # runner has already sourced. . "$FIXTURE_SETUP/installed.sh" pj_init pj_milestone M000 pj_issue I0001 M000 pj_issue I0002 M000 I0001
Run
$ majordomus plan blocked
Output contains
- I0002
exit
0
The complement of ready, and it names the dependency rather than only the fact of being blocked — the useful question is what to finish first.
Given this repository
# Installed, with a canonical project model carrying one milestone and two issues, the # second depending on the first. The helpers come from test/lib.sh, which the fixture # runner has already sourced. . "$FIXTURE_SETUP/installed.sh" pj_init pj_milestone M000 pj_issue I0001 M000 pj_issue I0002 M000 I0001
Run
$ majordomus plan explain
Output contains
- plan
exit
2
Exit 2 is USAGE. A mistyped subcommand is refused rather than treated as a default, so a script cannot silently ask a different question from the one it meant.
What enforces it
- majordomus.dag-integrity blocking enforced by doctor, watch The issue dependency graph is acyclic, every edge names an issue that exists, and no issue is executing ahead of a dependency that is not done.
- majordomus.project-integrity blocking enforced by doctor, watch Every milestone and issue file parses, carries the id its filename claims, and contains no key nobody reads.
- majordomus.roadmap-integrity blocking enforced by doctor, watch No document is a second authority for the roadmap, and no milestone types its own version. While a hand-written roadmap table exists, it can neither list a release no milestone was released in nor hide one that carried a milestone; a milestone's release (released_in) is inferred from the release records, never typed.
What is guaranteed
- guaranteed Milestones and issues are canonical repository files, and a key nobody reads is an errorproved by test/cases/40_project_model.sh
- guaranteed Issue and milestone status is derived from recorded facts and is stored nowhereproved by test/cases/41_project_status.sh
- guaranteed A cycle, a self-dependency and a dependency on an issue that does not exist are each refused by nameproved by test/cases/42_dag_waves.sh
- guaranteed Execution waves are computed from the graph, and issues in one wave that touch the same paths are reported as serialisedproved by test/cases/42_dag_waves.sh
- guaranteed An issue cannot be completed while a required piece of evidence is missingproved by test/cases/41_project_status.sh
- guaranteed The roadmap is derived from milestone state, and no document may be a second authority for itproved by test/cases/49_roadmap_doctrine.sh
- guaranteed GitHub milestones and issues are generated from the canonical model, and a hand-edited generated region is reported rather than overwrittenproved by test/cases/45_github_projection.sh
- guaranteed A projection that stops being applied turns a build red, and a gate that cannot reach GitHub reports that it cannot rather than passingproved by test/cases/97_github_gate.sh
- guaranteed A pull request that edits a projected plan record is not refused because GitHub does not show the edit yet, and the trunk is refused for it once a declared window has passedproved by test/cases/1038_a_head_is_judged_against_what_it_changes.sh
- guaranteed An intent's stage is derived from the milestones that realise it and each criterion from the evidence ledger, and it is satisfied only while every criterion has a current passing runproved by test/cases/367_an_intent_is_satisfied_only_by_evidence.sh
- guaranteed An intent whose criterion names evidence that resolves to nothing is refused by nameproved by test/cases/367_an_intent_is_satisfied_only_by_evidence.sh
- guaranteed Once work exists under an intent, a criterion no issue serves, an issue under its milestones that serves nothing and a link to a criterion that does not exist are each refused by nameproved by test/cases/386_a_plan_is_held_to_its_intent.sh
- guaranteed A gap that leaves a criterion unanswered is refused, and an issue serving an intent that started without a critique or while a blocking finding is open fails intent validationproved by test/cases/386_a_plan_is_held_to_its_intent.sh
- guaranteed The work realising an intent is joined across sessions, providers and handovers, and an intent whose milestones are all closed while a criterion's evidence fails is refused naming that criterionproved by test/cases/388_an_intent_is_realised_across_providers_and_held_to_reality.sh
- guaranteed Asked before work on an issue, `intent preflight` names the intents the issue serves with the governance that applies, or refuses naming the causeproved by apps/majordomus-cli/tests/intent.rs
- guaranteed The Cockpit shows every intent with its derived stage at /cockpit/intents, and one intent's criteria, evidence, serving issues and realising work at /cockpit/intents/<id>proved by apps/majordomus-cli/tests/intent_realization.rs
- guaranteed Where the policy requires binding, starting an issue or a task that serves an intent with no critique, or with a blocking finding open, is refused when it is started rather than reported afterwardsproved by test/cases/962_plan_start_asks_the_binding_in_both_engines.sh
The moments this answers
- read three roadmaps for one project and believed none of them A plan kept in prose is a snapshot, and every copy of it drifts on its own schedule; stored status is an assertion that stays true after the world moves.
- found finished work on a branch that nobody ever merged Workers produce far more than integration absorbs, and nothing distinguishes work that is done from work that is done and landed.
- found a substantial change that no issue, ticket or plan ever asked for Work arrives with no bounded contract behind it, so its scope, its acceptance criteria and its ownership are all decided after the fact.
- closed an issue whose acceptance criteria nothing had actually checked Completion is recorded as a state change in a tracker rather than as evidence in the repository, so the two drift immediately.
- was asked how far the milestone had got and had to go and ask four people Progress is an aggregate that exists only in people, because the parts it aggregates were never recorded in a form anything can add up.
- could not reproduce a result because nothing recorded the state it came from The result is recorded and the conditions that produced it are not, so it is an anecdote rather than a measurement.