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. While a hand-written roadmap table exists, it can neither list a version no milestone declares nor hide one the model does.
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
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.