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.rule-is-a-doctrine@1
A new enforced rule is a doctrine, not an inline check
What it is
- URI
majordomus://rule/project.rule-is-a-doctrine@1- Kind
rule- Identity
project.rule-is-a-doctrine@1- Path
.ai/repo/rules/project/rule-is-a-doctrine.v1.md- Provenance
- declared
- Media type
text/markdown- As a capability
- rule.project.rule-is-a-doctrine@1
Front matter
{
"id": "project.rule-is-a-doctrine",
"version": 1,
"kind": "rule",
"title": "A new enforced rule is a doctrine, not an inline check",
"description": "A rule the tool enforces declares an x-majordomus block — a validator named mj_validate_<name> when a command runs it, the tests that prove it when a gate does; no command selects checks by hand.",
"statement": "A rule the tool enforces declares an x-majordomus block — a validator named mj_validate_<name> when a command runs it, the tests that prove it when a gate does; no command selects checks by hand.",
"status": "active",
"class": "blocking",
"depends_on": [],
"tags": [
"doctrine"
],
"x-majordomus": {
"tests": [
"test/cases/18_doctrine_wiring.sh",
"test/cases/125_rule_proof.sh"
]
}
}Content
---
id: project.rule-is-a-doctrine
version: 1
kind: rule
title: A new enforced rule is a doctrine, not an inline check
description: A rule the tool enforces declares an x-majordomus block — a validator named mj_validate_<name> when a command runs it, the tests that prove it when a gate does; no command selects checks by hand.
statement: A rule the tool enforces declares an x-majordomus block — a validator named mj_validate_<name> when a command runs it, the tests that prove it when a gate does; no command selects checks by hand.
status: active
class: blocking
depends_on: []
tags: [doctrine]
x-majordomus:
tests: [test/cases/18_doctrine_wiring.sh, test/cases/125_rule_proof.sh]
---
# Rationale
A check that a command calls by name is enforcement nobody declared; the dispatcher reads the declared rules, so a rule added there runs from that moment and one whose validator is missing fails doctor. The same argument applies one step out: a rule proved by a behavioural case, with the case named only in English in its Verification section, is a relation nothing holds — the case can be renamed and the rule goes on reading as proven.
# Required behaviour
A rule the tool enforces declares an x-majordomus block, in the mode that matches how it is enforced. A rule a command runs is dispatched: it names a validator `mj_validate_<name>`, with the category, exit code and enforcing commands the dispatcher needs. A rule a gate proves is gated: it names the tests and no validator, and nothing dispatches it. Either way it names at least one test. No command selects checks by hand, and no rule states its proof only in prose.
# Failure behaviour
The loader refuses a block that is neither mode, naming the missing half, and the set does not resolve. A dispatched rule whose validator function is absent fails doctor. A blocking rule that names no proof at all is new debt that `scripts/ci/rule-proof-check` refuses, and a rule naming a test that is not in the tree fails it outright, because such a rule reads as proven and is not.
# Verification
`majordomus doctor` walks the dispatched chain; `test/cases/18_doctrine_wiring.sh` mutation-tests it by breaking each link. `scripts/ci/rule-proof-check` holds the gated relation, and `test/cases/125_rule_proof.sh` mutation-tests both modes, the loader's refusal of a half-declaration, and the ratchet.