Suche/Eine Plugin-Datenpipeline über zwei Laufzeitumgebungen ändern, ohne sie kaputtzumachen

Eine Plugin-Datenpipeline über zwei Laufzeitumgebungen ändern, ohne sie kaputtzumachen

Für Coding-Agenten in einem Plugin-Backend über zwei Laufzeitumgebungen. Hält Versionen in einer Datei, setzt Testfilter auf den richtigen Task, folgt den Regeln für Plugins, Nebenläufigkeit und Service-Schichten und prüft, dass jeder neue Test ohne seine Änderung fehlschlägt.

Diese KI-Regeln verwenden
## Versionen in einer Datei halten und immer vom selben Wurzelverzeichnis aus bauen Halte Versionsnummern und Toolchain-Koordinaten kanonisch, und baue immer vom selben Ort aus. - Speichere jede Versionsnummer und jede Toolchain-Koordinate in einer einzigen kanonischen Datei; alle Abnehmer (Build-Skripte, Hilfsprogramme, Tests, Paketbeschreibungen) lesen daraus, und du aktualisierst ausschließlich diese Datei. - Wenn ein Wert absichtlich fest codiert oder an mehreren Stellen dupliziert ist, etwa ein Pfadsegment für die Laufzeitkompatibilität, behandle das nicht als Fehler und berechne es nicht dynamisch; ermittle den aktuellen Wert an der kanonischen Stelle und übernimm ihn genau so. - Führe alle Build- und Testbefehle von einem einheitlichen Wurzelverzeichnis aus, damit sich relative Verknüpfungen jedes Mal identisch auflösen. - Bevorzuge eine eingebettete, fest versionierte Toolchain, die durch einen Bootstrap-Schritt bereitgestellt wird, gegenüber einer global installierten Sprach- oder Versionsverwaltung, damit jede Umgebung auf dieselbe Weise baut. ## Testfilter auf den Task setzen, dem die Tests wirklich gehören Verstehe, wie die Test-Tasks miteinander verdrahtet sind, bevor du eine gefilterte Teilmenge ausführst. - Ein übergeordneter Test-Task schließt oft selbst jede Klasse aus und delegiert nur an Unter-Tasks, sodass ein darauf angewendeter Namensfilter stillschweigend nichts trifft und wie ein Erfolg aussieht. Wende Filter auf den konkreten Task an, dem die Tests tatsächlich gehören. - Suiten werden absichtlich aufgeteilt: Manche Tests werden aus ihrer naheliegenden Suite herausgenommen, zum Beispiel laufen Tests, die eine zweite Laufzeitumgebung starten, in der Suite, die diese Laufzeitumgebung bereitstellt. Findet ein Filter nichts, liegt das Ziel höchstwahrscheinlich in der anderen Suite. - Wisse, wo Unit-, Integrations- und Abnahmetests jeweils liegen, damit du den richtigen Einstiegspunkt aufrufst. ## Code so umbauen, dass ein neuer Fall wie von Anfang an geplant wirkt Wenn du Code änderst, hänge nicht einfach etwas an das Bestehende an. Prüfe, ob der umgebende Code mit deiner Änderung noch sinnvoll ist. Wurde eine Abstraktion für einen Fall entworfen und du fügst einen zweiten hinzu, strukturiere so um, dass das Ergebnis liest, als wären von Anfang an beide Fälle vorgesehen gewesen. Gib jedem eigenständigen Konzept eine eigene, klar benannte Einheit, und setze diese Einheiten in übergeordnetem Code zusammen, damit die Absicht offensichtlich ist, ohne jede Implementierung lesen zu müssen. ## Plugins auf den Basisklassen und der Registry des Frameworks aufbauen Baue Erweiterungen auf den eigenen Erweiterungspunkten des Frameworks auf, nicht daran vorbei. - Erbe von der typspezifischen Basisklasse und überschreibe deren Lifecycle-Hooks: einen Setup- oder Register-Hook sowie den Einstiegspunkt des jeweiligen Typs. - Deklariere Konfiguration über die deklarative Options-DSL des Frameworks, statt Rohparameter direkt auszulesen, damit Validierung, Typumwandlung und Dokumentationserzeugung automatisch übernommen werden. - Lege die Nebenläufigkeit auf Klassenebene fest: shared, wenn mehrere Worker-Threads die Instanz gleichzeitig aufrufen dürfen, single, wenn Aufrufe serialisiert werden müssen, sodass immer nur ein Thread gleichzeitig läuft. - Leite jede Plugin-Suche über die zentrale Registry, registriere neue eingebaute Plugins dort, und lass externe Plugins als gepackte Einheiten entdeckt werden. ## Metriken und gemeinsame Logik in einen Delegator legen, der jedes Plugin umhüllt Halte Querschnittsbelange in einer umschließenden Schicht und beziehe Kollaborateure vom Framework. - Betreibe jedes Plugin hinter einem Delegator, der Querschnittsbelange wie Metriken und Thread-Sicherheit hinzufügt; entscheide bei Verhaltensänderungen, ob sie in den Delegator oder in das Plugin selbst gehören. - Lass Metriken einer Namespace-Hierarchie folgen und erzeuge Kindelemente über einen Namespace-Aufruf. - Stelle eine Null-Object-Metrik bereit, die dieselbe Schnittstelle implementiert, aber Daten verwirft, für den Fall, dass Metriken deaktiviert sind oder getestet wird. - Instanziiere Metrikobjekte niemals direkt; nimm sie vom Framework entgegen. Standard-Mixins wie Logging und Konfiguration werden automatisch eingebunden, während schwergewichtigere Fähigkeiten optional bleiben. ## Die Worker-Schleife schnell halten und in der richtigen Reihenfolge starten und stoppen Schütze den Hot Path und respektiere die Start- und Stopp-Reihenfolge. - Behandle die Worker-Schleife als Hot Path: Sie holt Batches ab, führt sie durch die kompilierte Pipeline und signalisiert den Abschluss. Füge darin keine blockierenden Aufrufe oder aufwendigen Allokationen hinzu. - Halte die strikte Lifecycle-Reihenfolge ein: Zuerst registrieren sich Outputs und Filter, dann starten die Worker, dann starten zuletzt die Inputs; beim Herunterfahren stoppen zuerst die Inputs, die Worker laufen leer, dann schließen Filter und Outputs. Ein Verstoß gegen diese Reihenfolge führt zu Datenverlust oder einem Deadlock. - Verwende atomare Primitive für threadübergreifend geteilten Zustand statt einfacher boolescher Felder, da die Speichersichtbarkeit über Threads hinweg sonst nicht garantiert ist. ## Die Pipeline-Konfiguration in Code kompilieren und zum Debuggen ausgeben Manche Engines kompilieren deklarative Konfiguration zur Laufzeit über eine gestufte Pipeline in ausführbare Klassen: Der Konfigurationstext wird mit einer Grammatik zu einem Syntaxbaum geparst; dieser Baum wird in eine Zwischenrepräsentation überführt, einen gerichteten azyklischen Graphen aus Knoten und Bedingungen; für jeden Knoten wird Quellcode erzeugt und zur Laufzeit innerhalb eines eigenen Namespace zu Klassen kompiliert; anschließend werden die kompilierten Einheiten in die Ausführungsschleife eingebunden. Bedingungen werden zu Prädikatfunktionen kompiliert, die mit Verzweigungsknoten verdrahtet werden. Stelle ein Debug-Flag bereit, das den generierten Quellcode in ein Verzeichnis schreibt, damit du prüfen kannst, was tatsächlich ausgeführt wird. ## Fehler, Nullwerte und Logging im ganzen Code einheitlich behandeln Wende konsistente Konventionen für Struktur, Fehler, Nullwerte und Logging in der gesamten Codebasis an. - Organisiere Module nach fachlicher Domäne, nicht nach Architekturschicht, und platziere jeden neuen Typ in der Domäne, zu der er gehört. - Halte die Benennung konsistent: reservierte Suffixe und Präfixe für Erweiterungs-, Delegator- und abstrakte Typen; markiere Klassen als final, sofern keine Vererbung vorgesehen ist. - Bevorzuge ungeprüfte Laufzeit-Exceptions; verwende geprüfte nur, wenn unvermeidbar, stets mit aussagekräftiger Meldung und erhaltener Ursachenkette. - Prüfe bei Nullwerten Konstruktorargumente mit einer expliziten Non-Null-Prüfung, kennzeichne absichtlich nullbare Verträge, und verwende einen optionalen Typ für Lookups, die fehlschlagen können, statt ihn als allgemeinen Ersatz für Null einzusetzen. - Deklariere pro Klasse einen einzigen statischen, finalen Logger und wähle die Level bewusst: feine Diagnosen niedrig, behebbare Probleme als Warnung, unbehandelte Fehler als Error. - Bevorzuge unveränderliche finale Felder, verwende nebenläufigkeitssichere Map-Typen für geteilte Caches, und dokumentiere die Thread-Sicherheit, wo ein Typ gemeinsam genutzt wird. ## API-Antworten über einen einzigen Helper senden und Logik in Command-Klassen legen Schichte den Service so, dass Routing, Antworten und Logik jeweils einen festen Platz haben. - Lass API-Module von einer gemeinsamen Basis erben, die die Basisklasse des Web-Frameworks erweitert und gemeinsame Abhängigkeiten über ihren Konstruktor erhält. - Binde Endpunkte an Pfadpräfixe, die in einer zentralen Namespace-Map deklariert sind; füge neue Namespaces dort hinzu, nicht über Ad-hoc-Routing. - Leite jede Antwort durch einen einzigen Response-Helper, der Payloads mit Standardmetadaten umschließt; schreibe keine rohen Antworten von Hand. - Bilde Fehler mit einer kleinen Menge typisierter Fehler-Unterklassen ab, etwa not-found, bad-request und timeout; wirf sie aus den Handlern und lass die Basis einen einheitlichen Envelope formatieren. - Halte die Fachlogik in dedizierten Command-Klassen, nicht in Route-Handlern; ein Handler baut ein Command und übergibt dessen Ergebnis an den Response-Helper. ## Zwei Laufzeitumgebungen verbinden und den Hin- und Rückweg durch beide testen Wenn Code zwei Laufzeitumgebungen überbrückt, etwa eine Host-virtuelle Maschine, die eine zweite Sprache steuert, registriere überbrückte Klassen an der dafür vorgesehenen Registrierungsstelle des Frameworks und folge den dort bereits vorhandenen Mustern. Beziehe pro-Thread-Kontextobjekte erst am Verwendungsort, speichere sie niemals zur Konstruktionszeit, da ein gespeicherter Kontext fehlschlägt, wenn er von einem anderen Thread wiederverwendet wird. Teste den Roundtrip durch beide Schichten statt jede Seite einzeln, damit du Typumwandlungs- und Registrierungsfehler erkennst, die reine Unit-Tests einer einzelnen Schicht übersehen. Tests, die die zweite Laufzeitumgebung laden, müssen ihre Modul-Suchpfade auf den eingebetteten Ort richten; dieser Pfad ist beabsichtigt, ändere ihn also nicht, während du diese Tests bearbeitest. ## Mit Helpern für die echte Pipeline testen und den Zustand danach aufräumen Prüfe reales Verhalten mit eigens dafür gebauten Helpern und hinterlasse keinen Zustand. - Bevorzuge bereitgestellte Pipeline-Helper, die eine echte Pipeline hochfahren und Ergebnisse erfassen, gegenüber von Hand konstruierten Pipeline-Objekten. - Definiere zur Isolation Wegwerf-Plugin-Klassen innerhalb der Spec-Datei und registriere sie bei der echten Registry. - Verwende Shared Examples für Schnittstellenverträge und Shared Context für gemeinsames Setup; treibe Integrationstests mit einem Fixture-und-Service-Muster an, das aus Fixture-Dateien bootstrapt. - Bestätige, dass bestehende rescue- oder catch-Blöcke neu hinzugefügte Codepfade tatsächlich abdecken. - Räume globalen oder virtuelle-Maschine-Zustand im Teardown auf: Zu jeder gesetzten Eigenschaft gehört ein passendes Zurücksetzen, damit kein Zustand in andere Tests durchsickert. - Bevorzuge konkrete, eigens gebaute Doubles, die von echten Basisklassen erben, gegenüber generischen Mocks; hebe Mocks für Infrastrukturgrenzen auf, und lass die echte Methode laufen, wenn ein Stub nur fürs Setup dient. ## Prüfen, dass jeder neue Test ohne seine Änderung fehlschlägt Stelle sicher, dass jeder Test tatsächlich von der Änderung abhängt, die er absichert. - Überprüfe, dass jeder neue Test ohne die zugehörige Änderung fehlschlägt; ein Test, der mit oder ohne den Fix besteht, beweist nichts. - Prüfe das konkrete Verhalten, das die Änderung einführt, nicht bereits vorher bestehende Bedingungen: Binde die Assertion an einen beobachtbaren Unterschied, etwa ein neues Feld, einen geänderten Wert oder eine zuvor fehlende Log-Zeile. - Reproduziere bei einem Bugfix den Fehler zuerst im Test; beobachte, wie er aus demselben Grund fehlschlägt, aus dem der Fehler auftritt, wende dann den Fix an und beobachte, wie er besteht. - Hüte dich vor tautologischen Mocks: Eine Methode so zu stubben, dass sie den Wert zurückgibt, den du anschließend prüfst, ist zirkulär, und wenn der Test auch nach Löschen der Implementierung bei bloßem Mock weiterhin besteht, prüft er nur den Mock selbst. ## Inkompatible Änderungen schrittweise einführen und Secrets in Logs maskieren Ändere Verhalten über eine gestufte Leiter, schütze sensible Werte, und halte die Dokumentation auf demselben Stand. - Staffle Breaking Changes: Führe neues Verhalten zunächst als Opt-in ein und ergänze Deprecation-Warnungen in Minor-Releases, ändere dann den Standard bei erhaltenem Opt-out und entferne das alte Verhalten schließlich in Major-Releases. - Wende bei Konfiguration mit sensiblen Werten den dafür vorgesehenen Secret-Typ oder Wrapper des Frameworks an und überschreibe die String-Repräsentation, damit der Wert maskiert wird; schreibe solche Werte standardmäßig auf keinem Level in Logs. - Behandle Dokumentation als Teil der Änderung: Aktualisiere die relevante Referenz- und Contributor-Dokumentation bei jeder Verhaltensänderung, halte dich an die Konventionen des jeweiligen Formats, und verlange Dokumentation, bevor nutzerseitige Features gemergt werden.