Problem
The bidirectional-relation check in scripts/validate-metamodel.rb (around
lines 236–247) compares only artifact ids. It warns whenever A points at B
and B points at A, whatever the two relation types mean.
That makes every warning it currently emits a false positive, and it drowns the
real violations it exists to catch.
Evidence
Measured across all source front matter, excluding generated/:
|
|
| Bidirectional pairs reported |
38 |
| Type combination, all 38 |
documents / refines |
Pairs involving a depends_on or relates_to back-pointer |
0 |
depends_on relations anywhere in the source |
0 |
The 38 are the chapter pattern: a chapter documents its detail page and the
detail page refines the chapter. Two distinct semantic claims, each
legitimately outgoing. Nothing to remove.
The signal cost was observed directly while working on #86. A genuine
back-pointer was added temporarily to a risk whose ADR already declared
introduces_risk, exactly the relation relation-rules.md forbids. The
validator reported it as one more line among 38 identical-looking warnings,
then the probe was reverted. A reader scanning that output has no way to tell
the one that matters from the thirty-eight that do not.
The authoritative-outgoing-relations convention itself is fully held: there is
not one back-pointer in the repository. The check simply cannot show that.
What the check must distinguish
Not by relation type equality — that rule is wrong, and the repository already
contains the counterexample. The canonical case in
features/metamodel-validation.feature:88, bridged to
test/validate_metamodel_test.rb:430, is ADR addresses QS plus
QS depends_on ADR: two different types, and a violation. It must keep
warning.
| Shape |
Example |
Verdict |
| Generic back-pointer mirroring a directional relation |
ADR introduces_risk R + R depends_on ADR |
warn — this is the forbidden reciprocal |
| Same relation type in both directions |
A refines B + B refines A |
warn — contradictory or duplicated |
| Two distinct, semantically active claims |
DOC documents DOC' + DOC' refines DOC |
silent — both are legitimate outgoing relations |
relation-rules.md already names the discriminator: depends_on is for
"legitimate hierarchical or dependency relationships, not as a reciprocal for
other relation types". The check needs to encode that distinction; the exact
classification is the work of this issue, not a foregone conclusion.
Decisions to make
- Whether a confirmed back-pointer stays a warning or becomes an error. It is
currently a warning, and ./build.sh validate passes with all 38 present.
- Whether
relates_to counts as a back-pointer alongside depends_on.
- Whether the legitimate
documents / refines pair is recognised structurally
or by an explicit allowed-combination list.
Behaviour specification
This changes observable validator behaviour, so bdd-specification applies. The
existing scenario is kept and narrowed rather than deleted — it describes a case
that must still warn — and the silent case needs one of its own:
Bidirectional relation is detected stays, and its fixture stays the
back-pointer shape.
- A new scenario covers two distinct outgoing claims between the same pair and
asserts that no warning is emitted.
Both bridge into test/validate_metamodel_test.rb under the existing naming
convention.
Acceptance criteria
Relationship to other work
Origin
Found on 2026-09-04 while answering whether the authoritative-outgoing-relations
convention still holds after #92. It does — but the only automated check
defending it cannot demonstrate that, because its output is entirely false
positives.
Problem
The bidirectional-relation check in
scripts/validate-metamodel.rb(aroundlines 236–247) compares only artifact ids. It warns whenever
Apoints atBand
Bpoints atA, whatever the two relation types mean.That makes every warning it currently emits a false positive, and it drowns the
real violations it exists to catch.
Evidence
Measured across all source front matter, excluding
generated/:documents/refinesdepends_onorrelates_toback-pointerdepends_onrelations anywhere in the sourceThe 38 are the chapter pattern: a chapter
documentsits detail page and thedetail page
refinesthe chapter. Two distinct semantic claims, eachlegitimately outgoing. Nothing to remove.
The signal cost was observed directly while working on #86. A genuine
back-pointer was added temporarily to a risk whose ADR already declared
introduces_risk, exactly the relationrelation-rules.mdforbids. Thevalidator reported it as one more line among 38 identical-looking warnings,
then the probe was reverted. A reader scanning that output has no way to tell
the one that matters from the thirty-eight that do not.
The authoritative-outgoing-relations convention itself is fully held: there is
not one back-pointer in the repository. The check simply cannot show that.
What the check must distinguish
Not by relation type equality — that rule is wrong, and the repository already
contains the counterexample. The canonical case in
features/metamodel-validation.feature:88, bridged totest/validate_metamodel_test.rb:430, isADR addresses QSplusQS depends_on ADR: two different types, and a violation. It must keepwarning.
ADR introduces_risk R+R depends_on ADRA refines B+B refines ADOC documents DOC'+DOC' refines DOCrelation-rules.mdalready names the discriminator:depends_onis for"legitimate hierarchical or dependency relationships, not as a reciprocal for
other relation types". The check needs to encode that distinction; the exact
classification is the work of this issue, not a foregone conclusion.
Decisions to make
currently a warning, and
./build.sh validatepasses with all 38 present.relates_tocounts as a back-pointer alongsidedepends_on.documents/refinespair is recognised structurallyor by an explicit allowed-combination list.
Behaviour specification
This changes observable validator behaviour, so
bdd-specificationapplies. Theexisting scenario is kept and narrowed rather than deleted — it describes a case
that must still warn — and the silent case needs one of its own:
Bidirectional relation is detectedstays, and its fixture stays theback-pointer shape.
asserts that no warning is emitted.
Both bridge into
test/validate_metamodel_test.rbunder the existing namingconvention.
Acceptance criteria
are not reported.
./build.sh validatereports 0 bidirectional warnings for the currentsource tree, down from 38.
a second source; it references
relation-rules.mdand the metamodel.reaching for an ADR.
Relationship to other work
This issue makes the check able to catch it when they do. Neither replaces the
other: one prevents, one detects.
the class of error at the source. This is the smaller, immediately useful
step and does not depend on it.
Origin
Found on 2026-09-04 while answering whether the authoritative-outgoing-relations
convention still holds after #92. It does — but the only automated check
defending it cannot demonstrate that, because its output is entirely false
positives.