codecharter req coverage und das
MCP-Tool req_coverage
melden ein Coverage-Modell aus beiden Richtungen der Traceability,
berechnet von einem einzigen gemeinsamen Aggregator, damit die beiden
Oberflächen bei keiner Zahl auseinanderlaufen können:
- Requirement → Code: Hält die Evidenz jedes Requirements gerade noch?
- Code → Requirement: Welche Methoden tragen überhaupt keine
[Satisfies]-Rechtfertigung, und warum?
Beide Hälften nutzen dieselben Evaluatoren und Indizes, die schon Requirements und Anforderungen über MCP beschreiben, kein Criterion, Verdict oder Justification-Walk wird für diesen Report neu implementiert.
Requirement → Code: Rollups
Jedes Criterion eines Requirements wird genau einmal ausgewertet, mit
demselben Pin-Evaluator, den req check --with-code und req_verify
verwenden, und dann zu einem einzigen Worst-of-Verdict aufgerollt, nach
dieser festen Rangfolge:
| Rang | Verdict | Warum dieser Rang |
|---|---|---|
| 5 (schlechtest) | violated |
Nachweislich gebrochen gegen den aktuellen Code. |
| 4 | stale |
Eine verfallene positive Aussage: hat einmal gehalten, die gepinnte Evidenz hat sich seitdem geändert, in einer alten Traceability-Matrix aktiv irreführend, deshalb rangiert es vor einer ehrlichen Nicht-Antwort. |
| 3 | unknown |
Auswertung konnte sich nicht entscheiden. Eine ehrliche Nicht-Antwort, kein Zeichen für Zustand oder Verfall. |
| 2 | open |
Eine deklarierte Lücke, das am wenigsten alarmierende nicht-grüne Ergebnis, weil bewusst statt entdeckt. |
| 1 (best) | holds |
Jedes Criterion hält. |
Nie unverdient grün. Ein Requirement ohne Criteria rollt nicht zu
holds auf, es rollt zu open mit einem unbound-Grund von
no-criteria auf. Dasselbe gilt für null Anchors (no-anchors) und
Anchors, die alle nicht aufgelöst werden konnten (anchors-unresolved).
Dasselbe Prinzip "nie Holds melden, ohne geprüft zu haben", das die
Criterion-Evaluatoren schon anwenden, nur auf Requirement-Ebene erweitert.
Der Frontmatter-Key status fließt nie in das Rollup ein, er wird
daneben gemeldet. Ein Requirement, dessen deklarierter Status vom Rollup
abweicht (status: implemented bei einem Rollup ungleich holds, oder
status: open bei einem Rollup von holds), wird als Status-Divergenz
gemeldet: zur Sichtbarkeit, nie um das Rollup zu ändern.
Code → Requirement: klassifizierte Methoden
Jede Methode im analysierten Codemodell fällt in genau einen von drei disjunkten Buckets, gemeldet als zwei Kopfzahlen, die nie zu einer einzigen mehrdeutigen Zahl zusammengeführt werden:
justified: von mindestens einem Justification-Walk eines Nicht-Infrastructure-Requirements erreicht.infrastructure-declared: siehe Infrastructure-Exemption.- unjustified, weiter unterteilt in:
dead-candidate: kein Caller ist für den Justification-Walk sichtbar, und die Methode ist auch kein Dispatch-Ziel (weder eine Interface-Implementierung noch ein Override noch eine Implementing-Method erreicht sie). Nie als bewiesen ungenutzt zu verstehen, nur als nicht erreichbar über die sichtbaren Kanten dieses Walks (keine Reflection, kein Delegate-Aufruf, kein dynamischer Dispatch wird verfolgt). Der Report sagt immer "candidate", nie "dead" allein.undocumented-requirement-candidate: hat sichtbare Caller, aber kein Anchor erreicht sie über seinen Justification-Walk. Trägt einennearest_anchored_neighbour: die nächste justified Method, gefunden durch eine begrenzte Rückwärtssuche über Caller und Dispatch-Vorgänger (auf vier Hops gedeckelt), zusammen mit den Requirement-Ids, die sie rechtfertigen,null, wenn die Grenze erreicht wird, nie eine Vermutung. Genau dieser Hinweis speistreq_draftbeim Entwurf eines neuen Requirements.
justified_percent = justified / total_methods;
accounted_percent = (justified + infrastructure_declared) / total_methods.
Hat der Corpus überhaupt keine [Satisfies]-Anchors, melden beide
Prozentwerte 0 mit einem expliziten Hinweis, und es wird keine
klassifizierte Liste erzeugt, ein Repository, das Anchoring noch nicht
eingeführt hat, soll beim ersten Lauf keine zehntausend "dead candidates"
bekommen.
Infrastructure-Exemption
Ein Requirement mit kind: infrastructure befreit genau die Methoden, die
seine [Satisfies]-Anchors direkt benennen, ein Method-Anchor befreit
diese Method, ein Type-Anchor befreit alle Methods dieses Typs. Diese
Befreiung ist deklariert, nie erraten: Es gibt keine Heuristik, die von
sich aus ableitet, "das sieht nach Glue-Code aus", eine fälschlich als
Infrastructure eingestufte Fachlogik ist deshalb immer eine sichtbare
Attribut-Änderung in einem Diff, nie eine stille Klassifikator-Entscheidung.
Sie ist außerdem bewusst nicht transitiv. Ein Anchor auf Program oder
eine DI-Registrierungsklasse befreit nicht alles, was diese Methoden
aufrufen, nur die direkt verankerten Deklarationen selbst. Eine transitive
Befreiung würde ein Attribut am Composition-Root die halbe Solution
befreien lassen, genau die Art Fehletikettierung, die dieser Report
sichtbar halten soll. Erreicht der Justification-Walk eines
Nicht-Infrastructure-Requirements eine Methode, die auch direkt als
Infrastructure befreit ist, gibt der Report einen Info-Hinweis
req-infrastructure-overreach aus, ein Signal, dass das
Infrastructure-Label möglicherweise auf Code angewendet wurde, von dem
Fachlogik tatsächlich abhängt.
Diff-Scope
--git-ref/git_ref (oder rohes diff-Text über MCP) schränkt die
klassifizierte Methoden- und die Rollup-Liste auf das ein, was der Diff
berührt. Jede Summe und jeder Prozentwert, einschließlich
justified_percent und accounted_percent, bleibt corpus-weit, nur die
beiden Listen schrumpfen, mit einem scope: diff-Hinweis, der das benennt.
Dasselbe Muster wie graph_hubss eigenes scope-Feld: eine
diff-eingeschränkte Ansicht verengt nie still, was eine Summenzahl zu
umfassen behauptet.
Das Tolerated-Legacy-Gate
Coverage meldet eine Zahl ab dem Moment der Einführung, selbst auf einer
großen, nur teilweise verankerten Codebase, aber diese Zahl muss nicht erst
100% sein, um nützlich zu sein, und sie soll auch nicht still zurückfallen.
req coverage liefert deshalb ein Einweg-Ratchet, dasselbe Muster wie
die Findings-Baseline:
codecharter req coverage --write-baselineschreibt einmalig die Identität jeder aktuell unjustified Method (Roslyn-Deklarations-Id, mit Fallback auf einen Graph-Key, wenn der Compiler keine erzeugt hat, ein Rename macht einen Fallback-Eintrag absichtlich ungültig) nach.codecharter/req/coverage-baseline.json.- Ab dann rendert eine in der Baseline stehende Method als
tolerated; eine neue unjustified Method, eine, die nicht in der Baseline steht, rendert alsnewund beendet den Lauf, sobald das Gate aktiv ist, mit einem Fehler-Exit. - Das Gate selbst (Exit ungleich 0 bei jeder
new-Method) ist Opt-in, hinterreq.coverage.gate.enabled: truein.codecharter/config.yml(Defaultfalse). Die Mechanik und der fehlschlagende Exit-Code existieren und sind getestet, unabhängig davon, ob ein Repository bereits opted-in hat.
Als Infrastructure deklarierte Methoden sind bewusst befreit und zählen nie
als Baseline-Schuld, nur dead-candidate- und
undocumented-requirement-candidate-Methoden werden verfolgt. Die Baseline
weitet sich nie von selbst aus: Nur --write-baseline schreibt sie, und
wiederholtes Schreiben derselben Menge ist bytegenau identisch (sortiert,
dedupliziert, ohne Zeitstempel).
Zuerst Report, dann Gate: ein Goodhart-Hinweis
Attributes are diff-visible and review-carried; the metric is not a target until the gate exists.
[Satisfies]-Anchors sind etwas, das ein Reviewer in einem Pull-Request-Diff
sieht wie jede andere Code-Änderung auch, keine Punktzahl, die ein Tool
still im Hintergrund ableitet. Coverage existiert, um eine ehrliche Zahl
sichtbar zu machen und ihr stilles Zurückfallen zu verhindern, sobald das
gewünscht ist, nicht um selbst zum verfolgten Ziel zu werden. Direkt auf die
Prozentzahl zu optimieren, etwa Requirements wahllos an Code zu ankern, nur
um justified_percent zu erhöhen, verfehlt den Zweck: Die Zahl ist nur so
vertrauenswürdig wie die Anchors, die ein Mensch tatsächlich reviewt hat.