The milestone and issue model — validate it, read it, and move one issue through it.
The milestone and issue model — validate it, read it, and move one issue through it.
state-mutating
plan
--json
Ten read-only subcommands and four that write one lifecycle marker each. The class names the command as a whole, which is what a single field can say; the syntax section separates READ from WRITE. It operates on the project model rather than on the active task record, so it needs no task.
Only the codes this command can produce. The full contract is on the commands index.
0ok
2usage
10contract unmet
12missing artifact
15refused
The model, validated and read
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.
A model that parses and hangs together
dependency satisfied
no
model present
yes
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
exit0
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.
A repository with no project model
dependency satisfied
no
model present
no
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
exit12
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.
What can be started now
dependency satisfied
no
model present
yes
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
exit0
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.
What is waiting, and on what
dependency satisfied
no
model present
yes
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
exit0
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.
A subcommand it does not have
dependency satisfied
no
model present
yes
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
exit2
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.
Reference
Read the canonical project model, and move one issue through its lifecycle. A milestone is
an executable specification of an outcome; an issue is a bounded execution contract; the
dependency graph between the issues decides what may be executed next. See
docs/PLANNING.md for the semantics.
Reads:.ai/repo/project/project.yaml, .ai/repo/project/milestones/*.yaml,
.ai/repo/project/issues/*.yaml, share/allow/{project,milestone,issue}.txt.
Writes: one lifecycle field in one issue file, and one ledger event — start, verify,
evidence and done only. Every other subcommand writes nothing.
majordomus plan validate schemas, references, the DAG, status consistencymajordomus plan status milestone progress and the next executable issuemajordomus plan list one line per issue: id, status, wave, milestone, titlemajordomus plan show <id> the full record of one milestone or issuemajordomus plan ready issues whose dependencies are all satisfiedmajordomus plan blocked issues waiting on a dependency, and on which onemajordomus plan waves topological execution waves, derived from the graphmajordomus plan graph the dependency DAG as Mermaidmajordomus plan next the one issue a worker should take nowmajordomus plan body <id> the provider-neutral projection body for one recordmajordomus plan start <id> record that execution beganmajordomus plan verify <id> record that implementation is complete, evidence pendingmajordomus plan evidence <id> attach one piece of evidencemajordomus plan done <id> record completion
Options:--json on the read subcommands a surface consumes; --milestone <id> to
restrict list, ready, blocked, waves and graph; --covers, --type, --command,
--result and --artifact on evidence.
Behaviour:
No status is stored anywhere. BLOCKED, READY, ACTIVE, VERIFY, DONE and
CANCELLED are derived from what an issue records about itself and from the state of its
dependencies, by lib/project.awk. Writing a status: field is an unknown key.
validate reports unknown keys, unknown and self dependencies, duplicate ids, cycles,
issues executing ahead of a dependency, and issues with no acceptance criteria. It exits
10 when any finding is a failure, 0 when only warnings remain.
start refuses (15) an issue that is not READY, naming what it waits on. verify
refuses an issue that is not ACTIVE.
evidence refuses (15) a token the issue does not declare, and refuses (2) without a
--command or an --artifact: narrative is not evidence. It appends to the issue's own
file with the commit and the timestamp.
done refuses (10) while any declared evidence token is uncovered, and refuses (15)
while a dependency is not DONE. Every writing subcommand prints the next ready issue
after the graph has been recomputed.
Exits 12 when the record named does not exist, or when the repository has no canonical
project model at all — the model is opt-in, and doctor skips it rather than failing
where it is absent.
The same model is projected to GitHub by scripts/github-sync and to the website by
scripts/generate-site-data. Neither re-derives a status; both read this engine.
Evidence
Every case below runs in CI on Linux and macOS. The site refuses to build if a public command has no behavioural case, no negative case, or no demonstration.