I3979 — Every label, branch prefix and commit type is declared once, projected to GitHub, described in CONTRIBUTING and checked
One tracked declaration under .ai/repo/ci (beside github.yaml) names every label with its colour, description and actor (projected from records, set by the pull request projection, or set by a person: hold, collision:accepted, ci:yielded), the area labels allowed from commit scopes, and the branch prefixes (feature, fix, docs, chore, refactor, integration/train). It does not become a second source of what already has one: the commit types stay declared in release/model.rs:23-43 and the hold label names in integration/classify.rs LABEL_POLICY (classify.rs:110-118), and a library test holds the declaration equal to both, so a type or hold label added in one place and not the other fails. github-sync creates and updates every declared label (today it creates only labels labels_of emits and never updates one); CONTRIBUTING.md gains a section generated from the declaration and from those two sources between majordomus markers; and a check refuses a label named by code, a workflow or a projection that is not declared, and reports a declared label missing on GitHub.
READY wave 0 · p1 · implementation profile · runs alone
Ready. Every dependency is done, so majordomus plan start I3979 will be accepted.
Objective
One tracked declaration under .ai/repo/ci (beside github.yaml) names every label with its colour, description and actor (projected from records, set by the pull request projection, or set by a person: hold, collision:accepted, ci:yielded), the area labels allowed from commit scopes, and the branch prefixes (feature, fix, docs, chore, refactor, integration/train). It does not become a second source of what already has one: the commit types stay declared in release/model.rs:23-43 and the hold label names in integration/classify.rs LABEL_POLICY (classify.rs:110-118), and a library test holds the declaration equal to both, so a type or hold label added in one place and not the other fails. github-sync creates and updates every declared label (today it creates only labels labels_of emits and never updates one); CONTRIBUTING.md gains a section generated from the declaration and from those two sources between majordomus markers; and a check refuses a label named by code, a workflow or a projection that is not declared, and reports a declared label missing on GitHub.
Why
Labels the executor and gates read (hold in integration/classify.rs, collision:accepted in scripts/ci/collision-gate, ci:yielded) exist on the repository only because someone created them by hand; hold was missing until 2026-10-22, so the hold policy existed for 18 days with no label to apply. The pull request projection of I3970 needs one vocabulary to project from.
Current state
Verified at 355a0cee79a: github-sync creates missing labels derived from labels_of (github-sync:439-465) and leaves existing ones alone; commit types are declared in release/model.rs:23-43 and hold labels in classify.rs:110-118; no file declares the label set; CONTRIBUTING.md states conventions in prose; gh label list on 2026-10-22 shows accessibility, bug, documentation, duplicate, enhancement, good first issue, help wanted, invalid, question and wontfix beside the projected labels.
Desired state
The declaration is the one source of labels and conventions; the projection makes GitHub agree; CONTRIBUTING's section is generated; the check fails on any drift.
Scope
- .ai/repo/ci/github.yaml
- scripts/github-sync
- scripts/ci/github-check
- CONTRIBUTING.md
- apps/majordomus-cli/src/commit/scopes.rs
- apps/majordomus-cli/src/release/model.rs
- apps/majordomus-cli/src/integration/classify.rs
- test/cases/97_github_gate.sh
- .ai/repo/project/issues/I3979.yaml
Out of scope
- Deleting GitHub's default labels a person may still use; undeclared labels on GitHub are reported, not removed
Dependencies
None. This issue is a root of the graph.
What waits on this
- I3970BLOCKED Every pull request carries its metadata, projected from the trunk's records: labels, milestone, assignee and Refs links, never a closing keyword
- I3975BLOCKED GitHub issues are created only by github-sync: an agent's raw create is refused, an issue nobody projected is reported on GitHub, and an unmarked issue is never adopted
Acceptance criteria
- A fixture workflow applying a label the declaration does not hold is refused naming the file and the label (case an_undeclared_label_is_refused)
- A library test fails when a commit type in release/model.rs or a hold label in classify.rs is missing from the declaration, or the reverse
- github-sync --apply against a scripted forge creates a declared label that is missing and updates one whose description differs
- CONTRIBUTING.md's generated section matches the declaration, and editing the declaration without regenerating fails the check
Validation
- bin/majordomus plan validate
- env -u MAJORDOMUS_SHARE cargo test --manifest-path apps/majordomus-cli/Cargo.toml --lib -- integration::classify release::model
- env -u MAJORDOMUS_SHARE -u MAJORDOMUS_HTTP_HOST -u MJ_ROOT bash test/run.sh 97_github_gate
Evidence required
- conventions_declared_once
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/I3979.yaml. Read it back with majordomus plan show I3979.