Cross-Project Sibling Detection with Annotate-Only Output¶
Problem¶
When a spec is authored in isolation, two failure modes appear at scale: overlap (the new spec touches the same surface as an existing one — a bundling opportunity missed) and contradiction (the new spec defines a behavior another spec already defined differently — an implementation divergence waiting to happen). The motivating incident: a developer built a ticket detail divergently because he had seen a different intent in another ticket, with no early signal that "das beißt sich hier gerade." It reproduced in our own workflow — a spec was authored that duplicated an existing one, caught only by luck.
Pattern¶
At spec-creation time, run a hybrid rule → LLM sibling-scan and annotate the source (the new spec) — never mutate the sibling. Three properties make it safe and useful:
-
Deterministic rule pre-filter, LLM classifier. A dependency-free scorer ranks existing specs by toolkit-native structural signals and emits a small top-K; the LLM reads only those K and classifies each
overlap|contradiction. The rule half is unit-testable with zero LLM context; the LLM half never sees the whole corpus. (Mirrors [[annotate-only-cross-cutting-features]] / SPEC-069.) -
Signals that actually exist in the corpus. Do not lift signals from another domain. In a prose spec corpus, "shared data models / endpoints" fire on almost nothing; the live signals are shared
depends_on, explicitSPEC-NNNmentions, shared skill-name mentions, tag intersection, and title-token similarity. Weight structural signals above lexical ones. -
Two-tier anti-wallpaper emission gate. Emit a candidate only if it has ≥1 structural signal OR ≥2 soft (categorical/lexical) signals. A single incidental token overlap must never surface — otherwise the annotations become ignorable wallpaper and authors reflexively delete the section. Ship the gate with a calibration fixture set + explicit false-positive budget ([[heuristic-calibration-gate]] / SPEC-127): a near-duplicate positive, a structural-only match, and a semantic-only near-miss that must not surface — pinning the thresholds from both sides.
-
Annotate-only, soft, human-decides. The new spec gets a
## Sibling-Candidatessection (ID | Title | Severity | Begründung) only after an interactive, non-blocking prune (accept / edit / drop). The sibling spec is byte-identical to what it would be with the feature off. Never a hard block — a conflict is a suggestion.
When to apply¶
- Relationship detection has any non-trivial false-positive rate, AND
- The downstream effect (bundling / closing / diverging) is a human judgment call, AND
- The corpus is prose (specs, tickets, docs) where structural cross-references exist.
When NOT to apply¶
- The relationship is structurally certain (a foreign key) — just link it.
- The corpus is small enough to eyeball, or matches are needed with high semantic recall on un-cross-referenced text — that needs an embedding index, deliberately out of scope here.
Known limitation (state it explicitly)¶
This catches overlap and structural contradiction. A purely semantic divergence — two specs
describing the same behavior differently with no shared dep, SPEC-ID, skill mention, or
near-identical title — falls below the gate and is not surfaced. Therefore an empty
## Sibling-Candidates section is not a "no conflict" guarantee. Say so in the output and the
docs so an empty result is never read as false assurance. Closing this gap requires an embedding
index (a future increment).
Evidence¶
- Rule engine + calibration:
plugins/core-standards/skills/spec-from-requirements/scripts/sibling_scan.py,plugins/core-standards/tests/test_sibling_scan.py(SPEC-127-style FP-budget gate). - Integration:
spec-from-requirementsSKILL Steps 3c / 4a / 5 / 6. - Source decision:
project_annotate_only_cross_cuttingmemory; SPEC-132decisions.md/research.md.