I3973 — A record is referenced only once it is on master: a pull request naming an unlanded id is refused, and convergence reports stranded records
A gate in the structure job (wired in .github/workflows/validate.yml and declared in .ai/repo/ci/gates.yaml) reads the record ids that a pull request ADDS, in the added lines of its diff hunks over records, documents and its commit messages, and refuses one that resolves neither on the base branch nor in the pull request's own tree, naming the file, line and id and the unmerged branch that holds it if any. The id grammars it judges are issue ids (I followed by four or more digits), ADR numbers written as ADR followed by four digits, and rule ids written as project. followed by a rule name; a line the pull request did not add is never judged, so the 62 ids master's records and docs already name without a record at 355a0cee79a stay where they are and are reported once as a baseline count that may not grow. The commit-msg judge already refuses an id the working tree's layer lacks (commit/verdict.rs UnresolvedReference, policy reference_unresolved: error), but the working tree may hold records another branch brought in. convergence (apps/majordomus-cli/src/convergence) adds a holding kind: a record id named on master that resolves only on an unmerged branch is reported as stranded, with that branch.
READY wave 0 · p1 · implementation profile · runs alone
Ready. Every dependency is done, so majordomus plan start I3973 will be accepted.
Objective
A gate in the structure job (wired in .github/workflows/validate.yml and declared in .ai/repo/ci/gates.yaml) reads the record ids that a pull request ADDS, in the added lines of its diff hunks over records, documents and its commit messages, and refuses one that resolves neither on the base branch nor in the pull request's own tree, naming the file, line and id and the unmerged branch that holds it if any. The id grammars it judges are issue ids (I followed by four or more digits), ADR numbers written as ADR followed by four digits, and rule ids written as project. followed by a rule name; a line the pull request did not add is never judged, so the 62 ids master's records and docs already name without a record at 355a0cee79a stay where they are and are reported once as a baseline count that may not grow. The commit-msg judge already refuses an id the working tree's layer lacks (commit/verdict.rs UnresolvedReference, policy reference_unresolved: error), but the working tree may hold records another branch brought in. convergence (apps/majordomus-cli/src/convergence) adds a holding kind: a record id named on master that resolves only on an unmerged branch is reported as stranded, with that branch.
Why
Records written on one branch and named from another reach master before what they name: the I3920 record exists only on chore/landing-survives-any-session while train 4 and the landing seat's messages already cite it, and plan validate refuses depends_on to an issue master lacks, which is why the landing-survives-any-session plan could not depend on I2790. A reference that does not resolve on master is a reference a reader cannot follow.
Current state
Verified at 355a0cee79a: commit validate refuses an unresolved record id against the tree it runs in (verdict.rs:312-321); scripts/ci/reference-check checks repository paths in prose and exempts records; convergence reports worktrees, branches and stashes at risk, not records; nothing compares a named id with the base branch; 62 distinct issue ids named in master's issue and intent records and docs/*.md have no record on master.
Desired state
A pull request citing an id that exists only on another unmerged branch is refused until that branch lands or the record is brought in; convergence lists stranded records on master.
Scope
- apps/majordomus-cli/src/commit/verdict.rs
- apps/majordomus-cli/src/convergence
- scripts/ci/reference-check
- .github/workflows/validate.yml
- .ai/repo/ci/gates.yaml
- test/cases/274_commit_policy.sh
- test/cases/383_convergence_is_enforced.sh
- .ai/repo/project/issues/I3973.yaml
Out of scope
- Rewriting a record that already cites an id; records state what was true when written
Dependencies
None. This issue is a root of the graph.
What waits on this
Acceptance criteria
- A fixture pull request whose added issue record line cites I9998, which exists only on another branch, is refused naming the file, the id and that branch (case a_pull_request_naming_an_unlanded_id_is_refused)
- The same pull request carrying I9998 itself passes
- A fixture pull request that edits a file already naming an unresolved id on an untouched line passes, and the baseline count of such ids on master may not grow
- convergence on a fixture master citing an id held only by an unmerged branch reports it stranded with the branch
- The gate runs in the structure job, and gates.yaml declares it
Validation
- bin/majordomus plan validate
- env -u MAJORDOMUS_SHARE cargo test --manifest-path apps/majordomus-cli/Cargo.toml --lib -- commit:: convergence::
- env -u MAJORDOMUS_SHARE bash test/run.sh 383_convergence_is_enforced
Evidence required
- references_resolve_on_master
Evidence
None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.
Timeline
- started
- —
- verified
- —
- completed
- —
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I3973.yaml. Read it back with majordomus plan show I3973.