Funktionsweise
loam.dev liest ein Dart/Flutter-Projekt semantisch und über die ganze Codebase — nicht Datei für Datei — und macht daraus eine deterministische Pass/Fail-Entscheidung in CI. Eine Baseline duldet den vorhandenen Code, sodass nur neu hinzugekommene Probleme den Build blockieren. Das geschieht in einer sechsstufigen Pipeline, ohne Raten, ohne Regex-Abkürzungen, ohne False Positives aus generiertem Code.
Die Pipeline
Der ProjectLoader speist das gesamte Projekt in
Darts eigene AnalysisContextCollection.
Jede Datei wird geparst und vollständig in ein typisiertes
Element-Model aufgelöst — dasselbe Model, das das Dart-SDK intern
verwendet. Das ist die Grundlage, die alles Nachgelagerte semantisch
statt syntaktisch macht.
Eine Menge von Rules läuft über das aufgelöste Model. Jede Rule ist eine eigenständige Analyseeinheit hinter einem gemeinsamen Interface; eine neue Fähigkeit hinzuzufügen ändert die Pipeline nicht — nur die Rule-Liste. Rules sind entweder deterministisch (reines AST/Element-Model, reproduzierbar ohne Netz) oder LLM-gestützt (geplant, immer verdict-gecacht, bevor sie CI berühren).
Jede Rule emittiert Findings — strukturierte
Ergebnisse der Form
{ ruleId, severity, location, message, fingerprint }.
Jedes Finding trägt einen stabilen Fingerprint
(einen positionsrobusten Hash), der Umformatierungen und
Zeilennummernverschiebungen überlebt. Das ist der Schlüssel,
der den Baseline-Diff exakt macht.
Die Baseline ist ein eingefrorener Stand
akzeptierter Findings, gespeichert in baseline.json.
Beim Onboarding eines bestehenden Projekts führt man einen
Vollaudit durch, bereinigt, was möglich ist, und friert den Rest
mit loam baseline --write ein. Ab diesem Punkt
interessiert das Gate nur noch neue Findings — nicht die
bereits bekannten.
Das Gate ist der CI-Entscheidungspunkt: Es vergleicht aktuelle Findings mit der Baseline und gibt einen strukturierten Exit-Code zurück. Keine Regression → Exit 0. Neues Finding → Exit 1. Keine Überraschungen beim Merge.
Der Reporter formatiert Ergebnisse für Menschen
oder Maschinen: human für die Terminal-Ausgabe,
json / sarif / markdown
für Agent-Pipelines und html für einen interaktiven,
in sich geschlossenen Report mit eingebautem Fix-Prompt-Builder.
loam scan vs. loam gate
Zwei Befehle, zwei Zwecke. Wer den richtigen kennt, hält den Workflow sauber.
loam scan
Vollaudit · baseline-unabhängig
Führt alle aktiven Rules über das gesamte Projekt aus und zeigt
alle Findings — unabhängig vom Stand der Baseline.
Dieser Befehl eignet sich beim erstmaligen Einsatz in einem
bestehenden Repo: Gesamtbild sehen, bereinigen, was vertretbar ist,
dann den Rest mit loam baseline --write einfrieren.
loam gate
CI-Ratchet · Baseline-bewusst
Der Alltagsbefehl für CI. Standardmäßig läuft er im
Ratchet-Modus: Nur Findings, die neu
sind (nicht in der Baseline), lassen den Build scheitern.
Der Score kann sich nur verbessern — die Codebasis ratchet
vorwärts, nie rückwärts. Für Greenfield-Projekte oder Pipelines,
die null Findings erzwingen, gibt es --absolute als
harte Schwelle.
Typische Onboarding-Sequenz: loam scan → relevante
Findings bereinigen → loam baseline --write →
loam gate ab sofort in CI.
Semantik vor Syntax
Die wichtigste Architekturentscheidung in loam.dev ist, worüber die Analyse läuft.
dart analyze meldet Typfehler, fehlende Imports und
Style-Lint-Verletzungen — alles, was sich auf Deklarationsebene erkennen
lässt. Was es nicht tut: über den Referenzgraphen des gesamten
Projekts nachdenken — welche öffentlichen API-Member tatsächlich von
außen erreichbar sind, welche Schichtgrenzen überschritten werden,
welche Hilfsfunktionen als Duplikat vorliegen.
Diese Dinge per Regex oder String-Matching zu finden ist fragil. Ein Regex, der einen Bezeichnernamen sucht, kann nicht zwischen Definition und Referenz unterscheiden, nicht zwischen einer lokalen Variable und einem öffentlichen Export, nicht zwischen einer generierten Datei und handgeschriebenem Code.
loam.dev arbeitet auf dem aufgelösten Element-Model: Jedes Symbol ist ein typisiertes Element mit kanonischer Identität, jede Referenz ist ein aufgelöster Zeiger. Rules operieren auf diesem Model — nicht auf Rohtext. Das bedeutet konkret:
- Keine False Positives durch zufällig ähnliche Namen.
- Generierte Dateien werden automatisch ausgeschlossen — so wie das SDK sie kennt.
- Das Umbenennen einer Klasse ist für die Rule unsichtbar — entscheidend ist, ob das Element referenziert wird, nicht wie es heißt.
- Bibliotheks- und paketübergreifende Referenzen werden korrekt nachverfolgt.
Das ist „Semantik vor Syntax" in der Praxis: Rules sehen den Code so, wie der Compiler ihn sieht — nicht wie ein Texteditor.
Mehr in die Tiefe
Der Developer Guide
(englisch) enthält die vollständige CLI-Referenz, Output-Formate,
Konfiguration und ausgearbeitete Beispiele — darunter die vollständige
Baseline-Onboarding-Sequenz und die Integration von
loam gate in GitHub Actions.