Skip to content

What already claims a path is checked before that path is built

What already claims a path is checked before that path is built

majordomus://rule/project.work-is-claimed-before-it-is-built@1active coordinationprocess

it references

Nothing. This object declares no reference.

Rationale

On 2026-09-09 eight sessions worked in this repository and two of them independently built a release subsystem — the same module path, the same command group, the same generated changelog — for several hours. Both had called majordomus_announce. Neither announcement existed when the other started. A peer board is a process's memory: it says who is working now, and it says nothing about a branch pushed an hour before this process started. The durable signal was there the whole time and nothing looked at it — origin/int/land-all already carried apps/majordomus-cli/src/release/ before either session began. Git already knew the answer; the protocol just never asked it.

Two designs arriving at one path is not a merge conflict, resolved by combining two diffs. It is a choice between two finished pieces of work, and the one that loses was written for nothing — hours of one session's time, spent on a question a git query would have answered before it started.

Required behaviour

Before implementing anything that creates a new module, command group, document kind or generated artifact, ask what already claims those paths: scripts/collision-check --new <paths> reads every local and remote branch for a version of those paths that differs from the base, not only the branches active right now. Announce afterwards, once the paths are clear or once a collision has been read and understood — majordomus_announce says who is working now, collision-check says what was already built.

The check reads committed refs, local and remote. Work that is staged or uncommitted is invisible to it, and so is an identifier a session is about to allocate but has not pushed — two sessions renumbering an ADR into each other in the same minute is exactly what a branch scan cannot see. So the two halves are not interchangeable: scripts/collision-check answers what has been pushed, majordomus_announce answers what is being written right now, and the intent announced should name the identifiers being allocated as well as the paths.

For a decision identity, neither of those is asked by hand any more, because asking by hand is what failed: on 2026-09-11 three branches claimed ADR 0044 and two claimed 0043, and three separate sessions each wrote the same survey to escape it. majordomus adr next is that survey — every ref, every tag, every sibling worktree's working tree and the peer board, each reporting what it claimed and saying so when it could not be reached — and it allocates above the high-water mark rather than into a hole, because a hole is an identity that was taken and withdrawn or taken and not yet written. Announce the number it gives you before you write it.

The other half of that is not advice. majordomus adr check refuses an identity this branch adds that another ref already carries a different decision at, naming both documents and both refs; doctor runs the same examination, and doctor is the pre-commit hook. That is what makes this rule followable for identities: a claim that lived only in an unpushed branch used to be unobservable until the branch was pushed, which is a precondition nobody could meet. ADR 0053 is the reasoning; test/cases/271_adr_allocation.sh is the proof.

Failure behaviour

scripts/collision-check exits 11 when a path is claimed by a collision, naming the claiming branches, their last commit and how long ago it landed; exit 0 means nothing collides on the paths named; exit 12 means it could not measure — a shallow repository, a base or head that does not resolve, or no branch besides the base to compare against — and never reads as "nothing claims this"; exit 2 is a usage error. For a path the base does not have, a branch carrying the same bytes (a rescue copy), sharing the commit that added the path (work built on the other), or byte-identical on other paths the head adds (a copy of the same work that has since diverged) is listed and is not a collision; only a path added on both sides independently, with different content, is. A path declared merge=derived is regenerated by the merge and is counted rather than examined.

The rule stays advisory as a habit — run the check before implementing — but it is no longer only a habit. The collision-check gate (scripts/ci/collision-gate) asks it on every pull request, over every path the pull request adds, and refuses a collision. A path collision is sometimes the right thing to accept deliberately — two implementations worth comparing, or an abandoned branch that is not yours to delete — and nothing here can decide that mechanically, so the decision is left where it belongs: a person labels the pull request collision:accepted, and the collision is still printed for the reviewer.

The identifier half is gated even so, because there the judgement does not exist: two decisions at one number is never deliberate. majordomus adr check exits 10 naming both documents, both refs and majordomus adr next; doctor reports it as an adr failure and refuses the commit.

Verification

scripts/collision-check is the check for paths, and until 2026-09-13 nothing ran it: the provider bootstrap templates named it and no hook, recipe or workflow called it. The collision-check gate in .ai/repo/ci/gates.yaml is its caller now, on every plan of the structure job, which checks out at fetch-depth 0. test/cases/566_the_collision_guard_has_a_caller.sh proves it against a fixture repository with a bare origin, in the shape CI has (a detached merge commit): a new path another unmerged branch adds differently is refused and the branch is named; a path only the pull request adds passes; the pull request's own branch, the pushed branch it is built on and a same-bytes rescue copy are not collisions; a merged branch holding an older version of a file is not a claimant; and a shallow or single-branch clone exits 12 rather than 0.

For decision identities there is a verdict rather than a habit: test/cases/271_adr_allocation.sh proves both directions — a tree that renumbers a decision onto an identity another ref already carries is refused by adr check and by doctor, and a tree that takes what adr next says is free passes — and the adr-collision gate in .ai/repo/ci/gates.yaml runs the same command in CI.

Where this comes from

canonical file
.ai/repo/rules/project/work-is-claimed-before-it-is-built.v1.md
identity
project.work-is-claimed-before-it-is-built@1
also served as
MCP resource majordomus://rule/project.work-is-claimed-before-it-is-built@1 · majordomus entity show rule/project-work-is-claimed-before-it-is-built-1 · GET /api/v1/entity?uri=majordomus://rule/project.work-is-claimed-before-it-is-built@1
in the Cockpit
/cockpit/objects/rule/project-work-is-claimed-before-it-is-built-1

This page is generated from the file above by scripts/generate-site-data; editing the page changes nothing, editing the file changes every surface. The relations above are edges of docs/generated/graph.json, which the executable writes from the same index.

Searches the titles and descriptions of every page this site publishes.