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
test/cases/18_doctrine_wiring.sh
An object of kind test
What it is
- URI
majordomus://test/test/cases/18_doctrine_wiring.sh- Kind
test- Identity
test/cases/18_doctrine_wiring.sh- Path
test/cases/18_doctrine_wiring.sh- Provenance
- declared
- Media type
text/plain- As a capability
- test.test/cases/18_doctrine_wiring.sh
Front matter
{}Content
# majordomus-covers: doctrine doctor
# majordomus-negative: doctrine doctor
# claims: doctrine-registry
# The wiring verifier is the reason this layer exists, so it is itself mutation-tested:
# each link of the chain is broken in a throwaway copy and doctor must go red. A verifier
# that survives broken wiring proves nothing.
. "$ROOT/test/lib.sh"
# The mutations below rewrite the tool itself, so the case runs against its own copy.
mkdir -p "$T/tool" "$T/tool/.github/workflows"
fixture_repo "$T/tool" docs
cp -R "$ROOT/test" "$T/tool/"
cp "$ROOT/.github/workflows/validate.yml" "$T/tool/.github/workflows/"
MJ="$T/tool/bin/majordomus"
LIBD="$T/tool/lib"
CI="$T/tool/.github/workflows/validate.yml"; RUN="$T/tool/test/run.sh"
# The two declared enforcement hooks: a fresh checkout has none, and their absence is a
# real doctor failure that would mask the one this case is about.
wire() {
mkdir -p .git/hooks
printf '#!/usr/bin/env bash\nmajordomus doctor\n' > .git/hooks/pre-commit
printf '#!/usr/bin/env bash\nmajordomus finish --check\n' > .git/hooks/pre-push
chmod +x .git/hooks/pre-commit .git/hooks/pre-push
PATH="$(dirname "$MJ"):$PATH"; export PATH
}
# A mutation must be shown to have taken before anything is asserted about what it
# caused. sed is silent when its pattern matches nothing: it exits 0 having changed
# nothing, and the case then asserts that a no-op produced no failure, which is
# trivially true. That is how mutation 2 here went green for a while after
# mj_run_task_checks stopped being a one-liner. One grep per probe closes it.
took() { # file, text the mutation must have introduced
grep -qF -- "$2" "$1" || { printf ' the probe did not take: %s is absent from %s\n' "$2" "$1"; exit 1; }
}
# ...and the inverse, for a mutation that removes rather than adds.
gone() { # file, text the mutation must have removed
grep -qF -- "$2" "$1" && { printf ' the probe did not take: %s is still present in %s\n' "$2" "$1"; exit 1; }
return 0
}
restore() {
cp "$T/rules.orig"/*.md "$RULES/"
cp "$ROOT/lib/check.sh" "$LIBD/check.sh"
cp "$ROOT/.github/workflows/validate.yml" "$CI"
cp "$ROOT/test/run.sh" "$RUN"
}
"$MJ" init >/dev/null; "$MJ" update >/dev/null
# the registry is the vendored rule package of the repository under test; every mutation
# below edits one rule file there. A hand-edited vendored file is also a rule_package
# finding, which is expected: the assertion is about the wiring line each break produces.
RULES="$T/.ai/repo/rules/vendor/majordomus/rules"
cp -R "$RULES" "$T/rules.orig"
wire
# A healthy tree passes, and says so in one line naming a derived count — and the count
# carries its denominator. The line used to read "41 doctrines — validator, dispatch,
# propagation, test and CI resolve for every one" while the same run reported 122 rules;
# "every one" meant every one of 41 and no reader could tell. Both numbers are asserted
# here, in that order, so the subset can never again be phrased as the whole
# (project.a-verdict-states-its-subject).
expect_exit 0 "$MJ" doctor
expect_grep 'OK +doctrine +[0-9]+ of [0-9]+ rule\(s\) carry a validator .* validator, dispatch, propagation, test and CI resolve for every one of the [0-9]+'
expect_grep 'OK +doctrine .* the remaining [0-9]+ declare no validator and this check does not examine them'
expect_grep 'OK +rules +[0-9]+ rule\(s\) .* [0-9]+ carry a validator, [0-9]+ are normative text this tool does not check'
# `doctrine status` derives its counts; nothing is written down. Its numbers are statements
# about the registry, which is a minority of the rule set, so the registry's size and the
# set's size are printed together and the blocking rules outside it are named rather than
# left where only another surface can see them.
expect_exit 0 "$MJ" doctrine status
expect_grep 'declared doctrines: [0-9]+ of [0-9]+ rule\(s\) in the effective set'
expect_grep 'missing validators: 0'
expect_grep 'blocking rules with no validator, outside this registry: [0-9]+'
expect_grep 'without a test file: 0'
expect_exit 0 "$MJ" doctrine list
expect_grep 'majordomus.scope-integrity +blocking +mj_validate_scope'
expect_exit 0 "$MJ" doctrine show majordomus.scope-integrity
expect_grep 'wired yes'
expect_exit 12 "$MJ" doctrine show no_such_doctrine
# 1. declared doctrine, no validator function
sed 's/^ validator: scope$/ validator: nothing_implements_this/' "$T/rules.orig/scope-integrity.v1.md" > "$RULES/scope-integrity.v1.md"
took "$RULES/scope-integrity.v1.md" "validator: nothing_implements_this"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine .* validator function mj_validate_nothing_implements_this is defined nowhere'
restore
# 2. validator exists but its command never dispatches
sed '/mj_doctrine_dispatch check/d' "$ROOT/lib/check.sh" > "$LIBD/check.sh"
gone "$LIBD/check.sh" "mj_doctrine_dispatch check"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine .* lib/check.sh never calls mj_doctrine_dispatch'
restore
# 3. a blocking doctrine whose command cannot turn a finding into a non-zero exit
sed -e 's/exit "\$MJ_EX_CONTRACT"/exit 0/g' -e 's/\[ "\$MJ_FAILS" = 0 \] \&\& exit "\$MJ_EX_OK" || exit 0/exit 0/' "$ROOT/lib/check.sh" \
| sed 's/MJ_FAILS/MJ_NOTHING/g' > "$LIBD/check.sh"
gone "$LIBD/check.sh" "MJ_FAILS"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine .* never turns a failing finding into a non-zero exit'
restore
# 4. the test that proves a doctrine does not exist
sed 's#^ tests: \[test/cases/04_start_check.sh\]$# tests: [test/cases/99_absent.sh]#' "$T/rules.orig/scope-integrity.v1.md" > "$RULES/scope-integrity.v1.md"
took "$RULES/scope-integrity.v1.md" "tests: [test/cases/99_absent.sh]"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine .* test test/cases/99_absent.sh does not exist'
restore
# 5. a doctrine naming a claim that is not in the claims file
sed 's/^ claims: \[scope-enforcement, scoped-task\]$/ claims: [scope-enforcement, invented-claim]/' "$T/rules.orig/scope-integrity.v1.md" > "$RULES/scope-integrity.v1.md"
took "$RULES/scope-integrity.v1.md" "invented-claim"
expect_exit 10 "$MJ" doctor
expect_grep "FAIL doctrine .* names claim 'invented-claim'"
restore
# 6. a validator no doctrine declares — enforcement that runs under no rule
printf '\nmj_validate_orphan_rule() { return 0; }\n' >> "$LIBD/check.sh"
took "$LIBD/check.sh" "mj_validate_orphan_rule"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine +mj_validate_orphan_rule .* no rule declares it'
restore
# 7. CI stops running the suite
sed 's#run: bash test/run.sh#run: echo skipped#' "$ROOT/.github/workflows/validate.yml" > "$CI"
gone "$CI" "run: bash test/run.sh"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine +ci .* does not run test/run.sh'
restore
# 8. CI runs the suite but swallows its exit code
sed 's#run: bash test/run.sh#run: bash test/run.sh || true#' "$ROOT/.github/workflows/validate.yml" > "$CI"
took "$CI" "bash test/run.sh || true"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine +ci .* does not let it fail the job'
restore
# 9. the runner stops globbing, so a new case would silently not run
sed 's#cases/\*\.sh#cases/01_init.sh#' "$ROOT/test/run.sh" > "$RUN"
gone "$RUN" "cases/*.sh"
expect_exit 10 "$MJ" doctor
expect_grep 'FAIL doctrine +runner .* does not glob'
restore
# healthy again after every mutation is reverted
expect_exit 0 "$MJ" doctor