Suche/Ereignisgesteuerte Zustandsautomaten bauen und ändern, ohne ihr Verhalten zu beschädigen

Ereignisgesteuerte Zustandsautomaten bauen und ändern, ohne ihr Verhalten zu beschädigen

Für Zustandsautomaten-Bibliotheken: Die KI verarbeitet jedes Ereignis ganz vor dem nächsten, überspringt bei Fehlern kein Aufräumen, schützt Threads und untergeordnete Automaten, schreibt zuerst Tests für synchrone und asynchrone Ausführungsmodule und hält Code, Doku und Schemas gleich.

Diese KI-Regeln verwenden
## Festlegen, welches Modul welchen Teil der Logik des Zustandsautomaten übernimmt Ordne die Verantwortlichkeiten zu, bevor du Code änderst, damit jedes Verhalten in der Komponente bleibt, die über das dafür erforderliche Wissen verfügt. ### Klare Grenzen definieren - Trenne Zustands- und Ereignisdefinitionen, die Zusammenstellung von Übergängen, die Weiterleitung von Rückruffunktionen, die Anpassung von Signaturen, Ausführungsmodule, Register, Integrationen und Serialisierung. - Halte allgemeine Infrastruktur wie Weiterleitung und Signaturanpassung unabhängig von der Domäne der Zustandsautomaten. - Gib jedem Modul, jeder Klasse und jeder Funktion genau einen klaren Grund für Änderungen. - Bevorzuge kleine, fokussierte Module mit expliziten Schnittstellen, statt ein sachfremdes Modul nur der Bequemlichkeit halber zu erweitern. ### Logik bewusst platzieren - Wende das Prinzip Information Expert an: Berechne ein Ergebnis dort, wo das erforderliche Wissen bereits vorhanden ist, und gib es dann zurück oder signalisiere es, statt es anderswo erneut zu berechnen. - Wende das Law of Demeter an: Übergib genau den Wert, den ein Mitwirkender benötigt, und nicht dessen übergeordnetes Objekt. - Wende Interface Segregation an: Akzeptiere ein eng begrenztes Feld oder Protokoll, wenn eine Hilfsfunktion nicht das vollständige Objekt benötigt. - Bewahre bei Refaktorierungen das öffentliche Verhalten und ermittle vor dem Verschieben von Logik die Tests, die dieses Verhalten beschreiben. ## Ein Ereignis vollständig verarbeiten, bevor das nächste drankommt Modelliere die Ereignisverarbeitung auf zwei getrennten Ebenen und führe sie bei der Implementierung nicht zusammen. ### Verarbeitungsebenen unterscheiden - Ein Mikroschritt führt eine ausgewählte Übergangsmenge atomar in der festgelegten Reihenfolge aus: vorherige Rückruffunktionen, Austrittsrückrufe, Übergangsaktionen, Eintrittsrückrufe und nachgelagerte Rückruffunktionen. - Ein Makroschritt verarbeitet ein externes Ereignis und wiederholt Mikroschritte, bis der Automat eine stabile Konfiguration erreicht. - Eine Konfiguration ist nur stabil, wenn kein ereignisloser Übergang aktiviert ist und die interne Warteschlange leer ist. ### Warteschlangen- und Ereignissemantik explizit halten - Extern gesendete Ereignisse gelangen in die externe Warteschlange und warten dort, bis der aktuelle Makroschritt abgeschlossen ist. - Intern ausgelöste Ereignisse gelangen in die interne Warteschlange und werden innerhalb des aktuellen Makroschritts vor späteren externen Ereignissen ausgeführt. - Eine nicht zugewiesene Übergangsdeklaration kann einen ereignislosen Übergang darstellen, der ausgelöst wird, sobald seine Wächterbedingung wahr wird. - Ein zugewiesener Übergang erzeugt ein benanntes Ereignis; werden Aliasse aus Namenskonventionen abgeleitet, hat eine explizite Kennung Vorrang. - Teste die Reihenfolge in Warteschlangen, den ereignislosen Fortschritt und die Beendigung bei stabiler Konfiguration als getrennte Verhaltensweisen. ## Fehler als Ereignisse behandeln, ohne Rückruffunktionen zum Aufräumen auszulassen Lege fest, wo Fehler abgefangen werden und welche Lebenszyklus-Rückruffunktionen nach einem Fehlschlag weiterhin ausgeführt werden. ### Fehlergrenze festlegen - Lege die Richtlinie, Fehler als Ereignisse abzufangen, für jeden Automatentyp explizit fest; diagrammorientierte und grundlegende Varianten können absichtlich entgegengesetzte Vorgaben verwenden. - Fange Fehler auf der Ebene eines Aktionsblocks ab, statt einen ganzen Mikroschritt als eine einzige Abfanggrenze zu behandeln, und führe Abschlussrückrufe bei Erfolg wie bei Fehlschlag dennoch aus, wenn der Lebenszyklus sie garantiert. - Leite einen abgefangenen Ausführungsfehler als internes Ereignis weiter, damit das Zustandsdiagramm ihn über deklarierte Übergänge behandeln kann. - Verhindere rekursive Fehlerschleifen, indem du Fehler unterdrückst oder beendest, die während der Verarbeitung des internen Ausführungsfehler-Ereignisses ausgelöst werden. ### Unterschiede zwischen Rückruffunktionen bewahren - Trenne allgemeine Lebenszyklus-Rückruffunktionen von ereignisspezifischen und zustandsspezifischen Rückruffunktionen. - Eine `error` genannte Rückruffunktion ist nicht automatisch allgemein; verlange den zugehörigen Fehlerübergang, bevor du sie per Konvention aufrufst. - Wenn Übergangsaktionen das lokale Abfangen von Fehlern während der Verarbeitung eines Fehlerereignisses deaktivieren, halte diese Regel von Eintritts- und Austrittsblöcken getrennt, die stets ihre Fehlerbehandlung verwenden. - Teste Erfolg, gewöhnlichen Fehlschlag, einen Fehlschlag innerhalb der Fehlerbehandlung und die Abschlussreihenfolge jeweils unabhängig voneinander. ## Threads und untergeordnete Automaten schützen, wenn sich Warteschlangen oder Sperren ändern Bewahre die Garantien für Threadsicherheit und Aufrufweiterleitung, wenn du Warteschlangen, Sperren oder die Verwaltung untergeordneter Automaten änderst. ### Sichere nebenläufige Verarbeitung aufrechterhalten - Erlaube höchstens einem Thread gleichzeitig, Übergänge für eine Automateninstanz auszuführen. - Verwende eine threadsichere Prioritätswarteschlange, wenn nebenläufige Sender unterstützt werden. - Ersetze eine threadsichere Warteschlange nicht durch eine einfache Liste oder eine nicht threadsichere doppelseitige Warteschlange, ohne den Nebenläufigkeitsvertrag neu zu entwerfen und nachzuweisen. - Prüfe echte Konkurrenzsituationen unter hoher Last mit koordinierten Sender-Threads und Barrieren. - Verwende in Tests mit blockierenden Threads unterbrechbare Ereigniswarteoperationen mit expliziten Zeitüberschreitungen statt fester Pausen, durch die beim Abbau Threads weiterlaufen können. ### Aufgerufene untergeordnete Instanzen präzise verwalten - Entferne einen untergeordneten Aufruf nur, wenn er sowohl beendet als auch abgebrochen wurde. - Halte eine beendete, aber nicht abgebrochene untergeordnete Instanz erreichbar, solange ihr zugehöriger Zustand aktiv bleibt. - Wenn die Konstruktion einer untergeordneten Instanz innerhalb der Verarbeitungsschleife blockiert, erfasse ihre Referenz über einen frühzeitigen Lebenszyklus-Listener, bevor die Schleife fortgesetzt wird. - Leite an untergeordnete Instanzen gerichtete Ereignisse über die Aufrufverwaltung und die Behandlung der untergeordneten Instanz, statt die Lebenszyklussteuerung zu umgehen. - Teste Abbruch, natürliche Beendigung, beibehaltene Weiterleitung und verspätete Ereigniszustellung als getrennte Fälle. ## Tests in der festgelegten Projektumgebung ausführen und die Zweigabdeckung messen Führe Installation, Tests, Qualitätsprüfungen und Dokumentation über die festgeschriebene Projektumgebung und deren Aufgabensteuerung aus. ### Tests sicher ausführen - Begrenze die gesamte Testsuite durch eine explizite Zeitüberschreitung, um weiterlaufende Threads oder Endlosschleifen aufzudecken. - Nutze die unterstützte parallele Ausführung der gesamten Suite, wenn Tests absichtliche Wartevorgänge oder ersatzweise Zeitüberschreitungen enthalten. - Starte die maßgebliche Suite aus dem Projektstamm, wenn eine pfadbeschränkte Ausführung Modul- oder Dokumentations-Doctests auslassen würde. - Untersuche Läufe, die den festgelegten Ausgangswert überschreiten, statt sie ohne Diagnose wiederholt neu zu starten. - Aktiviere ausführliche Debug-Protokollierung nur für gezielte Untersuchungen, wenn eine globale Debug-Protokollierung die Leistungsmessungen verändert. - Erfasse umfangreiche Ausgaben einmal und analysiere danach das gespeicherte Ergebnis, statt dieselbe aufwendige Suite wiederholt auszuführen. ### Vorgesehene Abdeckung messen - Aktiviere bei Bedarf die Abdeckungserfassung ausdrücklich und erzeuge anschließend für betroffene Module einen Bericht über fehlende Zeilen oder einen maschinenlesbaren Bericht; die bloße Auswahl eines Berichtsformats aktiviert die Erfassung möglicherweise nicht. - Überprüfe vor dem Commit die Zweigabdeckung und nicht nur die Zeilenabdeckung. ## Zuerst einen fehlschlagenden Test schreiben und ihn mit synchronen und asynchronen Ausführungsmodulen ausführen Behandle Tests als Anforderungen an die Implementierung und nicht als Aufräumarbeit nach dem Schreiben des Codes. ### Einer testgetriebenen Schleife folgen 1. Ermittle bei der Planung neue Zweige, Randfälle, Fehlerpfade und Nebenläufigkeitsrisiken. 2. Füge einen gezielten Test hinzu, der aufgrund des fehlenden oder fehlerhaften Verhaltens scheitert. 3. Implementiere die kleinste Verhaltensänderung, durch die der Test besteht. 4. Führe den gezielten Test, die Abdeckungsprüfung des betroffenen Moduls und die erforderlichen umfassenderen Prüfungen aus. 5. Refaktoriere erst, nachdem Verhalten und Abdeckung abgesichert sind. ### Testinfrastruktur wiederverwenden - Verwende eine gemeinsame parametrisierte Ausführung, um dasselbe Zustandsdiagramm mit synchronen und asynchronen Ausführungsmodulen zu prüfen. - Lass die Ausführung den Start der Ausführungsmodule und die Ereigniszustellung abstrahieren, statt Testklassen zu duplizieren oder wirkungslose Listener hinzuzufügen. - Verwende Fixtures für temporäre Pfade, Umgebungsänderungen und veränderlichen Zustand; kodiere keine Pfade fest ein und verlasse dich nicht auf veränderliche globale Werte. - Decke defensive Zweige durch Tests ab, wann immer sie erreichbar sind; nimm einen Zweig nur dann von der Abdeckung aus, wenn seine Unmöglichkeit nachgewiesen ist. - Halte den erforderlichen Schwellenwert für die Zweigabdeckung durch die standardmäßigen Commit-Prüfungen durchgesetzt. ## Code-Prüfungen ausführen und Parser und Schema aufeinander abstimmen Wende ein einheitliches Qualitätssystem auf Implementierung, Beispiele, erzeugte Verträge und Entwicklerdokumentation an. ### Codekonventionen durchsetzen - Führe Formatierung, Linter, Importsortierung, Typprüfung und Tests des Projekts über die maßgebliche Werkzeugkette aus. - Bevorzuge sortierte Importe auf oberster Ebene; verwende verzögerte Importe nur, wenn sie zum Auflösen einer zirkulären Abhängigkeit erforderlich sind. - Befolge die Namens- und Docstring-Konventionen des Projekts und dokumentiere verborgene Semantik, statt offensichtliche Namen oder Typen zu wiederholen. - Verwende bei Bedarf einen Attribut-Docstring direkt unter dem Attribut, wenn der Dokumentationsgenerator dies verlangt; reserviere Kommentare in derselben Zeile für nicht offensichtliche Logik. ### Dokumentation und deklarative Formate testen - Gestalte Dokumentationsbeispiele nach Möglichkeit als ausführbare Doctests. - Lege Beispiele, die echte Nebenläufigkeit erfordern, in Unit-Tests ab und verweise aus der Dokumentation darauf. - Halte Parser, Laufzeitverhalten und veröffentlichtes Schema synchron, wenn sich das deklarative Vokabular ändert. - Behandle die vom Parser akzeptierten Schlüsselmengen als maßgebliche Quelle und teste sie gegen die entsprechenden Schemaeigenschaften. - Validiere jedes strukturierte Beispiel und jedes Fixture gegen das Schema, damit Abweichungen zwischen Parser und Schema die Testsuite scheitern lassen. ## Änderungen abschließen, ohne Commit-Hooks zu umgehen oder Geheimnisse zu committen Nutze den Beitragsprozess des Repositorys, um Nachvollziehbarkeit, erzeugte Artefakte und Sicherheitsgrenzen zu bewahren. ### Qualitätsprüfung abschließen - Richte die Arbeit auf den vorgesehenen Entwicklungszweig des Repositorys aus und befolge dessen Konvention für Commit-Nachrichten. - Verwende signierte Commits, wenn das Projekt sie bevorzugt. - Umgehe niemals Commit-Hooks und deaktiviere keine vorgeschriebenen Prüfungen. - Wenn ein Hook Dateien neu erzeugt oder ändert, prüfe diese Ergebnisse und füge sie zur Staging-Area hinzu; führe danach erneut einen Commit durch, bis jeder Hook erfolgreich ist. - Prüfe den finalen Diff und bestätige, dass Implementierung, Tests, Dokumentation und erzeugte Verträge übereinstimmen. ### Vertrauensgrenzen schützen - Übertrage niemals Geheimnisse, Zugangsdaten oder Umgebungsdateien in das Repository. - Validiere nicht vertrauenswürdige Daten an Systemgrenzen und halte interne Invarianten explizit fest. - Schwäche weder die Warteschlangensicherheit noch die Verhinderung von Fehlerschleifen, die Schemavalidierung oder die Durchsetzung der Abdeckung ab, nur damit eine Änderung besteht. - Melde jede erforderliche Prüfung, die nicht ausgeführt werden konnte, und behaupte ohne direkte Ergebnisse nicht, dass die Arbeit abgeschlossen sei.