A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live
project.commands-are-projections@1
A command is declared once, and every surface that shows it is a projection
What it is
- URI
majordomus://rule/project.commands-are-projections@1- Kind
rule- Identity
project.commands-are-projections@1- Path
.ai/repo/rules/project/commands-are-projections.v1.md- Provenance
- declared
- Media type
text/markdown- As a capability
- rule.project.commands-are-projections@1
Front matter
{
"id": "project.commands-are-projections",
"version": 1,
"kind": "rule",
"title": "A command is declared once, and every surface that shows it is a projection",
"description": "Every command this repository offers is declared in the program that implements it and nowhere else; the workflow runner, the shell completion, the machine surfaces, the Cockpit and the documentation are projections of one composed graph, so adding a command requires no second registration and no surface can carry a command catalogue that goes stale.",
"statement": "A command is declared once, in the program that implements it; every other surface derives its name, its description, its arguments and its availability from the composed command graph, and none keeps a catalogue of its own.",
"status": "active",
"class": "blocking",
"depends_on": [
"project.interfaces-are-projections@1",
"project.hot-path-reads-once@1"
],
"tags": [
"commands",
"projections",
"completion",
"workflows"
],
"x-majordomus": {
"tests": [
"scripts/ci/command-graph",
"test/cases/101_command_graph.sh"
]
}
}Content
---
id: project.commands-are-projections
version: 1
kind: rule
title: A command is declared once, and every surface that shows it is a projection
description: Every command this repository offers is declared in the program that implements it and nowhere else; the workflow runner, the shell completion, the machine surfaces, the Cockpit and the documentation are projections of one composed graph, so adding a command requires no second registration and no surface can carry a command catalogue that goes stale.
statement: A command is declared once, in the program that implements it; every other surface derives its name, its description, its arguments and its availability from the composed command graph, and none keeps a catalogue of its own.
status: active
class: blocking
depends_on: [project.interfaces-are-projections@1, project.hot-path-reads-once@1]
tags: [commands, projections, completion, workflows]
x-majordomus:
tests: [scripts/ci/command-graph, test/cases/101_command_graph.sh]
---
# Rationale
Three programs answer to a person here, and every surface that wants to *show* what they
offer is tempted to keep a list. The temptation is reasonable each time: a `just` recipe per
command is four lines and reads well; a completion script with the command names in it works
immediately; a documentation page with a table of commands is easy to write.
Every one of those lists is correct on the day it is written. What happens afterwards is not
a hypothesis — this repository has the evidence. A capability declared that it was reached as
`majordomus scope classify`; the command line had no such command, and had never had one.
Nothing noticed, because nothing joined the two declarations. The generated reference showed
a command that could not be run.
The general shape is the one `DYNAMICITY.md` names: a fact written in two places, one of
which changes. A command's name, its one-line description, its arguments and the values they
accept are facts the implementing program already knows, and every copy of them decays
silently — a recipe that outlives the command it spells, a completion that offers a flag that
was removed, a page that documents a command that never existed.
The cost of the alternative is small and one-directional. The graph composes three
declarations that already exist and are already authority: the clap tree, the shipped command
registry the shell tool is reconciled against, and the workflow runner's own structured dump.
Nothing registers with it. Adding a command to any of those puts it on every surface the
exposure policy admits, and removing one takes it off.
# Required behaviour
**A command is declared in one place: the program that implements it.**
- the Rust executable's commands are the clap declaration in `apps/majordomus-cli/src/cli.rs`;
- the shell tool's commands are `share/commands.yaml`, reconciled against its dispatch table;
- a workflow is a recipe in `justfile` or `.just/*.just`.
**Semantics that a declaration cannot carry are annotated once, beside it.** What running a
command changes, whether it holds a terminal, what it requires, and where an argument's values
come from live in `apps/majordomus-cli/src/command_graph/semantics.rs`, next to the command
line they describe. The default is what most commands are, so only a departure is written
down, and an annotation that names a command which no longer exists fails the build.
**Every other surface derives.** Specifically, none of these may carry a command's name,
description or arguments:
- a workflow file may not declare a recipe whose body is a call to either program — that is a
hand-written bridge, and the generated one already carries it, with the description its own
declaration gives. Spelling a command is not the same as using one: a recipe that reads a
value out of a command and then does work no command of either program does — `just open`
asks `serve status` for an address and opens a browser at it — composes rather than
repeats, and no node of the graph projects to it;
- a shell completion adapter may not contain a command, a flag, an identifier or a value set;
- an MCP tool list, an HTTP route table, an OpenAPI document, a Cockpit palette and a
documentation index may not be hand-maintained for a command that the graph carries;
- a compatibility alias for an older recipe name is declared once, beside the command line,
and rendered into the generated bridge — never maintained in a workflow file.
**Projections are derived, not declared.** Which surface carries a command follows from what
running it changes and how it behaves towards a terminal, decided in one function
(`command_graph/policy.rs`). No command configures a surface, and every surface that withholds
a command carries the reason.
**Generated projections are not tracked.** The workflow bridge is a pure function of the tree
it sits in; it lives under `.ai/local/cache/`, it is written atomically, and it is refreshed
when a declaration behind it has changed. Entering the repository must not dirty the working
tree.
# Failure behaviour
The gate `command-graph` (`scripts/ci/command-graph`) fails, exit 10, when:
- the graph carries an error — a duplicate identity, a recipe name two commands would take, a
capability that claims a command line the executable does not declare, an annotation or an
alias that names a command that no longer exists, or a secret argument with a value source
that would enumerate it;
- a workflow file declares a recipe whose body *is* a call to one of the two programs — a
body line that, after the leading whitespace and the `@` and `-` the runner allows, begins
with a launcher — with the bootstrap recipe that produces the bridge named as the
exception. A program named inside a command substitution, a pipeline or an assignment is
a value the recipe consumed, not a name it repeated, and is not a finding.
`test/cases/102_completion_shell.sh` loads the generated adapter into a real zsh and reads
what it offers, because a completion that looks correct in a file and does nothing at all is
the normal failure of this kind of work.
`test/cases/101_command_graph.sh` is the behavioural half: it proves the graph builds in a
disposable repository, that the bridge it writes forwards arguments rather than interpolating
them, that a second materialisation writes nothing, that adding a command to the declaration
reaches the workflow projection and the completion without another edit, and that the check
can fail — a hand-written bridge introduced into a copy is rejected by the same gate.
There is no runtime enforcement of the derivation itself. The projections *are* the
derivation: there is nothing to check at run time, because there is no second copy that could
disagree.
# Verification
```sh
scripts/ci/command-graph # the gate
bash test/run.sh 101_command_graph # the behavioural case
majordomus commands graph --check # the graph alone; exit 10 on an error
majordomus commands explain <id> # where one command is projected, and why
```
`apps/majordomus-cli/src/command_graph/` carries the unit half: that no adapter names a
command, that a mutation never reaches a machine surface, that a workflow is never bridged
back into a workflow, and that the fingerprint of a graph over one tree is stable.