|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | # GENERIERT aus personal/tools-ref/traceability/ — nicht hier editieren; Aenderungen gehoeren nach ~/.claude/tools-ref/traceability/. |
3 | 3 | # source: personal-provider-ref |
4 | | -# ref-hash: sha256:e76cfbc5dcf2fb98d5403fbe8552b57c92abcdd461245c305280e08ca30c8843 |
| 4 | +# ref-hash: sha256:a2541ffdcf3e5f58f8644626143ebe99d85fe1dc5b3d7900c597667b84f3b757 |
5 | 5 | """ |
6 | 6 | traceability.py — prueft die Verkettung zwischen Anforderungen und Tests. |
7 | 7 |
|
@@ -159,20 +159,32 @@ def read_spec(path: Path, is_delta: bool): |
159 | 159 |
|
160 | 160 |
|
161 | 161 | def collect_requirements(spec_root: Path): |
162 | | - """Ist-Spec zuerst, Delta-Specs ueberschreiben — so gewinnt der laufende Change.""" |
| 162 | + """Ist-Spec zuerst, Delta-Specs ueberschreiben — so gewinnt der laufende Change. |
| 163 | +
|
| 164 | + Definieren mehrere offene Delta-Specs denselben Kurznamen, gewinnt die |
| 165 | + hoechste Revision, nicht die zuletzt in Sortierreihenfolge gelesene Datei. |
| 166 | + """ |
163 | 167 | definitions, duplicates = {}, [] |
164 | 168 | removed, tombstones = {}, {} |
| 169 | + delta_defined = set() |
165 | 170 |
|
166 | 171 | def record(defs, source_is_delta): |
167 | 172 | for a in defs: |
168 | 173 | previous = definitions.get(a.base) |
169 | | - if previous is not None and not source_is_delta: |
170 | | - duplicates.append(Finding( |
171 | | - "doppelter Kurzname", |
172 | | - f"req~{a.base} steht auch in {previous.file}:{previous.line}", |
173 | | - a.file, a.line)) |
174 | | - continue |
| 174 | + if previous is not None: |
| 175 | + if not source_is_delta: |
| 176 | + duplicates.append(Finding( |
| 177 | + "doppelter Kurzname", |
| 178 | + f"req~{a.base} steht auch in {previous.file}:{previous.line}", |
| 179 | + a.file, a.line)) |
| 180 | + continue |
| 181 | + # Revisionen werden der Reihe nach vergeben, die hoehere ist |
| 182 | + # immer das spaetere Wort — Dateireihenfolge entscheidet nicht. |
| 183 | + if a.base in delta_defined and a.revision < previous.revision: |
| 184 | + continue |
175 | 185 | definitions[a.base] = a |
| 186 | + if source_is_delta: |
| 187 | + delta_defined.add(a.base) |
176 | 188 |
|
177 | 189 | specs_dir = spec_root / "specs" |
178 | 190 | if specs_dir.is_dir(): |
@@ -565,6 +577,20 @@ def self_check(): |
565 | 577 | "a_test.go": "// [impl->req~zugang.ganz-neu~1]\n", |
566 | 578 | }, []), |
567 | 579 |
|
| 580 | + # Zwei offene Delta-Specs auf demselben Kurznamen: "b" sortiert vor "g", |
| 581 | + # die niedrigere Revision wird also NACH der hoeheren gelesen. Gewaenne |
| 582 | + # die zuletzt gelesene Datei (der fruehere Fehler), stuende die Anforderung |
| 583 | + # auf ~2 und der Verweis auf ~3 gaelte faelschlich als vorgegriffen. |
| 584 | + "delta ueberholt delta: hoehere revision gewinnt trotz dateireihenfolge": ({ |
| 585 | + "docs/changes/2026-09-21-bot-name-patterns/specs/pulls/spec.md": |
| 586 | + "## MODIFIED Requirements\n\n### Requirement: Bot-Filter\n" |
| 587 | + "`req~pulls.bot-filter~3`\n", |
| 588 | + "docs/changes/2026-09-21-gitea-bot-logins/specs/pulls/spec.md": |
| 589 | + "## MODIFIED Requirements\n\n### Requirement: Bot-Filter\n" |
| 590 | + "`req~pulls.bot-filter~2`\n", |
| 591 | + "a_test.go": "// [impl->req~pulls.bot-filter~3]\n", |
| 592 | + }, []), |
| 593 | + |
568 | 594 | "doppelter kurzname": ({ |
569 | 595 | "docs/specs/zugang/spec.md": |
570 | 596 | "### Requirement: Eins\n`req~zugang.doppelt~1`\n" |
|
0 commit comments