loam.dev
v0.1.4 · preview EN

Rules

loam.dev bietet sechs Analyse-Fähigkeiten, alle hinter einem stabilen Rule-Interface. Eine neue Rule hinzuzufügen ändert die Pipeline nicht — nur die Rule-Liste. Eine Rule ist heute live, fünf sind geplant und erscheinen als Post-MVP-Iterationen. Der Status ist immer ehrlich angegeben.

Live

unused-public-exports live

Ungenutzte öffentliche Exports

Findet öffentliche API-Member — Klassen, Methoden, Getter/Setter, Felder, Enums, Typedefs — ohne Referenz im gesamten Projekt. Die Analyse läuft auf dem aufgelösten Element-Model, nicht per Regex. Code-Generator-Eingaben (Drift, freezed, Riverpod, json_serializable) werden automatisch ausgeschlossen, ohne jede Konfiguration.

Slop (schlecht)
// öffentlich, aber nirgendwo verwendet
class OldHelper {
  static String format(String s) => s.trim();
}

// ebenfalls unreferenziert — wird gemeldet
enum LegacyStatus { active, archived }
sauber
// entweder entfernen, privat machen oder
// mit Begründung unterdrücken:
// loam-ignore: unused-public-exports – re-exported via barrel

// genutzt und behalten:
class ActiveHelper {
  static String format(String s) => s.trim();
}

Mehr in die Tiefe

Der Developer Guide (englisch) enthält die vollständige CLI-Referenz, Konfigurationsoptionen (Rule-Toggles, Pfad-Suppression, Inline-// loam-ignore:-Direktiven) und die vollständige Baseline-Onboarding-Sequenz — einschließlich der Integration von loam gate in GitHub Actions.

Die automatische Unterdrückung für Code-Generator-Eingaben ist im Guide unter „Automatic codegen-input suppression" beschrieben — die drei Erkennungssignale (Base-Type-Registry, Annotation-Registry, struktureller Fallback) funktionieren ohne loam.yaml-Konfiguration.

Geplant

Diese fünf Rules stehen auf der Post-MVP-Roadmap. Jede ist ein eigenständiges Plugin hinter demselben Rule-Interface — die Pipeline ändert sich beim Erscheinen nicht.

circular-dependencies geplant

Zirkuläre Dependencies

Erkennt Import-Zyklen zwischen Dart-Bibliotheken. Zyklen machen die Kompilierungsreihenfolge mehrdeutig, verhindern Tree-Shaking und sind ein häufiges Zeichen unklarer Schichtverantwortlichkeiten.

Slop (schlecht)
// lib/a.dart
import 'b.dart';

// lib/b.dart
import 'a.dart'; // ← Zyklus: a → b → a
sauber
// Gemeinsame Typen in eine dritte Bibliothek auslagern:
// lib/shared.dart  (importiert weder a noch b)
// lib/a.dart  importiert shared.dart
// lib/b.dart  importiert shared.dart
code-duplicates geplant

Code-Duplikate

Findet strukturell ähnliche Code-Blöcke per AST-normalisiertem Token-Hashing. Markiert Hilfsfunktionen, die kopiert statt extrahiert wurden — ein klassischer KI-Agent-Nebeneffekt.

Slop (schlecht)
// feature_a/utils.dart
String formatDate(DateTime d) =>
    '${d.day}.${d.month}.${d.year}';

// feature_b/helpers.dart  ← Beinahe-Duplikat
String renderDate(DateTime d) =>
    '${d.day}.${d.month}.${d.year}';
sauber
// lib/shared/date_format.dart
String formatDate(DateTime d) =>
    '${d.day}.${d.month}.${d.year}';

// beide Features importieren den gemeinsamen Helper
complexity-hotspots geplant

Komplexitäts-Hotspots

Misst zyklomatische und kognitive Komplexität je Funktion/Methode und aggregiert sie zu einem Projekt-Health-Score. Markiert Funktionen, die zu komplex zum Review oder Testen sind.

Slop (schlecht)
// kognitive Komplexität ≫ Schwellwert
Future<void> syncData(List<Item> items) async {
  for (final item in items) {
    if (item.isValid) {
      if (item.needsSync) {
        try {
          if (await remote.has(item.id)) {
            await remote.update(item);
          } else { await remote.create(item); }
        } catch (e) { /* geschluckt */ }
      }
    }
  }
}
sauber
// aufgeteilt in fokussierte, testbare Helpers
Future<void> syncData(List<Item> items) async {
  final candidates = items.where(_needsSync);
  await Future.wait(candidates.map(_syncOne));
}

Future<void> _syncOne(Item item) async { … }
architecture-boundaries geplant

Architektur-Grenzen

Erkennt das Layer-Layout automatisch (core/, features/, services/, …) und meldet Imports, die verbotene Grenzen überschreiten. Zero-Config: das abgeleitete Regelset wird transparent herausgeschrieben und kann eingefroren oder verfeinert werden.

Slop (schlecht)
// features/profile/profile_page.dart
// ↓ Features-Schicht greift in Services-Interna
import '../../services/db/internal_schema.dart';
sauber
// services exponiert stattdessen ein öffentliches API:
// services/user_repository.dart (öffentliches Interface)
// features/profile/profile_page.dart
import '../../services/user_repository.dart';
anti-ai-slop geplant

Anti-AI-Slop

Erkennt KI-typische Qualitätsmängel: leere catch-Blöcke, narrative Füll-Kommentare, grundlose // ignore:-Direktiven, tote Guard-Clauses und halluzinierte Abstraktionen. Deterministische AST-Muster laufen zuerst; ein optionaler LLM-Layer erkennt Slop auf Intentionsebene — über einen Verdict-Cache deterministisch und kostenarm.

Slop (schlecht)
// leerer catch — klassischer Slop
try {
  await fetchData();
} catch (e) {} // ← schluckt alle Fehler

// narrativer Füll-Kommentar
/// Diese Methode verarbeitet die Daten, indem sie
/// über alle Elemente iteriert und die Transformation
/// anwendet.
List<String> process(List<String> items) =>
    items.map((i) => i.toUpperCase()).toList();
sauber
// mit Kontext behandeln oder weiterwerfen
try {
  await fetchData();
} catch (e, st) {
  log.error('fetchData failed', e, st);
  rethrow;
}

// Doku-Kommentar mit Mehrwert, nicht Rauschen
/// Gibt [items] in Großbuchstaben zurück.
List<String> process(List<String> items) =>
    items.map((i) => i.toUpperCase()).toList();

Mehr in die Tiefe

Der Developer Guide (englisch) enthält die vollständige CLI-Referenz, Output-Formate, Konfiguration und ausgearbeitete Beispiele — einschließlich der Integration von loam gate in GitHub Actions.