TGV · Tower-Register · GENERIERTER SPIEGEL der SSOT-MD · Stand aus Quelle
check_artefakt_register.py fragte, ob irgendein Wort aus einem Dateinamen im Register.github/workflows/**, mit einer Positivkontrolle, die beweist, dass die Suche etwas findet.check_eml_sicherung_verdrahtet.py galt als belegt über „verdrahtet“ (2x),check_env_gelesen_aber_nie_geschrieben.py über „geschrieben“ (2x) — **beide kommen mitmaster grün, auf einem ZweigDies ist ein generierter Spiegel von 01_ITERATIONS/CONTROL_TOWER_HANDOFF/ARTEFAKT_REGISTER.md (Owner: Tower) — dort wird gepflegt, hier wird gesehen. Drift zwischen Quelle und Spiegel meldet familie_guard (C5, Hash-Vergleich). Rote Pillen = die ehrlichen Problemzeilen.
| Artefakt | Sinn | Status | Weg/Owner |
|---|---|---|---|
regelwerk.html — DAS Register (83 Regeln, 🛡/🧠-Tags) | JCs Abnahme-Brücke, live | 🔨 | Inventur GEMESSEN (17.07., reproduzierbar): 40 Regeln außerhalb — 20×R-V4 (kommen als EIN Schnitt mit #920-Merge) · 12×U-Regeln · 4+4 Guards; +3 Quellen in Zählung (UXAUDIT/MANDANT/RECONCILE-Checks). Baseline eingefroren, Neuzugang=ROT. Umsetzungs-Welle als Draft-Stapel fürs Bündel-Fenster |
engine.js assertConformance (43 ck-Guards) | Maschinen-Prüfung master | ✅ registriert (E2) | Härtung R-42 gestaut (Bündel-Fenster) |
core_shell TGV_CONFORMANCE R-V4-01…21 + DRIFT-* | Staging-Harness | 🔨 | in KANON-Inventur; Verschmelzung mit engine.js NACH #920 (beschlossen) |
SCREEN_COMPOSER_SKILL.md U0–U10 (v1.3) | Bau-Playbook | 🔨 | Kollisionsregel drin (E1); U-Regeln → Register = Teil KANON-Inventur |
| UXAUDIT Daily-Regression C1–C17 + G-1/2/3 | der Mikro-QA-Ersatz für JC | 🔨 | Checks → Register (KANON-Inventur); scharf mit QA-Zugang (Bündel) |
| CI-Guards (9 Workflows: freeze-gate · deploy-impact · quellen-ssot · skill-registry · no-sigpipe · testdata-prod · mandant-policy ×2 · lint) | mechanische Wächter | ✅ | jeder mit Selftest/Gegenbeweis (JE-156) — Stand heute bewiesen |
check_migration_numbers + check_deploy_impact + familie_guard + Header-Snapshot-Guard | Quellen-Hygiene | ✅ | laufen; Erweiterung A aktiv. check_deploy_impact 18.07. Falsch-Grün gefixt (las nur committete Diffs → „nichts zu prüfen"+exit 0 vor dem Commit = Freigabe, die nichts geprüft hat) |
check_katalog_coverage (Achse A + A-erw + C) | Katalog = JCs Abnahme-Fläche vollständig und ehrlich | ✅ | KANON#01. Achse A: ausführbarer Baustein ohne Katalog-Karte=ROT (Ratchet 4 Altlasten) · A-erw: ohne Register-Zeile=ROT · C: Register behauptet live/fund/stag, Code fehlt=ROT (Behauptungs-Drift). Verbindet die zwei Ebenen aus Towers Arbitrage 18.07. |
check_prompt_wahrheit (R-166) | KERN-Prompt verspricht nur, was der Stack hält | ✅ | KANON#01, Register #81: 3 Zusagen ungedeckt — „Fragen verlassen nie das Haus" (Orchestrator ruft api.anthropic.com/US) · „Self-extending" (kein Seiten-Tool) · „Internet" (kein Web-Tool). Prüft gegen Messpunkte im Engine-Code, nicht gegen eine gepflegte Liste → baut jemand das Tool, wird der Guard von selbst grün. ⛔ Wortlaut ist JC-gated — der Guard ändert nichts, er hält die Lücke sichtbar und verhindert NEUE |
check_test_sammlung (R-165) | Datei heisst test_*, pytest sammelt daraus 0 | ✅ | KANON#01, AGENT-CHAT-Fund: test_context_doc_id.py ist Skript-Test → pytest meldete GRÜN, 14 Checks liefen nie. Gemessen: 63 von 77 stumm (15 in aktiven Lanes). ⚠️ Ehrlich: pytest läuft in KEINER CI — der Guard schließt keine CI-Lücke, er schützt den Hand-Lauf. Dass 07_TESTS nirgends automatisch läuft, ist die größere offene Frage |
check_projekt_nummer.py (Workflows konformitaets-guards.yml · schema-register-guards.yml) | haelt die EINE Projektnummer-Regel NN-YY-XXX zusammen: C1 Regex-Paritaet Guard ⟷ Regel-Migration (tgv_projekt_nr_gueltig) — divergieren sie, gibt es zwei Regeln · C2 Bestands-Konformitaet jedes project_code aus den SQL-Seeds · C3 Entitaetscode-Beleg. | ✅ | Zeile nachgetragen von PERSONA#01 03.09. (#1313) — der Artefakt-Ratchet meldete sie als unregistriert, ausgeloest durch meine Nachbarzeile: ⭐⭐ sie war nie mit ihrem NAMEN belegt, sondern nur „selten“ ueber das Token nummer; meine Zeile nennt check_register_nummer.py und drueckt das Token ueber die Seltenheits-Schwelle. ⛔ Die Zeile hat also nichts kaputtgemacht, sondern eine Zufalls-Belegung entlarvt — genau die Klasse, die der Guard selbst als „OBERGRENZE des Zweifels, nicht die Zahl der Luecken“ ausweist. ⚠️ Owner-Zuordnung ist meine ANNAHME (aus git log: PROJEKT#07/#826, PR #2206; inhaltlich nennt der Kopf PROJEKT#01/Tower#33) — PROJEKT#01/#07 korrigiert sie, falls falsch. ⛔ Status ✅ steht fuer VORHANDEN UND LAUFEND (Echtlauf exit 0, in zwei Workflows eingehaengt, # LAUFORT: ci) — nicht fuer „inhaltlich abgenommen“: der Lauf meldet drei OFFEN/STRITTIG-Posten, die an JCs Arbitrage A2 haengen. Das ist Sache des Owners, nicht meine Messung. |
check_ausgangswert_wird_verworfen.py (Workflow kanon-quellen-ssot.yml) | wird ein Ausgangswert erzeugt und dann WEGGEWORFEN? Die Schwester von R-155, eine Stufe tiefer: R-155 fragt, ob ein Waechter beim MELDEN abstuerzt — dieser fragt, ob sein Urteil ueberhaupt jemanden ERREICHT. ⛔⛔ Anlass: check_register_nummer.py:796 gab return 3 (= KEIN URTEIL, master unlesbar) aus main() -> None; main() wurde BAR gerufen ⇒ Exitcode 0. Das Werkzeug druckte „eine Nummer aus einer unvollstaendigen Liste ist schlimmer als keine“ — und meldete gleichzeitig gruen. ⭐ Eine Enthaltung, die sich als Zustimmung ausgibt, ist schlimmer als ein falsches Urteil. ⭐⭐ Das Kriterium ist der VERWORFENE AUFRUF, nicht die Annotation — ein main() -> int, das bar gerufen wird, ist derselbe Ausfall und waere der Annotations-Regel entgangen; ein -> None mit sys.exit(main()) funktioniert und darf nicht rot werden. Rein statisch (ast), ohne den Pruefling zu importieren. | ✅ | PERSONA#01 (Bau #1313, TOWER#53-Auftrag 03.09.) · Selftest 8/8 in beiden Richtungen, mit Trefferkontrolle VOR der Behauptung (B-30) und einer Positivkontrolle, dass die Sammlung beide Richtungen enthaelt · ⭐ Echtlauf-Positivkontrolle an einer ECHTEN Datei: die Fassung von check_register_nummer.py VOR dem Fix ⇒ 1 Befund (Zeile 796, verworfen am baren Aufruf), danach 0 — die 0 ueber 677 Dateien ist damit gemessen und nicht blind |
check_testdatei_ist_angemeldet.py (Workflow agentchat01-suiten.yml) | laeuft jede Testdatei bei MINDESTENS EINEM Raster? Der Nachbar darueber faehrt namentlich genannte Skript-Suiten, die CI faengt per pytest nur Dateien mit test_-Funktionen. ⛔⛔ Wer im Skript-Stil geschrieben ist und nicht in der Namensliste steht, faellt durch beide — gemessen 02.09.: 10 von 58 Dateien laufen NIRGENDS, 2 davon ROT. ⭐ Es ist keine der drei erwarteten Ursachen (laeuft nicht · wird ignoriert · Ausgang verschluckt), sondern die Luecke zwischen zwei Rastern, die BEIDE korrekt arbeiten — bei den anderen dreien macht jemand etwas falsch, hier niemand, und genau deshalb faellt es nicht auf. ⭐⭐ Sein eigentlicher Ertrag ist der NENNER: er druckt, ueber wie viele Dateien der Nachbar-Pruefer ueberhaupt urteilt — ein Gruen ohne seine Reichweite ist eine Behauptung.* Beide Raster werden aus ihrer Quelle gelesen (Ablaufdatei + Pruefer), nie abgetippt. Ratsche 10, ROT beim Wachsen; exit 2 = KEIN URTEIL statt Gruen, wenn eine Quelle unlesbar ist. | ✅ | PERSONA#01 (Bau #1262, TOWER#53-Auftrag 02.09.) · Selftest 4/4 · Beissprobe: Ratsche auf 9 gesenkt ⇒ exit 1 ROT, wiederhergestellt · in der CI angemeldet inkl. eigenem Ausloeser (sonst waere er das erste Beispiel seiner eigenen Klasse) ⛔⛔ UEBERNOMMEN VON TOWER#57 am 20.09.2026 (#2257), weil PERSONA#01 nicht mehr existiert. Seither urteilt er ueber die volle Reichweite, und die Grundlinie ist GEMESSEN statt gespeichert: Basis (merge-base) und Kopf im SELBEN Lauf, Basis in einem frischen Arbeitsbaum mit DIESEM Code. Erste Messung 98/98 ⇒ 0 NEU. ⭐ Der alte Verzicht auf die weite Reichweite war richtig ohne dieses Werkzeug — mit ihm kuerzt sich die Altlast beidseits weg. ⛔ Anmeldung ist nicht Wirksamkeit: die Zahl sagt, ob eine Datei GEFAHREN wird, nie ob sie etwas FINDET. |
08_OPS/check_test_gibt_geliehenes_zurueck.py (Workflow konformitaets-guards.yml, mit --selftest) | Gibt jeder Test das GELIEHENE Modul-Attribut zurueck? Der vierte in der Familie „Falsch-Gruen in der Test-Harness selbst“ — die Geschwister fragen, ob eine Datei EINGESAMMELT wird (check_test_sammlung), ob sie ueberhaupt irgendwo LAEUFT (check_testdatei_ist_angemeldet) und ob ein Selbsttest seine FRACHT wegraeumt (check_selbsttest_raeumt_seine_spur). Dieser fragt das Vierte: der Test laeuft, wird eingesammelt, ist gruen — und macht den NAECHSTEN blind. ⛔ Belegt 14.09. (#1942): ein Test ersetzte den Netzleser von chat_engine/executors_rka nackt auf Modulebene und gab ihn nie zurueck; der SCOPE-Waechter danach mass an der Attrappe und meldete 4 rote Checks. ⭐ Einzeln waren beide Dateien gruen — sichtbar wurde es nur in der vollen Suite, und nur, weil das Alphabet die eine vor die andere sortiert. Syntaxbaum ueber 07_TESTS, drei Zustaende (im try mit Rueckgabe im finally · zurueckgegeben, aber nicht im finally · nie zurueckgegeben), Stundung statt Rot-Flut mit Eigner je Zeile. | ✅ | KAI-BELEG#01, Register #1945. Beissprobe: 7 Koeder + Gegenprobe an der geheilten Datei; sie hat zwei echte Fehler des Waechters gefangen, bevor er lief. |
08_OPS/check_deeplink_haelt_die_adresse.py (Workflow kanon-quellen-ssot.yml, Selftest zuerst) | Hält der Verlauf-Deep-Link seine Adresse — und enthält sich eine fremde Faden-ID ehrlich? ⛔⛔ Der Anlass ist eine Klasse, nicht ein Bug: #/verlauf/t/<tid> lud den Faden und endete danach ausnahmslos auf navigate("/chat") — die Adresse verwarf sich beim Öffnen selbst, und die Fläche versprach in 7 Sprachen das Gegenteil (verlauf.link_titel: „Dauerhafte Adresse dieses Gesprächs"). ⭐⭐ Zwei Messungen standen gegeneinander und beide waren richtig: der Quelltext zeigte, dass die Route den Parameter FÜHRT (er wird gelesen), die Fläche zeigte den Rückfall (er bleibt nicht) — ein Parameter in der Route beweist, dass er gelesen werden KANN, nicht dass er stehen BLEIBT. ⛔ Warum ein Wächter und nicht nur ein Fix: der Rückfall war EINE Zeile und ABSICHT (der dokumentierte #424a-Ablauf: Liste = Auswähler, fortgesetzt wurde auf /chat) — eine Zeile, die einmal richtig war, kommt beim nächsten Anfassen zurück. Sechs Achsen: Deep-Link verlangt bleib · Weiche if(bleib)…else navigate · Fortsetz-Aktion verdrahtet · Listen-Klick UNVERÄNDERT (JCs mitgekaufter Preis) · 404-Fall eigen behandelt (JC-Abnahmekriterium 3: fremde ID ⇒ verständliche Enthaltung, kein leerer Chat-Schirm) · Orakel-Riegel: EIN Satz für „fremd" und „existiert nicht", getrennte Wörter sind ROT (Hausregel RECHTE#02 §12 (6) — zwei Sätze wären eine Aussage über fremdes Material). ⛔ REICHWEITE (R-193), und sie steht in der MELDUNG statt im Kommentar: geurteilt wird über den Quelltext-Vertrag, NICHT über die Fläche. Ein Grün hier ist KEIN grüner Deep-Link-Nachweis — den liefert der Codex-Retest. Nicht geprüft ist auch, ob der Faden auf der Route ANGEZEIGT wird: das ist Variante A, Ziel und ausdrücklich nicht erledigt. | ✅ | KONVERSATION#01, Register #3213, JC-Entscheid 27.09. („Ja Empfehlung B zu deep link ist OK"). ⭐ Beissprobe in beide Richtungen mit VORHER-BEWEIS: die Gestalt OHNE den Fix ist nachweislich ROT — sonst wäre die Probe invariant gegen genau den Schaden, den sie messen soll — die gefixte grün, und alle sechs Achsen einzeln mutiert (bleib entfernt · Weiche entfernt · Aktion entkoppelt · Listen-Klick umgebaut · 404-Enthaltung entfernt · Orakel eingebaut), jeder Mutant schlägt an. Reine Funktion auf Text, kein Schreibrecht (die Nachbar-Lehre: ein Wächter, der schreiben kann, könnte sich freikaufen). R-155-Vorspann, check_konsolensicher grün. |
check_agentchat01_suiten.py + Workflow agentchat01-suiten.yml | die 147 Skript-Checks, die pytest NICHT einsammelt (main()-Suiten), laufen in JEDEM CI-Lauf - inkl. --selftest, der jeden bewachten Guard einzeln sabotiert und ROT verlangt | ✅ | AGENT-CHAT#01 (Bau #1163) · Zeile nachgetragen von RAG#02 24.07. (Tower-Auftrag „nimm die roten Guards“): der Ratchet fand beide Artefakte als Neuzugang ohne Register-Zeile. ⚠️ Owner-Zuordnung ist meine Annahme - Tower/AGENT-CHAT#01 korrigiert sie, falls falsch |
jc_klassen_guards.js (R-JC-01…09) + PER-SCREEN-GATE | JCs wiederkehrende Befund-Klassen mechanisch — „ein Befund darf auf KEINEM Screen wiederkehren" | ✅ | KANON#01, Tower#24 (Register #60–#79). Der Hebel ist das GATE, nicht die Einzelregel: screenGate() liefert EIN Urteil pro Screen statt Befund-Tropf — Antwort auf JCs Frust über den Detail-für-Detail-Rhythmus. Blind-Sperre: fehlen die Pflicht-Klassen (Container/Hero), meldet es „KEIN URTEIL MÖGLICH" statt vorlagefähig. Alle 9 gegenbewiesen (jc_klassen_guards_selftest.html, Browser, 0 Fehler). Eigene Datei, weil engine.js/core_shell Deploy-Trigger sind |
GUARD_BAU_KANON.md (R-155·158·159·160·161·162·163·164 + Checkliste) | DER EINE Ort, wie man in TGV einen Prüfer baut, der nicht lügt | ✅ | KANON#01. Führt die bisher über Docstrings/Register/Tower-Regelwerk verstreuten Guard-Regeln zusammen — keine Regel doppelt, Mechanik je Regel verlinkt. Markiert ehrlich, was NICHT mechanisiert ist (R-162/163/164: gemessen zu wenig Substrat für einen Lint → Bau-Vorlage statt Falsch-Rot-Quelle) |
check_guard_selektoren (R-161) | Guard prüft das sichtbare Element, nicht das erste im DOM | ✅ | KANON#01, aus UXAUDIT-Sweep (5 Fehler derselben Wurzel: Composer·Hero·Klärungs-Liste·Tabelle·Container). In der Shell liegen alle Sektionen gleichzeitig im DOM. Regel ist präziser als „nie querySelector": root./el.-gebunden und ID-Anker bleiben erlaubt (74× legitim in engine.js). Fängt auch den stillen No-op qa(sel, root) — der Helper nimmt nur 1 Argument. Gemessen: engine.js 0 · core_shell 19 (DESIGN#03, informativ) |
07_TESTS/buchhaltung01/test_mail_eine_schrift.py | Steht der Signaturblock in derselben Schrift-Huelle wie Rumpf und Tabelle? Gemessen am ZUSAMMENGESETZTEN Objekt (mail_bau baut den Rumpf, engine.js haengt die Signatur an, netzfrei via render_only) - und zwar die Ahnenkette des Signaturblocks, nicht die Zahl der font-family-Deklarationen. | ✅ | BUCHHALTUNG#10 (Register #488). ⛔ Der Unterschied ist der ganze Befund: vor dem Fix gab es GENAU EINE Deklaration - und sie umschloss die Signatur trotzdem nicht (leere Ahnenkette), also erbte diese nichts und fiel auf die Vorgabe des Anzeigers zurueck. ⭐ Eine Fundstelle beweist, was an ihr steht, nicht wo sie wirkt. Selbst-Sabotage stellt den Vorher-Stand her und verlangt Rot; ohne node wird geskippt, nicht gruen-geluegt. CI konformitaets-guards ⑯c. |
check_bundle_vollstaendig (#576) | Importiert der Dienst etwas, das im Deploy-Bundle NICHT liegt? 6 Stufen, ast-basiert (app.py-Importe · Importe INNERHALB gebuendelter Dateien · requirements · Bundle-Quelle gegen git ls-files · Stufe 5 DATEN-Dateien zur Laufzeit (#1096) · Stufe 6 .py-Dateien, die eine gebuendelte Datei ueber einen PFAD laedt statt per Import (gebaut TOWER#53 #1734, Verzeichnis-Globs werden aufgeloest; erweitert PAKET#02 #1735 um runpy.run_path/exec(open(, Literale mit Verzeichnisanteil und einen Selbsttest an einem Paar, das sich unterscheiden muss) — der Fall, der JCs Buchhalter-Screen am 06.09. auf allen Paketen leer stehen liess: presend_checks._paare_bilden() holt den C3-Waechter per spec_from_file_location, es gibt also keine import-Zeile, an der Stufe 2 haengen koennte, und der Ausfall faellt erst im AUFRUF an — als 502, waehrend der Bundle-Guard (503) schweigt.) | ✅ | HEIMHOLUNG#09 — gebaut während der Live-Störung 22./23.08. Läuft in schema-register-guards ⑯. ⛔ Zeile von GUARD#03 nachgetragen, nicht von der Eignerin (23.08.): der Artefakt-Ratchet meldete sie als einziges NEUES Artefakt ohne Zeile und blockierte damit die Merge-Strecke. Owner bleibt HEIMHOLUNG#09 — wer den Guard ändert, pflegt diese Zeile. |
08_OPS/check_pruefer_schreibt_in_den_baum.py — zweite Achse: die NACHBARSCHAFT (#2017) | Was faehrt die CI, das dieser Guard NICHT ansieht? Seine Population nimmt Dateien nach NAMENSMUSTER (check_/guard_/pruef_/validate_/verify_). ⭐ Die Achse misst bei jedem Lauf die Nachbarschaft und DRUCKT sie: 40 .py startet ein Workflow als Schritt, die nicht im Muster liegen — 22 schreiben nicht, 10 nur in Wegwerf-Verzeichnisse, 8 in den Baum. Jeder der acht steht mit GRUND (Erzeuger · Inventar · JCs Sitzungswerkzeug · Skill), nicht nur mit Namen. Neuzugang = ROT, Schrumpfen = FORTSCHRITT-Zeile. | ✅ | SCREEN#15, Register #2017. ⛔ Die Population wurde ABSICHTLICH NICHT erweitert: das haette acht neue Ausnahmen gebraucht — also genau die Duldungsliste, die NOSTALGIC-NOYCE in #2016 gerade losgeworden ist. ⭐ Eine Duldung ABSORBIERT den naechsten Fall, eine Ratsche MELDET ihn; beides sind Listen, nur eine ist ein Loch. ⛔ Der Anlass-Befund hat sich beim Messen gedreht — das erste Beispiel (ein Sabotage-Skript faellt aus der Population) traegt nicht: das ist ein PRUEFSTAND, er darf schreiben. Die echte Luecke zeigt in die Gegenrichtung. ⛔ Ein zweites Kriterium wurde gemessen und verworfen, damit es niemand noch einmal probiert: „wer schreibt, ohne es anzukuendigen" — 52 CI-Dateien schreiben, 16 kuendigen an, 36 nicht, aber die 36 sind fast alle TEMP-Schreiber, die der Guard zu Recht durchlaesst; das Kriterium haette eine Falsch-Rot-Flut erzeugt. Gegenbeweis: gegenbeweis.py --satz nachbarschaft, 5/5, plus Hauptlauf-Probe (Eintrag entfernt ⇒ exit 1, nennt die Datei und stellt die Frage Pruefer-oder-Erzeuger). ⚠️ REICHWEITE: erfasst ist python …*.py in run:-Zeilen — NICHT Aufrufe ueber ein Shell-Skript, ueber make oder ueber eine Variable. |
08_OPS/check_pruefer_gibt_den_zustand_zurueck.py (Workflow konformitaets-guards.yml ⑺-B, mit --selftest; Messstand 08_OPS/qa_harness_server.py + Chromium) | Veraendert eine Konformitaetsregel den Zustand, den sie MISST — und meldet sich dabei gruen? Die Verallgemeinerung des Nachbarn ⑺: der fragt es fuer EINE Regel (DRIFT-CMP-DROP raeumt seine Spur), dieser fuer ALLE auto-Regeln und feldweise. Ein ZEUGE ueber 22 Facetten des geteilten Zustands (Chat-Kontext je Feld · Composer-Feldwert, Chips und Fracht · __carryQuestion · _chatOrigin · Theme/Hash · Kopfzeilen mit Glyph, __manualHold, __lastManual, __heroFlips); jede Regel laeuft EINZELN zwischen zwei Abzuegen. Verhalten statt Quelltext — ein finally, das richtig aussieht und das falsche Feld schreibt, faellt hier auf. ⛔ Anlass (KAI-QUALITAET#02 #2193, PR #2906): DRIFT-CHAT-CTX-CHIP liess context_doc_id 8488 → null fallen und meldete zu genau dieser Regel GRUEN; live verlor jeder Panel→Chat-Uebergang den gepinnten Beleg. ⭐⭐ Freigaben pruefen AENDERUNGEN — dieser Fehler war ein ZUSTAND, den ein gruener Pruefer taeglich neu bestaetigte. ⭐ Ortsbestimmung: die Klasse sitzt NICHT in 08_OPS/check_*.js (alle 10 bauen ein synthetisches window), sondern in den Regeln IN core_shell.html, die ueber runHere() bei JEDEM Routenwechsel laufen — sie laufen, wenn jemand ARBEITET, nicht wenn jemand prueft. Ratsche auf NAMEN (frisch gemessen, nie eine gespeicherte Zahl) mit Vorgang je Zeile und FORTSCHRITT-Meldung; Angriffspunkt-Sonde je bekanntem Mutator, damit „unveraendert" nie ein Vakuum ist. | ✅ | PRUEFER#01, Register #2214. Befunde beim Erstlauf (67 auto-Regeln, 1280x900, ausgeloggt): DRIFT-CHAT-CTX-CHIP gruen und verliert docId/satz/icon · DRIFT-CHAT-CTX-BINDING gruen und verliert satz/icon (beide Fix in #2906) · ⭐ DRIFT-HERO-COLLAPSE hat der Pruefer selbst gefunden, ohne dass ihm der Fall beigebracht war: die Rueckgabe nennt vier Felder, der Handler setzt fuenf (hero.__lastManual) — die Rueckgabe ist ABGESCHRIEBEN, nicht abgeleitet. ENTHALTUNG benannt statt als Entwarnung: DRIFT-CARRY-QUESTION (Angriffspunkt #klFreeBtn nur auf /klaerung-min, dort ausgeloggt noauth). ENTLASTET mit Positivkontrolle: DRIFT-CMP-DROP (Aufnahme als synchron gemessen, 0 Chips und 0 Fracht danach). ⛔ Drei Messfallen verriegelt, jede bezahlt: stilloser DOM (deshalb qa_harness_server wiederverwendet statt nachgebaut) · innerWidth == 0 → exit 2 statt Scheinbefund (ein verborgenes Fenster machte DRIFT-CHAT-CTX-CHIP scheinbar rot) · leere Unauffaelligkeit → Angriffspunkt-Sonde. Beissprobe kumulativ: zwei Koeder (halbe und fehlende Rueckgabe, beide melden sich gruen) werden gefangen UND ein SAUBERER Koeder wird NICHT gefangen — ohne die dritte Haelfte waere jedes Rot Rauschen. |
08_OPS/check_naht_verliert_keine_deklaration.py (Workflow konformitaets-guards.yml ⑺-C, mit --selbsttest) | Verliert eine Naht eine Deklaration, die noch VERWENDET wird? Er zaehlt keine Marker, sondern stellt ALLE Deklarationen (const/let/var NAME =, function NAME() der einen Seite ALLEN der anderen gegenueber — gegen die Basis UND gegen jede eingespeiste Zweigspitze, weil eine Naht auch die NEUE Deklaration eines Zweiges verlieren kann, die master nie kannte. ⛔ Anlass (TOWER#57, Deploy-Naht 25, #2232): die Vereinigung von #2906 mit master verschluckte genau eine Zeile — const _ctxAn = (_chatCtx.docId != null); — und liess ihre VIER Verwendungen stehen. Live waere das ein ReferenceError bei JEDEM setChatContext, also bei jedem Beleg, den ein Mensch in den Chat zieht: kein Konflikt, keine Warnung, kein roter Test. ⭐⭐ Die Klasse: der Entfernungs-Gegencheck vor dem Push zaehlte fuenf Marker, die TOWER#57 NENNEN konnte, und war gruen — ein Gegencheck aus einer Namensliste prueft das Gedaechtnis dessen, der die Liste schrieb, nicht die Datei. ⛔ Kommentare sind kein Code (Narbe von check_tote_node_referenzen): Blockkommentare und reine Kommentarzeilen fallen vor dem Zaehlen der Verwendungen weg. Reichweite ausgewiesen statt behauptet (R-193): er druckt Dateipaare, verglichene Namen und Vergleichsseiten mit; findet er kein Dateipaar, meldet er UNGEMESSEN, nicht gruen. Ergaenzt ⑺-B, ersetzt ihn nicht: ⑺-B misst Verhalten (und findet nur, was auf der gefahrenen Route betreten wird), dieser misst Bestand. | ✅ | TOWER#57, Register #2232. Beissprobe an der echten Stelle, nicht am Koeder: gegen den kaputten Commit a47c85af7 meldet er _ctxAn mit 4 verbliebenen Verwendungen, gegen den reparierten HEAD schweigt er. Selbsttest 5/5 und in beide Richtungen: beisst bei Verlust mit verbliebener Verwendung · beisst bei verlorener function · schweigt bei bewusster Entfernung samt Verwendungen · schweigt, wenn der Name nur noch im Kommentar steht · schweigt beim Umbenennen. Scharf auf Naht 25: 295 Dateipaare, 71927 Namen, 24 Vergleichsseiten, GRUEN. |
08_OPS/screen15/gegenbeweis.py (Hand-Lauf, --satz mit formachse · nachbarschaft · alle) | Ist die Formachse von check_artefakt_register.py einzeln rot-faehig? Sechs Mutationen an der Achse selbst (Anker im Speicher, Ruecklage im finally, Pruefer als Subprozess): findet keine 5-Spalten-Zeile · verschweigt den verworfenen Inhalt · Zeilennummer wandert nicht mit · kopflose Fortsetzungstabelle uebersehen · Kopf und Trenner als Datenzeilen · Artefaktname leer. Jede muss GENAU ihre Probe rot machen, danach ist der Lauf wieder gruen. | ✅ | SCREEN#15, Register #2006. ⛔ Er hat beim ersten Lauf zwei echte Fehler im frisch gebauten Pruefer gefunden, und beide waren die Klasse, gegen die die Achse gebaut ist: ① alle sechs Mutationen blieben gruen, weil der ganze Probenblock HINTER if blind: return 2 stand — das Wort war da, das Tor fehlte, im Pruefer des Tores selbst; ② Probe 6 benutzte einen vierspaltigen Koeder-Kopf und konnte ihre eigene Verletzung nicht sehen (Kopf und Trenner hatten zufaellig die richtige Spaltenzahl) — R-290s zweite Frage, stellt mein Koeder die Schranke nach, die ich pruefe? ⛔ LAUFORT: hand, nicht CI — er mutiert die Datei des Pruefers; zwei gleichzeitige Laeufe wuerden einander die Mutation wegschreiben. ⭐ ZWEITER SATZ seit #2017 (nachbarschaft): fuenf Mutationen an der Nachbarschafts-Ratsche in check_pruefer_schreibt_in_den_baum.py — Eintrag fehlt · Eintrag zuviel · Grund leer · CI-Erkennung blind · ein Korb verliert Faelle. Elf Mutationen ueber beide Saetze, alle exit 0, beide Ruecklagen sauber. ⛔ Das Werkzeug wurde dafuer von gegenbeweis_formachse.py zu gegenbeweis.py verallgemeinert — die zweite Bilanz war strukturell dieselbe, ein zweites Skript daneben waere Doppelbau gewesen (NUXIN-N②). Beim DRITTEN Satz gehoert die Mutationsliste in eine eigene Datei. Wer eine der beiden Achsen aendert, faehrt ihn. |
08_OPS/screen15/gegenbeweis_formachse.py (verschmolzen, #2006 → #2017) | Sabotage-Bilanz der Formachse — sechs Mutationen an check_artefakt_register, jede muss GENAU ihre Zusicherung rot machen. | 🔀 | SCREEN#15, Register #2006 → #2017. ⛔ Nicht geloescht, sondern aufgegangen in 08_OPS/screen15/gegenbeweis.py --satz formachse (Zeile darueber): die zweite Bilanz (Nachbarschafts-Ratsche) war strukturell dieselbe, ein zweites Skript daneben waere Doppelbau gewesen (NUXIN-N②). ⭐ Die Zeile bleibt stehen, weil Guard ㉖ sonst eine Umbenennung nicht von einem Verlust unterscheiden kann — und genau das hat er hier zu Recht gemeldet. |
08_OPS/screen15/mess_spaltengriff.py (Hand-Lauf + --selbsttest; Messstand 08_OPS/qa_harness_server.py) | Was tut der K-33-Spaltenbreiten-Griff am ECHTEN DOM? Genau die Luecke, die check_k33_konsolidierung.py in seiner eigenen Ausgabe benennt („WAS DIESER GUARD NICHT MISST: Verhalten am echten DOM"). Acht Zusicherungen, jede einzeln rot-faehig: Z1 die ausgelieferte engine.js traegt die Griff-Quelle (Marker mit Bytes + Pruefsumme) · Z2 der Renderer lief (Tor, kein Verbot) · Z3 ausserhalb des Vollbilds KEIN Griff · Z4 im Vollbild jeder Daten-Kopf GENAU EINEN · Z5 ein Zug setzt width UND min-width · Z6 die Breite ueberlebt den naechsten _paint · Z7 … und das Schliessen des Vollbilds · Z8a/b der Ueberlauf endet an einem SCROLLBAREN Kasten, im Vollbild UND in der Flaeche. ⛔⛔ Z8 war anfangs nicht rot-faehig und die Sabotage hat es gefunden: das erste Kriterium documentElement.scrollWidth > clientWidth kann auf dieser Shell NIE eintreten, weil components.css:440 html, body{overflow-x:clip} setzt — ein clip-Kasten ist kein Scroll-Container. ⭐ Die richtige Frage stellt das Haus selbst (core_shell.html:2904, UX#01/#1669: „es gab KEINEN Wischweg … stiller Schnitt, kein Hinweis"), und sie ist uebernommen statt neu erfunden: gemessen wird die AHNENKETTE. ⭐ --griff-auch-ausserhalb dreht die Erwartung von Z3 um — sagt JC Ja zur Variante „Griff auch in der Flaeche", existiert der Pruefer fuer den neuen Kanon bereits. | ✅ | SCREEN#15, Register #2003, Bericht BEFUND_SPALTENGRIFF_AM_DOM_2026-09-15.md. Echtlauf exit 0, 36 von 36 Zusicherungen gruen, 0 uebersprungen (2 Flaechen x 2 Breiten). Selbsttest exit 0: sechs Mutationen am Draht (page.route, nie an der Datei), jede macht GENAU ihre Zusicherung rot, Rueckstellung sauber. ⛔ Zwei der Mutationen haben echte Fehler im Pruefer gefangen, bevor er lief — S1/S2 loesten anfangs den Marker Z1 statt der Zusicherung aus (eine Sabotage, die die Auslieferung verfaelscht, misst den Marker statt der Regel), und S6 deckte das nicht rot-faehige scrollWidth-Kriterium auf. ⚠️ Z2 hat bewusst KEINE Sabotage (R-291): es ist ein Tor, kein Verbot — „keine Tabelle auf der Flaeche" ist legitim und wird als UEBERSPRUNGEN mit Grund ausgewiesen; was Z2 schuetzt, traegt S5. LAUFORT: Hand — braucht Playwright + Chromium, laeuft nicht in der CI; ohne Playwright exit 2 mit Preis der Enthaltung, nie gruen. |
check_meldeweg_kommt_an (#535) | Kommt die Meldung eines Wächters an? Vier Zielorte statt 77 Einzelaufträge: agent_events-POST-Felder gegen die echte DDL · Stempel-Pfad nicht git-ignoriert + kein Selbsttest-Schreiben ohne finally (B-25) · kein && exit 0 als letzte Commit-Zeile · jeder Prüfer-Step läuft nach rotem Vorgänger. | ✅ | GUARD#03. JCs Zahl war 0 von 77 Prüfern mit Meldeweg-Testfall. Läuft in schema-register-guards ⑮. Selbsttest beißt je Achse in beide Richtungen. |
check_import_aufloesbar (#587) | Trägt der BRANCH das importierte Modul? Modulebene-Importe gegen git ls-files + git check-ignore — nicht gegen die Festplatte. | ✅ | GUARD#03, aus dem 502-Ausfall vom 22./23.08.: fünf Skills importierten credentials._drive, die Datei war von einer .gitignore-Allowlist still verschluckt, app.py lädt Skills beim START ⇒ uvicorn tot, 13 h unbemerkt. Schnitt zu ⑯ check_bundle_vollstaendig: der fragt „liegt die DATEI im Bundle?“, dieser „gibt es das MODUL überhaupt?“ — zwei Fragen, kein Überlapp. Läuft in schema-register-guards ⑰. |
messort.py (#574, Baustein) | Urteilt ein Wächter über den BRANCH oder über die Festplatte? getrackte() / nur_getrackte() — nicht messbar = ENTHALTUNG mit Meldung, kein stiller Rückfall auf glob. | ✅ | GUARD#03. Bewiesen am eigenen Guard: eine ungetrackte Kopie in 08_OPS/ erzeugte einen Befund über eine Datei, die kein PR je enthielt. In der CI fällt das nie auf (frischer Checkout) — es trifft nur den, der lokal misst. Kein eigener Wächter, sondern Baustein: verdrahtet in check_pruefer_nennt_laufort. |
ops_pfad.py (#606, Baustein) | Liegt 08_OPS auf dem Pfad — auch ohne Aufrufer und in BEIDEN Gestalten? sichern(__file__); der Baustein antwortet mit seinem eigenen ORT, nicht mit parents[1]. Grund: sechs gebuendelte Module leben im Repo geschachtelt und im Dienst-Bundle teils FLACH (imap_mark_seen.py) — parents[1] waere dort das Elternverzeichnis des Dienstes. Selbsttest misst beide Gestalten samt Gegenrichtung (ohne Vorspann ModuleNotFoundError). | ✅ | BAUSTEIN#01 (26.09.2026). Herkunft des Schnitts: 07_TESTS/iter67/_wurzel.ops_pfad (MCP#01, #1672) — Finden im Helfer, Handeln sichtbar an der Rufstelle. Kein eigener Waechter, sondern Baustein: bewacht von check_import_aufloesbar (ACHSE 3) und check_bundle_vollstaendig. |
check_kein_modellname_im_code (#1025) | Steht in dieser Datei ein Modellname, der nicht über die Policy aufgelöst wird? Löst JCs zweite Satzhälfte ein („ALLE 45 heute und zukünftige SKILLS CHECKEN") — den Bestand räumt man einmal auf, „und zukünftige" verlangt einen Wächter. Ratchet: modellname_ausnahmen.json friert 39 Dateien ein (bei 155 gelesenen), jeder Eintrag mit Eigentümer + Ziel-Etappe; die Liste darf nur schrumpfen. Beide Richtungen scharf: neuer Name ausserhalb der Liste = rot, verwaiste Ausnahme ebenfalls. ⛔ Unterscheidet Codezeile von Kommentar/Docstring (ein Name im Kommentar ist Dokumentation, keine Verdrahtung) und zählt gelesene Dateien mit — 0 gelesen = exit 2, nicht grün. ⛔ Reichweite steht in der Ausgabe: läuft im Repo, die n8n-Live-Definitionen liegen ausserhalb | ✅ | MODELL#01, Auftrag TOWER#52. Einlösung, kein Neubau-Wunsch: KONZEPT_MODELL_POLICY.md §10 beschrieb ihn seit Wochen, er existierte nicht. Kein Doppel zu check_modellkarte_gegen_code (ADMIN#03): der prüft die Anzeige-Seite, dieser die Code-Seite. Selftest 7/7 beide Richtungen + Mutationsprobe am echten Baum 4/4. Eingehängt als ㉒ in konformitaets-guards.yml |
check_js_syntax (#965) | Parst jeder Script-Block der grossen Flächen? core_shell.html (1,34 MB in EINEM Block, vier Lanes täglich) hatte KEINEN Syntax-Wächter — ein falsches Anführungszeichen tötet die ganze Shell. Prüft drei Flächen (core_shell + gabriel + dokumente2, SCREEN#03 05.09. / #1645) per node --check. ⛔⛔ Die Reichweite war ZWEIMAL zu klein, und das zweite Mal stand der Grund schon im Kopf der Datei: check_gabriel_syntax zeigte auf die kleinere Fläche, check_js_syntax dann auf zwei — und meldete GRÜN, während SCREEN#03 3.611 Bytes JavaScript in dokumente2.html änderte, ohne dass die Datei je geöffnet wurde. ⭐ Ein Prüfer, der die geänderte Datei nicht kennt, meldet nicht „kein Fehler“, sondern „ich habe nicht nachgesehen“ — und beides sieht gleich aus. ⛔ Weiter UNBEWACHT (ausdrücklich, R-193): fragekai2 · rka2 · rka_chat · verlauf2 · abgleich2 · inbox2 · versand2 · projekt2 · katalog · vertical_slice_standalone. Bekannte Lücke, kein Versehen — sie in einem Nutzlast-PR zu schließen hieße, fremdes Rot in einen fremden Vorgang zu ziehen. Als eigener Befund bei TOWER#53. ⛔ Enthaltung gemessen: node trennt Modul/klassisch nicht — beweist „parst", nicht „parst im richtigen Modus" | ✅ | TOWER#50, aus einem ADMIN#03-Befund. Kein Neubau: check_gabriel_syntax gab es seit 29.05., zeigte auf die kleinere Datei und lief in keinem Workflow — der alte Name bleibt als Weiterleitung (smoke_lm08/09 rufen ihn). Eingehängt als ㉑ in konformitaets-guards.yml |
check_kein_select_stern.py (#1645, Workflow konformitaets-guards.yml ㉒) | **Lädt eine Fläche eine SCHWERE Tabelle mit select=*?** select= war vier Mal dieselbe Wunde, und drei Mal wurde sie einzeln repariert, ohne dass die nächste auffiel: #1642 core_shell Belegliste 22.422,8 → 854,8 KB · #1645 dokumente2 6.098,0 → 83,0 KB · txn_master 1.410,2 → 428,8 KB · prf_contracts 588,5 → 103,0 KB (alle an der echten REST-Antwort gemessen, nicht an der Tabellengröße — die misst wegen TOAST zu klein). ⭐ Und keiner der Screens hatte sich geändert — der BESTAND hatte sich geändert (#2548): select= ist ein offener Scheck auf jede Spalte, die morgen dazukommt, deshalb ein Riegel und keine Sichtprüfung. Die Meldung nennt Tabelle UND Byte-Zahl („31.222 Bytes je Zeile“ bewegt, „unsauber“ nicht). ⛔ ENTHALTUNG, gemessen und angesagt: er streift zuerst die Kommentare ab — in #1642/#1645 steht select= mehr als ein Dutzend Mal als DEMENTI, und ein Textmuster kann Behauptung und Dementi nicht unterscheiden; ein Guard, der die Dokumentation der Reparatur als Verstoß meldet, bestraft die Verbesserung. Er sagt nicht, ob eine benannte Liste die RICHTIGEN Spalten nennt (das kann nur der Vergleich mit dem Renderer) und prüft nur 06_DASHBOARD/.html gegen vier benannte Tabellen — „nichts gefunden“ heißt „in DIESEN Dateien auf DIESEN Tabellen nichts“, nicht „nirgends“. | ✅ | SCREEN#03 → TOWER#53 (Register #1645), aus JCs Auftrag „dokumente2.html auch reparieren“ und der Hausregel „1× ändern → alle Screens. Morgen sind es 1000 Screens.“ ⛔ Status ✅ = VORHANDEN UND LAUFEND (Echtlauf exit 0, als ㉒ in konformitaets-guards.yml eingehängt, # LAUFORT: ci) — nicht „inhaltlich abgenommen“. ⭐⭐ Positivkontrolliert in BEIDE Richtungen, und der Läufer liegt in der CI: --selftest fährt 11 Fälle (5× muss anschlagen, 5× muss schweigen, 1× Zeilennummer hält) und läuft vor dem Guard. Die Fälle stehen auf Modulebene, 07_TESTS/screen03/ importiert sie (13 pytest-Fälle) statt sie nachzubauen — zwei getrennt gebaute Listen wären die nächste Drift-Quelle. ⛔ Warum der Selftest in die CI MUSSTE: pytest läuft in keinem Workflow — das sagt check_test_sammlung in diesem Register selbst. Eine Positivkontrolle, die nur von Hand läuft, ist beim nächsten Umbau nicht da. ⛔ Was er NICHT ersetzt: ein geteiltes Select-Register über core_shell.html ⟷ dokumente2.html gibt es nicht (getrennte Dokumente, verschiedene Auth-Träger) — das bleibt Baustein-Entscheid bei Tower; dieser Riegel verhindert die fünfte Instanz, er beseitigt nicht die Bauform. ⛔⛔ FAHNEN-RIEGEL NACHGERUESTET — auf einen FREMDEN Befund, der mich Stunden später traf: DEPLOY#02 (#1647, PR #2568) schrieb am 05.09. „ein vertipptes --selbstest wäre der scharfe Lauf gewesen“, und ihr Bestands-Wächter check_fahnen_riegel_bestand.py fand genau diese Bauart in diesem Prüfer: _selftest() if "--selftest" in sys.argv else main() fällt bei jedem Tippfehler still in den anderen Zweig. ⭐ Hier hätte das nichts zerstört (das Werkzeug liest nur), aber es hätte GRÜN gemeldet — wer --selbstest tippt, glaubt die Beißprobe gefahren zu haben und hat den Guard gefahren. Ein Werkzeug, das die Frage nicht versteht und trotzdem antwortet, ist gefährlicher als eines, das schweigt. Riegel nach Vorlage check_register_nummer.py:unbekannte_argumente (#1279), beißgeprobt in vier Richtungen am ECHTEN Einstieg als Unterprozess (--selftest→0 · --selbstest→2 · --self-test→2 · selftest→2 · --help→0), gesichert in 5 der nun 18 pytest-Fälle. ⭐ Ein Guard, der Stunden nach seiner Geburt die erste neue Instanz seiner eigenen Klasse fängt, ist der Beleg dafür, dass Riegel besser sind als Vorsätze. |
check_screen_factory_contracts.py (Workflow konformitaets-guards.yml) | Halten die Screens die Verträge der Screen-Factory? ⛔ Sinn hier bewusst KURZ und aus dem Kopf der Datei gelesen, nicht aus ihrem Verhalten hergeleitet — ich habe sie nicht inhaltlich geprüft, das ist Sache ihres Owners. | ✅ | Zeile nachgetragen von SCREEN#03 05.09. (#1645) — der Artefakt-Ratchet meldete sie als unregistriert, ausgelöst durch meine Nachbarzeile, und das ist GEMESSEN, nicht vermutet: sie war nie mit ihrem NAMEN belegt, sondern nur über das seltene Token contracts — das stand genau 2× im Register, und SELTEN_MAX ist 2. Meine Zeile nennt prf_contracts und drückt es auf 3; damit kippte die Zufalls-Belegung von selten:contracts auf leer. (screen 17→24, factory 0→0 — beide waren ohnehin keine Belege.) ⭐⭐ Die Zeile hat also nichts kaputtgemacht, sondern eine Zufalls-Belegung entlarvt — und zwar das ZWEITE dokumentierte Mal: PERSONA#01 hat am 03.09. (#1313) denselben Vorgang bei check_projekt_nummer.py beschrieben. Zwei Mal derselbe Mechanismus ist ein Muster, kein Einzelfall — die Seltenheits-Heuristik verteilt Grün nach Wortstatistik des Registers, also nach etwas, das jede fremde Zeile verändert. Der Guard weist das selbst als „OBERGRENZE des Zweifels, nicht die Zahl der Lücken“ aus; dass daraus eine wandernde Rot-Meldung wird, die immer die nächste Lane trifft, ist ein eigener Befund an TOWER#53 (Wurzel: Beleg über Namen erzwingen, nicht über Tokens). ⚠️ Owner-Zuordnung ist meine ANNAHME (aus git log: angelegt/erweitert von GUARD#03/#589 (#1991), später von PROJEKT#07/#826 im Sammel-Vorspann berührt (#2206)) — GUARD#03/PROJEKT#07 korrigiert sie, falls falsch. ⛔ Status ✅ steht für VORHANDEN UND LAUFEND — und genau das habe ich gemessen, nicht mehr: Echtlauf exit 0, Kopf trägt # LAUFORT: ci, in konformitaets-guards.yml eingehängt (Fundstelle gegriffen). NICHT geprüft: ob er inhaltlich das Richtige prüft, und ob seine Enthaltungen stimmen. Das ist Sache des Owners, nicht meine Messung. |
check_raw_zeiger_trifft.py (ABLAGE#01/#1266, PR #2403) | Trifft der RAW-Zeiger die Bytes, die er verspricht? ⛔ Sinn hier kurz und aus dem Kopf der Datei gelesen — ich habe sie nicht inhaltlich geprüft, das ist Sache ihres Owners. | ✅ | Zeile nachgetragen von SCREEN#03 05.09. (#1645) — kam mit dem master-Nachzug herein und wurde vom Ratchet gemeldet, ausgelöst durch meine Nachbarzeilen. GEMESSEN, nicht vermutet: nie mit dem NAMEN belegt, nur über das seltene Wort trifft — 2× auf master, Schwelle ist 2; meine Zeilen drücken es auf 3, also kippte die Zufalls-Belegung auf leer. ⚠️ Owner-Zuordnung ist meine ANNAHME (aus git log: 29b13e81 ABLAGE#01/#1266, PR #2403) — ABLAGE#01 korrigiert sie, falls falsch. ⛔ Status ✅ = VORHANDEN UND LAUFEND, und nur das habe ich gemessen (Datei existiert, kam mit #2403 auf master). NICHT geprüft: ob sie inhaltlich das Richtige prüft. ⛔⛔ UND HIER IST DER EIGENTLICHE BEFUND, der nicht in eine Artefakt-Zeile gehört, aber gemessen werden musste: DAS IST DAS DRITTE MAL DERSELBE MECHANISMUS — PERSONA#01 03.09. (#1313) bei check_projekt_nummer.py, SCREEN#03 heute bei check_screen_factory_contracts.py (contracts 2→3) und hier (trifft 2→3). Dreimal ist kein Zufall, sondern die Bauart. ⭐⭐ DIE ZAHLEN, damit die Wurzel keine Meinung ist: von 162 Artefakten im Scope sind 131 mit ihrem vollen Namen belegt, 30 nur über ein seltenes Wort — und 13 davon stehen GENAU AUF DER SCHWELLE (2×). ⇒ Jede weitere Nennung eines dieser 13 Wörter, irgendwo im Register und von irgendeiner Lane, macht ein fremdes Artefakt rot. Das ist eine wandernde Rot-Meldung, die immer die nächste Lane trifft und sie fremde Register-Zeilen schreiben lässt — inklusive fremder Owner-Zuordnungen, die dann Annahmen sind. Wurzel: Beleg über den NAMEN erzwingen, die Seltenheits-Heuristik höchstens als Warnung führen. Liegt als eigener Befund bei TOWER#53 — nicht in diesem Vorgang, weil der Guard KANON#01 gehört. |
miss_liegt_der_beleg_wo_er_steht.py (NACHTWAECHTER#02/#1825, betriebs-waechter.yml ㉛) | Liegt der Beleg dort, wo seine documents-Zeile es sagt? HEAD je Sonde (3 Klassen x 2), Bucket aus der Zeile, eigener Schlüsselsatz zuerst, sonst der globale als benannter Lesezeuge. ⛔ Drei Zustände: rc 0 grün · rc 3 Befund · rc 2 KEIN URTEIL (Secret fehlt, 403, Quelle tot, Positivkontrolle tot) — rc 2 färbt den Job nicht und wird als kein_urteil gestempelt (beleg-liegt). --selftest netzfrei in sechs Richtungen + Leck-Riegel (kein Bucket-Name im Klartext). Secrets stehen AM SCHRITT: auf Workflow-Ebene kippen sie ㉘ in 322 falsche „nirgends“ (simuliert 08.09.). | 🔨 eingehängt, gatet nur mit rc 3 | betriebs-waechter 03:47 UTC · Stempel beleg-liegt |
check_vorlagen_string_zerbrochen (#1452) | Zerbricht ein KOMMENTAR einen Vorlagen-String, ohne dass die Datei kaputtgeht? Ein Abtaster fuehrt Zeichen fuer Zeichen mit, worin er steht (Programmtext, die drei String-Arten mit ${…}-Verschachtelung, Kommentare, Regex). Steht er IM Vorlagen-String und sieht / oder <!--, meldet er nur, wenn darin ein Backtick steht — denn der beendet den String, und der Rest des Kommentars wird Programmtext. ABGRENZUNG zu check_js_syntax: der fragt parst der Block? und blieb beim Anlassfall GRUEN, weil die Datei weiter parst; dieser fragt steht der Kommentar an der falschen Stelle?*. ENTHALTUNG: kennt genau zwei Kommentar-Arten; andere Wege, einen String zu zerbrechen, sind nicht seine Frage. | ✅ | STERN#01 (Register #1452), aus dem EIGENEN Fehler in #1425: acht Backticks in einem HTML-Kommentar innerhalb eines Vorlagen-Strings. ⛔⛔ Drei eigene Denkfehler beim Bau, alle drei erst am echten Bestand sichtbar und jeder mit dreistellig viel Falsch-Rot: im Vorlagen-String bis */ gesprungen · das letzte bedeutende Zeichen aus dem Rohtext zurueckgelesen und im Zeilenkommentar der Vorzeile gelandet · verschachtelte Vorlagen mit einem Schalter statt einem Stapel gefuehrt. ⭐ Alle drei haben eigene Proben im Wortlaut ihrer Fundstellen. |
check_konsolensicher (R-155) | kein Guard stirbt auf JCs Windows-Konsole | ✅ | KANON#01, Tower-Auftrag nach 4 Fällen an einem Tag — der vierte ließ check_migration_numbers beim MELDEN einer echten Kollision abstürzen. Ratchet: 0 Absturz-Zeichen, jeder neue = ROT. Umlaute bleiben erlaubt (zielt auf Abstürze, nicht auf Entdeutschung). In CI unsichtbar, weil der Runner UTF-8 spricht |
check_workflow_startet.py (#1352) | Startet ein Workflow überhaupt? Konfliktmarken am Zeilenanfang · YAML-Parsbarkeit, über alle Dateien in .github/workflows (.yml UND .yaml). ⛔ Ein Workflow mit Startup-Fehler erzeugt KEINE Check-Runs — er fehlt in der Prüfliste des Vorgangs, nicht rot, gar nicht; sein Ausfall sieht aus wie Vollständigkeit. | ✅ | STERN#01 — Anlass: drei Konfliktmarken auf master (schema-register-guards.yml), 54 Schritte hinter einem grünen Haken, zehn Vorgänge betroffen. ⛔ ENTHALTUNG: noch in keinen Workflow eingehängt — ein Riegel, der nur von Hand läuft, fängt genau den Fall nicht, gegen den er gebaut ist. Einhängen gehört TOWER#53. |
check_sparse_checkout | CI-Step liest Datei, die der Runner nicht holt | ✅ | KANON#01. 3. Vorfall derselben Klasse — der Merksatz stand drei Zeilen über der vergessenen Stelle: ein Merksatz ist kein Guard. Ehrlich: schließt keine Lücke (CI merkt es ohnehin), spart die Runde. Selftest an den realen Vorfall gebunden |
iban-guard.yml (Workflow) | fängt echte IBANs im BESTAND, nicht nur im Zulauf | ✅ | 🔒SICHERHEIT / Tower#34-Auftrag 08.08. Anlass: gemeldet waren 2 Konto-IBANs in 7 Dateien — der vollständige Scan fand 98 gültige IBANs in 164 Dateien / 1536 Vorkommen, darunter 92 FREMD-IBANs (personenbezogene Daten Dritter) in einem unreferenzierten Live-Datenzug. ⛔ Bewusst KEIN paths-Filter — ein Leck-Guard, der nur registrierte Pfade liest, ist kein Leck-Guard. Zulauf bewachen scratch_csv_iban.json (#1646/#1649) + .gitignore |
betriebs-waechter.yml (Workflow) | prüft ZUSTAND (DB/n8n + Live-Parität), nicht Dateien | ✅ | RKA#10 / Tower#33-Auftrag 08.08. ⛔ Eigener Workflow mit Absicht: schema-register-guards ist netzfrei + secret-frei und läuft auf jedem PR; diese Guards brauchen Netz und Secrets. Hängte man sie dort ein, hätte jeder Fremd-PR Datenbank- und n8n-Zugriff — eine stille Ausweitung, die niemand beschlossen hat. Deshalb kein on: push, kein on: pull_request — nur Cron. ④ Live-Parität (SHELL#03, 17.08.): deploy_guard.py parity — sha256(live) == sha256(HEAD:src) je Manifest-Datei; fängt die Klasse „200, aber ALTER Inhalt" (engine.js ⋯-Menü, #1820), die deploy.yml nur beim Push sieht. Stempel live-paritaet |
check_leiter_umkehrprobe.py | Öffnet die neue Vertraulichkeits-Leiter irgendeinen Zugriff, der heute geschlossen ist? | ✅ | WELLE2 B3 / Tower#33 Welle 1 D. Die Umkehrprobe ist der Gegenbeweis zur Leiter: nicht „schließt sie richtig", sondern „öffnet sie nichts Neues" — die Richtung, die ein Scope-Guard sonst übersieht ([[feedback_scope_guards_muessen_beide_richtungen_pruefen]]) |
check_tote_node_referenzen.py | jede $('…')-Referenz zeigt auf einen Node, den es wirklich gibt | ✅ | RKA#11 / Tower#33-Auftrag 08.08., aus Fund #375: im Skill-Router referenzierte Record Skill Result einen Node, den es nicht mehr gab — der Zweig lief still ins Leere statt zu scheitern |
check_transfer_kanon_ssot.py | der Shell-Spiegel des Transfer-Kanons darf nicht vom SSOT abdriften | ✅ | FINANZ/KANON. Hat --selftest (prüft den Guard selbst, JE-156) |
check_abgleich2_matcher.py | abgleich2.html zeigt Zuordnungs-Vorschläge (K-21 approve-Karten) — zeigt der Screen, was der Matcher wirklich rechnet? | ✅ | RECONCILE#01 / BUCHHALTUNG#01. Spiegel-Guard gegen die Klasse „Anzeige verspricht, Funktion hält nicht" |
check_kontakte_stamm.py | READ-ONLY-Gegenprobe zum Verify-Block der Mig 251 | ✅ | KONTAKTE#02. Schwester von check_migration_ledger.py, Klasse „Mig-Header ist kein Beweis": das Ledger sagt nur „251 APPLIED" — es sagt nicht, ob die Migration auch gewirkt hat ([[reference_migrations_header_ist_kein_beweis]]) |
check_screen_sagt_seine_kappung_an.py | Wenn ein Screen eine Liste serverseitig kappt (&limit=N) — sagt er es dem Nutzer, oder sieht die Teilsicht aus wie der ganze Bestand? | ✅ | VERTRAG#03 (PR #1806), aus JCs Befund 16.08. „genau 100 Zeilen" — eine Liste, die exakt an ihrem Limit endete und sich als vollständig ausgab. ⭐ Klassen-Guard, kein Einzelfix: derselbe Screen-Code fährt an ~10 Stellen mit Limit; 5 sagen die Kappung schon an (Dokumente · Konten · Monatsabschluss · Abgleich · Klärung) — der Guard friert diesen Bestand als Ratchet ein und fängt jede weitere Liste, die es nicht tut. Prüft zweierlei: der Screen merkt die Kappung (.length >= <limit> in derselben Funktion) und er sagt sie (ds-callout / t("…kappung") / „gekappt"/„Teilsicht"). Hat --selftest. Zeile von Tower vorab eingetragen, damit #1806 nicht am Register-Ratchet hängt ⛔⛔ SCREEN#05 (05.09., #1645) — DRITTE MERK-FORM, und sie ist die stärkste: der Guard erkannte .length >= limit und capMeta(...), aber nicht den Vergleich gegen die echte Gesamtzahl aus restCount(...). ⭐ Genau das nennt der Guard selbst den Beleg (>= limit sei „nur ein VERDACHT“) — er erkannte also den Verdacht an und den Beleg nicht. GEMESSEN: secBuchungen stand auf merkt=False, sagt=True, obwohl SCOPE#03 dort die bessere Lösung gebaut hatte (_gesamt > geladen). ⛔ Der Ratchet-Eintrag secBuchungen ist damit ENTFERNT (5 → 4 eingefroren) — er war doppelt veraltet (nannte limit=100, es sind 1000) und hat drei Wochen fertige Arbeit verdeckt, während er wie Aufsicht aussah. ⭐ Die Mutationsprobe am echten Screen hat den ersten Bau des Musters in derselben Minute widerlegt: es suchte den Vergleich nur NACH der Zuweisung, im Screen steht er rund 50 Zeilen davor (Zeichenfunktion oben, Zähler unten) — Textreihenfolge ist in JS keine Ausführungsreihenfolge. --selftest fährt jetzt drei Prüfungen: Formen · Bestands-Form (8 Fälle, davon 4 Schweige-Fälle) · Sabotage. |
check_skill_select_liest_echte_spalte.py | Fragt ein Skill/Ops-Script per PostgREST <table>?select=… eine Spalte ab, die es nicht gibt? SELECT-Achse des Vokabel-Dubletten-Registers (vokabel_dubletten_twins.py, select_axis) | ✅ | KONTAKTE#03b (17.08.), Wurzel des Kontakte-500ers: drei Contact-Skills lasen user_mandant_roles.role, die Spalte heisst rolle (Mig 209:141) → 400 → 500 → Screen leer. Schwester von check_screen_liest_echte_spalte.py (Code-Achse = JS-Punktzugriff), dieselbe Register-SSOT. Netzfrei, --selftest, CI konformitaets-guards ⑨. Mutation (Fix zurückgedreht) = 3🔴 gemessen |
check_positionen_gehen_nicht_verloren.py | Wenn ein Baustein Einzelposten (positions/line_items) aus einem Dokument liest — kennt er dann auch einen ORT, an den sie gehören (Tabelle contract_positions, ihre Abbildung, ihr Schreibweg)? | ✅ | VERTRAG#05 (19.08., Register #466), aus der VERTRAG#04-Messung 18.08.: Positionen wurden an drei Stellen gelesen und an keiner gespeichert — contract-extract kannte sie gar nicht · vision-extract liefert line_items im Output ohne Schreibpfad (skill.py:471) · der n8n-PDF-Invoice-Zweig extrahiert sie (:267,:325) und lässt sie im Persist-PATCH (:338) weg. ⭐ Dieselbe Arbeit dreimal bezahlt, dreimal weggeworfen — und unsichtbar, weil ein Ergebnis ohne Positions-Feld wie ein Vertrag ohne Positionen aussieht. Fachlich hängt Geld daran: drei der fünf IONOS-Verträge mischen Eigentümer, ohne Positions-Ebene kann nur pauschal pro Vertrag weiterberechnet werden. ⛔ Prüft ZIEL, nicht „steht im Ergebnis": die erste Fassung liess ein Ergebnis-Feld genügen und war damit grün für genau den Zustand, in dem die Daten verloren gingen — der eigene --selftest hat das aufgedeckt, bevor der Guard in die CI kam. Ratchet: tgv-skill-vision-extract eingefroren mit Eigner HEIMHOLUNG#02 (Welle 3). Nennt seine Reichweite bei jedem Lauf (R-193). Netzfrei, --selftest, CI konformitaets-guards ⑬ |
check_vorschau_ist_die_mail.py | Der Screen sperrt den Versand, bis JC den Trockenlauf gesehen hat, und sagt ihm dabei zu „die geht raus, keine andere" — stimmt diese ZUSAGE, wird die Vorschau aus derselben Vorlage und mit denselben Eingaben gebaut wie der Versand? | ✅ | BUCHHALTUNG#06 (19.08., Register #474). Anlass am erzeugten Objekt gemessen (C3/Juni, 28 Belege, gleiche Auswahl): die Vorschau kam aus einem Port von wf_e_v2_4_build_mail.js — einer Datei, die im eigenen Kopf „[GESTAGED, nicht deployed]" trägt; gefahren wird n8n hVt5zeQkKX0vk521. Betreff verschieden · Rumpf 33.742 vs. 28.539 Zeichen · Konto-Spalte 8/28 vs. 0/28. ⛔ Sicherheits-Gate, keine Kosmetik: JC hat seine Freigabe für accountant_email ausdrücklich davon abhängig gemacht, die Mail vorher exakt so zu sehen. ⭐ Die bewachte Klasse: der alte Paritäts-Guard war GRÜN — er mass gegen ein Doppel, das niemand fährt; eine Parität gegen die falsche Vorlage ist teurer als gar keine, weil sie wie ein Beweis aussieht. 6 Fragen über 5 benannte Regale (Screen · versand2 · app.py · Port · gestempelte Live-Kopie), Selbst-Sabotage 6/6 beisst, Exit 2 bei stumpfem Guard. ⛔ Misst den per ast entnommenen, kommentar- und docstring-freien Code des Endpunkts — der erste Anlauf blieb grün, weil derselbe Ausdruck im SERVICE_VERSION-Kommentar stand: eine Prosa-Zeile hatte den Code-Beweis erfüllt. Netzfrei; die Frage „ist die Kopie noch die laufende?" hat einen eigenen Prüfer (check_wfe_kopie_ist_live.py, bewusst NICHT in CI — ein CI-Guard an fremder Verfügbarkeit wird beim ersten Ausfall abgeschaltet). CI konformitaets-guards ⑯ |
check_beleg_vorschau_zeigt_das_dokument.py (BELEG#02/#3210, ⛔ noch KEIN Workflow — Verdrahtung ist Tower-Ball, betriebs-waechter.yml gehört BETRIEB#01 und die Workflow-Laufzeit KOSTEN#02) | Kommt das Dokument auf dem Bildschirm AN? Die vierte Frage an dieselbe Datei: check_ein_weg_zum_original fragt nach der Adresse, check_belege_ohne_ablageort nach dem Ablageort, die Konformitäts-Regel nach dem Vorhandensein des Helfers — keine, ob die Bytes als das ausgeliefert werden, was sie sind. Anlass: JC sah keine Vorschau, urteilte nach Metadaten und hätte beinahe eine eindeutige Rechnung über 5.000 EUR löschen lassen. Wurzel: Content-Type: binary/octet-stream ⇒ leeres <iframe>, ohne Fehler, bei data-preview-state="ok". Drei Achsen: (1) die presignte URL sagt ihren Typ an und die Signatur trägt ihn · (2) unbekannte Endung bekommt keine Ansage · (3) live, optional: Name gegen Magic-Bytes — ohne Zugang kein Urteil, nicht grün. | ✅ | BELEG#02. Beissprobe 5 Lagen (0·3·3·8·0) — sie hat die erste Fassung des Guards widerlegt. Volllauf über 767 Objekte: Code-Achse 0, dazu 5 Bestandsbefunde (#8465 · #8433 · #8421 · #8209 · #7323, Klasse #7323 „der Name lügt über den Inhalt“), die den Wächter bewusst nicht rot machen (--daten-scharf; Bauform #537 — dauerrot verdeckt jeden Wächter dahinter). |
heile_beleg_endung_luegt.py (BELEG#02/#3210, ⛔ kein Workflow — schreibt mit --apply) | Der Dateiname verspricht etwas anderes als die Bytes. .pdf auf PNG/JPEG, .bin auf JPEG — die Vorschau bleibt leer, und kein Programmtext kann das heilen: der Leser sagt an, was der Name behauptet. Das Werkzeug legt das Objekt unter der WAHREN Endung ab (mit richtigem Content-Type) und setzt storage_url/raw_key um — serverseitig auf den ALTEN Wert gefiltert. ⛔⛔ Und es benennt NICHT blind um: liegen dieselben Bytes auch an einem anderen Beleg, ist es kein Namens-, sondern ein Datei-Problem (Klasse #7323) ⇒ übersprungen mit Grund. | ✅ | BELEG#02. --trocken ist die Vorgabe, --apply verlangt --jc-wort "<Zitat>". Selbsttest 8 Lagen (Testdoppel, kein Netz). ⭐⭐ Der Trockenlauf hat einen eigenen Fehler gefangen: die erste Fassung las überall nur 16 Bytes und bildete den sha256 darüber — die Zwillingsprobe fand nie etwas, und #7323 stand unter UMBENENNEN, genau der Fall, bei dem das falsch gewesen wäre. Sichtbar nur, weil der Lauf die Byte-Zahl mitdruckt. Jetzt: Kopf für das Format, volle Bytes für die Verdächtigen. Stand 27.09.: 4 umzubenennen, 1 übersprungen, nichts geschrieben — JCs Wort steht aus. |
check_orange_ist_erreichbar.py | JCs Ampel kennt drei Stufen (belegt · begruendet · offen) — kann ein Mensch die MITTLERE ueberhaupt herstellen, oder gibt es sie nur zum Anschauen? Haelt Leser (buchungs_basis.lade_begruendungen), Schreiber (Endpunkt txn-beleg-nicht-noetig) und Knopf (Klaerungskarte) zusammen — plus die Wand (Entitaet aus der GELESENEN Buchung, nie aus dem Client), den dry_run-Default und den Spalten-Kanon gegen Phantom-Spalten. | ✅ | BUCHHALTUNG#04 (22.08., Register #557). Anlass: beleg_nicht_noetig stand repo-weit GENAU EINMAL — an der lesenden Stelle; die orange Stufe war unerreichbar. CI konformitaets-guards Schritt ⑯d (beide Pfadlisten) mit --stempel (Meldeweg). Selbsttest in beide Richtungen: 8 Schadens-Mutationen ⇒ rot, 3 harmlose ⇒ bleiben gruen. |
check_wand_am_chunk.py | Traegt JEDER Chunk den Mandanten und die Vertraulichkeitsstufe — und stempelt JEDER Schreiber sie? | ✅ | SICHERHEIT#05 (24.08., Register #623). Anlass gemessen an Beleg #8274: der Such-Chunk trug entity=unassigned und confidentiality_level=None. Bestand: 101 von 1338 Chunks ohne mandant_id — und search_knowledge_scoped prueft c.mandant_id = ANY(...), NULL trifft kein ANY(). ⭐ Die Zahl, die den Auftrag traegt: 74 Chunks, deren QUELLDOKUMENT einen mandant_id hat, tragen selbst keinen (umgekehrt: 0) — der Wert lag vor und wurde nicht mitgenommen, also ein Schreiberfehler, kein Datenmangel. ⛔ ZWEI FRAGEN, GETRENNT, weil sie verschieden altern: ① netzfrei — stempelt jeder der fuenf benannten Schreiber beide Achsen (R-193: die Liste steht im Guard und wird mitgedruckt; wer einen sechsten baut, traegt ihn ein) ② --live — wie viele Zeilen tragen die Achsen heute. ⛔⛔ B-29 eingebaut: ② meldet IMMER zuerst die Gesamtzahl; ist sie 0, ist der Lauf UNGUELTIG statt gruen — „0 mandantlose Chunks“ heisst sonst entweder „alles sauber“ oder „ich habe die Tabelle nicht gesehen“. ⛔ Keine Objektnamen, keine Inhalte in der Ausgabe: ein Waechter, der Dateinamen druckt, macht aus einem Befund ein zweites Leck. Bestands-Nachzug (ohne Re-Embed, --apply = JC-Hand): 08_OPS/rag01/sync_chunk_mandant.py. Suite 07_TESTS/sicherheit05/test_wand_am_chunk.py (14 Tests, Bruchprobe 4/4 rot vor dem Einchecken — JE-156) |
check_rag_ausschluss.py | Hält der ausdrückliche RAG-Opt-out — und bleibt dabei alles andere im Gedächtnis? | ✅ | VERTRAULICHKEIT#01 (02.09., Register #1261). Anlass gemessen: documents#7734 trug confidentiality_level='excluded' — den per-Doc-Opt-out aus KONZEPT_VERTRAULICHKEIT §2b („dieses Doc soll die KI auch für MICH nie kennen") — und hatte trotzdem einen Chunk (rag_chunks#1284). ⭐ Die Wurzel stand seit dem 08.08. wörtlich im Kopf von 05_SCHEMA/241_leiter7_conf_rank.sql als vertagter Auftrag: „ein Rang kann kein NUTZUNGSVERBOT ausdrücken … braucht ein eigenes Feld — eigener Auftrag, nicht diese Datei." Er wurde nie geholt. ⛔⛔ ZWEI RICHTUNGEN, weil eine allein wertlos wäre: ① VERBOT — ein excluded-Dokument bekommt KEINEN Chunk (am Verhalten befragt: ist_rag_ausgeschlossen + stempel_wand wirft) ② GEBOT — alles andere bleibt drin, auch NULL und unbekannte Werte. Ein Wächter, der nur ① prüft, ist grün, wenn das Gedächtnis leer ist — und grün, wenn jemand aus Vorsicht alles abschaltet. Genau das ist die teuerste Bewegung dieses Projekts (§2c: 1062 von 1298 Chunks auf streng, JC fragte nach Hiscox und KAI fand nichts). ⛔ Die fail-closed-Richtung ist hier BEWUSST umgekehrt: ein FEHLENDER Wert schliesst NICHT aus, sonst wären die 352 uneingestuften Dokumente aus KAI verbannt. Nur der ausdrückliche Wert schliesst aus — eine Allowlist des Verbots, keine Heuristik. ⛔ B-29/Positivkontrolle: der Bestands-Teil meldet neben der Verbots-0 immer die Zahl der Chunks an ERLAUBTEN Dokumenten; bricht die ein, ist der Lauf ROT statt grün — „0 verbotene Chunks" hiesse sonst auch „die Tabelle ist leer". ⛔ R-193: die drei geprüften Schreiber werden mitgedruckt, samt der ZWEI bewusst NICHT geprüften (reindex_user_docs · ingest_life_memory_help) und ihrem Grund — sie haben kein documents-Row als Quelle. --selftest bricht die Regel viermal (Verbot ausgehängt · Verbot zu weit · Schreiber ohne Tor · zurückgesetzt=wieder grün) und verlangt jedes Mal ROT. Wurzel geschlossen in 08_OPS/rag_wand.py; Bestands-Vorschlag 05_SCHEMA/PROPOSED_1261_ausschluss_chunks_loeschen.sql liegt bei JC (Löschen ist seine Hand). CI: netzfrei in schema-register-guards.yml, Bestand in betriebs-waechter.yml über PostgREST — dort liegt bewusst kein Mgmt-Token, ein Wächter, der danach griffe, wäre jeden Lauf GRAU. Owner: VERTRAULICHKEIT#01 |
check_chunk_abdeckung.py | Wieviel vom Haus kann KAI ueberhaupt lesen — und ist ein leeres Dokument eine LUECKE oder eine GRENZE? | ✅ | KONTO#01 (21.09., Register #2404). Anlass gemessen: 212 von 892 Dokumenten haben keinen einzigen rag_chunks-Eintrag — und es gab kein Werkzeug, das diese Zahl misst. R-193 ueber 08_OPS, 07_TESTS, 02_SKILLS, 09_SKILLS, .github/workflows, 06_DASHBOARD gesucht: check_rag_ausschluss misst den OPT-OUT, check_wand_am_chunk die MANDANTEN-WAND, check_katalog_coverage die KOMPONENTEN. Keines die Abdeckung. ⛔⛔ DIE UNTERSCHEIDUNG IST DER GANZE INHALT, ohne sie waere es ein Alarmgeber statt eines Messgeraets: LUECKE = der Beleg faellt in die Menge, die dispatch.py zum Ingest schickt, hat Text, und hat trotzdem keinen Chunk ⇒ ROT (gemessen: 20, u.a. #8484 #8486 #8487). GRENZE = der Beleg faellt gar nicht in diese Menge ⇒ gezaehlt, nicht angeklagt (gemessen: 192). Ein Kontoauszug ist kein Brief; dass er nicht gechunkt wird, ist eine ENTSCHEIDUNG des Hauses. ⭐ Die Grenze ist gemessen, nicht vermutet: dispatch.py feuert rag-ingest nur bei target_stream ∈ (RECHNUNG, STEUER) oder doc_type ∈ (Rechnung, Gutschrift, Quittung, Zahlungsbeleg) und ≥120 Zeichen. ⛔ Und der Verteiler ist nicht der einzige Erzeuger — Vertrag steht in keiner Liste und hat 59 von 59 gechunkt; deshalb klagt das Werkzeug NUR die Menge an, fuer die der gemessene Weg zustaendig ist, und nennt den Rest als Zahl. ⛔ Die Kopie der zwei Listen wird gegen dispatch.py geprueft und der Lauf wird laut, wenn sie altert — ein Werkzeug, das gegen eine veraltete Kopie misst, sieht wie der richtige Weg aus. ⛔ B-29/Positivkontrolle in JEDEM Lauf: findet die Abfrage keinen einzigen Beleg MIT Chunks, ist nicht das Haus leer, sondern das Messgeraet kaputt ⇒ Enthaltung (exit 2), nie gruen — dasselbe bei nicht erreichbarer Datenbank. --selbsttest ist netzfrei, 15 Faelle, beide Richtungen (ein Beleg MIT Chunks MUSS gruen, einer OHNE MUSS rot, und die LEERE Menge darf nicht wie „alles gut“ aussehen). CI: nur der Selbsttest in konformitaets-guards.yml ㎾ — die Bestandsmessung braucht Supabase und gehoert nicht in einen PR-Lauf ohne Secrets (Narbe check_wfe_kopie_ist_live). Owner: KONTO#01 |
check_rangordnung_live_gegen_repo.py (#1287) | WAECHTER — laeuft in der DB dieselbe Vertraulichkeits-Rangordnung, die im Repo steht? rag_conf_rank wird an mehreren Stellen gebraucht (RAG-Tor, drei RPCs, Python-Zwilling im Persona-Pfad) — jede prueft gegen eine KOPIE, keine gegen die Datenbank. Vergleicht ZAHLEN je Wert (stille Aenderung) UND die ORDNUNG der vier Leiterstufen (die Aenderung, die wirkt); dazu: keine Nicht-Leiter-Stufe darf unter streng_vertraulich rutschen. exit 2 = KEIN URTEIL bei nicht erreichbarer DB — ausdruecklich kein Gruen. Luecke gemeldet von PERSONA#01 | ✅ | gerufen von betriebs-waechter.yml (taeglich 03:47) · Selbsttest: ja (5 Proben, beide Richtungen) · ADMIN#03 |
check_shell_skripte_sind_live.py (#3221) | WAECHTER — liefert der Server JEDES Skript aus, das die Shell RUFT? Anlass an der Live-Flaeche gemessen: core_shell.html laedt seit dem 27.09. /tgv_renderers_graph.js (Regal 5), und BEIDE Hosts antworteten HTTP 404 — die Shell rief ins Leere, der Graph-Schirm fiel auf den Notfall-Renderer zurueck. ⭐⭐ DIE KLASSE: ein PFADGEFILTERTES Ausliefer-Regal (deploy.yml, on: push: paths:) liefert nur, was ein Push AENDERT. Die Datei kam am 26.09. auf master, ihr Regal am 27.09. um 01:45 — eine Datei, die VOR ihrem Regal landet, wird NIE ausgeliefert; das Regal ist gebaut und bleibt fuer immer leer. ⛔ Und jeder Waechter war dabei gruen: Datei existiert, Konfiguration richtig, Deploy-Job meldet skipped. ⭐ Deshalb ist die einzige ehrliche Frage eine LIVE-Frage — netzfrei ist der Fehler nicht findbar, weil Deklaration UND Datei stimmen; nur der Server weiss, ob die Datei je bei ihm angekommen ist. Die Skriptliste wird aus core_shell.html abgeleitet, nie gelistet: eine Handliste waere derselbe Fehler eine Etage hoeher, sie wuesste nichts von einem neuen Regal. exit 2 = KEIN URTEIL bei unerreichbaren Hosts — ausdruecklich kein Gruen (R-310). | ✅ | gerufen von betriebs-waechter.yml ⑰c (taeglich 03:47) und im Meldeweg gestempelt · --selbsttest: 4 Kontrollen in beiden Richtungen, darunter die wichtigste (ein unerreichbarer Host darf NICHT gruen werden) · ⛔ Er ist bei Einbau ROT und das ist Absicht: der Handgriff ist ein Push, der die Datei beruehrt, und auf einer App-Live-Flaeche ist das JCs Wort (Vorlage Registerzeile #3222). Ein Rot mit genanntem Handgriff und genanntem Eigner ist ein Signal · TOWER#60 |
check_register_nichts_verloren.py | Geht beim Nachziehen eine Registerzeile verloren — oder kommt eine getilgte zurück? | ✅ | VERTRAULICHKEIT#01 (03.09., Register #1320). Anlass: zwölf PRs gleichzeitig auf CONFLICTING, weil jede Bahn ihre Zeile ans ENDE derselben Datei hängt. ⛔⛔ KORRIGIERT 03.09.: der Konflikt ist NICHT nur Anzeige — ein PR mit Konflikt bekommt GAR KEINE CI (Gemessen 03.09. an PR #2448, vier Zustände hintereinander: d7e8abdc CONFLICTING ⇒ 0 Läufe (10 min gewartet) · 1b39fa48 MERGEABLE ⇒ 9 Läufe sofort · ef128dfb MERGEABLE ⇒ 9 Läufe · 09873ab2 CONFLICTING ⇒ 0 Läufe (9 min). Ursache: bei einem Konflikt kann GitHub refs/pull/N/merge nicht bilden — und ohne die starten die pull_request-Läufe gar nicht.) ⇒ ein CONFLICTING-PR ist ungemessen, nicht ungeprüft-aber-grün. ⭐ Der zweite Schaden ist die HANDARBEIT: jede Auflösung von Hand ist eine Gelegenheit, eine fremde Zeile zu verlieren (dem Haus schon zweimal passiert). ⛔ Der naheliegende Automat wurde GEMESSEN und VERWORFEN: merge=union in .gitattributes löst im Arbeitsbaum auf (rc 0) und im BARE-Repo nicht (rc 1) — belegt, weil dieselbe Regel unter $GIT_DIR/info/attributes wieder rc 0 ergibt. Git liest die Merge-Regel aus dem Arbeitsbaum, nicht aus der .gitattributes im Commit; ein Server hat keinen Arbeitsbaum. Dasselbe gilt für das bereits ausgerollte merge=ours der vier Spiegel: es hilft lokal und nie auf GitHub. ⛔⛔ VIER FRAGEN AN DREI REFERENZEN: ⛔ ① misst gegen den merge-base (den Stand, den der Zweig aufgenommen hat), nicht gegen die Spitze — die erste Fassung tat das und wäre auf jedem PR rot gewesen; ①b misst gegen die eigene Vorfassung, weil ① eigene neue Zeilen nicht sieht (gebissen an #1302). Im Einzelnen: ① VERLUST (Kennung aus origin/master fehlt hier) ② AUFERSTEHUNG (auf master getilgte Kennung ist wieder da) ③ DOPPELUNG (dieselbe Kennung steht ZWEIMAL). ⛔⛔ ③ kam nachträglich, weil dieser Wächter sie ÜBERSEHEN hat (03.09.): #1312 stand nach dem Auflösen zweimal, in verschiedenen Fassungen (2951 gegen 2938 Zeichen) — nichts fehlte, nichts war auferstanden, der Wächter meldete grün. ⭐ Die dritte Listenart: eine FORTGESCHRIEBENE Zeile, zweimal unter derselben Nummer. Welche gilt, entscheidet die Herkunft, nicht die Länge — deshalb nennt die Meldung bei ungleichen Fassungen beide Längen, sonst sieht der gefährliche Fall aus wie der harmlose. ⭐ ② ruft master_nachziehen.auferstanden() statt es nachzubauen — eine zweite Fassung hielte bis zur nächsten Änderung an einer der beiden Stellen. ⛔ Dreiwertig (B-29): unlesbare Referenz ⇒ ENTHALTUNG, nie „sauber"; 0 gelesene Kennungen ⇒ ROT, denn ein leeres Register gibt es nicht. ⛔ LAUFORT CI und NICHT Commit-Haken: die ~50 Arbeitsbäume teilen sich .git — ein Haken träfe alle Bahnen ungefragt und ist mit --no-verify abgeschaltet. CI ist später, aber nicht zu spät für den Schaden, der zählt: eine verlorene Zeile erreicht master erst mit dem Merge. ⛔ Was er NICHT kann: die Arbeit der Bahn retten — sie erfährt es erst im PR. ⭐ Geltungsbereich GEMESSEN (je gegen den ersten Elternteil, nur Nicht-Merge-Commits): Anforderungs-Register 8/0 · Artefakt-Register 23/0 · Regelwerk 15/0 Tilgungen ⇒ anfügend. ⚠️ Aber „noch nie getilgt" ≠ „darf nicht getilgt werden": eine bewusste Tilgung im Artefakt-Register (Achse aus #2426) lässt ihn ROT werden — Aufforderung zu benennen, kein Fehlalarm. Selbstbiss 11 Proben (Doppelung dreifach: keine ⇒ still · zwei IDENTISCHE ⇒ rot · zwei VERSCHIEDENE ⇒ rot mit beiden Längen), Positivkontrolle an zwei echten Referenzen (af60fa58 alt / origin/master neu; der Fall selbst ist repariert und damit historisch), + Biss an echten Daten (#1304 entfernt ⇒ ROT mit Namen, Wiederherstellung aus der Kopie). Eingehängt: schema-register-guards.yml ㊖, mit --unshallow und ohne den Oder-Schlucker, der eine ENTHALTUNG verdecken würde. Owner: VERTRAULICHKEIT#01 |
check_fuer_diese_aenderung.py (#1347) | LAEUFER — welche Pruefer wuerden an DIESER Aenderung laufen? Leitet die Menge aus den pull_request-Bloecken der Workflows ab statt aus einer Merkliste (Anlass: ich fuhr 6 von 92). Meldet auch geaenderte Dateien OHNE zugeordneten Pruefer — sonst liest man sein Gruen als 'geprueft', wo 'nichts zu pruefen gefunden' steht. ⛔ Startet die zwei Pruefer NICHT, die Bestand ueberschreiben (Sperrliste ABLAGE#02 wird gerufen); fehlt sie, wird gar nicht gefahren (exit 2 = KEIN URTEIL) statt ungebremst | ✅ | von HAND vor dem Push — bewusst KEIN git-Hook (rund 50 Arbeitsbaeume teilen ein .git) · Selbsttest: ja (5 Proben + Sperre, 2 Gegenrichtungen) · ADMIN#03 |
check_ablage_form_im_kanon.py | Liegt ein Beleg an einem Ort, den weder der Kanon noch eine Oberflaeche kennt? | ✅ | SICHERHEIT#05 (24.08., Register #637). ⛔ Der Anlass ist KEIN Datenfehler: ein Vertrag ohne aufloesbare Entitaet landet unter CONTRACTS/_UNRESOLVED/ — eine EHRLICHE Marke, keine geratene Entitaet. ⭐ Der Unterschied ist nicht Richtigkeit, sondern ob je wieder jemand hinsieht: needs_review kennen mindestens ACHT Oberflaechen (core_shell · dokumente2 · inbox2 · abgleich2 …), _UNRESOLVED kennen GENAU ZWEI Code-Dateien (der erzeugende Skill + _ordnung.py) und keine Oberflaeche — und im ablage_kanon.yaml steht es nicht. Wer dort landet, ist nicht falsch abgelegt, er ist aus der Welt. Familie „Waechter ohne ausloesbare Bedingung“, nur an einem Ordner statt an einem Pruefer. ZWEI FRAGEN, EIN WERKZEUG: ① der EIMER — wie viele liegen in _UNRESOLVED; er schlaegt beim ERSTEN an, nicht bei einer Schwelle (bei einem Ordner, den niemand kennt, ist der erste Beleg schon der Verlust) ② die FORM — traegt jeder final_path eine Form, die ablage_kanon.yaml:folder_structure kennt (faengt auch die naechste stille Ablage-Form). Gemessen 24.08. (505 Zeilen): Eimer 0 — und ⭐ null ist hier der NORMALFALL, kein Fehler des Waechters; Bestand 4 Formen ausserhalb des Kanons: 29 final_path enden auf / (gar kein Dateiname), 16 Vertraege mit der Entitaet ZWEIMAL im Pfad, 6 Banking-CSV-Ketten, 2 in der Form, die der Live-Skill erzeugt und die im Kanon fehlt. ⛔ Bestand EINGEFROREN (Stundung, kein Freispruch) — rot wird nur ein NEUZUGANG, sonst waere er ab dem ersten Lauf dauerrot, und ein dauerrotes Signal ist kein Signal. ⛔ Keine Dateinamen in der Ausgabe, nur FORMEN. B-29: 0 Zeilen ⇒ UNGUELTIG statt gruen. Selbsttest beide Richtungen (9 Faelle + Dateinamen-Riegel + Abgleich der abgeschriebenen Liste gegen die YAML), Bruchprobe belegt. Echtlauf 24.08. GRUEN ⇒ er GATET (⑱ in betriebs-waechter.yml), wie ⑩/⑪ |
check_projekt_wand_wissenssuche.py | Ein Beleg liegt in Projekt A — findet ihn jemand, der in Projekt A keine Rolle hat? Als Treffer, als Zitat oder als Zusammenfassung? | ✅ | RAG#02 (20.08., Register #478). Anlass: RECHTE#02 hat 12 Regale gemessen — scope_gate.py:255-267 injiziert project_code UND project_id, und die Wissenssuche liest es nie (executors_core 0, executors_rag 0, RPC ohne Projekt-Parameter). ⭐ Die bewachte Unterscheidung: p_member_project_ids = die WAND (Mitgliedschaft, gilt IMMER) vs. p_project_id = der FILTER (Auswahl). Ein Filter an der Auswahl ist keine Wand — ein Nicht-Mitglied wählt das fremde Projekt gerade NICHT; Frage (a) prüft deshalb ausdrücklich den Fall OHNE Auswahl. ⛔ BEIDE RICHTUNGEN: Frage (b) verlangt, dass dieselbe Lage die projektFREIEN Treffer NICHT wegschneidet — wer die Wand einbaut und dabei die Wissenssuche abschaltet, wäre sonst grün. Verhaltens-Guard: ruft den ECHTEN Executor und fängt die ECHTEN RPC-Payloads beider Wege ab (kein Regex auf eine Zeile). 4 Fragen über 5 benannte Regale (executors_rag · scope_gate · app.py · Mig 263 · RECHTE#02 §12), Selbsttest 6/6 beisst (JE-156). Netzfrei; --live (applizierte RPC) und die Live-Gegenprobe 08_OPS/rag02/projekt_wand_gegenprobe.py brauchen Credentials und bleiben bewusst DRAUSSEN. CI konformitaets-guards ⑧ |
check_herabstufung_riegel.py | Herabstufen der Vertraulichkeit — hält der Riegel aus JC-Entscheid ① an allen fünf Stellen? ⑤ Fragen an ⑤ verschiedene Dateien: ① die WAND (Mig 264 — Verwalter via is_mandant_admin · neuer Klartext-Grund · Unterschrift gegen auth.uid() · Spur · und die Ausnahme für die Erst-Einstufung) · ② ⛔ repo-weit: nennt IRGENDEIN agent_events-Schreiber eine Spalte, die die Tabelle nicht hat? · ③ erklärt ein Chat-Deskriptor ein Schreib-Tool zum read-Tool (dann würde KAI es AUSFÜHREN statt vorzuschlagen) · ④ die KLINKE (/api/skill/confidentiality-set: JWT-only, _darf_regel_setzen, Grund-Mindestlänge, liest die eigene Spur nach) · ⑤ baut die Oberfläche einen zweiten Schreibweg — und wenn ja, hinter dem Gate und mit dem deutschen DB-Wort? | ✅ | MANDANT#05 (20.08., Register #482, JC-Entscheid ①). ⭐ Frage ② ist der eigentliche Gegenwert und gilt für JEDE Lane: PR #1841 starb daran, dass ein Wächter outcome nach agent_events schrieb — eine Spalte, die es nicht gibt ⇒ PostgREST 400 ⇒ die Meldung ging still verloren, zwei Befunde wurden nie zugestellt. Prüfer scheitern am MELDEN, nicht am Messen. Die Spaltenliste im Guard ist deshalb eine MESSUNG mit Datum (20.08., Live-OpenAPI), keine Erinnerung — wer eine Spalte ergänzt, ergänzt sie dort. ⛔ Frage ① bewacht ausdrücklich die Ausnahme, nicht nur den Riegel: rag_conf_rank(NULL)=6, also ist eine ERST-Einstufung NULL→intern (2) rechnerisch eine Herabstufung 6→2 — ohne sie wiese der Trigger jede erstmalige Klassifikation ab (contract-extract/skill.py:719). ⭐ Drei Fehlalarm-Bremsen, jede an einem gemessenen Fall: Header-Dict statt Body (app.py:624) · PostgREST-Leseparameter select/order/limit (iter56_*) · verschachtelte Nutzlast (iter76_revert_6903_private.py). ⛔ Und zwei eigene Narben im Werkzeug, beim Bau gefangen: der Pfad-Filter ".claude" in str(p) machte den Scan im Worktree auf 0 Dateien blind (grün, weil er nichts angesehen hatte — er meldet das jetzt selbst als rot), und Path.glob("**/*.py") lief trotzdem durch die vollen Repo-Kopien fremder Lanes ⇒ os.walk mit Prunen. Netzfrei, Selbsttest 15 Mutationen, alle beissen (JE-156); Laufzeit ~27 s auf 1468 Dateien. Die Live-Gegenprobe 08_OPS/mandant05_gegenprobe.py braucht Credentials und bleibt bewusst DRAUSSEN. CI schema-register-guards ⑧ |
check_to_regproc_signatur.py | Traegt irgendwo ein to_regproc('f(uuid)') eine Signatur? to_regproc nimmt NUR den nackten Namen — mit Klammern liefert sie IMMER NULL, unabhaengig davon, ob die Funktion existiert (die Variante MIT Argumentliste heisst to_regprocedure). Eine so geschriebene Vorbedingung kann per Konstruktion nie bestehen: die Migration weist sich selbst ab. ⭐ Umkehrung der ueblichen Fehlerklasse — nicht ein Waechter, der nicht meldet, sondern einer, der immer meldet; beide sind gleich blind. | ✅ | MANDANT#06 (Register #486). Der Anlass war der DRITTE Lauf: 10.08. + 11.08. (Mig 247) und 20.08. (Mig 264) starben JC-Apply-Laeufe an dieser Verwechslung. Nach den ersten zwei wurde die Regel zweimal als Kommentar niedergelegt (247:81-83, 260:95-96) — und verhinderte den dritten nicht. ⭐ Eine Regel, die nur als Fussnote lebt, verhindert nichts. Netzfrei (1477 Dateien, os.walk mit Prunen). --selftest 14/14: 7 Mutationen beissen, 6 Fehlalarm-Gegenproben schweigen (darunter to_regprocedure(sig) und die Kommentar-Zitate in 247/260), plus Echtlauf am Bestand. ⛔ Bekannter Fehlalarm-Vektor, im Kopf festgehalten: Docstrings sind keine Kommentarzeilen — er wurde beim ersten Echtlauf an einem Docstring rot und wurde bewusst nicht nachsichtig gemacht (das wuerde echtes SQL in Python-Strings blind machen). ⭐ Der erste Zaun ist nicht die CI, sondern der Wirkort: apply_migration.py ruft dasselbe urteil() VOR dem Senden und bricht ab — der Schaden entsteht in der Sekunde des Applies. CI schema-register-guards ⑨. |
08_OPS/check_werte_ohne_leser.py (Workflow kanon-quellen-ssot.yml) | ⚠️ DIESE ZEILE IST ZITIERT, NICHT VON IHREM AUTOR GESCHRIEBEN — KONZEPT#01 hat sie am 04.09. aus dem DATEIKOPF übernommen, weil der Artefakt-Ratchet das Werkzeug als unregistriert meldete und eine geratene Zeile schlimmer wäre als keine. Sie zitiert und behauptet nichts darüber hinaus. EINGABE#01 (6 Commits) bzw. DESIGN#05 (4) sind die häufigsten Bearbeiter — sie mögen sie fortschreiben, wenn sie etwas anderes meinen. ⇒ SEINE FRAGE, wörtlich aus dem Kopf: „Welche Felder, Konstanten und Dateiköpfe behaupten einen Zustand, den KEIN LESEPFAD LIEST?“ — die Klasse sind vier gemessene Fälle vom 08.08.: eine Signatur-Spalte ohne Leser · ein Migrations-Kopf, der „STAGED — DO NOT APPLY“ sagte, während die Migration seit dem 11.07. appliziert war · ein n8n-Parameter, den ein Übergabe-Bericht als „nicht gesetzt“ führte · eine Nummer, auf einem Baum ermittelt, den dieselbe Bahn im selben Atemzug als veraltet erkannt hatte. ⭐ Das Gemeinsame: eine Aussage über den Zustand steht an einem Ort, der NICHT der Lesepfad ist — und niemand merkt es, weil der Ort plausibel wirkt. ⛔⛔ WAS ER AUSDRÜCKLICH NICHT PRÜFT (aus dem Kopf zitiert): ob eine Spalte in der LIVE-DB wirklich gefüllt ist (er ist netzfrei; „geschrieben“ heißt: eine INSERT-Spaltenliste nennt sie) · **einen Leser per SELECT * oder per RPC-Rückgabe, der den Spaltennamen nie ausschreibt — solche Leser sind für ihn UNSICHTBAR, seine Sonde ① kann also FALSCH-ROT sein; deshalb meldet sie, sie urteilt nicht** · den n8n-Live-Zustand (nicht im Repo) · ob der EINE Baum, den er liest, der aktuelle ist — er kann nur seine eigene Herkunft mitausgeben, und das tut er. ⭐⭐ RATCHET STATT ROT, mit der Begründung im Kopf: „der Bestand ist gross und eingefroren. Nur WACHSTUM ist rot. Ein Guard, der ab Tag eins rot steht, wird weggeklickt — dann meldet er auch den Neuzugang nicht mehr.“ Und die Baselines dürfen nicht STILL wachsen: der --selftest hält jede gegen ihre Zahl. ⛔ „DIESER GUARD REPARIERT NICHTS. Er meldet. Die Fixes gehören den Lanes.“ | ✅ | Zitiert von KONZEPT#01 (04.09., Register #1553). ⚠️ ANLASS, und er ist selbst ein Befund: das Werkzeug war nie mit vollem Dateinamen im Register belegt, sondern nur über ein SELTENES WORT — und ein unbeteiligter Text (meine eigene Zeile, die sechsmal „gewertet“ sagt, worin „werte“ steckt) machte dieses Wort häufig und entzog ihm den Anker. ⭐⭐⭐ Der Ratchet hatte recht: das Grün davor war falsch, nicht das Rot danach. ⛔ 29 weitere Artefakte hängen laut Ratchet-Ausgabe an demselben schwachen Beleg — eigener Auftrag (TOWER#53 an STERN#01, 04.09.). |
08_OPS/check_code_ref_zeigt_ins_leere.py (Workflow betriebs-waechter.yml, Schritt ㉖e) | Der LESER, den check_werte_ohne_leser verlangt hat — und er misst drei Achsen mit drei Gewichten. Der Ausweis tunable_params.code_ref (ADMIN#02, gemeldet 05.09., Frist 21 Tage) liess zwei Enden: „Entweder die Cockpit-Flaeche liest sie, oder sie faellt.“ TOWER#59 hat am 26.09. ein drittes entschieden: ein Waechter als Leser — ein Zeiger ohne Pruefer ist eine Zusage, die beim ersten Umbenennen still bricht. ⛔⛔ DIE MESSUNG VOR DEM BAU HAT DEN BAU GEAENDERT, und das ist der Kern: der Auftrag lautete „pruefe, ob die Stelle noch existiert“. Gemessen an allen 8 Seed-Zeilen (05_SCHEMA/248_admin02_tunable_params_flaeche.sql:86-117): alle Dateien existieren, alle Symbole existieren — aber 6 von 7 Zeilennummern sind falsch, eine um 228 Zeilen (GESAMT_MAX sagt executors_rag.py:393, steht auf :621). ⇒ ⭐⭐ Ein Pruefer, der nur „existiert die Stelle“ fragt, waere heute GRUEN und koennte nie rot werden; einer, der die Zeile gatet, waere ab dem ersten Lauf DAUERROT. Beides waere kein Leser, sondern eine Attrappe. Deshalb: Datei fehlt oder nicht eindeutig ⇒ ROT · genanntes Symbol weg ⇒ ROT (der stille Bruch) · Zeilennummer veraltet ⇒ MERKE, nie rot. ⛔ Das Feld ist Freitext, nicht Datei:Zeile — eine Seed-Zeile nennt gar keinen Pfad („NEU — …“ ⇒ kein_urteil, keine erfundene Anklage), eine nur einen blanken Dateinamen (eindeutig ⇒ OK + MERKE, mehrdeutig ⇒ ROT, denn die Spalte verspricht EINE Stelle). ⛔ NICHT geprueft: ob der Wert dort noch hardcoded steht — das braucht einen Vergleich mit value und ist bewusst nicht vermengt. Kein Schreibpfad, reines Lesen. | ✅ | BETRIEB#01, 26.09. (Registerzeile #3157). Selbsttest 13/13, beide Richtungen: vier Positivkontrollen (Datei fehlt · Symbol umbenannt · Name mehrdeutig · Name nirgends ⇒ je BEFUND) und — die wichtigere Haelfte — Negativkontrollen, dass eine blosse Zeilen-Drift nicht rot wird und eine Enthaltung nicht als ok durchgeht. ⭐ Gestubbt ist nur die Aussengrenze (_existiert_echt, das Dateisystem), nie der gemessene Zweig. ⛔ STEMPELT, GATET NICHT (Muster provanker/ablageort): ein Echtlauf gegen die Live-Tabelle ist noch nicht gefahren — dieser Worktree hat keinen DB-Zugang, also kein Urteil statt eines geratenen Gruen. Befoerderung in die Gate-Zeile, sobald ein Cron-Lauf ihn einmal gruen ODER rot gesehen hat — und dann mit == '3', nicht != '0' (er trennt Messfehler 2 und Befund 3, TOWER#57-Regel). |
check_stummschaltung_hat_termin.py (Workflow betriebs-waechter.yml, ㈚) | Welche Guards laufen, ohne dass ihr ROT den Job färbt — und trägt jede dieser Stummschaltungen eine ABLAUF-BEDINGUNG im Kommentar? Hintergrund: provanker (08_OPS/check_provider_entity_anker.py) ist seit dem 19.08. bewusst nicht gewertet, mit sorgfältiger Begründung und der Bedingung „Beförderung, sobald die 12 re-triagiert sind“ — die Bedingung wurde nie geprüft, und aus 12 Belegen sind 17 geworden. ⭐⭐ Die beste Begründung der Welt verfällt, wenn niemand ihren Termin trägt. Gemessen: 32 Guards, 16 gewertet, 16 stumm — 8 mit Bedingung, 8 ohne. ⛔⛔ WAS ER AUSDRÜCKLICH NICHT FRAGT — und das ist die ganze Idee: ob eine Bedingung ERFÜLLT ist (fremde Fachfrage; ein Riegel, der über fremde Fachbedingungen urteilt, urteilt falsch) · ob die Stummschaltung richtig WAR (sie ist es oft) · andere Workflows — er liest GENAU EINE Datei und sagt das · ob ein Guard überhaupt läuft: das ist guard_meldeweg.pys Frage (Alter des LAUFS gegen Alter der STUMMSCHALTUNG — zwei Fragen, zwei Werkzeuge, vor dem Bau geprüft). ⭐⭐⭐ ER LISTET SICH SELBST MIT AUF, und das ist keine Koketterie: ein Riegel gegen vergessene Stummschaltungen, der seine eigene verschweigt, wäre die Bauform, die er bekämpft — und sein dritter Konstruktionsfehler wurde NUR dadurch sichtbar (die Hausform ist zwei Schritte je Guard, die Begründung steht über dem ersten, die id am zweiten; er klagte sich selbst an). ⛔ Zwei weitere Fehlgriffe stehen in seinem Kopf: sein Anker traf ein Kommentar-ZITAT statt den Schritt (30 statt 16 Befunde), und er las nur den Guard-Kommentar, während die Begründung im Haus oft AN DER GATE-ZEILE steht. | ✅ | KONZEPT#01 (04.09., Register #1553). STEMPELT, GATET NICHT — Auflage TOWER#53: ein neuer Riegel, der den Nacht-Job sofort rot färbt, verdeckt die Guards darüber. ⚠️ SEINE EIGENE ABLAUF-BEDINGUNG steht an der Gate-Zeile: Beförderung, sobald jede von ihm aufgezählte Stummschaltung eine Bedingung trägt. ⛔ Die 8 offenen Stummschaltungen füllt er NICHT — er nennt sie, ihr Eigner begründet sie. |
check_pfadlisten_beider_ausloeser.py (Workflow konformitaets-guards.yml, ㈨) | Sieht der pull_request-Auslöser eines Workflows WENIGER als sein push-Auslöser? Hintergrund: fünf Pfade standen in konformitaets-guards.yml nur im push-Block — darunter 06_DASHBOARD/core_shell.html, die meistgeänderte Datei des Hauses. Jeder PR, der sie anfasste, hat den Workflow nicht ausgelöst. ⭐⭐⭐ DIESE KLASSE ZEIGT SICH NIE ALS ROT, IMMER NUR ALS ABWESENHEIT: ein Job, der nicht startet, steht in der Prüfliste gar nicht — die übrigen Checks bleiben grün und der Vorgang meldet CLEAN. Deshalb fällt sie durch jede Prüfung, die auf FARBE schaut statt auf MENGE. ⛔⛔ DREI ZUSTÄNDE, NICHT ZWEI — und der mittlere hat den ersten Entwurf widerlegt: fehlt paths beim pull_request, filtert GitHub NICHT, der Auslöser feuert bei JEDEM PR und ist damit breiter. Ein erster Wurf zählte das als leere Menge und hätte schema-register-guards.yml 32 Lücken angehängt, die es nicht hat. Eine 0 muss immer gefragt werden: gemessen oder nicht gemessen? ⛔ WAS ER AUSDRÜCKLICH NICHT FRAGT: ob die Pfade die richtigen Dateien TREFFEN · andere Auslöser (schedule, workflow_dispatch, workflow_call) · branches-Filter · ob der Workflow inhaltlich etwas taugt. Und die push-ärmere GEGENRICHTUNG meldet er als HINWEIS, klagt sie aber nicht an — ein Prüfer, der zwei verschiedene Fragen in einen Topf wirft, macht seine eigene Meldung unbrauchbar. ⛔ Fehlt PyYAML, bricht er AB statt grün zu melden — eine fehlende Voraussetzung ist kein Freispruch. | ✅ | KONZEPT#01 (04.09., Register #1550). Positivkontrolle selbst getroffen: er fand die fünf Pfade VOR dem Nachtrag und meldet danach grün über dieselben 20 Dateien. ⛔ **Er läuft bei jeder Änderung an EINER .github/workflows/*.yml an — in BEIDEN Blöcken eingetragen, sonst hätte er genau die Krankheit, die er misst.** |
08_OPS/check_inhalt_liest_intake_text.py (LAUFORT hand + CI, netzfrei) | Bildet irgendwo im Bestand ein Ausdruck die Inhalts-Rueckfallkette einer documents-Zeile (BEIDE Extraktionsspalten) ohne intake_text — als Leser, als Namens-Sammlung oder als select-Liste? Hintergrund: mail_intake.py schreibt extracted_markdown/extracted_text_snippet nirgends; der Mailtext liegt ausschliesslich als intake_text. Eine Kette ohne ihn ist bei Mail-Eingaengen garantiert leer, nicht manchmal — 22 von 57 Belegen hinter dem 120-Zeichen-Tor tragen dort >= 120 Zeichen (06.09., lesend gemessen). ⭐ Er bewacht Leser UND Ladeliste als EIN Paar (wie #1549) und fragt den Syntaxbaum, nicht den Text — ein Wort im auskommentierten Code hat im Baum keinen Knoten. ⛔⛔ WAS ER AUSDRUECKLICH NICHT FRAGT: ob intake_text GEFUELLT ist (er liest Dateien, keine Datenbank) · eine Kette, die ueber mehrere Anweisungen verteilt ist — genau so sah braucht_retriage vor der Reparatur aus, er haette seinen eigenen Anlassfall nicht gefunden, und diese Luecke ist im Selbsttest als Fall BEWIESEN statt behauptet · ob DocSnapshot ein Feld fuer Mailtext hat (Vertragsfrage) · ob die Schwelle 120 an ihren drei Schreiborten auseinanderlaeuft (anderer Befund, anderer Waechter). ⛔ RATSCHE NAMENTLICH, NICHT GEZAEHLT — Schluessel datei::symbol, 12 offene Stellen mit Grund und Besitzer; eine Ratsche auf eine ZAHL zaehlt Zufallstreffer als erfuellt. ⭐⭐ sichtbar=False ist seine wichtigste Spalte: zwei bekannte offene Stellen kann er konstruktiv nicht sehen (agent_loop.DocSnapshot.content liest Attribute; app._ORCH_DOC_SELECT ist eine join-Liste) — ohne sie haette er beide als erledigt, Zeile streichen gemeldet. „Nicht gefunden“ und „nicht mehr da“ sind zwei Aussagen. ⛔ VIER BENANNTE, DATIERTE AUSNAHMEN — jede eine ANDERE FRAGE, keine erledigte: dispatch._has_text (entscheidet, ob ein PDF noch GELESEN werden muss — ein Mailtext unterdrueckte dort den Leser und liesse die Datei ungelesen) · das RAG-Ingest-Tor (ob ein Mailrumpf in die Wissensbasis gehoert, ist ein JC-Entscheid) · paritaet_kette.NICHT_VERGLICHEN (eine Ausschlussliste ist das Gegenteil einer Inhaltskette) · die Wiederherstellungs-Schluessel der Vision-Gegenprobe. Jede Ausnahme braucht einen ANKER im Fundtext. | ✅ | DISPATCH#01 (06.09., Register #1677). ⭐⭐⭐ DREI EIGENE NARBEN STEHEN IN SEINEM KOPF, jede von einer PROBE gefangen, keine vom Nachdenken: (a) eine Komma-Liste ist EINE Konstante, aber VIELE Spalten — ohne Zerlegen haette er orch_apply.SEL uebersehen, also eine der vier Stellen, wegen derer es ihn gibt; (b) er schuetzte seine eigene Reparatur nicht — inhalt_aus bildet die Kette als Schleife ueber ein Namens-Tupel, das weder Teil ① noch ② sieht; sichtbar erst, als die Mutation an der echten Datei lief statt an einem Beispiel; (c) ⭐⭐ eine Ausnahme auf das Symbol entlastete stillschweigend eine zweite Frage — dispatch.dispatch ist ueber 400 Zeilen lang und traegt das RAG-Tor und den reparierten Re-Triage-select: intake_text aus der Ladeliste entfernt, und der Waechter schwieg. Ein Symbol ist nicht fein genug, wenn eine Funktion zwei Fragen traegt. Positivkontrolle: scharfer Lauf rc=0 ueber 749 Dateien, dispatch.py null Treffer; beide Mutationen an der echten Datei (Tupel · Ladeliste) werden gefangen. |
08_OPS/check_inhaltskette_beide_fassungen.py (LAUFORT ci + hand, netzfrei) | Sagen die ZWEI Fassungen der Inhaltskette noch dasselbe? dispatch.inhalt_aus ist das TOR (ob der Kopf gefragt wird), agent_loop.DocSnapshot.content der PROMPT (was das Modell liest). Er füttert beide mit denselben Zeilen und verlangt dasselbe Ergebnis — Übereinstimmung, kein Zustand, deshalb beisst er auch bei einer fünften Quelle, die nur eine Seite kennt. Zweite Achse: nennen beide Ladelisten (orch_apply.SEL, app._ORCH_DOC_SELECT) jede Quelle, und tragen beide Leseorte denselben Deckel? Geschwister von check_inhalts_schwelle_hat_eine_fassung.py — der fragt, ob die Formel NEU entsteht, dieser, ob die beiden vorhandenen auseinanderlaufen. | ✅ | ORCH#01 (#3246, 27.09.) · Anlass: das Tor bekam rag_chunks, der Prompt nicht — 12 ehrliche Enthaltungen wären zu 12 Urteilen auf ein leeres Dokument geworden, jedes mit einem verbrauchten triage_attempt. Die Naht bleibt ein eigener Vorgang (Tower-Ball 06.09.), aber ein bewachter. · 13 Proben · Selbsttest mit Beissprobe an einer blinden Fassung UND einer abweichenden Schwelle · Mutationsprobe 7/7 (bei geprüfter grüner Vorbedingung — ein Prüfer, der abstürzt, fängt jede Mutation und beweist nichts) · verdrahtet in agentchat01-suiten.yml (Selbsttest zuerst, beide Fassungen als Auslöserpfad) |
08_OPS/check_inhalts_schwelle_hat_eine_fassung.py (LAUFORT hand + CI, netzfrei) | Entsteht die Inhalts-Schwelle (120) an einer weiteren Stelle als eigene Konstante — und baut irgendwo ein Ausdruck die Inhalts-Rueckfallkette (>= 2 der drei Spalten) mit einem Laengenvergleich, statt dispatch.inhalt_aus zu rufen? Zweite Achse: tragen die bekannten Fassungen noch denselben Wert? Geschwister von check_inhalt_liest_intake_text.py — dieselben Dateien, andere Frage. Rein per ast (ein Textmuster faellt auf jeden Kommentar herein). | ✅ | SCHWELLE#01 (#1690, 06.09.) · Ratsche namentlich mit Anker/Grund/Besitzer: 7 offene Fassungen (5 in 02_SKILLS, das RAG-Ingest-Tor in dispatch.dispatch, die zweite Welt agent_loop.content) · Selbsttest + Mutationsprobe 7/7 · scharfer Lauf rc=0 ueber 751 .py · verdrahtet in agentchat01-suiten.yml (Koeder zuerst, dann Bestand) |
check_absender_liest_die_kette.py (Workflow konformitaets-guards.yml, ㈧) | Holt irgendwo im Bestand jemand mail_sender aus einer Zeile, ohne sender oder original_from mitzulesen — und nennt irgendeine select-Liste mail_sender ohne die beiden anderen? Hintergrund: mail_sender ist zu 0 % gefüllt (856 Zeilen, 04.09.), sender zu 476. Zwei Leser im Orchestrator nahmen nur mail_sender und lasen damit garantiert leer, bei jedem Dokument; der leere Wert ging als „Absender: (unbekannt)“ in den Klassifizierer-Prompt. ⭐ Er bewacht ein PAAR, nicht eine Zeile: Leser UND Ladeliste — eine Spalte, die der select nicht nennt, fehlt in der Zeile, und ein reparierter Leser allein wäre ein stiller No-op. ⛔⛔ WAS ER AUSDRÜCKLICH NICHT FRAGT: ob die Spalten GEFÜLLT sind (er liest Dateien, keine Datenbank — seine Zahlen würden altern, seine Frage nicht) · ob am Absender eine ENTSCHEIDUNG hängt (ungemessen, R-193 — der Prompt führt den Volltext als autoritativ) · JavaScript, SQL und n8n-Knoten · Zugriffe, die den Spaltennamen zur Laufzeit zusammensetzen. ⛔ VIER BENANNTE, DATIERTE AUSNAHMEN: agent_loop.py (er IST die Kette) · der Prüfer selbst · tgv-skill-mail-body-to-pdf/skill.py und sein Deploy (dort ist mail_sender ein ANZEIGE-Feld in einer PDF-Fusszeile, keine Absender-Ermittlung — eine Kette wäre dort nicht falsch, sondern sinnlos). ⭐⭐⭐ SEINE EIGENE NARBE STEHT IN SEINEM KOPF: der erste Wurf prüfte if "sender" in text — und sender ist ein Teilstring von mail_sender, also entlastete sich jede rote Stelle selbst. Er meldete GRÜN über 707 Dateien, während sein eigener Selftest zeigte, dass er nicht auslöst. Jetzt fragt er den Syntaxbaum nach String-Konstanten statt eine Zeichenkette abzusuchen. ✅ Und sein --selftest läuft über den GANZEN Bestand mit — damit einer, der nichts mehr FINDET, nie mit einem verwechselt wird, der nichts mehr MESSEN kann. | ✅ | KONZEPT#01 (04.09., Register #1549). Positivkontrolle selbst getroffen: gegen origin/master (den Stand vor der Reparatur) findet er genau 4 Stellen — vier Fehler, vier Meldungen, nachdem verschachtelte Doppelmeldungen zusammengefasst wurden. ⛔ Er meldet ROT auch dann, wenn eine der vier Ausnahmen verschwindet — dann ist die Stelle umgebaut oder weg; beides ist ein Befund, keine Entwarnung. |
check_versand_fuehrt_durch_buchfuehrung.py (Workflow konformitaets-guards.yml) | Nennt eine .py unter 08_OPS/02_SKILLS die Resend-Adresse — ausser der einen, in der auch gebucht wird? Hintergrund: von drei Sendewegen führte nur einer Buch (#1529), und outbound_mails schwieg deshalb seit dem 10.07., ohne dass der Versand aufgehört hatte. TOWER#53 schärfte die Bedingung für Lösung B: „der EINZIGE Weg zum Versand führt DURCH die Buchführung" — deshalb wohnt der Transport in 08_OPS/betrieb/mail_buchfuehrung.py (senden_gebucht()), und dieser Prüfer misst, ob es dabei bleibt. ⭐ Er bewacht eine BAUFORM, keine Absicht: der erste Entwurf rief die Buchführung korrekt und hatte den Resend-Aufruf danebenstehen — wer eine zweite Sendefunktion geschrieben hätte, wäre daran vorbeigekommen, ohne eine Regel zu brechen. ⛔ WAS ER AUSDRÜCKLICH NICHT FRAGT: ob überhaupt gesendet wird · ob die Buchung gelingt (sie ist fail-open) · JavaScript (09_SKILLS/tgv-skill-send-mail/engine.js — der n8n-Weg, er bleibt 1–3 Monate und führt sein eigenes Buch) · n8n-Workflows in der Cloud, die nie exportiert wurden (Export-Stand 06.08.) · SMTP oder einen anderen Anbieter. ⇒ Ein vierter Sendeweg fällt hier NICHT auf — dafür läuft die Kennzahl mail_buchfuehrung.auslieferungen_ohne_zeile() (#1530/#1532). ⛔ Fünf Fundstellen sind BENANNTE, DATIERTE Ausnahmen, keine stille Duldung: cron_health_check.py (HEIMHOLUNG#09 — bucht seit #1532, hat den Transport aber noch selbst) · deploy_lm07_b2_health_cron.py und deploy_morning_briefing_cron.py (STORAGE#04 — Deploy-Skripte, der Versand geschieht IN n8n) · check_vorschau_ist_die_mail.py und dieser Prüfer selbst (sie nennen die Adresse, um sie zu suchen). ⭐ Ein Prüfer, der beim ersten Lauf rot ist, wird abgeschaltet; einer, der seine Altlasten nennt, bleibt scharf und macht sie sichtbar. Selbstprobe in beiden Richtungen (ein neuer Sender wird gemeldet, die Heimat nicht) — eine Positivkontrolle, die nur das Grün prüft, beweist nur, dass er schweigt. | ✅ | KONZEPT#01 (04.09., Register #1541). Und er meldet ROT auch dann, wenn die erlaubte Heimat die Marke NICHT mehr nennt — dann ist der Transport umgezogen (Prüfer nachziehen) oder verschwunden (niemand sendet mehr über Python); beides ist ein Befund, keine Entwarnung. |
check_echte_ibans_binaer.py | Steht im INHALT einer getrackten Container-/PDF-Datei (.zip .docx .xlsx .pdf) eine IBAN mit gültiger mod-97-Prüfsumme, die weder Platzhalter noch freigegeben ist? Die zweite Reichweite zu check_echte_ibans.py, der seine Lücke selbst nennt: „NICHT GELESEN: 451 Binärdateien". | 🔨 gebaut, bewusst NOCH NICHT in der CI | TOWER#40 (20.08.). ⛔ Nicht verdrahtet, weil er rot ist — zwei Funde stehen offen (manus-Code-dateien.zip :: recent_executions_summary.json = n8n-Ausführungs-Dump mit Zahlungsdaten · sample.docx = die bekannte deutsche Test-IBAN). ⭐ Ein roter Guard verdeckt jeden hinter ihm — erst räumen, dann verdrahten. ⭐⭐ Seine eigene Bau-Narbe ist der Grund, warum er existiert: die erste Fassung reichte das Paar (schreibweise, iban) aus kandidaten() als Tupel an ist_iban() weiter, das darauf immer False gibt — der Guard war blind und meldete grün über alles, und Tower hatte das Falsch-Grün bereits als Entlastung an JC gemeldet. Gefangen hat es der Selbsttest (präparierte IBAN im Bauch eines ZIP), nicht das Nachdenken. R-159 wörtlich. Fach-Logik (mod-97, Platzhalter, Allowlist) wird aus check_echte_ibans.py importiert, nicht kopiert (ARTEFAKT-GESETZ). Nennt jede ungelesene Datei beim Namen und sagt ausdrücklich, dass er nichts über die git-Historie aussagt (R-193). Befund: BEFUND_IBAN_REICHWEITE_2026-08-20.md |
check_wfe_kopie_ist_live.py | Ist wf_e_live_build_mail.js im Repo noch Zeichen für Zeichen der n8n-Knoten, gegen den mail_bau.py gemessen wird — oder hat jemand in n8n geändert, während die Kopie stehenblieb? | ✅ | BUCHHALTUNG#06 (19.08., Register #474) — Tagesmotto ① als Mechanik: „Vor JEDER Messung: welche Quelle liest das Werkzeug gerade?" ⭐ Eine Kopie veraltet lautlos; nur ein Prüfer macht daraus Lärm. Vergleicht sha256 des reinen Knoten-Codes (ab der Schnittmarke) gegen n8n und gegen den Stempel HERKUNFT_wf_e_live.json; unterscheidet „Kopie == n8n, aber Stempel veraltet" (gelb) von echter Divergenz (rot). ⛔ Bewusst NICHT in der CI (braucht Netz + n8n-Key): CI misst netzfrei gegen die Kopie, dieser Prüfer beantwortet von Hand, ob die Kopie noch gilt. Kein Netz ⇒ Enthaltung (Exit 3), nie grün. Vor jedem Anfassen der Buchhalter-Mail und nach jeder n8n-Änderung laufen lassen |
check_versandweg_ist_eingeschaltet.py | Ist der Buchhalter-Versandweg IM DIENST eingeschaltet — gebaut, gepackt, geladen — oder zeigt der Screen „Kein blockierender Punkt“ über einem Weg, der nicht senden kann? | ✅ | VERSAND#01 (08.09., Register #1818) — umgestellt von PAKET#03 (#1772). Die erste Fassung fragte die n8n-API nach dem active-Schalter zweier Workflows; JC-Entscheid 08.09. „Im Dienst neu bauen, ohne n8n“ — seitdem gibt es diesen Schalter nicht mehr. ⛔ Ein Prüfer, der weiter den n8n-Schalter misst, misst den TOTEN Weg (für immer rot, nach dem Löschen für immer „kein Urteil“) und sagt über den lebenden nichts — „die Kontrolle nimmt einen anderen Gegenstand als das, was sie kontrolliert“. Misst jetzt drei Regale mit Namen (R-193): A Quelle app.py — accountant-send ruft mail_versand.senden(apply=True) mit freigegeben_von=auth.user_id, Status-Write mit Vorschau-Riegel (nur der AUFRUF wird gelesen, nicht der ganze Endpunkt), Scope-Guard, kein Webhook, und kein toter n8n-Name in benutztem Code (per AST — Kommentare/Docstrings dürfen den Grund tragen); _mail_mit_signatur baut über signatur_holen + mail_bauen(…, sig). B Bundle deploy_lm04_2 packt betrieb/mail_versand.py, betrieb/mail_buchfuehrung.py, fes_ap2/versand.py und rendert einen Resend-Schlüsselnamen in die Dienst-.env, keinen N8N-Namen mehr (gemessen 08.09. vor dem Bau: NICHTS davon fuhr mit). C Live nur mit --live: /api/skills/health.versandweg {baustein_geladen · anhang_baustein_geladen · schluessel_lesbar} — Zustand, nie Wert; ohne Netz oder bei altem Dienst ohne Feld Enthaltung (rc 3), nie Grün. --selftest: Grundlinie A+B grün, 4 A-Mutationen + 3 B-Mutationen rot, C in drei Fällen (Schlüssel fehlt rot · gesund grün · altes Feld Enthaltung). ⭐ Was er NICHT kann und sagt: er misst gebaut/gepackt/geladen — nicht, ob Resend die Mail annimmt; das beweist der erste Versand mit JCs Hand in outbound_mails. LAUFORT ci (A+B netzfrei), --live als Hand-/Cron-Lauf nach dem Ausrollen |
check_generatoren_gelaufen.py | Wenn ich die drei Generatoren jetzt laufen lasse — kommt inhaltlich das heraus, was im Commit steht? Oder hat jemand eine QUELLE geändert und den SPIEGEL nicht nachgezogen? | ✅ | PLAT#07 (#1865, Register #471), aus Towers Narbe 19.08.: an EINEM Tag fünfmal von Hand gen_anforderungs_register.py → gen_artefakt_register.py → gen_familie_export.py nachgezogen — geprüft wurde, was die CI genauso gut erwarten kann, und man erfuhr es jedes Mal NACH dem Merge. ⛔ Alle drei Ausgaben sind nicht-deterministisch (Herkunfts-Stempel: Datum + source_sha); der Guard neutralisiert sie, sonst wäre er an jedem PR rot und würde weggeklickt. Zeile von SCOPE#07 nachgetragen (19.08.): der Guard kam ohne sie ins Repo und machte damit check_artefakt_register.py auf jedem offenen PR rot — ⭐ der Wächter gegen ungepflegte Spiegel war selbst das ungemeldete Einzelteil |
check_eu_pflicht_riegel.py | Gibt es auf dem Weg vom Aufrufer zum Modell eine Stelle, an der ein Modellname gesetzt wird, ohne dass die Policy ihn gesehen hat? Vier Fragen, alle am AST/Text gemessen, nie an einem Kommentar. ⭐ Sein Kopf sagt selbst, warum er neben den Tests noetig ist: die Tests beweisen, dass der Aufloeser richtig entscheidet — sie beweisen NICHT, dass ihn morgen noch jemand fragt. Der Anlassbefund war genau das: ein Zweig expliziter Override -> kein Routing, und kein Routing hiess keine Sperre. Kein Fehler im Code, ein VORBEIWEG — und ein Vorbeiweg faellt keinem Testfall auf, der die richtige Tuer benutzt. | ✅ | MODELL#02 (#494, KONZEPT_MODELL_POLICY §10, JC-Entscheid ⑥ vom 20.08.2026) — ⛔ die Owner-Zuordnung ist eine ANNAHME von TOWER#53 (03.09.), gelesen aus dem Dateikopf, nicht vom Eigner bestaetigt (Hausmuster RAG#02 24.07.). ⭐ Nachgetragen, weil der Ratchet ihn als neu und unregistriert fuehrte und damit quellen-no-divergenz an JEDEM offenen PR gatete — waehrend er seit #494 in konformitaets-guards.yml eingehaengt ist und laeuft (hier gefahren: exit=0). ⛔ Der Haken steht fuer VORHANDEN UND LAUFEND, nicht fuer inhaltlich abgenommen. |
check_merge_treiber_gesetzt.py | Steht der Merge-Treiber, den .gitattributes voraussetzt — und will jemand eine Mechanik, die nur halb da ist? merge=ours verlangt merge.ours.driver. Fehlt er, tut die Zeile STILL NICHTS: kein Fehler, keine Warnung, nur der alte Konflikt. Umgekehrt ist ein GESETZTER Treiber ohne jeden Pfad der Beweis, dass jemand die andere Hälfte wollte — auch das ist rot. ENTHALTUNG: ob ein Merge inhaltlich richtig auflöst, misst er nicht. | ✅ | TOWER#53 (Register #1300), aus der eigenen Narbe 02./03.09.: ich setzte den Treiber und committete die .gitattributes-Zeilen nie — drei Lanes zogen in EINER Nacht siebenmal an denselben vier erzeugten Spiegeln nach, während ich ihnen die Mechanik als stehend meldete. Erlaubt ist das Veralten der Spiegel nur, weil check_generatoren_gelaufen.py sie rot meldet; dieser Prüfer misst deshalb auch, dass es jenen Wächter noch gibt. |
08_OPS/check_deploy_filter_kennt_das_bundle.py (#1648) | Was im Deploy-Bundle mitfährt — löst es bei Änderung auch einen Deploy aus? Liest per AST das QUELL-Argument jedes _add(tar, <quelle>, <ziel>) im Deploy-Skript und hält es gegen die on: push: paths:-Einträge des Workflows (214 ausgelieferte Dateien gegen 63 Muster, Stand 06.10.2026). ⛔⛔ BIS ZUM 06.10.2026 LAS ER MIT EINEM REGEX DEN QUELLTEXT — und sah damit nur Pfade, die als EINE zusammenhängende Zeichenkette dastanden, also meist den ZIELNAMEN im Tar statt des QUELLPFADS im Repo. ⭐⭐ Er mass, wo eine Datei auf dem Server landet, und urteilte darüber, welche Datei im Repo einen Auslöser braucht; wo beide zufällig gleich waren, stimmte es. 48 gesehene Einträge vorher, 214 nachher — unsichtbar waren elf gleichzeitig gemergte und nie ausgelieferte Dateien, darunter 08_OPS/check_routing_zwei_tabellen.py: eine .py, also innerhalb der Klasse, die sein eigener Kopf ausdrücklich beanspruchte. Dazu verwarf eine Typregel jeden Nicht-.py-Pfad, der kein Verzeichnis ist (sieben Datendateien, Befund TOWER#65). ⭐ Eine Änderung schloss beide Lücken, weil beide dieselbe Ursache hatten; eine Typregel braucht es seither nicht — jede aufgelöste Quelldatei braucht einen Auslöser, ganz gleich welche Endung. ⭐⭐ Nebengewinn, der eine Narbe strukturell auflöst: ein AST liest Kommentare gar nicht — die drei Fehlalarm-Narben dieser Datei (einmal klagte sie dadurch sich selbst an) können in dieser Leseart nicht mehr entstehen. ⛔ Statisch nicht auflösbare _add (Schleifenvariablen) und per TarInfo erzeugte Dateien werden in der Ausgabe einzeln als Enthaltung genannt, nicht stillschweigend verloren (BETRIEB#02, #3792). ⛔⛔ Fünf Instanzen in vier Monaten: #465 intake_raw · #479 die drei Buchhalter-Module · #636 mail_intake/orch_apply/imap_mark_seen · der R-155-Pfadfilter · und am 05.09. chat_engine/ · modell01/ · agent_orch01/ — NULL Treffer im Pfadfilter, obwohl alle drei im Bundle mitfahren. ⭐ Jedes Mal dieselbe Klasse: eine Datei wird ausgeliefert, und die Liste, die ihre Änderung bemerken müsste, kennt sie nicht — die Änderung wird gemergt und liegt still, und sie sieht dabei aus wie erledigt. ⭐⭐ Warum es diesmal auffiel: die Reparatur an chat_engine/gateway.py kam nur live, weil app.py zufällig im selben Vorgang lag — Glück ist kein Riegel. ⛔ ABGRENZUNG: check_bundle_vollstaendig.py fragt, ob alles mitfährt, was der Dienst braucht — das ist eine ANDERE Frage; ein grünes Bundle sagt über das AUSLÖSEN nichts. ⛔ REICHWEITE (R-193): gilt für genau zwei Regale — literale Pfade im Deploy-Skript und die paths:-Einträge im Workflow. Nicht gesehen: dynamisch gebaute Pfade · Verzeichnis-Globs ohne Namensnennung · andere Deploy-Wege (Frontend, n8n, Hetzner-Timer). | ✅ | TOWER#53 (#1648). ⛔⛔ DIE EIGENTLICHE NARBE ENTSTAND AM WÄCHTER SELBST: seine erste Fassung nahm den YAML-Zeilenkommentar in das Muster auf (- '08_OPS/fes_ap2/__init__.py' # …), das Muster traf dann nichts, und er meldete ROT für eine Datei, deren Eintrag sehr wohl dasteht. ⭐ Ein Fehlalarm ist hier teurer als ein Schweigen: er schickt jemanden dazu, einen bereits vorhandenen Eintrag noch einmal einzutragen — Arbeit an einer heilen Stelle. Behoben über _wert_ohne_kommentar(). GEGENPROBE: Selbstprobe 0 von 8 rot · Echtlauf grün · Positivkontrolle: Muster chat_engine/ entfernt ⇒ 30 Dateien ROT, nach Wiederherstellung wieder grün. ⛔ FAHNEN-RIEGEL nachgetragen, weil check_fahnen_riegel_bestand recht hatte — dritter Fang dieses Wächters am Tag seiner Geburt (nach DEPLOY#02 selbst und SCREEN#03). Bauform aus check_register_nummer.unbekannte_argumente (#1279), am ECHTEN Einstieg als Unterprozess gebissen: --selftest→0 · --selbstest→2 · --self-test→2 · selftest→2 · (leer)→0. ⛔ R-155 nachgetragen: ein ⇒ in einer print()-Zeile hätte das Werkzeug auf JCs cp1252-Konsole genau beim Melden eines echten Befundes getötet — durch FOLGE: ersetzt. ⛔ Ohne yaml-Modul gebaut (Narbe 08_OPS/credentials/_ordnung.py:150). ⛔ NOCH NICHT IN CI VERDRAHTET** — offener Rest, ausdrücklich benannt: bis dahin ist er ein Werkzeug, kein Riegel. |
08_OPS/check_sammelfehler_bestand.py (#1731) | Welche Testdatei kann pytest nicht einmal LADEN? Ratsche auf Korb (3): pytest --collect-only über 07_TESTS/, ROT wenn die Klasse wächst. ⛔⛔ Der Anlass sind zwei Messungen, die dieselbe Klasse sind: ein pfadgefilterter Lauf startet nicht (RUECKSTAND#01), eine Testdatei lädt nicht (ROT#01) — beide Male kein Rot, kein Gelb, keine Enthaltung, sondern Abwesenheit. ⭐ Ein Sammelfehler ist nicht ein roter Test, sondern ein Test, der nie stattfand — und beim Abbruch nimmt er alle übrigen mit. ⛔ ABGRENZUNG, damit kein zweites Muster entsteht: check_test_sammlung (R-165) fragt heißt wie ein Test, wird nie gesammelt? · check_test_sammlung_bricht_nicht fragt killt eine Datei die Sammlung der anderen per Modulebenen-Exit? · check_testdatei_ist_angemeldet fragt läuft sie bei irgendeinem Raster? · und Korb (2) „startet der Workflowüberhaupt“ wird hier ausdrücklich NICHT nachgebaut — das beantwortet pruefe_ci_zustaendigkeit.py. ⛔ REICHWEITE (R-193): gemessen ist die Ladbarkeit. Nicht gemessen: ob die gesammelten Tests etwas prüfen. ⭐⭐ Und der größere Befund, den dieser Wächter selbst NICHT fangen kann, steht in seinem Kopf: 66 Dateien heißen skill.py, 15 Testdateien tun import skill — sys.modules schlüsselt nach Basisnamen. Vier brechen sichtbar, elf brechen nicht und prüfen trotzdem ein fremdes Modul: einzeln 33 passed / 0 failed, zusammen 21 passed / 12 FAILED. | ✅ | WAECHTER#01 (#1731). ⛔ Status ✅ = VORHANDEN, SELBSTGEPRÜFT UND POSITIVKONTROLLIERT — nicht „in CI verdrahtet“: sein Bestand ist bei Anlage 5 und nicht 0, ein Einhängen wäre sofort ein Riegel über fremde Altstellen (Rolle BAUER). Einhängen, sobald der Bestand 0 ist — oder auf Tower-Entscheid. ⭐ Beissprobe mit Köder, beide Richtungen: Datei mit unauflösbarem Import gelegt ⇒ rc=1, namentlich; Köder samt __pycache__ abgeräumt ⇒ rc=0. Übernommene Bauarten, nichts neu erfunden: Ratsche + --schreibe-bestand von check_fahnen_riegel_bestand.py · Leermengen-Sperre von check_js_syntax.py:265 (keine collected-Zeile ⇒ rc=3 KEIN URTEIL, nicht grün) · Fahnen-Riegel nach #1279 (rc=2 statt still scharf). ⛔⛔ R-155 SCHLUG AM WERKZEUG SELBST ZU: check_konsolensicher meldete ROT — es druckt Laufzeitdaten aus fremdem Material (Testdateipfade, pytest-Fehlertexte) und wäre unter cp1252 genau beim Aufzählen eines echten Befundes gestorben. ⭐ Ein Wächter über „Wächter, die man nie beissen sieht“, der im Moment seiner Richtigkeit stirbt, wäre das erste Beispiel seiner eigenen Klasse. Behoben, beide Richtungen nachgemessen. |
08_OPS/check_lesefehler_ist_kein_wert.py (#2424) | Stellt ein catch-Zweig einen LESEFEHLER als WERT dar? catch(e){ cell.textContent="—"; } macht aus einer Aussage über die ABFRAGE eine über den BESTAND — der Lesende sieht in einer Zahlen-Kachel einen Strich und liest „keine“, während die Zahl nur nicht ermittelbar war. Drei Körbe: (A) .textContent="—" · (C) Helfer setKpi(k,"—") · (B) return 0/[]/"". Ratsche über die 37 Live-Flächen (06_DASHBOARD/.html, .js), rot nur bei Zuwachs (Bestand 18 in 12 catch-Rümpfen — Bauart von check_sammelfehler_bestand.py übernommen). ⛔ Eigener JS-Lexer statt grep: in diesem Haus stehen die alten Zeilen wörtlich als Narbe im Kommentar neben der reparierten Stelle — ein grep meldet die Narbe als Wunde. Kommentare werden vor der Messung geschnitten; die Negativkontrolle im --selftest hält genau das fest. ⭐ Der Bestand zählt je Schlüssel eine ANZAHL: zwei wörtlich gleiche catch-Rümpfe in derselben Datei ergeben denselben Fingerabdruck — die fünfte Kopie wäre sonst unsichtbar gewesen, weil die Klasse schon da war. Selbsttest im SELBEN Lauf; fällt er durch ODER ist der Suchraum leer ⇒ rc=3 KEIN URTEIL. ⛔ Benannte Lücke: Korb (C) erkennt den Setzer am NAMEN (set, Kpi, render* …) — wer anders heisst, fällt durch; Python-except ist nicht gemessen. | ✅ | PROJEKT#08 — einhängen, sobald der Bestand auf 0 steht (Bedingung steht im Kopf der Datei) |
08_OPS/check_ernte_gate.py (#2001, Workflow schema-register-guards.yml ⑫) | Steht jeder JC-Entscheid im Konzept seines Gegenstands — oder verlinkt dorthin? Ratsche auf die verwaisten Entscheide in 00_LEITDOKUMENT/*.md (Grundlinie 08_OPS/ernte_gate_bestand.json mit _warum), ROT nur bei Wachstum, Schrumpfen erzeugt eine FORTSCHRITT-Zeile. ⛔ Anlass: der Mailer-Anbieterentscheid vom 03.08. lag sechs Wochen in der Schublade, weil er im Konzept des einen Gegenstands stand und im anderen nicht verlinkt war (#1993). ⭐ Datum ist Feld der Meldung, nicht Bedingung des Treffers; Zitat, Tabellenzelle und Prozess-Form sind gezählte Klassen, kein stiller Ausschluss (drei Fehlalarme, von MAIL#02 am eigenen Dokument gefunden). Beissprobe zieht den echten historischen Text aus git (krank c90fd4b3 → geheilt e83f1607). | ✅ | NOSTALGIC-NOYCE (#2001, Auftrag TOWER#56), gegengelesen von MAIL#02. ⛔ Status ✅ = VORHANDEN, SELBSTGEPRÜFT (20 Achsen) UND AN EINEM ECHTEN FALL GEMESSEN: die Ratsche schrumpfte 26 → 24, als #2749 die Heilung von KONZEPT_WEBSITE_FACTORY.md:61 auf master brachte — und blieb bei +6 neuen Treffern aus masters Inhalt ohne Neuzugang. 🔒 Reichweite: nur 00_LEITDOKUMENT/*.md, Dateiebene, nicht rekursiv; nicht geprüft, ob ein Verweis das RICHTIGE Konzept trifft. |
08_OPS/check_firmennamen_kanon.py (#2163, Workflow schema-register-guards.yml ㉝) | Schreibt jemand neu „C3 = Convis GmbH“? C3 ist CONVIS Consult & Marketing GmbH; „convis GmbH“ ist ein KUNDE von C3 (JC 19.09., #2157). Ratsche mit Basis (merge-base) und Kopf im SELBEN Lauf, Urteil über die MENGE der Fundstellen (Pfad + Zeilentext). Zitate als Fehler (⛔/falsch/Kunde) sind erlaubt. | ✅ | ENTITAETEN#01 (#2163). Status ✅ = vorhanden, Selbsttest (7 rote, 7 grüne Fälle, Mengen-Urteil) und Beißprobe (neue Zeile → rc 1). 🔒 Reichweite: git-verfolgte Textdateien + unverfolgte UTF-8-Dateien; nicht DB-Inhalte (prompt_config, entities.name, kb_suppliers). Erste Messung: Basis 98 · Kopf 72 · geheilt 26. |
08_OPS/check_pfadfilter_kennt_die_gelesenen_quellen.py (#2184, Workflow schema-register-guards.yml ㉞) | Kennt der paths:-Filter eines Workflows die Quellen, die seine Suiten LESEN? ⛔⛔ Die Klasse zeigt sich nie als ROT, nur als ABWESENHEIT — ein Workflow, der nicht startet, steht in der Prüfliste nicht als Fehler, sondern gar nicht, und das liest sich wie Zustimmung. Anlass: am 19.09. lief dreimal eine geänderte Quelle ohne ihre Suite — 08_OPS/heimholung11 (#2872, machte master rot), 08_OPS/rag01 (#2879), 18 weitere (#2880/#2176). Misst statt zu lesen: lässt die Suiten über mess_gelesene_quellen.py laufen und vergleicht jede gelesene Quelle gegen beide Auslöser (push · pull_request sind zwei Listen). Ratsche mit Basis (merge-base) und Kopf im SELBEN Lauf, beide in einem frischen detachten Arbeitsbaum — sonst fielen an der Basis Suiten aus, die am Kopf laufen (.env), und jede Altlast erschiene als neue Lücke. Urteil über die MENGE. ⛔ ABGRENZUNG, damit kein zweites Muster entsteht: check_pfadlisten_beider_ausloeser fragt sieht pull_request weniger als push? (Listen gegen Listen) · pruefe_ci_zustaendigkeit fragt welche Workflows feuern für MEINE Dateien? · check_deploy_filter_kennt_das_bundle fragt dasselbe für das Deploy-Bündel, aber statisch aus dem Deploy-Skript. Dieser hier ist der einzige, der die Antwort am laufenden Test misst. | ✅ | TOWER#57 (#2184, 20.09.). Status ✅ = vorhanden, selbstgeprüft, positivkontrolliert UND in CI verdrahtet (4 Schritte ㉞, if: !cancelled()). ⭐ Biss ende-zu-ende: **/.py aus dem push-Filter entfernt ⇒ 14 NEU namentlich, rc=1. ⛔⛔ Der erste Sabotage-Versuch war UNGÜLTIG und blieb zu Recht grün — er entfernte eine Zeile, die derselbe Vorgang gerade erst hinzugefügt hatte, die Lücke war an der Basis schon offen. Eine Beissprobe an einer ohnehin offenen Stelle prüft nichts und sieht aus wie ein bestandener Beweis.* Steht als Narbe im Kopf. ⭐ Erste Messung: 13 Altlast-Lücken derselben Klasse sofort geschlossen (familie-guard 5 · konformitaets-guards 6 · schema-register-guards 2) ⇒ 3 von 4 Workflows auf 0. Bestand agentchat01-suiten 1030, sichtbar und nicht blockierend (288 unter 01_ITERATIONS/ — Suiten, die ganze Regale ablaufen); ein Erstlauf mit 1043 Rot wäre ein Guard, den man wegklickt. ⛔ NICHT in agentchat01-suiten.yml eingehängt, obwohl das der Anlass ist: der klont flach (keine Basis ⇒ immer rc=3), und sein pull_request hat einen Pfadfilter — ein Wächter gegen lückenhafte Pfadfilter, der selbst an einem hängt, ist genau dann stumm, wenn er gebraucht wird. 🔒 Reichweite (R-193): Pfade, die nur auf einem Zweig gelesen werden, den die Tests nicht betreten, sieht er NICHT — er kann eine Lücke belegen, nie ihre Abwesenheit. Gemessen wird der committete Stand. |
08_OPS/stand_melden.py (#2305, nicht in der CI — Melde-Werkzeug fuer Bahnen und TOWER) | Wogegen gilt das Urteil? Vier Zahlen je Zweig: Kopf · Basis samt Alter in Commits · git merge-tree --write-tree als Zeuge (mit Konfliktdateien) · total_count an genau diesem Kopf. ⛔⛔ Anlass: Naht 28 trug 9/9 gegen eine 52 Commits alte Basis; am selben Nachmittag zweimal dieselbe Luecke in anderen Bahnen. ⛔ gh pr view --json mergeable bleibt draussen — die Antwort wechselt zwischen zwei Abfragen. ⭐ Die Null der Laufzahl ist ein eigener, roter Zustand (UNGEPRUEFT), neben einem dritten Zustand nicht abgefragt (KEIN URTEIL), der nicht rot macht: mergeStateStatus sagt etwas ueber die Mergbarkeit, nichts ueber die Pruefung. ⛔ Bei null Laeufen raet es zu neuem Push oder Sammel-Naht, nicht zu close && reopen — das hat bei #2963 nachweislich nicht gewirkt. | ✅ registriert | 18/18 Achsen, jede Rot-Achse einzeln; Urteil als reine Funktion, damit die netzabhaengige Null-Achse nicht aus der Selbstprobe faellt; Fahnen-Riegel rc=2; cp1252 als Verhaltenstest — Owner TOWER#57 |
08_OPS/check_naht_ist_die_vereinigung.py (#2300, Workflow schema-register-guards.yml ㊱) | Traegt jede Naht genau die VEREINIGUNG ihrer Eltern? Nichts verloren, nichts erfunden — gemessen an der Nummern-Spalte des Anforderungs-Registers, an JEDER Naht in <basis>..HEAD. ⛔⛔ Gebaut, weil sein Vorgaenger gruen war, WEIL er wegsah: er las stur HEAD^1/HEAD^2, und bei einem zweistufigen Nachzug (master_nachziehen committet die Naht, die Generator-Runde legt einen gewoehnlichen Commit darauf) hat HEAD nur EINEN Elternteil — er meldete „soll = ist (aus 1 Elternteil)“ und sah die Naht nie an. ⭐ Nicht die leere Menge, die FALSCHE. ⛔ NUXIN-N② gegen mich: das Verfahren stand schon im Haus (check_naht_verliert_keine_deklaration.py, rev-list --min-parents=2 --parents) — jetzt uebernommen, die beiden sind Geschwister. ⛔ Reichweite (R-193): nur die Nummern-Spalte, nicht Inhalt (validate_register_felder), nicht Reihenfolge, nicht Scherbe↔Datei (register_md_bauen --pruefe); ohne Naht im Bereich meldet er 0 geprueft, kein stilles Gruen. | ✅ registriert | 10/10 Achsen, Beissprobe stellt den zweistufigen Nachzug nach und traegt ihre eigene Gegenrichtung (Achse ②); Fahnen-Riegel rc=2; R-155 als Verhaltenstest unter cp1252 auf allen drei Ausgabewegen — Owner TOWER#57 |
08_OPS/check_create_table_hat_rls.py (#2145, Workflow schema-register-guards.yml ㈠) | Legt eine noch nicht gefahrene SQL eine Tabelle OHNE Row Level Security an? ⛔⛔ Der Anlass kam von AUSSEN: Supabase stoppte JC am 21.09. bei einer vorbereiteten Datei („creates a table without enabling Row Level Security“) — die Tabelle verband contract_id mit der Vertraulichkeitsstufe, ablesbar wäre gewesen, welche Verträge streng sind, ohne einen zu öffnen. Die Frage war im Haus unbewacht. ⭐ DIE KLASSE: eine Hilfstabelle erbt die Schutzpflicht der Daten, die sie spiegelt — die Bahn hatte an den Rückweg gedacht, nicht an die Wand um den Rückweg. Gemessen vor dem Bau (103 Tabellen): PROPOSED_ 0 · STAGED 2 (llm_verdikte, loeschsperren — beide VOR ihrem ersten Lauf gewandet) · Altbestand 29 (16 in EINER Monolith-Datei). ⛔ ABGRENZUNG: check_migration_headers fragt lügt der Kopf? · mandantenwand_gegenbeweis fragt hält die Wand LIVE?* — dieser fragt als einziger, ob eine Tabelle überhaupt eine bekommt, bevor sie existiert. Ratsche mit namentlicher Grundlinie (eine Zahl kann man einfrieren, ohne je hinzusehen), Ausnahme nur MIT Grund. | ✅ | DSGVO#01 (#2145, 21.09.). Status ✅ = vorhanden, selbstgeprüft (9 Zusicherungen, beide Richtungen) UND in CI verdrahtet. ⭐ Der eigene Selbsttest fing einen Fehler IM WÄCHTER: die Leerraum-Klasse fraß den Zeilenumbruch, und eine Ausnahme ohne Grund nahm das BEGIN; der nächsten Zeile als Begründung — ohne die Gegenprobe wäre ein Wächter ausgeliefert worden, der genau das durchlässt, wogegen er gebaut ist. 🔒 Reichweite: urteilt NUR über PROPOSED_/_STAGED (Altbestand gemeldet, nicht bewacht — sonst Dauerrot statt Tor); liest den Repo-Text, „kein ENABLE hier“ heißt NICHT „in der DB offen“; prüft NICHT, ob die Policies inhaltlich richtig sind. |
08_OPS/check_pytest_schritt_hat_skip_tor.py (#2244, Workflow schema-register-guards.yml ㉟) | Gilt ein pytest-Schritt als bestanden, wenn alles übersprungen wurde? ⛔⛔ Ein Lauf, der nichts misst, sieht aus wie ein Lauf, der nichts gefunden hat — pytest meldet X passed, Y skipped mit rc=0. Gemessen vor dem Bau: 45 Aufrufe in 35 Schritten, rc=5 0, Exitcode verschluckt 0, aber 32 ohne Skip-Tor. ⛔ ABGRENZUNG: check_testdatei_ist_angemeldet fragt läuft sie irgendwo? · check_sammelfehler_bestand fragt lässt sie sich LADEN? · check_pfadfilter_kennt_die_gelesenen_quellen fragt startet der Workflow überhaupt? — dieser fragt als einziger, ob ein GESTARTETER, GELADENER, GELAUFENER Schritt tatsächlich etwas gemessen hat. Ratsche, ROT nur bei Zuwachs, Basis und Kopf im selben Lauf per git show (kein zweiter Testlauf). | ✅ | TOWER#57 (#2244, 20.09.). Status ✅ = vorhanden, selbstgeprüft (acht Achsen, beide Richtungen) UND in CI verdrahtet. ⭐ Die drei Vorbilder ㊱/㊴/㊵ stehen namentlich im Kopf — ich hatte sie zuerst fälschlich als Befund gemeldet und set -o pipefail übersehen; die Korrektur steht dort, damit sie nicht beim nächsten Leser wieder als Befund auftaucht. 🔒 Reichweite: nicht geprüft wird, ob ein Skip berechtigt ist, ob der Test taugt, oder rc=5 — dafür bräuchte er einen Lauf, und er liest nur. |
08_OPS/check_brief_ist_im_kompendium.py (#2278, Workflow familie-guard.yml ㈛) | Ist ein Lane-Brief vom Kompendium aus AUFFINDBAR — oder nur vorhanden? Die Arbeitsanweisung verlangt, zuerst im Kompendium nachzuschlagen; jede Bahn schreibt ihr Konzept in ihren BRIEF.md. Gemessen: 132 Briefe, 0 erreichbar (Positivkontrolle: 337 Pfade nach draussen werden genannt — der Weg lebt, er führt nur nie dorthin). Preis an einem Fall: 19.06. im Brief, 20.09. neu gebaut — 93 Tage. ⭐ Geschwister von D7 in familie_guard.py: der verbietet Inseln INNERHALB konzepte/ und schaut nach innen; dieser fragt nach aussen. ⛔ Zwei übernommene Fallen: ein generischer Dateiname ist kein Beleg (der blosse Name hätte 132 Scheintreffer ergeben — geprüft wird der PFAD) · generierte Spiegel zählen nicht. Ratsche, Grundlinie im selben Lauf. | ✅ | TOWER#57 (#2278, 20.09.), Messung RKA-UX#01 (#2275) — nachgemessen, nicht übernommen. Status ✅ = vorhanden, selbstgeprüft (sieben Achsen), in CI verdrahtet, und der Auslöser 01_ITERATIONS//BRIEF.md mitgesetzt. 🔒 Reichweite, und sie ist die wichtigste Zeile: er sieht, ob ein Brief VERZEICHNET ist — nicht, ob sein Inhalt EINGEFALTET ist. Ein Link hätte die 93 Tage nicht verhindert.** Nicht gemessen: Gabriel-Archiv, n8n-Workflows. |
08_OPS/mess_gelesene_quellen.py (#2184) — Baustein, kein Guard | Welche Repo-Dateien liest eine pytest-Suite WIRKLICH? PEP-578-Audit-Hook über einen sitecustomize-Shim, je Prozess eine Ausgabe (auch Unterprozesse), sys.modules-Nachschau, .pyc auf die Quelle abgebildet. ⭐ Entstanden, weil RAG#01 in #2880 genau so gemessen hat — dort aber als Einmal-Skript, das nie committet wurde; hier als wiederverwendbares Werkzeug mit Fahnen-Riegel und --selftest (6 Achsen). ⛔⛔ Zwei Funde, die das LESEN nicht hergab, beide erst am scharfen Lauf: ① der atomare Schreibweg hängt eine Zufallszahl hinter das .pyc (…cpython-313.pyc.2935749451200) — die erste Fassung prüfte die Endung und lief bei jedem leeren Cache, also jeder CI, ins Leere; Anker ist jetzt .cpython-<zahl>. ② Gemessen statt geglaubt: die .pyc-Abbildung bringt an 07_TESTS/rag01 genau zwei Quellen, die sonst fehlen — rag01/inventar_volltext.py + nachlauf_volltext.py, per spec_from_file_location geladen und deshalb nicht in sys.modules. Damit ist RAG#01s Positivkontrolle belegt. | ✅ | TOWER#57 (#2184, 20.09.). Status ✅ = vorhanden, selbstgeprüft, in CI gerufen (㉞ Selftest). ⭐ Die Positivkontrolle wird ROT, wenn man die Abbildung ausbaut — der erste Entwurf prüfte ein normal importiertes Modul und konnte damit nie rot werden (feedback_kriterium_muss_rot_werden_koennen). 🔒 Reichweite (R-193): nicht gesehen sind Quellen auf Zweigen, die die Tests nicht betreten, und Prozesse ohne PYTHONPATH-Erbe. |
08_OPS/check_ctx_ueberlebt_den_pruefer.js (#2193, Workflow konformitaets-guards.yml ㍶) | Gibt ein Prüfer, der den Chat-Kontext anfasst, ihn GANZ zurück — oder nur sein Etikett? ⛔⛔ Anlass (JC 20.09.): Beleg #8488 gepinnt, Frage „welches Jahr betrifft es?“, Antwort = der FILTER-Zeitraum der Oberfläche. Ursache war der EIGENE Selbsttest: DRIFT-CHAT-CTX-CHIP gab im finally nur setChatContext(vorher.label) zurück (ohne opts ⇒ docId null, satz null), während der Kommentar daneben „exakt zurück, nie einfach auf leer“ versprach — und er läuft bei JEDEM Routenwechsel (runHere), also auf genau der Navigation, die den Kontext herstellt. Verhaltens-Guard: schneidet den Kontext-Kern UND die zwei ECHTEN Regel-Objekte aus der Shell und FÄHRT sie (kein Nachbau — ein nachgebauter finally bewiese nur, dass das Testdouble stimmt). 8 Zusicherungen, darunter die Kette BEIDER Prüfer in RULES-Reihenfolge (JCs Lage) und die Gegenrichtung (ohne Pin wird kein Bezug erfunden, Demo-Riegel #80 bleibt zu). ⛔ ABGRENZUNG: die Shell-Hälfte. Die Dienst-Hälfte desselben Befundes ist 07_TESTS/agent_chat01/test_beleg_im_kontext_2193.py (㍷). | ✅ | KAI-QUALITAET#02 (#2193, 20.09.). ✅ = vorhanden, selbst-sabotiert (4 Mutationen, alle beißen), Echtlauf 8/8, in CI verdrahtet (㍶, if: !cancelled(), beide Pfadlisten). ⭐ Gegenprobe ende-zu-ende gegen origin/master f3f88bb37: ④ meldet docId=null während der Prüfer selbst „gruen“ sagt — JCs Bildschirmfoto, deterministisch reproduziert; ⑤ (beide nacheinander) ebenfalls null. ⛔ Ohne bewiesenen Biss sagt er weder grün noch rot, sondern druckt ein Zustandsbild und geht mit Exit 2 — sonst wäre die Abbruchmeldung eine Null mit drei Bedeutungen. 🔒 Reichweite (R-193): gemessen wird 06_DASHBOARD/core_shell.html. NICHT gemessen: ob der Body im Browser wirklich rausgeht (entsteht erst im ask() eines gerenderten Chats), und ein DRITTER Prüfer, der den Kontext ohne _getChatCtx anfasst, fällt hier nicht auf. |
07_TESTS/agent_chat01/test_beleg_im_kontext_2193.py (#2193, Workflow konformitaets-guards.yml ㍷ + check_agentchat01_suiten.py) | Liegt ein Beleg im Kontext — nennt die Antwort ihn, oder sagt sie ehrlich, dass sie die Angabe dort nicht findet? ⭐ Die neue Prüf-Klasse in chat_engine/antwort_pruefer.py: B1 beleg_nicht_genannt · B2 beleg_ungelesen. ⛔⛔ Sie ist bewusst KEINE sechste Variante von „Zahl ohne Quelle“: die fünf Prüfungen von KAI-QUALITAET#01 P3 fragen stimmt, was dasteht? — JCs Antwort war in JEDEM Satz wahr und trotzdem zur falschen Sache. B1/B2 fragen worüber redet ihr?, und das ist die einzige Frage, die der Nutzer am Bildschirm selbst nicht nachprüfen kann. ⭐ Die ehrliche Enthaltung ist ausdrücklich grün — ein Prüfer, der sie bestraft, treibt das Modell ins Raten. | ✅ | KAI-QUALITAET#02 (#2193, 20.09.). ✅ = vorhanden, in CI verdrahtet (㍷, beide Pfadlisten) UND im Suiten-Runner angemeldet (17 Fälle, Zahl gemessen). ⭐ Gegenprobe gegen origin/master f3f88bb37: 9 rot / heute 17 grün — und er stürzt dort nicht ab, sondern MELDET, was fehlt (pruefe_antwort kennt context_doc_id nicht). ⛔⛔ Der Fall, der alles trägt, ist Fall E: agent_loop rief den Prüfer nur if spur: — JCs Turn rief KEIN Werkzeug, also sah ihn nie einer. Ein Tor, das auf „es wurde gearbeitet“ prüft, ist blind für „es wurde nicht gearbeitet“. 🔒 Reichweite (R-193): offline, ohne Netz/DB/LLM; geprüft wird die PROSA gegen die Werkzeug-Spur, nicht der Inhalt von Karten und Blöcken. |
07_TESTS/agent_chat01/test_beleg_volltext_2193.py (#2193-B, Workflow konformitaets-guards.yml ㍸ + check_agentchat01_suiten.py) | Kann KAI den Belegtext lesen, den der Screen zwei Handbreit über dem Composer anzeigt? ⛔⛔ Der Anlass ist größer als JCs Frage und wäre auch ohne sie zu finden gewesen: extracted_markdown kam in keiner der 34 Dateien von 08_OPS/chat_engine/ vor (grep, 0 Treffer), während das Beleg-Panel genau dieses Feld anzeigt (core_shell.html:22002). Ein gepinnter Beleg lieferte dem Modell 11 Metadatenfelder ohne ein Zeichen Text (query_documents), ≤ 6 ähnlichkeitsnahe Abschnitte (search_knowledge, lexikalische Zweitsicht bei Pin abgeschaltet) — und klaerung-get-detail, das einzige Werkzeug mit Belegtext, las context_doc_id gar nicht. ⭐ Am Bestand gemessen (571 gefüllte Belege): Median 1608 Zeichen · p95 15007 · max 130801, 23 über 20k. Der Regelfall ist klein, der Schwanz ist das Problem ⇒ Deckel mit Ansage (20000, an p95 gewählt), kein Verzicht. ⛔ Die Ansage ist der eigentliche Inhalt: ein still gekappter Text ist schlimmer als kein Text — das Modell hält das Ende des Ausschnitts für das Ende des Belegs und schließt, die Angabe stehe nicht darin. ⛔ ABGRENZUNG: diese Suite prüft, ob der Text ankommt (Mechanik); die Nachbarin test_beleg_im_kontext_2193.py (㍷), ob die Antwort ihn benutzt (Ergebnis). | ✅ | KAI-QUALITAET#02 (#2193, 20.09.). ✅ = vorhanden, in CI verdrahtet (㍸, beide Pfadlisten) UND im Suiten-Runner angemeldet (19 Fälle, Zahl gemessen). ⭐ Gegenprobe gegen origin/master f3f88bb37: 9 rot / heute 19 grün, und er meldet dort einzeln, WAS fehlt, statt zu stürzen. ⭐⭐ An JCs echtem Beleg nachgemessen (#8488, read-only): extracted_markdown 2283 Zeichen; der Text nennt „Bescheid vom 05.04.2023“ und enthält das Veranlagungsjahr nicht (Jahreszahlen im ganzen Text: 2023 zweimal, 1996 einmal). ⇒ Die richtige Antwort auf „welches Jahr betrifft es?“ ist die ehrliche Enthaltung — genau deshalb lässt der Antwort-Prüfer aus #2193-A sie als grün durch. Ein Guard, der eine Nennung erzwungen hätte, hätte KAI hier ins Raten getrieben. 🔒 Reichweite (R-193): geprüft werden _beleg_text, der Detail-Executor (Stub-DB) und doc_directive. NICHT geprüft: ob die Extraktion den Text richtig liest (Pipeline), ob das Modell den Hinweis befolgt (misst ㍷), und der Live-Dienst. |
08_OPS/check_dienstfassung_eindeutig.py (#1649, Workflow schema-register-guards.yml ㉖) | Gehoert eine SERVICE_VERSION genau EINER Bahn? Drei Achsen, alle laufen immer: ① mehrere aktive Zuweisungen in app.py (Python nimmt die LETZTE — Narbe #1647) · ② ein OFFENER Zweig beansprucht dieselbe Fassung (der Fall, den git NIE meldet, weil kein Konflikt entsteht) · ③ eine fremde Reservierung haelt sie. Zugleich die Vergabe: --ziehe [--reserviere] zieht die naechste freie Fassung ueber origin/master + alle offenen Zweige + Reservierungen und haelt sie ATOMAR. ⛔ Beanspruchen ist nicht tragen: ein Zweig, der master's Zahl aus einem Nachzug traegt, und einer, der unter master liegt, sind KEIN Anspruch (beide falschen Anklagen an der ersten Live-Messung gefangen). ⛔ NICHT geprueft: ob sich die Zahl bewegt hat (das ist check_dienst_version_wandert_mit) und ob der Dienst sie ausliefert (B-26). Selbstbiss 19/19 inkl. ROT-Proben. | ✅ | ZAEHLER#01 |
08_OPS/nummernraum.py (#1649) | Der Mechanismus, nicht der Zaehler: atomare Reservierung per Ref-Push (refs/tgv/<raum>-reservierung/<schluessel>), Lesen eines Wertes ueber origin/master UND jeden offenen PR-Zweig, Enthaltung statt stillem Gruen. ⭐ Herausgeloest aus check_register_nummer.py:272 statt kopiert — ein Baustein, zwei Zaehler (Registerzeile · Dienstfassung). Wer einen dritten Zaehler absichern will, parametriert raum= und baut nichts neu. Selbstbiss 12/12 inkl. Raum-Trennung. | ✅ | ZAEHLER#01 |
08_OPS/check_je_nummer.py (#2162, Workflow familie-guard.yml Job je-nummer, Nachbar von D1) | Bezeichnet eine JE-Nummer in jc_entscheide.html genau EINEN JC-Entscheid — auch ueber offene PRs hinweg? Vier Achsen: ① Dublette in der Datei (netzfrei) · ② eigene neue Nummer inzwischen auf origin/master · ③ ein offener PR beansprucht dieselbe Nummer · ④ fremde Reservierung. Zugleich die Vergabe: --ziehe-bis-frei [N] [--reserviere] (Raum je, Ref refs/tgv/je-reservierung/JE-NNN). ⛔ Anlass 19.09.: JE-175 und JE-176 je zweimal in offenen Zweigen vergeben, #2845 von Hand aufgeloest; D1 liest das Register in ein dict und verschluckt die Dublette. Dritter Nutzer von nummernraum, kein neuer Mechanismus. | ✅ | TOWER#57-Auftrag 19.09. · Selftest mit ROT-Proben am Anlassfall · 07_TESTS/tower57/test_je_nummer_riegel.py (8 Faelle; Gegenprobe auf bae20e9b3 rot, Positivkontrolle: Leser sieht alle 130 Eintraege, eingespritzte Dublette wird rot) |
check_fahnen_riegel_bestand.py | Wie viele unserer Werkzeuge KOENNTEN eine unbekannte Fahne stumm schlucken — und ist diese Zahl gewachsen? ⛔ Er sagt ausdruecklich NICHT „diese sind kaputt“: „liest sys.argv ohne argparse" ist eine BAUART, kein Nachweis von Blindheit. Er sagt: „so viele koennten es sein, hier ist die Liste, und niemand hat sie gebissen.“ | ✅ | SICHERHEIT#04 (#1279, 02.09.2026). DIE NARBE: check_register_nummer.py --register <Kopie> verwarf die Fahne kommentarlos und mass weiter die ECHTE Datei — zwei absichtlich verdorbene Kopien meldeten daraufhin „kein Biss“, beide Male, weil die Sabotage nie ankam. ⭐ Eine Beissprobe, die nicht beisst, sieht aus wie eine bestandene Pruefung. ⛔ Dieselbe Klasse wurde am 29.08. schon einmal getroffen (DESIGN#05: --help startete stumm den Normallauf) — repariert wurde damals die FAHNE, nicht die BLINDHEIT. ⭐⭐ Zaehne an genau einer Achse: ROT, wenn die Klasse WAECHST — ohne die waere er ewig gruen und von einem kaputten nicht zu unterscheiden. Und er hat am ersten Tag seinen eigenen Erbauer gefangen: mein naechstes Werkzeug trug den Riegel inline, ohne den Namen der Vorlage. Ich habe mich gebeugt statt ihn zu beruhigen (R-204). Grundlinie 08_OPS/fahnen_riegel_bestand.json; eingehaengt in schema-register-guards.yml (dort kein Pfadfilter und voller Checkout — in konformitaets-guards.yml waere er bei genau der Aenderung stumm geblieben, die er fangen soll). |
TOWER_REGELWERK.md (80 Tower-Regeln + §0 Rolle) | Tower-Selbstbindung | ✅ | lebt; Guard-Idee (Transkript-Scan) = Tower#25-Backlog |
ORCH_FLIP_ANLEITUNG.md | überholt (Placebo) | 🗑️→ ersetzt | ⛔-Banner drauf (#988); FLIP_VORLAGE ersetzt sie |
check_baum_und_liste_zaehlen_gleich.py | Zaehlen der Verzeichnis-Knoten und die Liste daneben DIESELBE Menge? Anlass: JC klickte am 05.09. auf Aug 2026 — am Knoten stand 23, die Liste meldete 29. Ursache war nicht ein falscher Filter, sondern ZWEI Filter: _buildTree ordnet exklusiv (Kanon-Ordner schlaegt Monat), _filtered leitete dieselbe Frage ein zweites Mal her und liess die Ausschliesslichkeit weg. ⛔⛔ Er misst keine Nachbildung: er schneidet die echten Funktionen aus core_shell.html, fuehrt sie in node aus und haelt jeden Knoten gegen _filtered mit genau dessen Scope. Drei Achsen: Struktur (beide entscheiden ueber _treeKnoten) · Zaehlung (Knoten == Liste) · Offenlegung (Knotenzahl + +N == alle Belege des Monats — eine Zahl, die zu wenig meldet, sieht aus wie Ordnung). Netzfrei. | ✅ | SCREEN#04 (#1654). ⭐ Mutationsprobe statt geglaubter Selbstprobe: --selftest stellt JCs Fehler in einer Kopie WIEDER HER und verlangt ROT — dazu zwei weitere Mutationen (Offenlegung entfernt · Ausschliesslichkeit entfernt) und eine Falsch-Rot-Probe: ein Kommentar, der die verbotenen Namen nennt, darf nichts ausloesen. ⛔ Der erste Bau war genau daran rot — er las den #1643-Kommentar als Code und haette die Erklaerung bestraft. Argument-Riegel rc=2 (--selbstest beisst). Ehrliche Grenzen im Kopf der Datei: kein Browser, keine Bestands-Kappung, keine RLS. Eingehaengt: konformitaets-guards.yml ㈛. |
check_werkzeug_adressen.py (Workflow konformitaets-guards.yml, ㉜) | Traegt ein Werkzeug eine Adresse in einen FREMDEN Worktree — raet es also seinen Bezugspunkt, statt ihn zu finden? Anlass: SCREEN#09 meldete 07_TESTS/ablage02/pruef_panel_metadaten.js lese core_shell.html aus einem Worktree, „den es nicht mehr gibt“. ⛔ Gegengemessen: den Worktree GIBT es (registriert, Zweig ablage02/zeiger-kanon-waechter bei 427686d8) — der gemeldete Grund war falsch. ⭐ Der echte Defekt war schlimmer: das Werkzeug LIEF und mass eine eingefrorene FREMDE Kopie (Blob 545adc7f gegen c890d1f0, 64.050 Bytes Unterschied) — es pruefte den Renderer einer anderen Bahn und haette gruen darueber gemeldet. ⭐⭐ Ein Werkzeug, das abstuerzt, merkt jeder. Eines, das gruen meldet ueber die falsche Datei, niemand. Ein zweiter Fall (zwoelf Werkzeuge in 07_TESTS/iter67/ auf eine fruehere Repo-Wurzel) macht es zur Bauform. ⛔⛔ RATSCHE, kein Urteil: rot wird nur, wer NEU dazukommt; ob eine Adresse heute schadet, haengt am Worktree UND an seinem Stand — beides ist auf einem CI-Laeufer nicht messbar, ein Guard der das behauptet wuerde raten. Er misst die Bauform, nicht die Wirkung. ⛔ AST bei .py (nur echte Literale, keine Docstrings/Kommentare) — ein grep haette die Rueckweg-Kommentare der reparierten Dateien angeklagt und dauerhaft rot gestanden. ⛔ Ehrliche Grenze: bei .js kein AST, dort Kommentarabzug vor der Messung — Naeherung, keine Zerlegung, so im Bericht. ⛔ Was er NICHT abdeckt: absolute Repo-Wurzel-Pfade ohne worktrees (45 Fundstellen, eigene Klasse) · .md (dort ist ein Worktree-Name ein Protokoll) · Gebrauchstexte in Docstrings. Netzfrei. | ✅ | MCP#01 (#1672, 06.09.). Grundlinie LEER, weil die Klasse in derselben Runde vollstaendig geraeumt wurde (Fundstellen gesamt: 0). ⭐ Mutationsprobe mit drei Werten: [1] saubere Datei 0 · [2] Mutation hat gegriffen · [3] 1 Fundstelle — ⛔ gleiche Zahl vorher/nachher ist im Code als ABBRUCHGRUND verdrahtet, nicht als Ergebnis; dazu [4] Gegenrichtung (ein .js-Kommentar darf nichts ausloesen). ⛔ Drei eigene Fehler fand erst der LAUF, nicht das Lesen: R-155 verletzt (Symbol in print() — der Guard starb an der eigenen Meldung), er klagte sich SELBST an (seine Selftest-Adressen sind echte Literale), und fremde SyntaxWarnings flossen in seinen Bericht. Danach meldete check_konsolensicher ihn erneut: er druckt Laufzeitdaten aus fremdem Material, die Literal-Achse sieht das nicht ⇒ reconfigure-Vorspann. Eingehaengt in beide Ausloeser (07_TESTS/, 08_OPS/) — sonst waere er gebaut und nie gerufen worden. |
check_jc_sql_liest_nur.py (Workflow konformitaets-guards.yml, ㉝) | Haelt die JC-Pruefdatei ihr Versprechen „LIEST NUR“ — und ist die Ausfuehrdatei EINE Transaktion? Anlass: JC am 06.09. — „Kannst du mir das nicht bitte abnehmen oder vereinfachen?“ Aus fuenf PROPOSED_.sql wurden zwei Dateien (05_SCHEMA/JC_PRUEFEN_2026-09-06.sql liest nur, JC_AUSFUEHREN_2026-09-06.sql faehrt alle fuenf in EINER Transaktion). ⭐⭐ Warum dieser Guard: der Kopf der Pruefdatei behauptet „DIESE DATEI SCHREIBT NICHTS“. ⛔ Eine Zusicherung im Kommentar ist ein Versprechen, kein Gate — wer die Datei spaeter um einen „kleinen“ UPDATE ergaenzt, aendert den Kommentar nicht mit, und JC faehrt sie weiter im Vertrauen auf einen Satz, der nicht mehr stimmt. Drei Achsen: Pruefdatei enthaelt ausschliesslich SelectStmt und KEINEN schreibenden Knoten (auch nicht tief in einer CTE) · Ausfuehrdatei traegt genau BEGIN+COMMIT und fuenf DO-Bloecke, die auch als PL/pgSQL parsen · jeder Block kann sein Ueberspringen SAGEN (5x UEBERSPRUNGEN) und bricht benannt ab (RAISE EXCEPTION). ⛔⛔ Echte PostgreSQL-Grammatik statt Textmuster (pglast/libpg_query): ein grep haette hier ZWEIMAL gelogen — es haette das Wort UPDATE in den KOMMENTAREN angeklagt, und es haette WITH weg AS (DELETE … RETURNING ) SELECT … NICHT gesehen, weil dessen Oberflaeche ein SelectStmt ist. ⛔ Fehlt pglast, bricht er AB statt auf ein Muster auszuweichen — eine fehlende Voraussetzung ist kein Freispruch. ⛔ Was er NICHT prueft: ob die Zahlen stimmen (das misst nur JCs Bestand) · ob die Anweisungen fachlich richtig sind · ob der n8n-Workflow Xrhc3ajB3onuI8lv abgeschaltet ist (SQL sieht n8n nicht) · Laufzeitfehler — ein Parser sagt „grammatisch gueltig“, nicht „laeuft durch“. Netzfrei, KEINE Datenbank. | ✅ | MCP#01 (#1674, 06.09.). ⭐ Mutationsprobe am ECHTEN Quelltext, drei Werte: Grundlinie keine schreibenden Knoten · Mutation A (UPDATE angehaengt) ⇒ UpdateStmt · Mutation B (DELETE in einer CTE der echten Pruefdatei) ⇒ DeleteStmt, obwohl die Oberflaeche SelectStmt bleibt · Mutation C (COMMIT auskommentiert) ⇒ 1 statt 2 Transaktionsbefehle. Selftest zusaetzlich mit Gegenrichtung (ein UPDATE im Kommentar darf nichts ausloesen) und Positivkontrolle des Parsers selbst (ein gebauter Syntaxfehler wird gefangen — sonst waere jedes „PARSE OK“ wertlos). ⛔ Er waere sonst nie gerufen worden: 05_SCHEMA/** stand in KEINER der beiden Ausloeser-Pfadlisten; nachgetragen in BEIDE, check_pfadlisten_beider_ausloeser danach gruen. |
08_OPS/heimholung02/n8n_doppel.py — das DOPPEL (PostgREST-Nachbau + node-Runner) | jeder n8n→Python-Port wird gegen den ECHTEN JS-Zweig auf GEMEINSAMER Fixture geprüft — Antwort UND jeder Schreibvorgang | ✅ | HEIMHOLUNG#02 (#473). ⛔ Bewusst eine Fixture-REST-Schicht statt eines gestubbten httpRequest: die n8n-Knoten bauen ihre Query SELBST (id=lt., amount=gte., Datumsfenster) — ein Stub prüfte die Auswertung und nie die AUSWAHL. Die Filter-Logik existiert genau einmal, beide Seiten sprechen mit DERSELBEN Instanz; zweimal gebaut bewiese ein grüner Lauf nur, dass beide Nachbauten gleich falsch sind. Trägt alle 7 Skills der Welle 3. Ohne node: SKIP, nie grün |
08_OPS/heimholung02/mutationsprobe.py (JE-156) | ist der Paritäts-Guard scharf? — verbiegt den Port an 6 Stellen und verlangt ROT | ✅ | HEIMHOLUNG#02 (#473). Eigenes Werkzeug statt Einmal-Handlung, weil genau das passierte: die erste Mutation (DUPLICATE_SCORE 70→60) lief grün durch — nicht weil der Port stimmte, sondern weil keine Fixture im Fenster 60..69 lag; Betrags-Toleranz und Datumsfenster ebenso. Drei Klemm-Fixturen nachgezogen. ⭐ Ein bestandener Testlauf ist kein Beweis für einen scharfen Guard — nur die gefahrene Mutation ist einer |
08_OPS/check_deploy_route_traegt.py (+ Einhängung lint-wrappers.yml, Job deploy-route-traegt) | „die Route existiert, trägt aber nie“ — jedes der 44 Manifest-Artefakte gegen ALLE VIER Regale (① Manifest · ② CI-Trigger in deploy*.yml · ③ Wrapper-Allowlist · ④ nginx-Location), plus Gegenrichtung (tote Allowlist-Zeile) und die Drift der ZWEI Repo-Kopien der EINEN nginx-conf | ✅ gebaut+eingehängt+Selbsttest 14/14 (5 Mutationen, ein Fall je nginx-Form) | PLAT#10 (#548). ⛔ REICHWEITE ausgewiesen (R-193): ③+④ am REPO-Stand — was LIVE installiert ist, sagt nur deploy_guard.py smoke/ssh, und genau diese Lücke WAR die Wurzel (der Wrapper-Fix lag seit #1852 im Repo, nie live). ⛔ NARBE AM PRÜFER SELBST, im Code dokumentiert: die erste Fassung kannte nur 2 der 4 nginx-location-Formen und warf zehn Fehlalarme auf live-200-Artefakte (R-194: der leere Griff bewies nur mein Suchmuster). Echtlauf: 44/44 grün netzfrei · --live genau EIN Rot: /tgv_e4_labels.js 404 — der offene JC-Handgriff |
07_TESTS/plat10/wrapper_js_sanity_selbsttest.sh | beisst die .js-Allowlist überhaupt? — 4 Gutfälle + 4 Landeorte + 5 Mutationen (leer · HTML-Fehlerseite · ohne benannten Marker · verwechselte Quelle · nicht gelistetes .js), in einem Sandkasten-WWW_DIR, schreibt nichts live | ✅ gebaut+eingehängt (lint-wrappers.yml, Job js-allowlist-beisst) · 14/14 grün | PLAT#10 (#548). ⛔ NARBE AM TESTLÄUFER SELBST: die erste Fassung rief sed … | bash < datei — bash liest das SKRIPT von stdin, die Eingabe kam nie an; zwei Mutationen meldeten GRÜN mit falscher Begründung. ⇒ Die Erwartung prüft jetzt den GRUND mit, nicht nur „REFUSED“ |
08_OPS/master_nachziehen.py | master in einen Lane-Branch nachziehen, ohne Register-Zeilen zu verlieren — Konflikte in der Registerdatei werden additiv aufgelöst (Zeilen sind Zeilen, keine Alternativen), Spiegel neu gebaut, danach B-28-Gegenprobe: fehlt eine Nummer aus master/Remote? gibt es Dubletten? | ✅ gebaut, hat sich sofort bewährt | PLAT#10 (#560). ⛔ Entstanden aus vier Handläufen desselben Merges an EINEM Tag — dabei einmal #551 verloren und einmal eine byte-identische #538-Dublette erzeugt; beim ersten Werkzeuglauf fing es prompt eine zweite 551-Dublette. ⭐ Ein Handgriff, der still schiefgehen kann, gehört in ein Werkzeug, das hinterher nachzählt — genau die Klasse, die TOWER#41 mit B-28 benannt hat. Es committet und pusht nicht: das bleibt Hand. |
check_bucket_versionierung (#907) | Sind die Hetzner-Buckets gegen versehentliches Löschen geschützt? Read-only je Bucket: ?versioning + ?object-lock; 0 geprüfte Buckets = KEIN URTEIL, nie Grün. | ✅ | DS-NDA#01. Heute ehrlich ROT 9/9 (Messung 30.08.) — GRÜN ist die Abnahme von Baustein 1 der Backup-Vorlage. LAUFORT hand (netzabhängig, nicht CI); Cron-Verdrahtung mit Baustein 1. |
pruefe_bucket_wand (#1536) | Trennt die Eimer-Wand wirklich, und wo fehlt sie ganz? Read-only GET /?policy je Bucket: traegt der Eimer eine Regel, und wie ist sie geformt (Principal / Condition / NotPrincipal)? 0 Schreib-Aufrufe, Vorgabe --trocken. --gegenbeweis DRUCKT, was ein Schreibversuch taete, und fuehrt ihn NICHT aus — die Buckets sind seit dem Drive-Ende die einzige Kopie der Originale. | ✅ | SICHERHEIT#04. Befund 04.09.: 8 von 9 Eimern tragen Deny+NotPrincipal ohne Condition, tgv-mb-lm-gabriel traegt GAR KEINE Regel und hat keinen eigenen Schluesselsatz. |
08_OPS/check_ein_weg_zum_original.py (#916, + Einhängung konformitaets-guards.yml) | „Führt ein Beleg-Klick zum Bucket — oder immer noch nach Drive?“ Zwei Fragen an core_shell.html: ① Ratsche auf die Stellen, die eine Drive-Datei-Adresse erzeugen (6 erlaubt, alle sechs im Register benannt) · ② _belegZeiger fragt file-url, _belegOeffnen trennt Bucket von Drive und hält die Google-Wand am Rückfall. ⭐ Gemessen wird an der ERZEUGUNG, nicht am href — beim Zählen sass ich zweimal derselben Falle auf: der Bau stand in einer Variablen, der Gebrauch 100 Zeilen später, und esc(qUrl) verbirgt ihn ganz. Wer nach „href + Drive“ sucht, sieht nur die Hälfte — meine erste Meldung sagte „zehn“, es waren vierzehn. | ✅ | EINGABE#01. Sabotage je Frage EINZELN (4 Richtungen, alle rot, ohne Sabotage grün). ⛔ Messgrenze (R-193): prüft genau core_shell.html — über engine.js, kai_renderer oder andere Flächen sagt er nichts. |
08_OPS/check_konzept_master_contract.py (#1062, # LAUFORT: ci, + Einhängung konformitaets-guards.yml) | „Trägt jede Konzept-Aussage einen Zustand, den ihre Felder decken?“ Vier Prüfungen über konzept_index.json: Zustand gültig · verdrahtet ⇒ Beleg mit :Zeile, der auflöst · geprueft ⇒ existierende Guard-Datei · geschrieben ⇒ gesucht_in. ⭐ Er schliesst die Lücke, die die Bilanz nicht sieht: die bilanziert verdrahtet_ohne_beleg: 0, misst aber nur, ob das Feld gefüllt ist — ein gefülltes Feld beweist das Feld, nicht die Sache. ⭐ R-260 eingebaut: jede Bedingung getrennt gezählt, die Überschneidung wird mitgedruckt. ⛔ Ratsche: unbekannt 250 (Baseline mit Datum, darf nur sinken) — eine Frist, keine Entlastung. ⛔⛔ Und er benennt seine eigene Selbst-Deckung: 259 Aussagen nennen ihn als next_guard und bestehen Prüfung (3) allein dadurch, dass es ihn gibt — geprüft ist ihre Adresse, nicht ihre Aussage. | ✅ | DESIGN#05 gebaut, Wirkort gehört dem Haus (TOWER#52-Platzierung 01.09.). Erster scharfer Lauf: 16 Befunde in 16 Aussagen (1× Beleg ohne Datei:Zeile, 15× geschrieben ohne gesucht_in). Selbsttest in beide Richtungen; unter cp1252 gefahren, weil er Laufzeitdaten druckt. |
08_OPS/check_konzept_nennt_ungebautes.py (#1020, + 08_OPS/konzept_ausnahmen.json, + Einhängung konformitaets-guards.yml) | „Nennt ein Konzept ein Artefakt, das es nicht gibt?“ Zieht aus 63 Konzept-Dateien die genannten Code-Artefakte (Pfade und Skriptnamen) und hält sie gegen den Bestand. ⭐ Ausnahmeliste statt Ratsche — 13 bekannte Fälle datiert benannt, jede NEUE Nennung sofort rot; ein Wächter, der ab Tag eins dauerrot steht, verdeckt jeden echten Rückfall. ⛔ Unterscheidet „ungebaut“ (braucht einen Bau) von „falscher Pfad“ (braucht eine Textkorrektur) — zwei Fehler, zwei Handgriffe. | ✅ | EINGABE#01. Selbsttest beide Richtungen (findet Erfundenes, lässt Vorhandenes in Ruhe, führt Bekanntes als bekannt) — läuft in der CI mit. ⛔ Exit-Kanon geprüft: neue Nennung → 1 · Ausnahmeliste fehlt/unlesbar → 2 (KEIN URTEIL, nie 1). ⛔⛔ GRENZEN im Kopf: er kann keine Zeitform lesen (ein Konzept darf einen Plan beschreiben — sein Grün heißt nicht „alle Konzepte sind ehrlich“) · seine Zahl ist eine Untergrenze (Prosa-Nennungen wie „der Klärungs-Skill“ sieht er nicht) · R-193: er liest genau die zwei Regale, die er ausdruckt. |
08_OPS/check_bildart_wirkt.py (#1042, # LAUFORT: cron) | „Wirkt die Bild-Klassifikation — oder sieht sie nur so aus?“ Zählt Vision-Läufe der neuen skill_version und darin die gesetzten bild_art-Werte, dazu die verworfenen Bilder nach Grund. ⭐⭐ Sein Kern ist die unterscheidbare Null: „0 gesetzt, weil 0 Bilder kamen“ ist exit 2, „0 gesetzt, obwohl 7 Bilder kamen“ ist exit 1 — beide drücken sonst dieselbe Zeile. Zähler mit Nenner, immer. ⛔ Bezugspunkt ist die skill_version, nicht die Uhrzeit: ein zeitlicher Schnitt zählte Läufe eines Dienstes mit, der noch nicht neu ausgeliefert war. | ✅ | EINGABE#01. Ersetzt ein Durchfall-Kriterium, auf das jemand hätte WARTEN müssen — warten ist keine Messung. Selbsttest ohne Datenbank, vier Lagen, darunter beide Nullen. ⛔ Messgrenze (R-193): liest genau agent_events (skill.vision_extract* · mail_intake.anhang.verworfen) — über alles andere sagt er nichts. |
08_OPS/quellen_abdeckung.py (#1116, + Einhängung konformitaets-guards.yml ㉕) | „Haben die Abschnitte ZUSAMMEN das Ganze ergeben?" Je Quelle N von M, laut wenn N < M. ⛔⛔ Der Kern ist, dass NICHT addiert wird: vereinigt wird über IDENTITÄTEN, nicht über Zähler — zwei Abschnitte mit je 200 können dieselben 200 sein; wer addiert, bekommt eine Obergrenze, die sich als Messung ausgibt und die Lücke versteckt. ⭐ Drei Ausgänge: vollständig · LÜCKE · UNBEKANNT (keine Identitäten oder kein gesamt) — der dritte darf nie als der erste durchgehen. ANLASS: acht ehrliche Teilmessungen ergaben 543 von 617; jede Zahl richtig, die Summe der Zahlen war die Lücke, und in den 74 hätte die 8.115-Euro-Rechnung stecken können. | ✅ | EINGABE#01. Selbsttest 8 Lagen (u. a. zwei Abschnitte mit DENSELBEN Mails → Summe wäre grün, Vereinigung nicht) · Beissprobe 7 von 7 rot. ⛔ Zwei davon waren echte TESTLÜCKEN, nicht falsche Sonden: „ohne Identitäten" ergab zufällig auch False, aber aus dem falschen Grund (0 von 10 statt UNBEKANNT von 10), und für widersprechende Gesamtzahlen gab es gar keinen Fall. Beide geschlossen, mit dem Grund im Test. ⛔ Abgrenzung: check_katalog_coverage.py fragt EINMALIG, ob eine Liste eine andere abdeckt. Hier geht es um Abdeckung, die sich über MEHRERE Läufe ansammelt — die beiden ersetzen einander nicht. |
.github/workflows/i18n-ssot-guard.yml (I18N#01, Abbruch-Gruppe aus #672) | Halten die vier Sprachlisten die EINE Wahrheit? Der Ablauf fährt check_i18n_ssot.py gegen den Dateibestand 06_DASHBOARD/i18n/*.json; jede Abweichung ist ein Abbruch. Dependenzfrei, ohne Netz, ohne Geheimnis. | ✅ | I18N#01 · nachgetragen 02.09. von TOWER#53 — der Ablauf war 22 Tage im Betrieb und stand nie mit seinem Namen im Register. |
08_OPS/check_i18n_ssot.py (I18N#01, 10.08.) | Die unterstützten Sprachen standen an SECHS Stellen und wichen voneinander ab. Folge, gemessen: es.json war live nicht erreichbar — die Auslieferung schob sie nie (Liste mit 6) und der Webserver lieferte sie nie (Muster mit 6), obwohl die Datei im Bestand lag. ⛔ Und die Abnahme prüfte die Sechserliste gegen sich selbst und meldete in Ordnung, während die siebte Sprache fehlte. Der Prüfer bindet alle Listen an den Dateibestand. | ✅ | I18N#01 · nachgetragen 02.09. von TOWER#53 |
08_OPS/check_ladefehler_spricht_sieben_sprachen.py (#2197, 20.09.) | Spricht der EINE Satz-Erzeuger der Ladefehler sieben Sprachen — Satz UND Betreff? ladeFehlerHtml ist seit #1201 der eine Ort fuer Ladefehler und hat 11 Rufer; er gab hart deutsche Saetze zurueck. ⭐ Beim Zusammenlegen erbt das Ergebnis die ENGSTE Eigenschaft aller Beteiligten: eine der abgeloesten Kopien sprach sieben Sprachen (fahrtenbuch.error_forbidden) — aus 7 wurde 1, fuer 11 Flaechen auf einmal. ⛔ DREI Fragen, nicht eine: (A) deutscher Klartext im Erzeuger · (B) jeder gerufene Schluessel x7 plus Platzhalter {was} vorhanden · (C) jeder Rufer uebergibt einen ladefehler.was.-SCHLUESSEL. (A) allein waere die Falle: Satz uebersetzt, Betreff deutsch — dann sieht es uebersetzt AUS.* Blockschnitt und Prosa-Probe sind aus check_ablege_flaeche_spricht_sieben_sprachen.py IMPORTIERT, nicht abgeschrieben. Beissprobe an der Basis: gegen origin/master meldet er 16 Befunde namentlich, auf dem geheilten Stand 0. Selbsttest = 4 Sabotagen EINZELN. ⛔ Seine Enthaltung steht in jeder Ausgabe: weitere hart-deutsche Fehlersaetze in core_shell.html ausserhalb dieser Familie (14 Fundstellen + ein isFr-Zweig) sieht er nicht. | ✅ vollstaendig (#2197) | TOWER#57 · CI netzfrei in i18n-ssot-guard.yml (Selbsttest und Echtlauf); der gepruefte core_shell.html, die 7 Sprachdateien und der Vorbild-Baustein stehen in BEIDEN Ausloeser-Bloecken |
08_OPS/modell01/entscheidungs_schreiber.py (DOCWEG#01, 07.09.) | Trägt die Zeile, die wir an llm_entscheidungen absenden WÜRDEN, genau die Spalten der Tabelle — und welche Aufgaben erreicht dieser Schreiber überhaupt? Die Spalten kommen aus der Migration statt aus einer zweiten Liste. ⛔ --trocken ist die Vorgabe: ein scharfer Lauf schreibt in JCs Datenbank und braucht sein Wort. ⭐ Er druckt seine Reichweite bei jedem Lauf (2 Aufgaben mit Profil gegen 67 angemeldete Skills) — ein Hauptbuch mit unsichtbarem Rand ist gefährlicher als keins. | 🔨 | DOCWEG#01 · Register #1800 · --selftest netzfrei, 17/17 |
07_TESTS/modell01/test_entscheidungs_spur_spaltenquelle.py (DOCWEG#02, 11.09.) | Fehlt die Migration 266 am Laufort — ist der Ausfall der Entscheidungs-Spur LAUT (Zähler + Log-Zeile), und stempelt derselbe Aufruf wie in app.py mit ihr eine Zeile? Beissprobe in beide Richtungen; die Bundle-Lage (Tar gebaut, entpackt, Pfad vom entpackten Schreiber aus) hält test_policy_bundle::test_F. ⭐ Gebaut, weil der Rückfall vom 09.–11.09. still war: 218 Bundle-Einträge, 0 unter 05_SCHEMA/. | ✅ | DOCWEG#02 · Register #1902 · pytest offline, 4 Fälle |
08_OPS/check_nachsortierer_haben_arbeit.py (ABLAGE#02, 31.08.) | Liegt Bestand herum, für den ein Nachsortier-Werkzeug zuständig wäre — und weiss jemand davon? Mit Nenner, nicht als blosse Zahl. ⭐ Er ersetzt ausdrücklich nicht drei Zeitpläne: der Befund lautete „vier Nachsortierer ohne Zeitplan, 0 Rufer" — richtig gezählt, aber die naheliegende Folgerung wäre falsch gewesen, denn im Trockenlauf hatten zwei der vier nichts zu tun. | ✅ | ABLAGE#02 · Träger .github/workflows/betriebs-waechter.yml · nachgetragen 02.09. von TOWER#53 |
08_OPS/check_deploy_marke_liste.py (#1060, + 08_OPS/deploy_ohne_versionsmarke.json, + Einhängung konformitaets-guards.yml ㉓) | „Bestätigt sich eine Auslieferung mit einer LEEREN GLEICHUNG?" Der Verify-Schritt in deploy.yml vergleicht die Versionsmarke cockpit vX.Y der lokalen mit der ausgelieferten Datei. ⛔⛔ Trägt eine Datei GAR KEINE Marke, lautet der Vergleich ["" = ""] — er ist wahr, und der Haken bestätigte NICHTS. ⭐⭐ GEMESSEN: 20 von 26 Matrix-Dateien waren so — 77 %. Und es war kein verborgener Fehler: der Matrix-Kommentar nennt es seit Monaten für die AGENT-UX-Mockups. Gezählt hat es niemand, und die Liste war längst über „Mockups" hinausgewachsen (die ganze 2.html-Familie, regelwerk.html, vertical_slice_standalone.html). ⭐ Eine benannte Ausnahme, die auf 77 % wächst, ist keine Ausnahme mehr — sie ist der Normalfall, und der Kommentar liest sich weiter wie eine Randnotiz. ⇒ Er hält die Menge fest: rot bei Zuwachs · rot bei einem Eintrag, der inzwischen eine Marke TRÄGT (eine Karteileiche deckt nichts zu, sie sieht nur so aus*) · rot bei einem verwaisten Eintrag. | ✅ | EINGABE#01. Selbsttest vier Lagen (der wichtigste: der eingelöste Eintrag muss rot werden) · Beissprobe 4 von 4 rot am echten Bestand, Liste aus der Kopie zurück. ⛔⛔ ER HÄLT KEINEN DEPLOY AN, ausdrücklich: ob eine Datei eine Marke tragen SOLL, ist eine Einschätzung und gehört nicht in einen Prüfer — er misst nur, dass die Menge nicht heimlich wächst. Abweichung vom Auftrag (TOWER#52 wollte die leere Gleichheit im Deploy rot): als CI-Wächter gebaut, damit ein Versehen an einer Datendatei nie eine Auslieferung von JCs Oberflächen anhält. ⭐ Die eigentliche Schuld steht im GRÜNEN Lauf: „AUSGEWIESEN: 20 von 20 Einträgen haben noch keinen benannten Grund oder Eigner" — die Zahl kann nur kleiner werden. ⭐ #1065: von 20 auf 12 gesenkt, kein Grund geraten — sieben Einträge belegt der TITEL der Datei selbst („Mockup-Abnahme", „App-Mockup", „Mockup v6"), einer nur der DATEINAME und ist als schwächer gekennzeichnet (ein Dateiname ist eine Absicht von damals, ein Titel die Aussage von heute). Die Frist wird deshalb in DREI Stufen gedruckt, nicht als eine Zahl — „8 von 20 erklärt" hätte den schwachen Beleg unsichtbar gemacht. ⛔ Messgrenze (R-193): liest genau die file:-Matrix aus deploy.yml, 06_DASHBOARD/.html und die Liste — über CSS, Assets und i18n sagt er nichts. ⛔ Leere Matrix ⇒ exit 2, nicht 0: so sieht eine kaputte Messung aus, und ihre 0 hiesse „nichts gelesen" statt „alles in Ordnung".* |
08_OPS/check_marke_wandert_mit.py (#1076, + Einhängung konformitaets-guards.yml ㉔) | „Datei geändert, Versionsmarke nicht?" Die Frage, die check_dienst_version_wandert_mit.py seit #639 an den Dienst stellt, hier an die ausgelieferten Dateien. ⛔ ZWEI SCHÄRFEN, und die Trennung ist die ganze Konstruktion: ROT nur bei VERLUST einer Marke (kein Ermessen — der Verify-Vergleich fällt für die Datei auf die leere Gleichung zurück) · WARNUNG bei STILLSTAND, keine Sperre — nicht jede Änderung braucht eine neue Fassung; ein Wächter, der bei jedem Komma rot wird, wird weggeklickt, und DANN verdeckt er den echten Fall. ⭐ Er nennt seine DECKUNG: 26 Matrix-Dateien, 14 mit Marke — über die 12 ohne sagt er NICHTS, die haben ihren eigenen Wächter. ⭐⭐ NUXIN gemessen, nicht behauptet: eine Erweiterung des Dienst-Wächters wäre falsch (dort EINE Zahl für VIELE Dateien, hier je Datei eine) — aber geaendert() und _git() werden IMPORTIERT statt kopiert, samt Narbe #592 (Drei-/Zwei-Punkt-Schnittmenge). | ✅ | EINGABE#01. Selbsttest 5 Lagen + Deckungs-Prüfung · Beissprobe END-TO-END 3 von 3 mit temporärem Commit und reset --mixed zurück. ⛔⛔ Zwei Sonden liefen zuerst ins Leere, und das hat die Messgrenze aufgedeckt: geaendert() vergleicht COMMITS, nicht den Arbeitsbaum — gemessen: nach einer uncommitteten Änderung lieferte es 14 Pfade, und die geänderte Datei war nicht dabei. Steht als EIGENSCHAFT im Kopf. Ohne die dritte Sonde wäre ein Wächter ausgeliefert worden, dessen schärfste Zusicherung nie geprüft war. ⛔ Messgrenze (R-193): liest genau die file:-Matrix aus deploy.yml und 06_DASHBOARD/*.html gegen origin/master — über CSS, Assets und i18n sagt er nichts. |
08_OPS/check_zeile_und_ablage_nennen_dieselbe_firma.py (ABLAGE#01, #1278, 02.09.) | „Sagt der ABLAGEORT dasselbe wie die ZEILE?“ Von den Belegen, deren Pfad die Form DOCUMENTS/<Firma>/… hat: bei wie vielen nennt der Pfad eine andere Firma als die Zeile — getrennt nach dem neuen Weg (Objektspeicher) und dem alten (Drive). ⛔⛔ ZWEI NENNER, NIE EINER — das ist der ganze Bau: im Objektspeicher zählt jeder Zuwachs (Ratsche: gemessener Stand 2 vom 02.09., Zuwachs ist ein Befund), Drive ist benannter Altbestand (DRIVE_BASELINE=88), der mit der Abschaltung stirbt und nicht geheilt wird. Beide zusammenzuzählen hätte den Wächter im Sollzustand dauerhaft rot gemacht — und ein dauerrotes Signal ist kein Signal. ⭐ WARUM ER TROTZ FIX NÖTIG IST: #1278 heilt nur die eine Ursache (der Ausgang schrieb die selbst ermittelte Firma nicht zurück). Die andere läuft weiter: jede Umklassifikation über eine Firmengrenze lässt ein bereits abgelegtes Objekt zurück (an der Uhr belegt: Ablage 01.09. 18:01, Umklassifikation 02.09. 00:23). Ein Fix ohne Wächter heilt sieben Fälle und lässt die Quelle laufen. | ✅ | ABLAGE#01 · # LAUFORT: cron (braucht DB) · Träger .github/workflows/betriebs-waechter.yml · Zeile nachgetragen 04.09. vom Autor (#1549) — der Artefakt-Ratchet meldete sie erst jetzt, weil sein paths-Auslöser 36 Einzelnamen bei 141 Prüfern listete; ABLAGE#02 hat ihn in #2496 auf ein Muster repariert. Der Rückstand wurde damit beim nächsten Vorgang sichtbar, nicht beim Verursacher — er färbte elf offene Vorgänge rot. ⛔⛔ WAS ER AUSDRÜCKLICH NICHT FRAGT, und das gehört zur Zeile: ① ob die Zeile oder der Pfad RECHT HAT — er meldet den Widerspruch, nicht den Schuldigen; welche Seite stimmt, hängt an der Uhr und ist eine Fachfrage. ② er heilt nichts — NUR LESEN, ein SELECT, kein PATCH. ③ er sagt nichts über Belege ohne Firmenordner (_PARKING · _REJECTED · _NICHT_BELEG) und über Zeilen mit unbekannter Firma — sie stehen ausserhalb des Nenners und werden eigens ausgewiesen: weder gut noch schlecht, sondern nicht gemessen (R-193, jeder Lauf druckt die Reichweite mit). ④ er ist kein Ablage-Prüfer: verglichen wird genau der erste Pfadabschnitt gegen die Ordnerkarte aus ablage_kanon.yaml — über Unterordner, Dateinamen oder Inhalt sagt er nichts. ⛔⛔ DIE MESSFALLE, an der er selbst zweimal scheiterte, steht in seinem Kopf: 326 ohne Pfadfilter (split_part zerlegte auch oeuvre-de-vie/photos/… und meldete photos als Firmenordner) → 222 mit Filter im WHERE, aber coalesce() im SELECT (der Filter traf zu, zerlegt wurde trotzdem der falsche Pfad) → 88 richtig. ⭐⭐ Die Korrektur der ersten Stufe hat die zweite nicht mitgeheilt — eine Messfalle kann mehrere Böden haben, und die Zwischenzahl sieht schon plausibel aus. Gefangen hat es erst der Wächter selbst, gegen die Handmessung gehalten. · --selftest vorhanden · Exit 1 = Messfehler, Exit 3 = Befund (eine kaputte Messung darf nicht wie ein sauberer Lauf aussehen) |
08_OPS/mcp_werkzeug_inventar.py (#1952) → MCP_WERKZEUG_INVENTAR.md | das Regal, das fehlte: je MCP-Werkzeug (27) WOHIN es ruft (Supabase-Tabelle/Op · RPC · Dienst-Pfad · HTTP) und ob das Ziel lebt — AST, Modul-Konstanten aufgelöst (Narbe #1930), Helfer eine Ebene tief; Regale: app.py-Routen (endpunkt_registry), 05_SCHEMA, N8N_INVENTAR, .mcp.json; --live nur lesend; --check = Ratsche (0 n8n-Webhooks, 0 tote Ziele) | ✅ | MCP#01 · Lauf: betriebs-waechter.yml ㉖d (netzfrei) · --selftest mit Positivkontrolle. Vorgänger (Archiv, 25.05.): 01_AUDITS/REALITY_CHECK_2026-05-25/MCP_INVENTORY.md |
| Artefakt | Sinn | Status | Weg/Owner |
|---|---|---|---|
katalog.html (33 Bausteine + Freigabe-Status) | DER visuelle Komponenten-SSOT | 🔨 LIVE (HTTP 200, gemessen 18.07.) — aber HINTERHER | Serve-Skript 08_OPS/server/serve_katalog.sh geliefert (#999), JC hat gefahren ✅. ⚠️ Gemessen 18.07.: live ≠ master — das ai-spark-Icon aus #1006 fehlt live, weil der Serve VOR dem Spiegel-Fix lief. JC-Aktion: Ein-Zeiler erneut fahren (idempotent) → git fetch origin && git show origin/master:08_OPS/server/serve_katalog.sh | bash. Strukturelle Lücke: der Serve ist manuell, also hinkt der Katalog jedem Design-Merge hinterher — Mechanismus dafür ist offen (Deploy bleibt JC-gated). Coverage/Spiegel bewacht check_katalog_coverage + Achse B |
chat_component_library.html (43 Komp., 200 live) | Chat-Bausteine visuell | ✅ sichtbar | Neuzugänge über DESIGN#01-Kandidatenliste |
Konzept-Familie /konzepte/ (23 Dateien, live, familie_guard inkl. C5-Spiegel-Guard) | Menschen-Doku + JC-Entscheide-Register + Artefakt-Register-Spiegel (generiert aus dieser MD, Hash-bewacht) | ✅ | lebt (KONZEPT#01); Re-Serve nach jedem Merge |
Icon-Set K1 icons-global.json (93, autoritativ) | EINE Icon-Serie | 🔨 | DESIGN#01-Paket D läuft (Funke-Gelb + Kompakt-Slug + Weiß) |
core_app_mockup.html (Gold v3.8) | Design-Referenz | 🔨 | beim Antwort-Ort ÜBERHOLT (U10) — Überholt-Marker setzen wie Bibel §F (DESIGN#01-Zeile) |
core_app_mockup_V2.html | JC-Nacht-Bau 08.07. | ❓→🔀 | JC-Frage im nächsten Kontakt: hat V2 eigenen Sinn oder verschmelzen→🗑️? |
CHATBOT_UX_BIBEL.md | Konversations-Kanon | 🔨 | 3 überholte Stellen markiert (§F/:166/:315/:380 ✅); Rest = KANON-Inventur |
| i18n 7 Sprachen | Mehrsprachigkeit (hart) | 🔨 HALB: es 409 · it/pt/ru 413 vs de 498 | DESIGN#04-Auftrag nach Bündel: Sync + Guard „Keycount-Parität alle 7" (Deploy prüft heute nur de!) |
| tokens/components/foundation.css | Design-Fundament | ✅ | teal/pink heute nachgezogen; pink-300 bei DESIGN#01 |
| Artefakt | Sinn | Status | Weg/Owner |
|---|---|---|---|
08_OPS/nachlauf_liegende_belege.py (#1711) | der fehlende AUSLOESER: die Re-Triage feuert nur im laufenden Vorgang — fuer liegende Belege gibt es keinen Zeitgeber, weder im Repo noch auf n8n (alle 59 Workflows geprueft). Dieses Werkzeug faehrt den Bestand ab, meldet fuenf Koerbe und nennt die Uebersprungenen beim Namen | ✅ gebaut, Trockenlauf gefahren (06.09.: A 42 · A2 119 · B 87 · C 59 · D 10) | NACHEXTRAKTION#01. Heimholung, kein Neubau: aus 01_ITERATIONS/TRI_01_RETRIAGE_CORE_FINANZ/n8n/retriage_backlog.py, jetzt auf die lokalen Endpunkte statt der seit 23.08. stillen n8n-Strasse; Schwelle per Import aus dispatch.py (SCHWELLE#01). ⛔ --apply ist JC-Ball und verlangt --ich-habe-JCs-freigabe |
08_OPS/betrieb/zeitgeber.py (ZEITGEBER#01, #1934, 14.09.; Thread in app.py 1.12.152, Bundle-Zeilen in deploy_lm04_2) | DER DIENST-SCHEDULER — die Zeitgeber laufen auf Hetzner, nicht auf GitHub. Kalender in UTC zeichengleich zu betriebs-crons.yml, je Tick der jüngste Slot seit dem letzten Tick (kein Nachholen nach Neustart), Schalter je Zeitgeber system_config.zeitgeber_hetzner_<name> (fail-closed), Takt TGV_ZEITGEBER_TICK_SEC. ⭐ EIN Weg, zwei Rufer: jedes Cron-Skript trägt zeitgeber_lauf(apply, herkunft), dieselbe Funktion ruft main() auf GitHub und der Scheduler auf Hetzner; zeitgeber.melden stempelt und schreibt je Lauf EINE Zeile agent_events zeitgeber.lauf mit HERKUNFT-Feld. ⛔ Health-Check bleibt auf GitHub (Zeuge ≠ Beobachteter). Selbsttest 18 Fälle, netzfrei; Tests 07_TESTS/zeitgeber01, CI ⑱g. | 🔨 gebaut, Deploy = JC-Wort | ZEITGEBER#01 → TOWER#55; Umschalten je Zeitgeber = JC |
08_OPS/mess_zeitgeber_zwillinge.py (ZEITGEBER#01, #1934) | „Laufen Dienst-Scheduler und GitHub-Zwilling zeilengleich?“ Liest nur agent_events zeitgeber.lauf, paart je Zeitgeber und Slot die Herkünfte github-actions und hetzner-scheduler, zählt GLEICH · UNGLEICH · nur GitHub · nur Hetzner; beim Cost Collector Vergleich auf Art (Werte zeitabhängig). Die Zahl ist der Stand, kein Verdikt — die Vorlage für JCs OK je Zeitgeber (#1908 §3, Periodic Engine zuerst). --md Bericht, --selftest 9 Fälle. | 🔨 gebaut; erste Messung frühestens zwei Kalendertage nach dem Deploy | ZEITGEBER#01 → TOWER#55 |
08_OPS/zeitgeber_schalter.py (ZEITGEBER#01, #1934 Nachtrag) | „Welcher Zeitgeber DARF auf Hetzner laufen — und wie lege ich EINEN Schalter um, ohne SQL?“ Zeigt die fünf system_config.zeitgeber_hetzner_*-Schalter und /health.zeitgeber; --an / --aus mit Name oder alle, trocken per Default, schreibt nur mit --apply (Upsert auf key). Namen aus der EINEN Quelle zeitgeber.ZEITGEBER; der Health-Check hat keinen Schalter und wird abgewiesen. ⛔ Schaltet weder n8n noch GitHub — nur den dritten Läufer für die Messung. Selbsttest 6 Fälle. Vorlage mit WO: ZEITGEBER01_RUECKKEHR_BOARD_2026-09-14.md. | 🔨 gebaut; wirkt erst mit Dienst 1.12.152 | ZEITGEBER#01 → JC (Rückkehr-Board) |
02_SKILLS/_registry.yaml (~45 Skills) | Skills-SSOT | ✅ SSOT | CI-Guard vorhanden |
08_OPS/betrieb/mail_rueckkanal.py (#2061, LAUFORT bibliothek) | Der Rückkanal: Bounce- und Beschwerde-Meldungen im Dienst. Port von n8n TGV Webhook Resend Bounce v0.1 — Statuslauf und Ereignisnamen zeichengleich, damit der Umzug keine zweite Fassung erzeugt. ⛔ Eine Abweichung, und sie ist Absicht: die Svix-Signatur ist PFLICHT (der n8n-Weg prüft nur if (WEBHOOK_SECRET) und läuft ohne Secret unverifiziert) — fail-closed, dazu ein Zeitfenster gegen Wiederholung und compare_digest statt ==. ⭐ Warum er der einzige seiner Art ist: der Failure-Melder liest status=failed, also Nicht-Annahme durch den Anbieter; ein Bounce entsteht danach und lässt die Zeile auf sent — zwei Melder, die Verschiedenes messen, sind keine Redundanz, sie sehen nur so aus. Selbstprobe 18 Fälle netzfrei; Endpunkt-Test 07_TESTS/buchhaltung01/test_rueckkanal_endpunkt.py (6 Fälle, CI ㊱). | ✅ | MAIL#02, gebaut 16.09.2026 auf TOWER#56-Auftrag, Dienstfassung 1.12.160. B-26: live nicht gemessen — der Beweis ist die erste echte Bounce-Zustellung im Dienst (N8N#03s Vorbedingung). ⛔ TGV_RESEND_WEBHOOK_SECRET steht in der Ablage ohne Wert: eine Zeile aus der Anbieter-Konsole, JCs Hand. |
08_OPS/heile_leere_upload_zeilen.py (#2010, LAUFORT hand) | Holt den Dateinamen aus dem Ereignis in die leere Zeile zurück — und sonst nichts. Anlass: 16 Upload-Zeilen ohne Typ, Medium, Text und Namen; der Name stand die ganze Zeit in upload.abgelegt, nur nicht in documents.drive_file_name. ⭐ Zwei Quellen als Beweis statt einer als Behauptung: der Name muss im Ereignis UND im raw_key denselben Kern tragen (der Schlüssel streicht Umlaute ersatzlos, die Vergleichsform bildet das nach). Enthält sich bei Mehrdeutigkeit, bei None-Schlüssel und bei Namens-Widerspruch — mit Grund je Zeile. ⛔ Schreibt NUR drive_file_name (kein Typ, kein Medium, keine Entität — wer aus einem Dateinamen einen Beleg-Typ macht, hat geraten, R-298), Trockenlauf ist die Vorgabe, --apply bricht ohne --freigabe ab, und der Vorher-Zustand wird als Rückweg weggeschrieben, bevor geschrieben wird. Selbstprobe 17 Fälle, netzfrei, beisst in beide Richtungen (ein fremder Name und zwei benachbarte IBAN-Dateien fallen durch). | ✅ | MAIL#02, gebaut 15.09.2026 auf TOWER#56-Auftrag; Trockenlauf live: 9 heilbar · 7 Enthaltung · 0 widersprüchlich. Der echte Lauf ist JCs Wort. |
08_OPS/check_absender_auth.py (#2015, LAUFORT hand + cron, Träger noch offen) | Trägt jede Absenderdomain SPF mit dem Versender-Include und einen DMARC-Eintrag? Anlass: der Zustell-Vorfall vom 03.08.2026 (126 Angemeldete, 83 % @gmail im Spam) und die Haltbarkeit des DNS-Audits vom 14.05., das vier Monate später nicht mehr stimmte. Domains abgeleitet statt gelistet (R-168, aus entity_signatures.email_default — eine Reply-To ist kein Absender), Positivkontrolle vor jedem Urteil (blinder Auflöser ⇒ „nicht gemessen", exit 3, nie „nichts gefunden"), DKIM ausdrücklich draussen mit Preis im Kopf (Selektor nur in der Anbieter-Konsole; ein geratener Selektor liefert eine SOA-Antwort, die wie „kein Eintrag" aussieht), DMARC scharf ohne SPF steht als erster Mangel (kein Aussortieren, sondern Abweisen), Nenner im Schlusssatz. Selbstprobe 16 Fälle, netzfrei, beisst in beide Richtungen. | ✅ | MAIL#02, gebaut 15.09.2026 auf TOWER#56-Ansage; Echtlauf: 1 von 1 Absenderdomain grün. Träger-Einhängung ist ein eigener Schritt (Prüfsatz-Kosten = Tower). |
08_OPS/betrieb/mail_use_cases.json (#1969) | Das Use-Case-Register des EINEN Mailers (MAIL_VERSAND_CONCEPT.md §U, R-293): 13 Zeilen — je use_case Klasse (§J), Absender, Signatur, Freigabe-Stufe, Spur, Träger heute, Phase (§O4), Fundstelle, optional erkennung für die Zuordnung ungebuchter Resend-Zustellungen (Magic Link = Supabase Auth). Gelesen von mail_buchfuehrung.use_case_register(); senden() sendet nur mit einer Zeile; fährt im Dienst-Bundle mit. | ✅ SSOT | MAIL#02 · Guard check_use_case_register.py (CI ㉟) · Erweiterung = neue Zeile mit Fundstelle, kein Literal im Aufrufer |
DB skill_definitions | alte Skill-Liste | 🗑️/🔀 DOPPELT + veraltet (bekannter Drift) | Entscheid: nicht pflegen — System-Tab liest künftig _registry.yaml; Zeile stirbt mit PLAT#03-Neubau |
tgv_migrations + _applied_snapshot.json + Header | Migrations-Wahrheit | ✅ | Header=keine Statusquelle (Gesetz); Snapshot=gemessener Ausschnitt; Guard läuft |
prompt_config | LLM-Steuerung/SSOT | 🔨 | ORCH-taxonomy hängt sich dran (Strangler); CORE-Skills-Migration = ADMIN#01-Vision (nach 31.07.) |
Ablage-Kanon ablage_kanon.yaml | Dokumentalist-Grundlage | ✅ Struktur | Dokumentalist-BAU nach Screen-Boden |
| ROADMAP + Fertigungsgrad + User-Story-Register + Lane-Karte | Steuerungs-Sichten | 🔨 | JC-Regel „beide Tabellen" gilt; Konsolidierungs-Check = Tower#25-Handoff-Punkt |
| MEMORY-Index | Tower-Gedächtnis | 🔨 überläuft | Konsolidierung = Tower-Pflegepunkt (nach Bündel) |
| Handoffs/Gate-Reports (CONTROL_TOWER_HANDOFF) | Historie + Verträge | ✅ | Tower#23-Korpus gerettet (#962); Neues immer auf master |
DS_NDA_01_BACKUP_KONZEPT_2026-08-30.md (+ Schwester DS_NDA_01_AVV_DOSSIER_2026-08-29.md, #836/#907) | AVV-/Backup-Beweisblatt: jede Aussage mit URL+Abschnitt+wörtlichem Zitat+Abrufdatum; Enthaltungen benannt | ✅ | DS-NDA#01. Fortschreibung statt Neubau: §6a/§6b Vertraulichkeits-Konzept + INFRA §5 verweisen hierher. JC-Bälle: OK-Fragen B.4 + Checkliste Teil 4 (⏰ Papier-Teil 14.09.). |
KAI_INTENTION doppelt→verschmolzen (#960) · 3 Mig-Nummern-Kollisionen→Guard · errors-SSOT-Ort→einer (tgv_errors) · testdata-Filter kopiert→SSOT+Sync-Guard · Regelwerk 23 unsichtbare→0 · Bibel/Gold-Chip-Zeilen→Überholt-Marker · „is_test vs test_run"→ein Vokabular.
| Artefakt | Sinn (aus dem eigenen Kopf) | Status | Weg/Owner — gemessen |
|---|---|---|---|
deploy-doku.yml | deploy-doku | ✅ | gerufen von lint-wrappers.yml · ⛔ ohne --selftest |
kanon-quellen-ssot.yml | kanon-quellen-ssot | ✅ | gerufen von agentchat01-suiten.yml · iban-guard.yml · konformitaets-guards.yml · schema-register-guards.yml · testdata-prod-guard.yml · ⛔ ohne --selftest |
kanon-waechter.yml | kanon-waechter | ✅ | gerufen von agentchat01-suiten.yml · ⛔ ohne --selftest |
skill-registry-ssot.yml | skill-registry-ssot | ✅ | gerufen von kanon-merge-freeze.yml · kanon-quellen-ssot.yml · ⛔ ohne --selftest |
check_befund_hat_fortsetzung.py | GUARD — behauptet eine Registerzeile laufende Arbeit, waehrend es keine gibt? | ✅ | gerufen von konformitaets-guards.yml · schema-register-guards.yml · Selbsttest: ja |
check_spalten_existieren.py (#1162) | GUARD — gibt es JEDE Spalte, die wir auswaehlen? Eine falsche Spalte laesst PostgREST die GANZE Abfrage mit 400 42703 ablehnen; /orch/retriage war dadurch 11 Tage tot (mime_type statt media_type) und meldete es NIRGENDS. Prueft select=-Listen aus 08_OPS/tgv-skills-service und 02_SKILLS gegen information_schema, beide Richtungen (auch: wie viele Listen geprueft wurden). | ✅ | LOKAL, nicht in CI: braucht Schema-Zugriff (SUPABASE_MGMT_TOKEN) — exit 2 statt gruen, wenn er nicht messen kann |
check_conf_stufen_ssot.py | GUARD · Kennen alle Einstufer DIESELBEN Stufen — und dieselbe Trennung? | ✅ | gerufen von schema-register-guards.yml · Selbsttest: ja |
check_documents_mandant_id.py | WÄCHTER — trägt jede documents-Zeile der letzten N Tage ihren Mandanten? + K4 (TRIAGE#01, 05.09.): liegt die Antwort schon in der Zeile? | ✅ | gerufen von betriebs-waechter.yml (scharf) und schema-register-guards.yml ㉚ (Selbsttest, netzfrei — jener Workflow hat kein on: pull_request, ein Riegel nur dort läuft auf keinem Vorgang) · Selbsttest: ja. ⛔⛔ K4 schliesst eine Lücke, die in DIESER Datei stand: K3 zählte unassigned als legitime Warteschlange — richtig für Zeilen, deren Antwort nirgends steht, falsch für 31 von 70, bei denen die Entität wörtlich in der eigenen Zeile liegt (Bill-to im extrahierten Belegtext, JCs Hashtag, JCs getippter Vorspann). ⭐ Ein Wächter, der eine Schlange zählt, ohne zu fragen, ob sie warten MUSS, entlastet falsch. Ratsche 08_OPS/triage01/k4_grundlinie.json (31, darf nur schrumpfen) statt Sofort-Rot — ein dauerrotes K4 verdeckt jeden Befund hinter ihm. Enthaltung als eigener Ausgang (exit 2), wenn K4 ungemessen ist oder die Ratsche fehlt — nie grün. Beißprobe am ECHTEN Bestand 6/6 (Grundlinie 31→0, 31→30 ROT, Ratsche entfernt→2, --ohne-k4→2, --selbstest→2, zurück auf 31→0), Selftest 9 K4-Fälle inkl. Mutationsprobe (Auflösung taub gestellt ⇒ Treffer fallen auf 0 ⇒ Probe merkt es). |
08_OPS/triage01/vorschlag_mandant.py + k4_grundlinie.json | TROCKENLAUF — Mandanten-VORSCHLÄGE mit Fundstelle, für JCs Bestätigung statt Sortierarbeit. ⛔ Kein --apply, kein PATCH, kein DDL — die Mandantenzuordnung ist JCs Entscheid, nicht die Schlussfolgerung eines Skripts. | ✅ | TRIAGE#01. ⭐ R-192, kein zweites Regelwerk: Auflösung kommt aus agent_orch01.provider_map.resolve_entity (der Funktion des Live-Verifizierers) + agent_orch01.mandant_map.propose_mandant; neu ist allein, dass JCs Hashtag auch im Rumpf zählt (mail_intake.py:695 liest ihn nur im Betreff) und dass sein getippter Vorspann gelesen wird. ⛔⛔ Die eigene Selbstprobe hat einen Rateautomaten in mir gefangen: die erste Fassung gab intake_text vollständig an entity_from_billto — damit las sie JCs eigenen Signaturblock unter der weitergeleiteten Mail als Bill-to und schlug c3 vor, mit korrekter Fundstelle daneben. ⭐ Ein Rateautomat mit Beleg ist gefährlicher als einer ohne: die Fundstelle stimmt, sie beweist nur etwas anderes. Seither zählt aus dem Rumpf nur der Text vor dem Weiterleitungskopf; der Anhangs-Extrakt bleibt ganz drin, dort steht der echte Empfänger. 12 Selftest-Fälle netzfrei, beide Richtungen (5× muss greifen, 3× muss schweigen). Argument-Riegel: --selbstest ⇒ rc=2, nicht stiller Zweigwechsel. Nicht in CI eingehängt — es urteilt nicht, es legt vor; sein Regelmodul wird über ㉚ mitgeprüft (Pfadfilter 08_OPS/triage01/**). |
check_eingang_zeigt_den_upload_weg.py | DER VERLAUF MUSS DEN NEUESTEN EINGANGSWEG FUEHREN. | ✅ | gerufen von konformitaets-guards.yml · ⛔ ohne --selftest |
check_entitaets_karte_gegen_db.py | GUARD - kuerzt die Shell einen Entitaets-Slug so, dass ZWEI Entitaeten denselben | ✅ | gerufen von betriebs-waechter.yml · konformitaets-guards.yml · Selbsttest: ja |
check_entitaets_wert_hat_bruecke.py | GUARD - loest jeder entity-WERT im Bestand eindeutig auf genau einen entities.slug auf? DATEN-Achse zu check_entitaets_karte_gegen_db.py (CODE-Achse). | ✅ | gerufen von betriebs-waechter.yml (Echtlauf) · konformitaets-guards.yml (Selftest) · Selbsttest: ja · VOKABULAR#01 #1653 |
check_fk_auf_welt_b.py | GUARD - zeigt ein Fremdschluessel ausserhalb von Welt B auf projects, und ist jede Bestands-Ausnahme noch echt? Liest die PostgREST-OpenAPI live (ID-Raeume projects/prf_projects ueberlappen, Verwechslung waere still). | ✅ | gerufen von betriebs-waechter.yml (Echtlauf) · konformitaets-guards.yml (Selftest) · Selbsttest: ja · PROJEKT#09 #3885 |
check_gate_a_presence.py | kein CORE-Screen ohne Gate-A-Report (Sinn/Zeitgewinn geklärt). | ✅ | gerufen von kanon-quellen-ssot.yml · Selbsttest: ja |
check_grund_vor_der_flaeche.py | DIE ORDNUNGS-ACHSE — steht der Anbringungs-Aufruf des BEGRÜNDUNGS-Bereichs VOR dem der Fläche, die er erklärt? (JC-Fund 27./28.09.2026: Reiter verspricht Inhalt → leere Fläche → Grund darunter. Kein Layout-Unfall, sondern ein unbedingtes appendChild an letzter Stelle.) | ✅ | gerufen von konformitaets-guards.yml (Schritt ⓄRD) — bewusst nur --nur-fixtures: der BESTAND ist heute rot (tgv_renderers_graph.js, Eigner COREGRAPH#02, #3279), und ein Wächter, der vom ersten Tag an rot steht, erzieht zum Wegklicken (B-25). Positivmessung in BEIDE Richtungen plus „nicht auffindbar = kein Urteil“. Sobald #3279 grün ist, fällt --nur-fixtures weg und die gemessene Datei kommt in den Pfadfilter. Owner: BEFUND#01 |
check_eingangsdatum_ist_pflicht.py | GUARD — steht die Grenze um documents.date_received (Mig 288: NOT NULL + DEFAULT now()) noch, und setzt der Engpass intake_raw.py den Wert noch vor dem INSERT? ⭐ Der zweite Halbsatz der Hausregel: eine Grenze, die man loeschen kann, ohne dass eine Probe faellt, ist eine Absicht. ⛔ R-193: urteilt ueber DREI Regale im Repo, NICHT ueber die Datenbank — Mig 288 ist PROPOSED und JCs Hand; den SELECT fuer die Live-Lage druckt er mit. | ✅ | gerufen von schema-register-guards.yml · Selbsttest: ja (10 Beissproben; am echten Baum nachgemessen: Grenze auskommentiert ⇒ rc=3) |
check_intake_raw_vor_zeile.py | GUARD — steht hinter jeder documents-Zeile wirklich eine gesicherte Rohdatei? | ✅ | gerufen von betriebs-waechter.yml · konformitaets-guards.yml · schema-register-guards.yml · Selbsttest: ja |
check_jc_text_ohne_hausjargon.py | LIEST EIN BLATT AN JC MIT FREMDEN AUGEN. | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_kai_dialog_konformitaet.py | Dialog-Prüfer über KAI-Antworten (Envelope-Struktur). | ✅ | kein Rufer in .github/workflows/ — mit Grund im eigenen Kopf: cron (Fixture-Selbsttest ist netzfrei und laeuft via pytest-Wrapper in CI; · ⛔ ohne --selftest |
check_kontakte_propose_vertrag.py | KONTAKTE#07 — Guard 12: der Anlege-Weg scheitert nicht am eigenen Vertrag, und wenn er | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_live_skill_versions.py | Compare LIVE skill versions against THIS checkout — the coordination check | ✅ | kein Rufer in .github/workflows/ — mit Grund im eigenen Kopf: hand — braucht Netz, hat KEINEN Selbsttest (JE-156: ohne Negativ-Test keine CI). Werkzeug auf Zuruf (GUARD#01 Vorlage §2d). · ⛔ ohne --selftest |
check_mail_port_zielprobe.py | mail_port_zielprobe — baut der lokale Port aus einer echten .eml die richtige Zeile, | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_modellkarte_gegen_code.py | stimmt die Modell-Karte im Screen noch mit den Skills? | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_panel_zeigt_gespeicherte_metadaten.py | was gespeichert ist, MUSS sichtbar sein. | ✅ | gerufen von betriebs-waechter.yml · Selbsttest: ja |
check_pr_kuendigt_seine_dateien_an.py | TRAEGT DIESER PR DATEIEN, DIE ER NICHT ANKUENDIGT? (GUARD#03, Register #592) | ✅ | gerufen von schema-register-guards.yml · Selbsttest: ja |
check_pruefplan_adressen.py | WAECHTER — fuehren die Adressen in den Pruefplaenen dorthin, wo die Sache liegt? Zwei Achsen: (A) genannte Datei/Tabelle fehlt im Bestand · (B) blosser Dateiname neben einem FREMDEN Verzeichnis — jeder Pfad existiert, die Zeile fuehrt trotzdem in die Irre (ADMIN#04, Register #2035) | ✅ | gerufen von konformitaets-guards.yml (Selbsttest + Echtlauf, .claude/skills/** in BEIDEN Ausloeser-Bloecken) · Selbsttest: ja (8/8) · netzfrei, schreibt nichts · drei Enthaltungen mit Grund (Vorlagen-Platzhalter · mehrdeutiger Name · Tabellen per --tabellen-befehl) |
check_projekt_ablage_kette_schliesst.py | schreibt der SETZER, was die ABLAGE verlangt? | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_projekt_thread_scope.py | PROJEKT#07 — GUARD: reist die Projekt-Achse mit der FRAGE, und schneidet sie in BEIDE Richtungen? | ✅ | gerufen von konformitaets-guards.yml · ⛔ ohne --selftest |
check_regel_tabelle_hat_achse.py | GUARD — entsteht eine neue FACHLICHE Tabelle ohne Mandanten-Achse? (MANDANT#04, #476) | ✅ | gerufen von schema-register-guards.yml · Selbsttest: ja |
check_regelwerk_ist_sichtbar.py | JEDE R-REGEL, DIE EIN TOWER SCHREIBT, MUSS JC AUCH SEHEN. | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_register_completeness.py | VOLLSTAENDIGKEITS-RATCHET (JC-Mandat 2026-07-17). | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_rka_zeitraum_vertrag.py | aendert die Zeitraum-Achse die Abfrage MESSBAR? | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_rollenquellen_stimmen_ueberein.py (#1940) | Zwei Tabellen sagen, wer wer ist — und niemand hält sie synchron. auth_allowed_users.role (die TÜR: darf dieser Mensch herein) gegen user_mandant_roles.rolle (die RECHTE: was darf er worin) — verschiedene Tabellen, verschiedene Spaltennamen, ein Begriff. ⛔ BEIDE RICHTUNGEN, eine allein wäre wertlos: ① Mitgliedschaft ohne Freischaltung (hat Rechte, kommt nicht durch die Tür) · ② Freischaltung ohne Mitgliedschaft (kommt herein, findet nichts). Beides endet gleich: account-status gibt 403 und die Shell hält keinen Ausweis — vor #1940 hiess das T4 (volle Sicht), seither T0. Gemessen 14.09. über alle 11 Konten: 2 Rechte ohne Tür (beide mandant_admin), 4 Tür ohne Rechte (Welt-B-Testkonten); nur 2 Konten hatten einen gültigen Ausweis. ⭐ Er ENTSCHEIDET nicht, welche Tabelle die Wahrheit ist (Kanon-Frage, Eingang ADMIN#04) und ÄNDERT nichts — Konten sind JCs Hand. Bestand der 6 eingefroren, ROT nur bei einer NEUEN Abweichung (sonst dauerrot ⇒ weggeklickt), Selbsttest 10 Prüfungen, jede Lage in beide Richtungen. | ✅ | Owner SICHERHEIT#07 · LAUFORT hand (Mgmt-API, netzabhängig) · ⛔ bewusst NOCH NICHT in betriebs-waechter.yml: solange die 6 Bestandszeilen stehen, wäre er ab dem ersten Lauf rot — er gehört dorthin, sobald JC die zwei Freischaltungszeilen entschieden hat (Entscheidungsvorlage in Register #1940). Die netzfreie Hälfte derselben Frage läuft bereits in CI: sicherheit03_gegenprobe.py (Lagen vorschau/abgelehnt) |
check_esc_ist_vollstaendig.py (#1959) | Dieselbe esc liegt als KOPIE an 27 Stellen — escapen sie auch dasselbe? Gemessen 14.09.: 14 vollständig · 2 delegiert · 11 unvollständig. Pflicht sind & < > " (deckt Textknoten UND doppelt-quotierte Attribute); ⛔ ' wird gezählt, nicht erzwungen — eine Regel, die ab dem ersten Lauf fast alles rot färbt, wird weggeklickt und nimmt ihre echten Funde mit. ⭐ Unvollständige Kopien brauchen einen AUSWEIS mit Eigner und Frist, keine Baseline: eine Baseline sagt „so ist es nun mal", ein Ausweis „bis wann, und wer" — läuft eine Frist ab, wird der Guard rot. ⛔ Der Rumpf wird per KLAMMERZÄHLUNG erfasst, nicht per Regex: zwei Regex-Anläufe ergaben zwei Zahlen (27 und 13), und eine Messung mit zwei Wahrheiten ist keine. ⭐⭐ Er ersetzt die Wurzelreparatur NICHT: die wäre EINE esc in der Engine und 26 Verweise darauf (Mig-263-Klasse, „eine Kopie ist eine Zusage, kein Mechanismus"), und sie gehört AGENT-UX. Dieser Wächter hält nur die Klasse davon ab, weiter zu wachsen, bis sie kommt. Anlass: die Gegenprüfung zu #1958 — beim Verbreitern der Reichweite fiel ux_feedback_panel.js:53 auf (escapt nur < >, Ergebnis geht in ein title-Attribut), jetzt als Fremdbefund mit 14-Tage-Frist an AGENT-UX. Selbsttest 9 Prüfungen, jede Achse in beide Richtungen (vollständig/unvollständig · mit/ohne Ausweis · abgelaufene Frist · Delegation ist kein Verstoß · innere Klammern beenden den Rumpf nicht). | ✅ | Owner SICHERHEIT#07 · LAUFORT ci, netzfrei · gerufen von konformitaets-guards.yml (⑨b, Selbsttest + Guard); Pfad 06_DASHBOARD/** löst aus, der Guard selbst ebenfalls |
check_route_ohne_kanon_zeile.py | check_route_ohne_kanon_zeile.py — gibt es eine ERREICHBARE Route ohne Kanon-Zeile? | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_schluessel_im_repo.py (#1957) | Welche Schlüssel im Repo sind LEBENDIG? Trennt jede Fundstelle in DREI Klassen: LEBEND (Fingerabdruck zeichengleich mit dem aktuellen .env-Wert ⇒ wirkt heute) · NICHT_AKTUELL (echt aussehender Schlüssel, aber nicht der aktuelle — ⛔ das heisst AUSDRÜCKLICH NICHT „tot": ob er serverseitig widerrufen wurde, steht nicht im Repo) · PLATZHALTER (Vorlagen, Prüf- und Beispieltexte). ⛔⛔ Was er BEWUSST NICHT tut, und das ist der Punkt: er prüft KEINEN gefundenen Schlüssel gegen einen Live-Dienst. Einen Schlüssel zu benutzen, um herauszufinden, ob er noch gilt, IST ein Anmeldeversuch — mit einem Schlüssel, dessen Status man gerade erst klären will. Der Fingerabdruck-Vergleich beantwortet dieselbe Frage für die Menge, auf die es ankommt, ohne dieses Risiko; für den Rest bleibt eine ehrliche Enthaltung. ⛔⛔ Er druckt NIE einen Wert — nicht in der Ausgabe, nicht im Fehlerfall, nicht im JSON-Bericht; ausgewiesen wird ein Fingerabdruck (SHA-256, 10 Hex), der gruppiert und nicht entschlüsselt. Drei der fünfzehn Selbsttest-Prüfungen verbieten genau diesen Austritt; eine vierte verlangt, dass OHNE lesbare .env gar nichts als „lebend" gilt (eine fehlende Grundlage ist keine Entwarnung). Erste Messung 14.09.: 6037 Textdateien, 867 Fundstellen ⇒ 75 Platzhalter · 758 nicht aktuell (nur 9 verschiedene Schlüssel) · 34 lebend, davon 33 derselbe öffentliche sb_publishable_ (by design unkritisch) und EIN Supabase-Management-Token (#1957). ⛔⛔ NACHTRAG 14.09. (#1980) — DAS URTEIL IST GETRENNT, DIE MESSUNG NICHT: bis dahin machte JEDER lebende Fund rot, also auch die 33 öffentlichen — der Prüfer war damit bauartbedingt dauerrot, und mein JC zugesagter Gegenbeweis (nach der Rotation ist der Alarm weg) wäre von 34 auf 33 gefallen und rot geblieben. Jetzt teilt urteil() die LEBENDEN in geheim und öffentlich per Bauart (OEFFENTLICH_ART); rot machen nur die geheimen. Der publishable Key bleibt lebend, gezählt und mit allen Fundstellen gelistet — wer ihn rotieren will, braucht die Liste. Stand: 34 lebend = 1 geheim + 33 öffentlich, Exit 1 wegen genau dieser einen Zeile; nach JCs Rotation muss er auf Exit 0 gehen. Selbsttest 9 → 15, sechs neu in beide Richtungen, darunter eine Beissprobe an der Ausnahmeliste selbst. | ✅ | Owner SICHERHEIT#07 · LAUFORT lokal — netzfrei, aber er braucht die lokale .env als Massstab; ⛔ NICHT CI (dort fehlt sie, und ohne sie fiele genau die Achse „lebt er?" weg), ⛔ NICHT cron, solange der Bestand steht |
08_OPS/sicherheit07_menue_gegenbeweis.py (#1940) | Sieht ein abgelehnter Ausweis mehr als ein gültiger? Misst die DOM-Tür für vier Lagen an einer Fläche: anonym · viewer mit gültigem Ausweis · zwei Verwalter, deren account-status 403 gibt. ⭐ Die Bauart, die ihn von einem Bericht unterscheidet: --fassung lokal fängt die Anfrage nach core_shell.html ab und beantwortet sie aus dem ARBEITSBAUM — Anmeldung, Backend und Daten bleiben echt. Damit ist eine Reparatur an der laufenden Anlage belegbar, BEVOR sie ausgeliefert ist (gemessen 14.09.: 49 → 1 Menüpunkte, viewer 27 unverändert, Vorschau unberührt); --fassung live liefert dasselbe Bild ohne Ersetzung, die beiden nebeneinander sind Vorher/Nachher in derselben Stunde. ⛔ Er ersetzt NUR diese eine Datei — wer mehr ersetzt, misst am Ende seine eigene Anlage. Wartet auf den ZUSTAND (data-authed gesetzt, Anmeldeansicht weg, Mandanten-Nachlauf fertig), nicht auf die Uhr: eine feste Frist gegen einen asynchronen Vorgang ist ein Wettlauf, und genau der hat den Vorläufer zweimal falsch urteilen lassen. Drei Ausgänge (grün · Befund · NICHT GEMESSEN), Urteil als eigene Funktion, Selbsttest 6 Achsen in beide Richtungen. | ✅ | Owner SICHERHEIT#07 · LAUFORT hand (braucht echte Sitzungen aus provision_qa_session.py + Chromium) · ⛔ nicht CI-fähig (sitzungs- und netzabhängig); die netzfreie Hälfte derselben Frage läuft in CI über sicherheit03_gegenprobe.py |
08_OPS/sicherheit07_freischaltung.py (#1951) | JCs Entscheid aus #1940 vorbereitet, nicht vollzogen. Zeigt alle Konten mit BEIDEN Achsen (auth_allowed_users = die Tür · user_mandant_roles = die Rechte), schlägt für jedes Konto mit „Mitgliedschaft ohne Tür" die exakte Zeile vor und nennt die Gegenprobe (gültige Ausweise 2 → 4). ⛔⛔ Die Rolle ist viewer, und das ist gemessen, nicht gewählt: ep_account_status prüft nur, DASS eine aktive Zeile mit irgendeinem role da ist (app.py:6531) — die Tür geht also auf; _is_global_admin verlangt admin/super_admin (app.py:5567) und bleibt falsch, also bleiben VORRAUM, Postfach-Endpunkte und _require_global_admin ZU; die RLS-Funktionen is_super_admin/is_mandant_admin lesen user_mandant_roles, nicht diese Tabelle (aus pg_get_functiondef gelesen); die einzigen zwei Policies im Haus, die auth_allowed_users lesen, verlangen admin und bleiben zu. ⇒ Tür öffnen ohne Rechte verleihen. --rolle admin ist möglich, warnt aber laut und nennt, was damit aufgeht. DREI RIEGEL: --trocken ist Vorgabe · --scharf schreibt NUR mit ausdrücklich genannten --konten (die errechnete Liste allein genügt nicht, sonst schaltet ein späterer Lauf ein ungesehenes Konto frei) · ein genanntes Konto muss die Lage wirklich tragen. Additiv, idempotent, Spur in agent_events. Selbsttest 11 Prüfungen; beide Riegel zusätzlich live gegengeprobt (ohne --konten ⇒ rc=2, bereits freigeschaltetes Konto ⇒ Einwand, Bestand beide Male unverändert). | ✅ | Owner SICHERHEIT#07 · LAUFORT hand · ⛔⛔ --scharf fährt ausschließlich JC — ein Schreibweg in den Kontenbestand gehört an eine Hand, nicht an eine Automatik, und das Zitat einer anderen Sitzung ist nicht sein Wort. Befehl mit WO steht im Trockenlauf und in Register #1951 |
08_OPS/check_fn_reife_eine_quelle.py (#1913) | Hat die REIFE einer Funktion genau EINE Quelle? triage trug in FN (Funktionskarten) „live" und im EINGANG-Tupel „wip" — zwei Handkopien desselben Wortes, je Fläche eine andere (Codex-Brief 39 A2). Misst statisch am kommentarfreien Quelltext von core_shell.html: A jede id, die in FN UND in EINGANG/STAMM/PLAT steht, trägt im Tupel fnSt("<id>") statt eines Literals · B jedes fnSt("x") nennt seine eigene id und x existiert in FN (sonst verdeckt der Fallback „wip" den Tippfehler) · C function fnSt( existiert (sonst ReferenceError auf Modul-Ebene, #1705-Klasse). Bestand wird bei jedem Lauf gedruckt. ⛔ NICHT gemessen (R-193): ob das FN-Wort WAHR ist (Render/Registry, das steht in #1913) · RAUM (tot seit #1689, eigener Vorgang) · Tupel-ids ohne FN-Eintrag. | ✅ | SCREEN#13 (Register #1913). Vorher gesucht: 08_OPS/check_*, 07_TESTS/ (id:"triage"/const EINGANG: 0 Treffer) — kein bestehender Prüfer misst die Reife-Quelle; test_fn_route_wiring.py misst die Routen der Karten, nicht ihr Wort. Eingehängt als ㉒ in konformitaets-guards.yml (Selbsttest zuerst). Selbsttest:** 3 Köder (Literal statt fnSt · fremde id · fnSt fehlt) beissen, heiler Text grün; R-155 cp1252 grün; Fahnen-Riegel per argparse (Vorlage der Nachbarn, #1279). |
08_OPS/check_ausgeliefert_ist_master.py (#1647, + Einhängung betriebs-crons.yml ①b) | „Läuft live, was auf master liegt?“ — master.SERVICE_VERSION (per ast) gegen /health.version, öffentlich und ohne Zugangsdaten. Die Gegenhälfte zu check_dienst_version_wandert_mit.py: der fragt vor dem Merge, ob die Zahl sich BEWEGT; dieser fragt danach, ob sie ANKOMMT — eine Lücke, die jener Prüfer in seinem eigenen Kopf benennt (Zeile 47). ⭐ Er misst die WIRKUNG, nicht den Kontrollfluss: weichen die Zahlen ab, ist etwas nicht ausgeliefert — egal warum, und auch dann, wenn gar kein Deploy-Lauf stattgefunden hat. Ein failure()-Zweig am Lauf könnte das nicht. ⛔ Er nennt das ALTER der Abweichung (Commit-Datum der ersten master-Zeile über der Live-Zahl) plus die Zahl betroffener Bundle-Dateien — „nicht ausgeliefert“ ist harmlos für zwei Minuten und ein Befund nach zwei Stunden; 45-min-Gnadenfrist für laufende Deploys. ⛔ Enthaltung ist ein EIGENER Ausgang (rc=2), nie grün — sonst wäre der Ausfall des Wächters von Normalbetrieb nicht zu unterscheiden. ⭐⭐ NUXIN gemessen: die Bundle-Liste wird aus check_dienst_version_wandert_mit.bundle_dateien() importiert, nicht nachgebaut — sie stammt damit weiter aus check_bundle_vollstaendig.bundle_ziele (EINE Quelle). | ✅ | DEPLOY#02. Selbsttest 11 Lagen inkl. Positivkontrolle · 07_TESTS/deploy02/test_ausgeliefert_ist_master.py 16 Proben · zwei Sabotagen gefahren (Leser zurück auf die erste Zuweisung ⇒ 2 rot; app.py zurück auf drei Zuweisungen ⇒ 1 rot; Reparatur ⇒ 16 grün). ⛔⛔ MESSGRENZEN, ausdrücklich (R-193): ① SERVICE_VERSION steigt nur, wenn jemand sie hebt — ein Deploy von Code, der die Zahl nicht anfässt, ist so NICHT messbar (dafür steht der Riegel vor dem Merge); ② Takt 6 h ⇒ Alarmverzögerung ≤ 6 h — er ersetzt keinen Sofort-Alarm, er ersetzt das SCHWEIGEN; ③ er sagt nicht ausgeliefert, nicht warum — die Ursache steht im Lauf-Log. ⛔ Braucht Historie: der Cron-Checkout ist flach, der Schritt holt --depth=400 nach; scheitert das, enthält er sich (rc=2). ⛔ FAHNEN-RIEGEL nachgetragen (check_fahnen_riegel_bestand hatte recht): er las seine Fahnen mit in sys.argv — ein vertipptes --selbstest haette nicht den Selbsttest gefahren, sondern den scharfen Lauf, und dabei ausgesehen wie ein gelungener Selbsttest. Bauform aus check_register_nummer.unbekannte_argumente (#1279) importiert; --help gibt jetzt Auskunft statt still zu messen. Vier Richtungen gebissen + Sabotage-Gegenprobe. |
.github/workflows/deploy-service.yml | Der Deploy des Skills-Dienstes — pfadgefiltert auf app.py, 02_SKILLS/, requirements.txt und die einzeln nachgetragenen Bundle-Module; baut den Tarball und spielt ihn per SSH auf den Hetzner. ⛔⛔ BEKANNTE LÜCKE, benannt statt verschwiegen (#1647): er hat KEINEN failure()-Zweig, keinen Meldeweg, keine Mail. Am 05.09. fiel er 11:56 und 13:11 aus (SSH refused), der Lauf um 14:20 heilte es — drei Stunden lang war ein gescheiterter Deploy von einem gelungenen nicht zu unterscheiden, und der Ausfall hinterliess nichts Nachweisbares. Gegengewicht ist seit #1647 check_ausgeliefert_ist_master.py: er misst die Wirkung** (/health gegen master) statt den Kontrollfluss und fängt damit auch den Fall, dass gar kein Lauf stattfand — aber erst im 6-h-Takt. Ein Sofort-Alarm am Lauf selbst fehlt weiter. | ✅ | DEPLOY#02 · Diese Zeile schliesst zugleich eine Messlücke am Prüfer: check_artefakt_register.py hielt dieses Artefakt bis 05.09. nur über das Stichwort service für belegt (selten:service, 2x im Register, SELTEN_MAX=2). Als eine fremde Zeile dasselbe Wort zweimal mehr benutzte, kippte der Beleg auf leer und das Artefakt wurde ROT — ohne dass sich an ihm etwas geändert hätte. ⭐ Ein Grün, das auf einem Stichwort ruht, kann von einem Dritten widerrufen werden, der von dem Artefakt nichts weiss — und der Widerruf sieht aus wie ein Befund. Der Prüfer führt die Belegstärke bereits mit und warnt davor (beleg(), Zeile 85); die Folge war nur nicht zu Ende gezogen. Gemessen an diesem Vorgang: drei weitere Artefakte verloren ihren Beleg allein durch fremde Prosa. Jetzt steht der volle Name hier, der Beleg ist voll und hängt an nichts Fremdem mehr. |
check_stille_wie_normalbetrieb.py | GUARD — Stille, die wie Normalbetrieb aussieht. (RKA#10, Tower#33 08.08.2026) | ✅ | gerufen von betriebs-waechter.yml · Selbsttest: ja |
check_store_endstation_bucket.py | traegt der Kanon-Ausgang der Endstation? MIT NENNER. | ✅ | gerufen von betriebs-waechter.yml · Selbsttest: ja |
check_triage_entitaet_wurde_ueberschrieben.py | WAECHTER - stimmt die Entitaet, die die Triage ERKANNT hat, mit der ueberein, | ✅ | gerufen von betriebs-waechter.yml · Selbsttest: ja |
check_upload_sendet_echte_spalten.py | WAECHTER — schickt der Upload-Weg nur Spalten, die es WIRKLICH GIBT? (EINGABE#01, #743-⑤) | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
08_OPS/check_qa_durchgang_faellig.py (#2410, + Einhängung schema-register-guards.yml ⑤b) | Ist ein EINGELOGGTER QA-Durchgang fällig? — aus drei GEMESSENEN Achsen, nie aus einer gespeicherten Zahl: core_shell.html auf origin/master seit dem letzten Durchgang bewegt (Commits gezählt, nicht Zeilen) · Deploy (SERVICE_VERSION per ast über check_ausgeliefert_ist_master.versionen_im_text, importiert statt nachgebaut) · letzter Durchgang älter als 7 Tage. Quelle ist der Stempel, den uxaudit_runner.py nur im eingeloggten Lauf schreibt (08_OPS/status/kanon_waechter_stempel.json, lauf_utc) — kein zweites Format, und er liest ihn aus Arbeitsbaum UND origin/master und nimmt den jüngeren (R-298 ③: welche Kopie liest das Werkzeug gerade?). ⛔⛔ BERICHT, KEIN TOR: rc=0 AUCH WENN FÄLLIG — ein Fristen-Melder als Tor kippt um Mitternacht und blockiert danach jeden Vorgang, ohne dass jemand etwas getan hat (im Haus zweimal bezahlt). Rot wird genau ein Fall: Stempel fehlt/unlesbar (rc=2), denn dann ist die Frage nicht messbar — eine 0 heisst dreierlei. ⭐ Flacher Klon wird ERKANNT (--is-shallow-repository): dort fände git log --since einen Commit und meldete „nichts geändert“ — die Achse heisst dann BLIND, und das Urteil lautet UNKLAR, nie „nicht fällig“. Druckt Datum und Uhrzeit in Europe/Berlin (zoneinfo, Rückfall auf die EU-Regel, wenn tzdata fehlt — auf JCs Windows der Normalfall) und die drei Befehle zum Kopieren mit Fenster und cd IM Block. | ✅ | QA-TAKT#01, Register #2410. Anlass: JC-Entscheid 21.09.2026 — „nein, das Erzeugen [der QA-Sitzung] bleibt bei mir; ok wenn du mir aktiv fragst, wann es fällig ist, und mir genau die Befehle gibst.“ Eine Bringschuld, die nur als Vorsatz lebt, ist eine Schublade — deshalb ein Werkzeug. Selbsttest 9 Lagen + Positivkontrolle in beide Richtungen (alt⇒fällig ≠ frisch⇒nicht fällig), Grenze beidseitig geprüft (7 d 1 h ⇒ fällig, 6 d 23 h ⇒ nicht), Berliner Zeit an festen Zeitpunkten in Sommer und Winter. Gegenprobe am echten main(), nur die Aussengrenze (git-Antworten, Stempeldatei) gestubbt, nie der urteilende Zweig: 5 Lagen, jede Achse schlägt einzeln um. ⛔ Er bietet die Befehle NUR an, wenn etwas zu tun ist — ein „nicht fällig“ mit Befehlen darunter gewöhnt JC daran, sie wegzuklicken. REICHWEITE (R-193), in der Ausgabe: sagt NICHTS über den Dienst-Stand (netzfrei), ob der letzte Durchgang aus CI oder von Hand kam, mit welcher Identität er lief (der Stempel trägt sie nicht), ob eine Sitzungsdatei gerade lebt, oder was der letzte Durchgang inhaltlich fand. |
08_OPS/qa_sitzung_pfad.py (#2410) | WO liegt die qa@-Sitzungsdatei? Gemessen, nicht geraten. Der Baustein hinter dem Befund, dass die eingeloggte QA von Hand seit Monaten in der ersten Zeile abbrach: uxaudit_runner.py:88 zeigte auf <eigener Baum>/08_OPS/credentials/qa_session.json — an zwei Stellen gleichzeitig falsch. ① credentials/ ist gitignored und wandert NICHT in einen Worktree (gemessen 21.09.: Arbeitsbaum 0 Dateien, Haupt-Checkout drei). ② Den Namen qa_session.json schreibt provision_qa_session.py NIE — je Identität eine eigene Datei, ohne --als qa_session_viewer.json. ⭐ Verzeichnis wird jetzt über git rev-parse --path-format=absolute --git-common-dir gemessen (in BEIDEN Lagen belegt: aus einem Worktree und aus dem Haupt-Checkout), Name gesucht statt gesetzt. Mit Positivkontrolle: eine gefundene Wurzel ohne 08_OPS/ gilt als UNGEMESSEN — eine geratene Wurzel wäre schlimmer als keine. | ✅ | QA-TAKT#01, Register #2410. Zwei Rufer, damit es ein Baustein ist und keine Kopie: uxaudit_runner.DEFAULT_SESSION und check_pruefer_gibt_den_zustand_zurueck._session_vorgabe (letzterer suchte den NAMEN bereits richtig und riet das VERZEICHNIS weiter — die zweite Hälfte desselben Fehlers). ⛔ Der Abbruch ist LAUT: er nennt die vollständige GESUCHT-Liste mit Herkunft je Pfad (R-193), die danebenliegenden Identitäten mit dem fertigen --session-Aufruf und die drei Provisionierungs-Befehle mit Fenster und cd. Eine stille Vorgabe, die ins Leere zeigt, sieht aus wie „nicht gemacht“ statt wie „kann nicht“ — dann sucht der Nutzer den Fehler bei der Arbeit statt beim Weg. Selbsttest 5 Proben, darunter die Lage-Erkennung in beide Richtungen und eine Positivkontrolle ausserhalb jedes Repos. |
betriebs-crons.yml | betriebs-crons | ✅ | kein Rufer in .github/workflows/ — ⛔ KEIN LAUFORT-Stempel — Owner: TOWER · ⛔ ohne --selftest |
check_port_liest_ueber_resolver.py | WAECHTER — holt ein heimgeholter Skill seine Bytes ueber den Resolver? (STORAGE#04, #538) | ✅ | gerufen von konformitaets-guards.yml · Selbsttest: ja |
check_testdata_prod_guard.py | GUARD: kein Testobjekt erreicht eine Prod-Auswertung. | ✅ | gerufen von testdata-prod-guard.yml · Selbsttest: ja |
check_flaeche_liest_ueber_resolver.py | WAECHTER — baut eine FLAECHE ihren Beleg-Link blind aus drive_file_id? (HEIMHOLUNG#12, #1761/#1771) | ✅ | LAUFORT: ci (netzfrei — liest nur 06_DASHBOARD/.html|.js). Traeger 07_TESTS/heimholung12/test_flaeche_resolver.py · Selbsttest: ja (14/14). ⭐ DAS DRITTE REGAL: dieselbe Frage ist an zwei Regalen bewacht — STORAGE#03 (Service-Endpunkte) und STORAGE#04 (check_port_liest_ueber_resolver.py, Skill-Ports) — und in keiner der beiden Zaehlungen kam die FLAECHE je vor (R-193). Gemessen 07.09.: 322 Belege tragen raw_key UND drive_file_id, 216 zusaetzlich storage_url. ⛔ ZWEI ACHSEN, weil eine nicht reicht: A = Google-URL als Literal im Quelltext (10 Flaechen gestundet). B = ein serverseitig gebautes URL-Feld ROH im href — dafuer ist A grundsaetzlich blind, weil dort nie ein drive.google.com steht. Eine Flaeche kann einen Nutzer zu Google schicken, ohne ein einziges drive.google.com zu enthalten. ⭐ Achse B ist an einem Paar geeicht, das sich WIRKLICH unterscheidet (core_shell.html, master gegen PR #2657: A 5/5 — blind, B 2/0 — sieht den Fix); Test (5)d schreibt die Blindheit von A ausdruecklich fest. ⛔ Baseline = STUNDUNG, kein Freispruch — Achse A als Namensmenge, Achse B als ZAHL je Datei (sonst sieht er nicht, ob es besser wurde). Offen: versand2.html:711/:949. |
check_lernschleife_ist_geschlossen.py | WAECHTER — sammeln wir Korrekturen, ohne sie je zu benutzen? (HEIMHOLUNG#12, #1768) | ✅ | LAUFORT: hand/cron (liest Supabase, nur lesend; --nur-code ist netzfrei und CI-faehig). Traeger 07_TESTS/heimholung12/test_lernschleife.py · Selbsttest: ja (6/6). ⛔ ER EXISTIERT WEGEN EINER FALSCHEN BEHAUPTUNG — meiner eigenen: ich meldete „es gibt keinen Ort, an dem eine Korrektur des Menschen landet“. learning_events gibt es seit Iter 13 und traegt 98 Zeilen aus 14 Quellen ueber vier Monate. Ich hatte drei GENANNTE Namen geprueft statt der FAEHIGKEIT. ⭐ Kaputt ist die LETZTE Stufe: ① learning_events 98 ✅ · ② readback.pattern_rules_candidates() gebaut, schreibt NICHTS · ③ pattern_rules 0 · ④ Promoter kann nichts befoerdern. ⛔⛔ Und die Wurzel ist dieselbe wie beim ORCH: readback.py gehoert zu agent_orch01 („Aendert NICHTS Live“, Live-Flip post-Hamburg + JC-direkt). EIN Tor, ZWEI Symptome — es entsperrt ORCH-Fuehrung und Lern-Schleife in einem Griff. Kein DDL noetig, beide Tabellen leben. ⭐ RATSCHE statt Dauerrot: misst die DIFFERENZ (neue Korrekturen bei weiterhin 0 Regeln), nicht den Absolutwert — „pattern_rules ist leer“ waere ab heute fuer immer rot. Basis 07.09.: 98/0. |
check_betrag_nur_in_einer_spalte.py | WAECHTER — traegt ein Beleg seinen Betrag nur in EINER der zwei Spalten? (HEIMHOLUNG#12, #1816) | ✅ | LAUFORT: cron (liest Supabase, nur lesend; --selftest ist netzfrei). Traeger 07_TESTS/heimholung12/test_betrag_eine_spalte.py · Selbsttest: ja (13/13). ⛔⛔ ER IST AUS VIER EIGENEN FEHLERN EINER SCHICHT ENTSTANDEN, nicht aus einer Idee: viermal wurde amount_eur gelesen und daraus „kein Betrag“ geschlossen — 323 Klaerungen (103 tragen amount) · 28 von 30 Dubletten-Gruppen (es waren 5) · #7744 (traegt 3,20 €) · ⛔ und sechs FREIGEGEBENE Belege als „kein Alarm“ ENTLASTET (53,07 €). ⭐⭐ Dreimal verfaelschte die Falle eine ZAHL, beim vierten Mal eine ENTLASTUNG — und eine falsche Entlastung wird nicht nachgeprueft, weil sie beruhigt. ⛔ WAS ER AUSDRUECKLICH NICHT KANN und was in seinem Kopf steht: die Regel „wer ‚ohne Betrag‘ sagt, muss BEIDE Spalten genannt haben“ ist eine Aussage ueber das DENKEN — kein Riegel kann sie beissen. Er misst deshalb die DATENLAGE, die die Falle aufstellt, und macht sie sichtbar, statt eine Denkregel zu behaupten. ⭐ RATSCHE, kein Alarm: zwei Achsen (A amount ohne amount_eur = 240 · B umgekehrt = 3, Stichtag 07.09., Grundgesamtheit 872), rot nur bei WACHSTUM — wer den Absolutwert rot faerbt, hat ab morgen einen dauerroten Waechter. ⛔ amount = 0 zaehlt NICHT als Betrag — sonst kehren die 42 Null-Zeilen zurueck, die der Riegel in fesp2_b_amount_eur_backfill.py (#1784) ausschliesst. ⭐ Und ein Testfall gegen Drift: (5)/(5)b lesen den Quelltext von main() und verlangen, dass das SQL dieselbe Regel traegt wie die reine Funktion — sonst bleibt der Selbsttest gruen, waehrend der Lauf weniger misst. |
check_eml_sicherung_verdrahtet.py | WAECHTER — wird die Traegermail je als .eml gesichert? (STORAGE#04, #537) | ✅ | gerufen von betriebs-waechter.yml · Selbsttest: ja |
check_env_gelesen_aber_nie_geschrieben.py | Liest der Server etwas, das der Deploy nie schreibt? (GUARD#03, Register #611) | ✅ | gerufen von schema-register-guards.yml · Selbsttest: ja |
check_pruefer_schreibt_in_den_baum.py | RATSCHE — welcher Pruefer schreibt beim Laufen in den Arbeitsbaum, und wer davon ueberschreibt BESTAND? Sie urteilt bewusst NICHT ueber die bekannten Faelle (das Ziel eines Schreibaufrufs steht meist in einer Variablen und ist statisch nicht zu bestimmen) — sie haelt den Stand fest und beisst ab dem naechsten Zuwachs. ENTHALTUNG: sie sieht nur DATEISYSTEM-Schreibvorgaenge; ein requests.post in die Datenbank ist fuer sie unsichtbar, und test_* liegt ausserhalb ihres Namensmusters. | ✅ | ABLAGE#02 (Register #1321), aus dem eigenen Absturz 03.09.: check_generatoren_gelaufen.py starb zweimal in seinem eigenen RETTUNGS-Pfad (write_bytes, OSError 22). Beide Male blieb die Datei heil — Glueck, nicht Bauart: der Fehler lag IM open(), und open('wb') kuerzt erst beim Oeffnen. ⭐ Der Trichter: 164 Pruefer → 28 mit echtem Schreibaufruf → 17 in Wegwerf-Verzeichnisse → 11 in den Baum → 2 auf Bestand. ⛔ Die erste Zahl war 59 — text.replace() und pfad.replace() heissen gleich. ⛔ Und tempfile im Quelltext war KEIN Freispruch: wer unteilbar schreibt (os.replace(tmp, ziel)), schreibt trotzdem — diese Fehlklassifikation hatte ich schon committet und am selben Tag zurueckgenommen. |
check_schreibt_beim_ansehen.py | RATSCHE — schreibt ein Werkzeug in JCs BESTAND, wenn man es nur ANSIEHT? AST-Achse ueber 07_TESTS und 08_OPS (1165 Dateien, ohne Namensfilter): welcher Schreibaufruf steht auf MODULEBENE und laeuft damit schon beim Import. ⭐ Der Ausloeser ist der Unterschied zum Nachbarn check_pruefer_schreibt_in_den_baum: nicht das LAUFEN, sondern das IMPORTIEREN — ein Sammellauf importiert tausend Dateien, ohne eine auszufuehren. ⛔ ZWEI Ausnahmen mit ZWEI Gruenden: Funktions- und Klassenruempfe laufen erst beim AUFRUF, if __name__ nur beim DIREKTEN Start. ENTHALTUNG: er misst, ob eine Datei schreiben KOENNTE, nicht ob sie es TUT — er oeffnet keine Verbindung und fragt nichts ab; die Liste ist eine OBERGRENZE der Erreichbarkeit. | ✅ | STERN#01 (Register #1407), aus ABLAGE#01s Fund 03.09.: 59 Datensaetze in learning_events, 58 an EINEM Tag, erzeugt durch unsere eigenen Messlaeufe. ⭐⭐ Nicht nachgebaut, sondern eine erklaerte Luecke gefuellt: der Nachbar-Prueferzeile oben benennt genau diese Achse als ihren blinden Fleck (ein requests.post in die Datenbank ist fuer sie unsichtbar, und test_ liegt ausserhalb ihres Namensmusters*). ⛔⛔ Eigener Fehler beim Bau, an Testdoppel nachgemessen: ast.walk steigt in verschachtelte Funktionskoerper hinab — dieselbe Klasse, die ABLAGE#01 mit ast.dump Faktor 3 kostete. Wurzel geheilt, Probe (8) deckt sie. ⭐ Positivkontrolle: der Anlassfall wurde waehrend des Baus repariert (#1406, 39ee7cbb) — vorher gemeldet, nachher nicht, und keine Ratschen-Zeile dafuer: eine Duldung fuer eine geheilte Wunde waere eine dauerhafte Begnadigung. |
08_OPS/check_test_sammlung_bricht_nicht.py (LAUFORT ci, netzfrei) | Killt eine Testdatei die Sammlung der anderen? Zitiert aus dem Dateikopf: 07_TESTS/buchhaltung01/test_buchungs_basis.py:329 rief auf MODULEBENE raise SystemExit(1); pytest IMPORTIERT jede Testdatei beim Sammeln, also beendete der Import den Sammler — INTERNALERROR> SystemExit: 1 / no tests collected in 0.91s. ⭐ „no tests collected“ sieht aus wie „es gibt keine Tests“ und heißt „die Sammlung ist abgebrochen“ — zwei völlig verschiedene Aussagen, eine Zeile. Liest nur getrackte Testdateien per ast, kein Import. | ✅ | ⚠️ NACHGETRAGEN, NICHT VON IHREM AUTOR GESCHRIEBEN — ZEIGER#01 am 06.09. beim Nachzug von PR #2399. Der Artefakt-Ratchet meldete das Werkzeug als [NEU] ohne Register-Zeile, obwohl es bereits auf master liegt: ⛔ geerbtes Rot, keine Neuverursachung dieses Vorgangs. Zeile aus dem Dateikopf ZITIERT (GUARD#03, Register #593) — sie behauptet nichts darüber hinaus, GUARD#03 möge sie fortschreiben. |
08_OPS/check_eimer_steht_in_der_zeile.py (LAUFORT cron, täglich, Träger betriebs-waechter.yml) | Jede Zeile mit Bytes muss sagen, WO sie liegen — die Regel: raw_key gesetzt ⇒ storage_bucket gesetzt. Zitiert aus dem Dateikopf (ABLAGE#02, 01.09.2026): 350 Belege hatten Bytes, aber keine Angabe wo. save_raw berechnete den Eimer und gab ihn zurück; intake_raw warf ihn weg — der Wert wurde erzeugt und fallengelassen. ⭐ Wer eine Funktion um einen Rückgabewert erweitert, erweitert nicht automatisch den, der sie ruft. Braucht nur Supabase, kein Bucket-Zugriff, rein lesend. | ✅ | ⚠️ NACHGETRAGEN, NICHT VON IHREM AUTOR GESCHRIEBEN — ZEIGER#01 am 06.09. beim Nachzug von PR #2512. Der Artefakt-Ratchet meldete das Werkzeug als [NEU] ohne Register-Zeile, obwohl es bereits auf master liegt und auch dort nicht registriert ist: ⛔ geerbtes Rot, keine Neuverursachung dieses Vorgangs — sein eigenes Artefakt pruefe_bucket_wand.py ist ordentlich eingetragen. Zeile aus dem Dateikopf ZITIERT; ABLAGE#02 möge sie fortschreiben. |
08_OPS/check_unbekannte_stufe_ist_nicht_oeffentlich.py (Workflow konformitaets-guards.yml, Schritt ㊙b) | Eine Stufe, die die Leiter nicht kennt, ist NICHT öffentlich. app.py:_conf_rank gab jedem unbekannten Wort except: return 0 (= öffentlich) und lag damit unter JEDER Decke, während executors_core dasselbe Wort per Allowlist ausschliesst — dieselbe Wand, zwei Bauarten, entgegengesetzte Richtung bei unbekannten Werten. ⭐ PORTIERT, nicht erfunden: chat_engine/scope_gate.py hat denselben Defekt am 08.08. an sich gemessen und mit ZWEI Funktionen repariert (Decke vs. Inhalt). | ✅ | ⚠️ DIESE ZEILE IST NACHGETRAGEN, NICHT VON IHREM AUTOR GESCHRIEBEN — ZEIGER#01 hat sie am 06.09. beim Nachzug von PR #2553 aus dem Dateikopf und der Registerzeile #1623 übernommen, weil der Artefakt-Ratchet (quellen-no-divergenz) das Werkzeug als [NEU] ohne Register-Zeile meldete. Sie zitiert und behauptet nichts darüber hinaus — VERTRAULICHKEIT#01 möge sie fortschreiben, wenn sie etwas anderes meint. ⭐ Es ist derselbe Handgriff und dieselbe Vorsicht wie bei check_werte_ohne_leser.py weiter oben: eine geratene Zeile wäre schlimmer als keine. |
08_OPS/heimholung11/baseline_drive_verluste.json (Daten, kein Werkzeug) | Die Schuld-Liste von check_drive_belege_verschwinden.py — 64 Belege, deren Drive-Zeiger am 31.08.2026 mit 404 antwortete. ⛔ Sie wird von NIEMANDEM automatisch geschrieben — nur von Hand, nur mit Begründung, und sie trägt den Grund dafür in ihrem eigenen Kopf. ⭐⭐ WARUM SIE EXISTIERT: der Wächter las vorher zwischenstand_zeiger.json — die Arbeitsdatei von heimhol_drive_bestand.py:429. Am 06.09. wurde sie zur Laufzeit überschrieben (im Commit 101 Einträge mit 64 weg, im Arbeitsbaum 37 ohne ein einziges weg, git status = M), und der Wächter enthielt sich mit „Baseline ist leer“. Der Name sagte es die ganze Zeit: ZWISCHENSTAND, nicht Baseline. ⛔ Eine Schuld-Liste, die ein anderes Werkzeug überschreiben kann, ist keine Schuld-Liste, sondern eine Variable — und sie verschwindet genau dann, wenn das andere Werkzeug erfolgreich war. Bewacht von schreiber_von() im Selbsttest des Wächters: diese Datei muss 0 fremde Schreiber haben, und die alte muss welche haben (Positivkontrolle am selben Werkzeug). | ✅ | ZEIGER#01 (06.09.2026, Register #1680). ⚠️ Fortschreibung ist eine offene Frage und ausdrücklich nicht mitentschieden: nach JCs geglückter Rettung sind 5 Zeiger repariert und 20 Belege als source_lost verworfen — wer die Baseline danach kürzt und mit welcher Begründung, steht noch aus. ⛔ Sie darf nur schrumpfen, wenn wiederhergestellt oder bewusst verworfen wurde, nie weil sie im Weg war. |
check_konsolensicher_verhalten.py | R-155, ZWEITE ACHSE — laeuft das Werkzeug auf cp1252 zu ENDE? check_konsolensicher fragt steht ein gefaehrliches Zeichen an einer Stelle, die ich als Ausgabe erkenne; dieser startet jedes Werkzeug mit erzwungenem cp1252:strict und fragt kommt die Ausgabe an. Er ERSETZT den Muster-Pruefer nicht — dessen eigener Kopf schreibt genau das vor. ENTHALTUNG: ein Lauf prueft den WEG, den er nimmt, nicht alle Wege (darum zusaetzlich --selftest, wo es einen gibt). | ✅ | ABLAGE#02 (Register #1324/#1350), am eigenen Werkzeug: mein neuer Waechter starb auf cp1252 im ROT-Zweig, also genau beim Melden eines echten Befundes — der Muster-Pruefer fand die zwei print()-Zeilen, nicht aber dieselben Symbole in DICT-Konstanten. ⭐⭐ Diese Grenze stand seit dem 31.08. als Enthaltungs-Block in seinem Kopf, von mir selbst eingetragen, und wir haben uns in derselben Nacht zweimal auf sein Gruen verlassen: eine benannte Grenze ist keine Absicherung — ein Enthaltungs-Block schuetzt den, der ihn liest; gruen liest jeder. GEMESSEN: 158 Laeufe, 263s, 0 Tote, 58 ausdruecklich nicht gemessen. ⛔ Bauer (build_) werden NICHT gestartet — der erste Lauf hat zwei erzeugte HTML-Dateien neu gebaut (3088 Zeilen), ein Pruefer FRAGT, ein Bauer ERZEUGT*. |
check_ablage02_suiten.py | FAEHRT die Skript-Suiten aus 07_TESTS/ablage02 — und ZAEHLT ihre Faelle nach. Er prueft drei Dinge, die einzeln je eine falsche Entlastung erzeugen: ① laeuft jede Suite gruen durch ② stimmt ihre FALLZAHL mit der eingetragenen Erwartung (eine Suite, die auf 0 von 0 zusammenschrumpft, endet mit 0 und saehe sonst gesund aus) ③ ist jede Testdatei des Verzeichnisses ueberhaupt eingetragen — sonst liefe die naechste wieder nirgends. ENTHALTUNG, und sie ist der Kern: er sagt NICHTS ueber die pytest-Suiten desselben Verzeichnisses. Das Verzeichnis hat ZWEI Bauarten — eine pytest-Suite ist als Skript stumm und endet mit 0, eine Skript-Suite liefert pytest null Faelle. Deshalb zwei Laeufe im selben Job; einzeln saehe jede gruen aus. | ✅ | ABLAGE#02 (Register #1496, 03.09.), aus einem Befund, den ich nicht gesucht habe: 07_TESTS/ablage02 hatte 20 Testdateien und stand in KEINEM Workflow — jeder pytest-Aufruf im Haus ist namentlich gebunden, dieses Verzeichnis war in keinem. ⭐ Der Beweis kam sofort: test_dateiart_kommt_aus_dem_kanon.py war ROT und niemand wusste es; er hielt eine Router-Stufe fest (csv_triage), die meine eigene spaetere Reparatur (#1410) ueberholt hatte. ⛔⛔ Ein roter Test, den niemand faehrt, ist von einem gruenen nicht zu unterscheiden — hier ist die Meldung nicht falsch, sondern ABWESEND, und Abwesenheit liest sich als Zustimmung. ⛔ Die Lehre stand zweimal in genau der Datei, in die ich haette sehen muessen (ERNTE#01 #828, SICHERHEIT#04 #831, beide 29.08.) — eine Narbe schuetzt nur den, der sie VORHER liest. GEMESSEN: 147 Faelle in 19 Skript-Suiten, 0 rot; dazu 66 unter pytest. ⛔ Der Schwester-Sammler check_agentchat01_suiten.py ADDIERT seine Erwartung nur und prueft sie nicht — als Ball an TOWER#53 vergeben, nicht hier geflickt: seine Suiten gehoeren fremden Bahnen. |
check_frist_beim_einspielen.py | RATSCHE — wer setzt doc_type, ohne die Aufbewahrungsfrist mitzusetzen? Er fragt den SYNTAXBAUM (ast), nicht den Text: ein grep faellt auf jeden Kommentar herein, in dem das Wort vorkommt. Gefunden werden beide Setz-Formen — Dict-Schluessel und Zuweisung. ENTHALTUNG, ausdruecklich: er sieht nur LITERALE. Ein Setzer, der den Schluessel dynamisch baut, faellt durch — und das steht in seinem Kopf, statt dass sein Gruen es verschweigt. REICHWEITE (R-193) wird MITGEFUEHRT: er nennt die drei Regale und die Suchart in jeder Ausgabe, nicht nur eine Zahl. | ✅ | ABLAGE#02 (Register #1502, 03.09.), aus JCs Auftrag damit es gleich beim Dokument-Einspielen mit vergeben wird. ⛔⛔ Der Waechter ist die Lieferung, nicht der Anbau (TOWER#53-Entscheid): ein Anbau schliesst EINE Tuer und laesst die anderen stumm — dieselbe Klasse wie vier Tueren, ein Grund. ⭐ GEMESSEN: es sind ZWOLF Setzer, nicht vier — acht Skills plus app.py, dispatch.py, mail_intake.py, orch_apply.py. Der Anbau an einer Stelle haette 1 von 12 geheilt und ausgesehen wie eine Loesung. ⛔ Als RATSCHE gebaut, nicht als sofortiges Rot: die zwoelf stehen namentlich als BEKANNTE SCHULD, der DREIZEHNTE macht ihn rot. Ein Waechter, der master ab heute dauerhaft rot faerbt, wird binnen einer Woche abgeschaltet. ⛔ Und der Selbsttest ist Pflicht-Schritt in der CI, weil mir beim Bau genau das passiert ist, wovor er schuetzt: mein erster --selftest fiel still in den Normallauf und meldete Exit 0. Ein Selbsttest, der den Echtlauf fuehrt, ist eine Luege mit gruener Farbe. |
check_entitaet_hat_steckbrief.py | Welche Entität hat einen Steckbrief — und welche hat einen unter ANDEREM Namen? Er trennt DREI Mengen, und der Unterschied ist der ganze Punkt: PASST (gleicher Schlüssel) · OHNE (das ist Arbeit, keine Täuschung) · UNTER ANDEREM NAMEN (das ist der Befund). ⭐ Ein Namens-Kandidat wird als GERATEN ausgewiesen, nie als Urteil — gabriel → gabriel-binetti ist wahrscheinlich und nicht bewiesen. ENTHALTUNG: er prüft NICHT, ob der Steckbrief inhaltlich stimmt (ein leerer gilt als vorhanden), und NICHT md_companies / ablage_kanon.yaml — die führen eigene Listen, ein Bruch dort fällt hier durch. Als RATSCHE gebaut: der bekannte Bruch steht namentlich, der nächste macht ihn rot. | ✅ | ABLAGE#02 (Register #1534, 04.09.), aus der eigenen Arbeit: documents.entity und entities.slug sagen gabriel, entity_signatures.entity sagt gabriel-binetti, ohne Alias. ⇒ Jeder Leser, der über den Schlüssel verbindet, bekommt gibt es nicht statt heisst anders — für JCs 58 Gabriel-Belege war der Steckbrief unerreichbar, und dabei ist er da. ⭐⭐ Das ist die gefährlichste Auskunft überhaupt: plausibel, falsch, und sie führt zur teuersten Handlung — etwas neu bauen, das existiert. In derselben Nacht zweimal passiert: TOWER#53 fragte entities ab, sah zehn Spalten und meldete, der Steckbrief fehle vollständig. Er fehlte nicht. Er hing an einem anderen Schlüssel. GEMESSEN: 6 Entitäten — 4 passen, 1 ohne Steckbrief (anna-leonenko), 1 unter anderem Namen. |
check_kein_vierter_pfadbauer.py | RATSCHE — wer baut einen Ablagepfad im Code, statt ordne() zu rufen? Er fragt den SYNTAXBAUM (ast) nach String-Literalen, die mit DOCUMENTS/ · CONTRACTS/ · TIMETRACKS/ beginnen — eine Erwähnung im Fliesstext oder Kommentar löst nichts aus. Anlass: ein VIERTER Bauer in tgv-skill-store-file-to-drive/skill.py:594, gefunden beim Messen der „drei konkurrierenden Pfadbauer" (#1572). ⛔ Seine Enthaltung steht in seiner eigenen Ausgabe, namentlich: _storage.beleg_key setzt den Pfad zusammen (Wurzelwort nur im Docstring, Zeile 830) — der Wächter findet ihn nicht. Grün heisst „kein neuer Bauer DIESER Machart". Basislinie = 2 bekannte Bauer, als Schuld geführt; Selbsttest mit Köder + Gegenprobe; Fahnen-Riegel. | ✅ vollständig (#1572) | ABLAGE#02 · CI netzfrei in agentchat01-suiten.yml (Selbsttest und Echtlauf); die drei Regal-Pfade stehen in BEIDEN Auslöser-Blöcken |
check_dateizeiger_kanon.py | RATSCHE — wer liest einen Dateizeiger, ohne raw_key zu kennen? Kanon: storage_url > raw_key > drive_file_id. Anlass: zwei Bestandszahlen zerbrachen in EINER Nacht an genau dieser Lücke (386 statt 64 · 178 statt 75) — 103 Belege galten als verloren, weil raw_key nicht mitgezählt wurde. Fragt den SYNTAXBAUM (ast), nicht den Text: eine Erwähnung in Kommentar oder Docstring zählt nicht. ⭐ DREI Mengen statt einer Anklageliste: BELEGT (2, mit Fundstelle) · ENTLASTET (2, freigesprochen mit Grund) · UNGEPRÜFT (7, weder schuldig noch sauber — sie machen den Lauf nicht rot). Selbsttest mit Köder und drei Gegenproben; Fahnen-Riegel. ⛔ Grenze in der eigenen Ausgabe: „Grün heisst 'kein neuer Leser DIESER Machart'" — nicht gemessen sind 06_DASHBOARD (JS), n8n und SQL-Sichten, und wer den Zeiger aus einer Variablen zieht. | ✅ vollständig (#1626) | ABLAGE#02 · CI netzfrei in agentchat01-suiten.yml (Selbsttest und Echtlauf); die vier Regal-Pfade stehen in BEIDEN Auslöser-Blöcken |
check_belege_ohne_ablageort.py (no_orphan_dbrow) | Gibt es FREIGEGEBENE Belege, die auf gar keine Datei zeigen? — ein freigegebener Beleg mit Betrag, aber ohne Beweisstück, ist ein Buchungsposten ohne Beleg | ✅ | STORAGE#03 (#492). ⭐⭐ Die Regel stand seit Monaten in 08_OPS/ablage_kanon.yaml und hatte KEINEN Implementierer — geprüft am 21.08. über 08_OPS, 07_TESTS, 02_SKILLS, 09_SKILLS und .github, einziger Treffer war die YAML-Zeile selbst. ⛔ Der Zaun stand nur auf Papier. Er ist genau der, der die vier echten Waisen gefunden hätte: #7134 · #7190 (379,14 €) · #7509 · #7613 — freigegeben, mit Betrag, ohne Datei und ohne intake_text. ⛔ Was er BEWUSST NICHT fragt, und das ist die Hälfte seiner Genauigkeit: rejected und duplicate brauchen keinen Ablageort — wer sie mitzählt, meldet „43 Waisen" statt „10" und wird deshalb weggeklickt (27 der 43 dateilosen Zeilen sind korrekt ohne Ablage). ⭐ Ein Wächter, der Richtiges als Fehler meldet, verbrennt seinen eigenen Kredit. LAUFORT: cron (braucht DB/Netz). ⛔ Diese Zeile ist von TOWER#53 am 04.09. aus dem Dateikopf des Werkzeugs GESCHRIEBEN, nicht von seinem Autor — sie zitiert, was das Werkzeug über sich selbst sagt, und behauptet nichts darüber hinaus. Anlass: das Werkzeug stand seit #1960 ohne Artefakt-Zeile und färbte master rot, nachdem der paths-Auslöser des Ratchets repariert war (#2496) und der Prüfer wieder lief. STORAGE#04 möge sie fortschreiben, wenn sie etwas anderes meint. |
check_beleg_kopf_ratsche.py | Trägt jeder lebende Beleg seinen Kopf — Absender, Absender-Land, Empfänger, Datum — oder ist eine Lücke laut als „steht nicht auf dem Beleg“ vermerkt? Ratsche: der ANTEIL still leerer Belege und die Zahl der Kanalwörter in sender dürfen nur sinken; ein Beleg, der seinen Kopf VERLIERT, ist ROT. Kanon, Belegarten und Belegpflicht kommen aus 08_OPS/beleg_kopf.py (EINE Fassung). | ✅ | BELEGKOPF#01 (#3916), JC 10.10.2026. Grundlinie 08_OPS/beleg_kopf_grundlinie.json (10.10.: 453 Belege, 114 still leer = 25,2 %, 101 Kanalwörter) — eine SCHULD, gesenkt per PR mit --grundlinie-vorschlag. LAUFORT: cron · gerufen von betriebs-waechter.yml ㉗b (Selbsttest + Messung, stempelt, gatet nicht — ob ein Zuwachs den Nacht-Job rot macht, ist Tower-Arbitrage). ⛔ Warum Anteil statt reiner ID-Menge: Neuzugänge kommen täglich, eine Mengen-Ratsche wäre ab dem ersten unvollständigen Neuzugang dauerrot. ⛔ Grün sagt NICHTS über die Richtigkeit eines vorhandenen Werts — die Belegpflicht sitzt im Heilwerkzeug heile_beleg_kopf.py. |
check_beleg_hat_stufe.py | Traegt jeder NEUE Beleg eine Vertraulichkeitsstufe — und setzt jeder INSERT-Weg sie vor dem Anlegen? A: jeder documents-POST in 02_SKILLS · 08_OPS/tgv-skills-service · 08_OPS/chat_engine · 08_OPS/credentials ruft stufe_eingang.stufe_stempeln VOR dem POST (Attrappe = ROT) · B: kein Spalten-DEFAULT auf documents.confidentiality_level · C: Grundlinie steigt nicht gegen origin/master · D (live): neuer Beleg ohne Stufe und ohne Vermerk = ROT, neuer Beleg ohne Vorgabe = ROT, Bestand nur sinkend. | ✅ | VERTRAULICH#04 (#3998), JC 11.10.2026: „haben wir sichergestellt das JEDER dokument eine vertraulichkeitsstuffe jetzt hat ?“ Grundlinie 08_OPS/beleg_stufe_grundlinie.json (11.10.: 339 ohne Stufe bis #8561) — eine SCHULD, gesenkt per PR mit --grundlinie-vorschlag. LAUFORT: cron · gerufen von schema-register-guards.yml (--netzfrei A–C + Selbsttest + 07_TESTS/vertraulich04) und betriebs-waechter.yml ㉗c (D, stempelt). Selbsttest: 21 Proben; am echten Baum: Stempel in intake_raw/mail_intake entfernt ⇒ rc=3. ⛔ R-193: NICHT n8n, NICHT der SQL-Editor, NICHT ob eine gesetzte Stufe fachlich stimmt. |
check_klaerungszahl_monat.py | Wie viele ECHTE Klärungen bleiben dem Menschen je Entität und Monat — heute, und nicht nur an dem Tag, an dem zufällig jemand gemessen hat? | ✅ | AGENTEN#01, aus JCs Maßgabe vom 20./21.08.: „das Ziel ist, dass kaum Sachen zu klären sind“. ⭐⭐ Die Messgröße des Agenten-Ausbaus ist nicht „wie viel wurde gebaut“, sondern wie viele Klärungen übrig bleiben. Die Zahl existierte als Werkzeug (08_OPS/mess_klaerungs_ernte.py, #491) — aber nur als HANDAUFRUF. ⛔ Eine Messgröße, die nur dann entsteht, wenn jemand daran denkt, ist keine Messgröße, sondern eine Anekdote. Dieses Werkzeug macht daraus die Nacht-Zeile im betriebs-waechter. ⭐ Es RUFT die Einordnung aus #491, es kopiert sie nicht — eine zweite Fassung der Beleg-Erwartung wäre eine zweite Wahrheit. LAUFORT: cron. ⛔ Diese Zeile ist von TOWER#53 am 04.09. aus dem Dateikopf GESCHRIEBEN, nicht von ihrem Autor — sie zitiert, was das Werkzeug über sich selbst sagt. Anlass: das Werkzeug färbte master rot, nachdem der paths-Auslöser des Ratchets repariert war (#2496). AGENTEN#01 möge sie fortschreiben. |
check_titel_ohne_stufe.py | Liefert ein Endpunkt einen BELEGTITEL, ohne nach der Stufe zu filtern? Konzept:63 verlangt für streng_vertraulich „Dokument existiert“ statt Titel (30 Belege betroffen). ⛔⛔ Zwei Achsen, weil eine allein die Regel nicht trägt: ① ein dekorierter Endpunkt mit documents-Abfrage UND Titelfeld muss die Stufe in einer Abfrage-Konstante führen — nicht bloss das Wort irgendwo im Rumpf haben (Nähe ist nicht Wirkung) ② der bekannte Fall skills_ablage steht in einer Grundlinie und wird von Achse ② bewacht: sobald /api/skills/ablage einen Aufrufer bekommt, wird der Lauf ROT. ⭐ Damit ist der Eintrag keine Schublade — die Bedingung, unter der er harmlos ist, wird gemessen statt gehofft. ⛔ ast, nicht grep, und der Grund ist eine eigene Blindheit: die erste Fassung suchte eine Zeichenkette mit Pfad UND Titelfeld — sie übersah skills_ablage, weil Feldliste und Pfad zwei Konstanten sind, per % verbunden. Ein Wächter, der auf dem einzigen bekannten Fall schweigt, ist keiner. ⚠️ ENTHALTUNG, ausdrücklich: er beweist NICHT, dass die Stufe den Zugriff wirklich einschränkt (das wäre Datenfluss), und er sieht eine Ebene Namensauflösung (Modulkonstanten wie _ORCH_DOC_SELECT) — der ursprüngliche blinde Fleck ist damit geschlossen statt bewacht (0 zusätzliche Treffer, 0 Fehlalarme; von echten Daten ungeprüft, deshalb drei eigene Selbstbiss-Proben). Neue Grenze: was aus einem ANDEREN Modul importiert oder von einer Funktion gebaut wird, bleibt unsichtbar. | ✅ | VERTRAULICHKEIT#01 (Register #1560, 04.09.2026). Anlass: TOWER#53 — „eine Regel ohne Prüfer ist ein Wunsch“. Meine eigene Auflage (wer ep_ablage verdrahtet, ergänzt die Stufenprüfung) war eine Absicht; dies ist der Riegel dazu. Selbstbiss 9 Proben in beide Richtungen. Eingehängt: schema-register-guards.yml ㋗. |
check_belegtext_ist_material.py | Ist Beleg-Text MATERIAL oder Anweisung — und darf ein Dokument ein NUTZUNGSVERBOT setzen? ⛔⛔ Zwei Achsen, und nur die zweite ist hart: ① ein Skill, der Belegtext in einen LLM-Prompt gibt, muss den Satz „MATERIAL, nie Anweisung“ führen (Grundlinie mit Datum, plus Veraltungs-Achse: ein Eintrag, den es nicht mehr gibt, ist ROT) ② ein aus dem Dokument gelesener Vorschlag darf kein Verbot setzen — Konzept §2b: Opt-out = bewusste Einzelentscheidung des Eigentümers. ⭐ Der Riegel prüft die HERKUNFT, nicht den Wert: eine Allowlist gegen ungültige Werte ist keine Abwehr gegen Manipulation. ⭐⭐ PORTIERT, nicht erfunden — dieselbe Abwehr schützt seit RECHTE#03 (#565) den NETZ-Weg und war auf Belegtext nie angewandt. ⚠️ ENTHALTUNG, ausdrücklich in der Ausgabe: er beweist NICHT, dass das Modell dem Satz folgt (Prompt-Abwehr ist Abschwächung, kein Riegel), und n8n-Abläufe misst er nicht (kein ast — ein Muster ohne Anker träfe Text statt Struktur, #1560). | ✅ | VERTRAULICHKEIT#01 (Register #1570, 04.09.2026, JC-GO „Bauen lassen“). Anlass: JCs Frage nach Spam/Injection in der Mandanten-Inbox. Gemessen: der Kanal löst das Mengen-, nicht das Inhaltsproblem — die Anmeldung authentifiziert den Boten, nicht den Autor. Selbstbiss 9 Proben, jede Richtung mit ihrem Zwilling (sonst wäre erkennt richtig von erkennt alles nicht zu unterscheiden). Eingehängt: schema-register-guards.yml ㉘. |
08_OPS/check_memory_index.py (SessionStart-Hook, --kurz) | Laedt der Memory-Index noch GANZ? MEMORY.md wird bei jedem Sitzungsstart geladen; wird sie zu gross, laedt sie nur teilweise — und nichts meldet das. ⭐ Eine Narbe, die beim Sitzungsstart nicht geladen wird, schuetzt niemanden. Drei Achsen: Bytes · Zeilen ueber 400 Zeichen (der Index ist ein Etikett-Verzeichnis, kein Speicher) · Verweise ins Leere. Zwei Schwellen MIT Herkunft: harte Grenze = unbelegte Zone (die ~24,4-KB-Angabe ist uebernommen und wurde widerlegt — 24558 B luden vollstaendig), Betriebsbudget = ausgewiesene Setzung. | ✅ | GEDAECHTNIS#02, Registerzeile #1732. ⛔ Gerufen im .claude/hooks/session-start-status.sh — und das ist der einzig moegliche Verteiler: MEMORY.md liegt ausserhalb von git, kein CI-Lauf kann sie je sehen. Beissprobe an einer KOPIE (--selbsttest, tempfile, der Bestand wird nie angefasst): zu gross ⇒ ROT · toter Verweis ⇒ ROT · zu lange Zeile ⇒ ROT · Positivkontrolle unauffaellige Datei ⇒ GRUEN · echter Bestand ⇒ GRUEN. |
08_OPS/check_kein_google_link_im_haus.py (#1824, + Einhängung konformitaets-guards.yml) | „Baut TGV noch eine Google-Drive-Adresse — und wird ein HEIMGEHOLTER Beleg mit ihr ausgegeben?" Zwei Achsen: ① Quelltext-Ratsche über 8 Regale (06_DASHBOARD produktiv · Skills · app.py · chat_engine · fes_ap2 · _storage · engine.js): gezählt wird die ERZEUGUNG (Literal in Nicht-Kommentar-Zeile), ERLAUBT sind benannte Rückfall-Schnipsel (Engine-Helfer, datei_zeiger, Shell-Helfer, EIN Renderer-Rückfall, Selbsttest-Fixtures), BASELINE je Datei die Zahl der gestundeten Stellen (11 in 8 Alt-Flächen) — Wachstum rot, Schrumpfen „UMGESTELLT". ② Schwester 08_OPS/check_kein_google_link_im_haus_bestand.py (LAUFORT hand, Netz, nur SELECT): datei_zeiger über alle documents mit drive_file_id — heimgeholte Zeilen dürfen NIE bei Drive enden (08.09.: 538 im Haus+Drive · 64 nur Drive · 0 falsch). Selbsttest 14 Fälle, beide Richtungen, Kanon-Sabotage rot. | ✅ | DRIVE#02. ⛔ Messgrenze (R-193): misst Quelltext + Kanon, nicht den DOM (das ist check_ein_weg_zum_original für die Shell) und nicht Mail-Templates (08_OPS/betrieb, dort kein Literal; daten-getragene Links deckt Achse B von check_flaeche_liest_ueber_resolver). |
check_vat_anreicherung_laeuft.py | Schreibt die Steuer-Anreicherung noch — oder liegen Finanz-Belege länger als N Tage ohne vat_treatment? ⛔ Die Wunde: seit dem 09.06. füllte NIEMAND das Feld, das Quality-Gate prüfte es alle 6 h trotzdem grün, und sichtbar wurde es erst, als JC die USt-Liste brauchte (#1809). ⭐ Ratsche als Differenz, nie als Absolutwert: Bestands-Riegel id>8472 (Stand 08.09., Rest = Nachzug) + Vergleich mit der gestempelten Zahl vom Vortag (offen=N im Stempel vat-anreicherung) — rot nur, wenn offen>0 und nicht fällt. Selftest beide Richtungen, Echtlauf grün, Köder --riegel 8200 rot rc 1. | ✅ | FES-AP4 (#1810), 08.09.2026. Träger betriebs-waechter.yml ㊱ (gatend), Laufort cron — nicht konformitaets-guards.yml (netzfrei). Partner: Poller 08_OPS/betrieb/cron_vat_determine.py (betriebs-crons ②a). PR #2676. |
08_OPS/check_token_existiert.py (#1932, CI (21) konformitaets-guards.yml) | Hat jedes var(--x) im Screen einen Token in tokens.css? Drei von fünf SCREEN#14-Befunden (#1923 --c-border 4×, #1925 --c-muted 3×) waren Tokens, die es nicht gibt — der Kurzsatz fällt auf initial oder zieht eine Rückfall-Farbe außerhalb des Systems, im Quelltext unsichtbar, am DOM falsch. Regale: 06_DASHBOARD/.html (inkl. JS-injizierte Style-Strings), PLAT_06_DESIGN_SYSTEM/.css, DESIGN_01/engine.js; Quelle: tokens.css + Kanon-eigene --*-Deklarationen; Rückfall-Werte sind Befund, kein Freispruch; Reichweite wird gedruckt; Selbsttest beißt bei --c-border, schweigt bei --border. | ✅ | SCREEN#14, gebaut 14.09.2026 (vor Frist Fr 18.09.): Selbsttest 9/9, R-290 8/8, Echtlauf 35 Dateien, 38 Alt-Posten eingefroren, 4 Hooks, Neuzugang rot (Register #1932). |
08_OPS/check_use_case_register.py (#1969, CI ㉟ konformitaets-guards.yml) | Steht jeder Sendeweg im Use-Case-Register, und beisst die Naht? (R-293) Vier Fragen: ① Form jeder Zeile in 08_OPS/betrieb/mail_use_cases.json (Pflichtfelder, Wertebereiche, erkennung-Muster) · ② jedes use_case="…"-Literal unter 08_OPS/, 02_SKILLS/, TGV -App files/ steht im Register · ③ das Dienst-Bundle packt die Datei (sonst fail-closed) · ④ Beissprobe im Trockenlauf: mail_versand.senden() weist einen Fremdling mit apply=True + Freigabe ab, jede Registerzeile läuft als use_case_registriert=True durch. Reichweite gedruckt; nicht geprüft: engine.js, n8n-Cloud, Supabase Auth, 07_TESTS. Selbsttest: 4 Form-Mutationen, Rufer- und Bundle-Achse beide Richtungen. | ✅ | MAIL#02, gebaut 14.09.2026 (Register #1969); Beissprobe je Sendeweg aus MAIL#02 Rang 2. |
08_OPS/check_ki_funke_ist_icon.py (#2147, CI ⑳d konformitaets-guards.yml) | Steht in der Haupt-App ein KI-Hinweis als Emoji oder eigenes Zeichen (✨ 🪄 ✦ ✧) statt als Kanon-Funke ai-spark (K-57, JE-161)? Zwei Achsen: A Quelltext über 12 Dateien (core_shell · kai_renderer · rka_chat · fragekai2 · verlauf2 · i18n ×7), erlaubt nur das Zeichen als Rückfall-Argument eines Icon-Aufrufs; B der Laufzeit-Wächter DRIFT-WAND-EMOJI kennt alle vier Zeichen und durchläuft alle Textknoten der Zone. Auf dem Stand vor KONTAKTE#01 rot (15 Befunde). Selbsttest 15 Fälle, beide Richtungen. | ✅ | vollstaendig · KONTAKTE#01 |
08_OPS/check_composer_flaeche_wird_gemessen.py (#2223, CI ㊹ konformitaets-guards.yml) | Wird die Composer-/Modusleisten-Zusage JEDER Kanon-Fläche von irgendeinem Prüfer angefasst — und sagt eine Fläche OHNE Composer, dass sie keinen hat? Anlass: JC zog die Abnahme „Composer-Linie & Modusleiste“ zurück. Gemessen an allen 29 Kanon-Routen am DOM: 0 Abweichungen — der Befund ist nicht Drift, sondern Reichweite: DRIFT-KANON-BAUSTEINE läuft nur auf den 12 Routen in AUDIT_SCREENS, 17 von 29 sind unbesucht. Drei Prüfungen: ① Deckung des Selbst-Gates (Ratsche auf den 17 namentlich geführten) · ② Ratsche auf 25 Composer-/24 Modusleisten-Zusagen · ③ keine stille composer:0-Lücke. Netzfrei; --dom führt zusätzlich die Tabelle am gerenderten Bild. | ✅ | COMPOSER-QA#01, gebaut 20.09.2026 (Register #2223); 10/10 Selbsttests, beissen in BEIDE Richtungen. |
08_OPS/mess_composer_funktionen.py (#2250, Hand-Werkzeug — bewusst NICHT in der CI) | TUT der Composer, was er verspricht — je Fläche, am gerenderten Bild? Anlass: JC 20.09. „prüfe bitte unbedingt die FUNKTIONEN des Composers … diktieren, Anhang 1 oder mehrere Dateien mit einem Hinweis zur Zuordnung“. Anwesenheit ist keine Funktion — der Nachbar check_composer_flaeche_wird_gemessen.py fragt, ob der Composer DA ist. Dieses Werkzeug wirft drei Dateien hinein, klickt das Mikrofon bei verweigerter Erlaubnis und zählt, was danach dasteht — 17 Funktionen, abgeleitet aus engine.js composerHtml, mountScreenComposer und den Regeln DRIFT-CMP-CAP/DRIFT-CMP-DROP. Drei Zustände, nicht zwei: GEHT · BEFUND · UNGEMESSEN-mit-Grund. | ✅ | COMPOSER-QA#01, gebaut 20.09.2026 (Register #2250); 17/17 Selbsttests. Braucht Playwright + 08_OPS/qa_harness_server.py — deshalb Hand, nicht CI. |
08_OPS/check_menuepunkt_ohne_flaeche.py (#2236, CI ㉻ konformitaets-guards.yml) | Führt jeder Menüpunkt auf eine echte Fläche — oder ist er als Platzhalter deklariert? Anlass: JCs Befund 20.09. — „du zeigst mir Sterne im Menü, aber es sind Screens, wo NIX drin ist“. ⛔ Der Waechter bewacht die stille Hälfte: ein dritter Zustand — weder echte Fläche noch deklarierter Platzhalter — ist genau der, aus dem der Befund entstand. Netzfrei, liest core_shell.html. | ✅ | RECHTE-UX#01, Register #2236. Zeile nachgetragen von TOWER#57 in Naht 28 (#2274), weil der Artefakt-Ratschen-Waechter sie beim Zusammenlauf vermisst hat. |
08_OPS/check_eine_fusszeile.py (#2420, CI ⑼-B konformitaets-guards.yml) | Steht auf jedem Schirm GENAU EINE Fusszeile — und traegt sie die Fassung des DIENSTES? Anlass: JC meldete am 21.09. zwei Fusszeilen („EU-Server … v2.5“ UND „TGV CORE v2.5 · staging“) und schrieb dazu „War bereits gemeldet!“. Gemeldet war es als #397 — und #397 wurde gebaut und steht noch. ⭐ Es war unvollstaendige Reichweite: #397 legte die EU-Zeile unter ihren drei EIGENEN Kopien zusammen und fragte nie, ob auf demselben Schirm ein ZWEITER Traeger steht. Zwei Achsen, beide laufen immer: A Quelltext (netzfrei — ein Erzeuger, Einmal-Griff auf beiden Render-Pfaden, kein zweiter Handbau), B DOM ueber den Haus-Messstand (zaehlt sichtbare Fusszeilen je Route; ohne qa-Session ausdrueckliches kein_urteil, nie gruen). Beissprobe an der echten Stelle: gegen origin/master@557568b27 3 Befunde, gegen HEAD gruen. | ✅ | KLAERUNG#02, Register #2420. |
08_OPS/check_anwesenheit_faellt_geschlossen.py (#2421, CI ⑪-C konformitaets-guards.yml) | Faellt eine Anwesenheits-Frage auf der unsicheren Seite zum NACHLADEN — oder zum BEHAUPTEN? Anlass: JC 21.09. live — „Unter Belegart und Empfänger stehen keine WERTE, obwohl diese von TGV BEKANNT SIND!!!!“. Wurzel war _KERN_FELDER.some(f => f in p): der Klickweg gibt provider aus Freundlichkeit mit (für den Titel) und schaltete damit den Nachlader ab. Gezählt: 7 von 16 Klickwegen betroffen, 8 reine Referenzen waren nie kaputt. Vier Achsen, alle laufen immer: A Operator fällt geschlossen · B keine Teilmenge erfüllt die Frage und kein Literal baut sich die Zeile selbst · C p.id-Falle und Etiketten-Verwechslung (doc_label als provider) · D DOM-Durchreich-Test gegen die DB-Zeile (ohne qa-Session ausdrückliches kein_urteil, nie grün). Beissprobe an der echten Stelle: gegen origin/master@557568b27 7 Befunde mit Zeilennummer, gegen HEAD grün. | ✅ | KLAERUNG#02, Register #2421. |
08_OPS/check_freigabe_sagt_was_sie_tut.py (#2422, CI ⑪-D konformitaets-guards.yml) | Benennt der Freigabe-Knopf, was er SCHREIBT? Anlass: JC 21.09. — „Ich weiss als User wieder nicht, was ich hier als Vorschlag annehme.“ Von drei Seiten gemessen: doAccept, die Chat-Karte und confirm_doc schreiben genau vier Felder (status, processing_state, review_required, approved_date) — am Inhalt ändert der Knopf nichts, und er hiess „Vorschlag annehmen“ über einem Kasten, der eine Belegart-Änderung vorschlug. Drei Achsen: A der Schreibweg trägt keine Inhaltsfelder · B die Karte nennt die Wirkung feldweise (auch was UNVERÄNDERT bleibt) · C das Etikett verspricht keine Übernahme und kommt nicht aus dem Envelope (wer den Schreibweg besitzt, besitzt das Etikett). Beissprobe: gegen origin/master@557568b27 4 Befunde, gegen HEAD grün. | ✅ | KLAERUNG#02, Register #2422. |
08_OPS/check_composer_vorschlag_trennt_die_stufen.py (3114, CI ㊽ konformitaets-guards.yml) | Bleiben Vorschlag, Freigabe und Vollzug DREI Schritte, wenn KAI spaeter Seiten entwirft? Anlass: Codex-Sammeluebergabe 25.09., Vorhaben AGENT-ARCHITEKT#01 / Screen-Builder — der Agent darf nie selbst aendern, mergen oder deployen. ⭐ Der Waechter misst nicht eine Absicht, sondern die Mechanik am Griff: ① Default-Deny in descriptor.is_action_tool ② kein Executor fuer action-Werkzeuge in tools.build_tool_defs ③ kein Vollzug ohne VOLLZUG_REGISTER-Eintrag ④ ein screen_composer.*-Werkzeug traegt sein eigenes Schema, propose gehoert in tool_modes.read, preview/uebergabe NIE. ⛔ Frage ④ hat heute keinen Angriffspunkt (kein solches Werkzeug deklariert) und wird als NOTIZ gemeldet, nicht als Gruen — Abwesenheit sieht sonst aus wie Bestaetigung. Netzfrei; liest staged/chat_instances.json und das Paket 08_OPS/chat_engine. ⛔ Die super_admin-Wand der Vorschau ist nicht gemessen (Dienst-Achse). | ✅ | ARCHITEKT#01, Register 3114, gebaut 25.09.2026; Selbsttest 8/8, darunter zwei Mutationen am gemessenen Code (M6 hebelt is_action_tool aus, M7 haengt einem action-Werkzeug einen Executor an) und die Negativkontrolle M8. |
08_OPS/check_core_graph_naht_haelt.py (3150, CI Ⓓ konformitaets-guards.yml) | Halten die vier Naehte des Core-Graph-Screens, die NIE von selbst rot werden? Anlass: /plat/system ist als erstes natives Modul des Hauses gebaut (Stufe 3 des Modulvertrags; TGV_MODULES.define hatte vorher 0 Treffer). Vier Invarianten teilen die boesartige Eigenschaft, dass ihr Bruch den Screen nicht kaputt macht — er sieht danach richtig aus und tut heimlich anderes: ① ["system", …] rutscht hinter ["simple", …] ⇒ der generische Platzhalter kommt zurueck (genau die Falle, die /eingang/klaerung und /stamm/entitaeten schon getroffen hat) ② define("system") rutscht vor die adaptLegacy-Schleife ⇒ das Modul ist still wieder Legacy, ohne onScopeChange und ohne eigenes Aufraeumen ③ ein Schreibpfad (skillFetch, service_role ⇒ RLS umgangen) im Block ⇒ JCs Grenze „nur zeigen“ gebrochen ④ system:"system" fehlt in contextForRoute ⇒ kein Composer (INBOX#01-Narbe #1268: „ein Eintrag genuegt“ war an der Quelle geprueft und am Bild falsch — es sind zwei Tabellen). ⭐ Auswahlregel: nicht „was koennte brechen“, sondern was bricht, ohne rot zu werden — alles andere faengt schon jemand. Netzfrei, liest nur 06_DASHBOARD/core_shell.html. ⛔ NICHT gemessen: Bild, Rechte, Daten, Browser — das sind die G-Tests (G01–G16) bei Paket F. Und Pruefung ③ beweist „kein Schreibpfad im Block“, nicht „der Screen schreibt nichts“; dafuer ist check_schreibt_beim_ansehen da. | ✅ | COREGRAPH-D#01, Register 3150, gebaut 26.09.2026; Beissprobe: jede Pruefung an ihrer eigenen Mutation, mit Positiv- und Negativkontrolle (4 davon am Fahnen-Riegel, in beide Richtungen: Tippfehler MIT Namen · nacktes Stellungsargument · die GÜLTIGE Fahne ist KEIN Fehlalarm · main() gibt rc=2). ⭐ Die Beissprobe hat beim Bau zwei echte Fehler im Pruefer selbst gefangen: die Marke find('["system",') traf core_shell.html:3950 (den PLAT-Array-Eintrag) statt der Route und meldete dauerhaft falsches Gruen — genau die Klasse, gegen die der Pruefer gebaut ist, in ihm selbst; und die BLIND-Meldung starb an cp1252, also am eigenen Fehlertext. |
08_OPS/check_kontakt_unsichtbar_im_scope.py (#2234/#2243, CI agentchat01-suiten.yml) | Trägt jeder aktive Kontakt mindestens eine Rollenzeile — und zeigt jede Rollenzeile auf einen Kanon-Slug? Zwei Achsen, weil beide dieselbe Wirkung haben und verschiedene Ursachen: ein Kontakt ohne Rollenzeile ist nur unter „alle“ sichtbar, eine Rollenzeile auf eine unbekannte Entität wird vom Scope nie getroffen. ⛔ Anlass: JCs C3-Kontaktliste war leer, und die Fläche sagte kein Wort darüber. | ✅ | KONTAKTE#01, Register #2234/#2243. Zeile nachgetragen von TOWER#57 in Naht 28 (#2274); dabei zugleich vier Meldezeilen konsolensicher gemacht und der reconfigure-Vorspann ergänzt — der Waechter hätte auf JCs Konsole beim Melden abgebrochen. |
check_soll_hat_ausweis.py (Workflow konformitaets-guards.yml, mit --selftest) | Traegt jedes offene soll:-Feld im Screen-Kanon einen EIGNER? Der Nachbar von check_befund_hat_fortsetzung, eine Ebene weiter aussen: jener fragt es fuer das REGISTER, dieser fuer den Bestand DAVOR. ⛔⛔ Anlass (PRUEFER#01, 20.09.): 15 offene Soll-Felder, die aeltesten seit 54 Tagen, sieben ohne jeden Eigner; drei Flaechen warten auf denselben Baustein K-05, den fuenf Registerzeilen NENNEN und keine BEAUFTRAGT. ⭐⭐ Der Kern ist die dritte Sorte, die jede automatische Suche uebersieht: eine Quittung liest sich wie eine Zuteilung — /einstellungen traegt „K-16 nachziehen — hero+K-11 seit #766 gebaut“, und die Nummer belegt den FERTIGEN Teil. ⛔ Deshalb gilt hier eine blosse Registernummer nicht als Eigner, nur eine Lane-Kennung oder ein Ausweis mit Frist. ⭐ Kein neuer Mechanismus: er benutzt fremdbefund.py (Eigner · Zustellbeleg · Frist) — die Ratsche existiert seit dem 21.08., sie war nur an dieses Regal nicht angeschlossen. Selbsttest 11 Faelle, darunter drei Gegenproben: ein ZITAT ist kein Eintrag, der RENDERER ist keiner, ein leeres soll: ist kein offener Punkt. | 🔨 | BUERO-TOWER#01 (Bau 20.09., Zaehlung im Auftrag TOWER#57) · Workflow-Anschluss offen, Zeile im Kopf als LAUFORT-TRAEGER benannt · 8 von 15 stehen beim Bau auf ROT und warten auf Ausweise |
08_OPS/check_modul_kanon.py (#2392, CI ㊽ konformitaets-guards.yml) | Kennt 08_OPS/modul_kanon.json jeden module_key, der im Haus BENUTZT wird? Anlass: JC 20.09. „module_key vereinheitlichen: JA unbedingt“. Gemessen waren VIER Schreibweisen desselben Schlüssels — Seed Mig 219 (rka/costs/documents, der einzige mit echten Zeilen) · RECHTE_01 §5 · Katalog M0–M8 · tgv_werkzeug_landkarte.py (PR #2928). Der Kanon gibt jedem Schlüssel eine Bedeutung (RECHTE_01:272: „es fehlt die BEDEUTUNG“), der Wächter hält sie. Zwei Extraktor-Achsen (Python/JSON-Deklaration und SQL-Seed), Reichweite und Muster werden gedruckt. ⛔ NICHT geprüft: die Datenbank (ein von Hand gesetzter Schlüssel ist unsichtbar) und .md (nennen ist nicht benutzen). Fehlt die Landkarte, meldet er UEBERSPRUNGEN statt still grün. Selbsttest 4/4: beide Achsen einzeln, plus Gegenprobe, dass echte Schlüssel grün bleiben. | ✅ | MODUL#01, gebaut 21.09.2026 (Register #2392); Leser am Endpunkt ep_mandant_detail + core_shell.html, Bündel-Zeile im Dienst-Deploy. |
08_OPS/check_403_sagt_warum.py (#3155, RECHTE-UX#02) | Bezieht jeder schreibende 403-Zweig seinen Satz aus dem Kanon? — statt eines handgeschriebenen "Nicht sichtbar fuer deine Rolle". Setzt data-data-mode="verboten" mit, damit eine Maschine eine BERECHTIGUNG nicht als DEFEKT liest (Narbe #1201). ⛔ REICHWEITE (R-193): geprueft ist die QUELLE des Satzes im Code. NICHT geprueft: ob der Satz beim Nutzer ANKOMMT — das misst nur ein Lauf von uxaudit_runner.py am gerenderten Text. | ✅ | RECHTE-UX#02, Bahn TOT — uebernommen von TOWER#60 in der Shell-Naht. ⛔⛔ ZWEI BEFUNDE, DIE NUR DIE NAHT FAND, BEIDE FUER DIE CI UNSICHTBAR: :202 druckte ein U+26D4 ⇒ UnicodeEncodeError auf cp1252, im schlimmsten Fall genau beim MELDEN eines echten Befundes; und es druckt geprueftes Material (Pfade, Fundstellen) ohne reconfigure-Vorspann, was die Literal-Achse per Bauart nicht sieht. ⭐ Der Runner spricht UTF-8 — "CI gruen" hiess hier nur "gruen auf dem Runner". Beides repariert (Signalwort statt Symbol, Vorspann nach Bauart check_register_nummer.py), check_konsolensicher danach rc=0, das Werkzeug selbst rc=0. |
08_OPS/check_workflow_bedingung_ist_positivliste.py (#3126, CI laufort-traeger-guard.yml) | Sagt eine if:-Bedingung, WAS sie meint — oder nur, was sie NICHT meint? github.event_name != 'push' heisst „alles ausser push“ und beschreibt damit nur den Bestand von gestern; ein neuer Ausloeser faellt still hinein. Der Waechter findet jede github.event_name !=-Vergleichung in .github/workflows/.yml und nennt dazu, welche der deklarierten Ausloeser ungenannt hineinfallen. ⛔⛔ Anlass, und er ist selbst der Befund: die Lehre stand seit dem 19.08.2026 als Kommentar in familie-guard.yml:200 (PLAT#07), samt dem Satz „(Register #472 vorgeschlagen: dieselbe Klasse ueberall suchen.)“ — gesucht wurde nie. 38 Tage spaeter sass die Klasse noch an drei Stellen (schema-register-guards.yml:296/299, claude-agent.yml:87), darunter im teuersten Workflow des Hauses, wo sie jeden PR rot machte. ⭐⭐ Eine Lehre, die als Kommentar an ihrem Fundort steht, schuetzt genau die Datei, in der schon jemand hingesehen hat — also die einzige, die den Schutz nicht mehr braucht. Nur ein Waechter reist. ⛔ Reichweite (R-193): sieht nicht getarnte Negativlisten (!contains(...)) · andere Achsen (github.actor !=, github.ref !=) · fremde Composite-Actions · und nicht, ob die Positivliste danach die richtigen* Ereignisse nennt — er verhindert die stille Ausweitung, nicht den Denkfehler. | ✅ | KOSTEN#02. LAUFORT: ci, netzfrei. Eingehaengt als Schritt in laufort-traeger-guard.yml, weil der bereits auf .github/workflows/ ausloest — ein eigener Workflow haette bei 46 Laeufen +46 Minuten am Tag gekostet (GitHub rundet je JOB auf die volle Minute auf), als Schritt sind es ~1,5 min/Tag. Faktor 30 bei gleicher Reichweite. Selbsttest: 7 Zusicherungen, 1 Positiv- und 3 Negativkontrollen (geheilte Form · Kommentarzeile · leerer Ordner). Beissprobe am echten Baum: rot mit 3 Fundstellen vorher, gruen nachher.** |
.github/workflows/mess-checkout-kosten.yml (#3216, KOSTEN#03, workflow_dispatch-only) | Was kostet der checkout wirklich — und traegt filter: blob:none netto? Misst Vorher/Nachher in EINEM Lauf (zwei Jobs, dieselbe Runner-Generation), ruft dazu 08_OPS/mess_checkout_probe.sh zweimal statt zweier Abschriften. ⛔ Er prueft nicht nur den ABRUF, sondern die GRIFFE, die das Haus wirklich benutzt — merge-base, git log --all (C1 liest --all, nicht origin/master), origin/master...HEAD — UND liest echte Blobs aus der Historie, weil blob:none Bytes VERSCHIEBT statt sie zu loeschen: wer nur den Abruf misst, verkauft eine Verschiebung als Ersparnis. Faellt laut (set -euo pipefail), bricht bei flachem Repo und bei unplausibel wenigen Commits ab. ⭐ workflow_dispatch-only: kostet NULL, solange ihn niemand startet — und er ist ueberhaupt nur vom Vorgabezweig startbar, weshalb er ueber die Naht auf master musste. | ✅ vollstaendig — Selbsttest netzfrei gruen; die Runner-Zahl steht noch aus (erster Lauf nach dem Merge, er ist workflow_dispatch-only). | KOSTEN#03 |
08_OPS/check_lernregel_k_stimmt.py (#3255, TELEMETRIE#01, Traeger betriebs-waechter.yml ㈝) | Stimmt die Zahl, die eine Lernregel ueber ihre beitragenden MANDANTEN behauptet — gegen die Belege nachgerechnet? Mig 285 §3 setzt JCs Entscheid 5 (27.09.2026) als CHECK in die Datenbank. ⛔⛔ Ein CHECK sieht nur seine EIGENE ZEILE: er erzwingt, dass beitragende_mandanten >= 5 behauptet wird — nicht, dass es wahr ist. ⭐ Eine Bedingung, die eine Selbstauskunft prueft, prueft die Selbstauskunft, nicht den Sachverhalt — ohne diesen Waechter ist das Gate eine Ehrenerklaerung. ⛔ Drei Klassen, und BEIDE Richtungen sind rot (Auflage TOWER#60): K1 zu hoch (die k-Schranke ist unterlaufen — der Schadensfall) · K2 zu niedrig (Buchhaltungsfehler, sperrt eine belegte Regel aus) · K3 Schwelle am tatsaechlichen Bestand. ⭐ Nur K1 faengt den Betrug, nur K2 den Fehler. ⛔⛔ Der Zubringer war der eigentliche Befund: zwischen pattern_rules und learning_events gab es keine Verbindung (gemessen 27.09. ueber fuenf Regale, darunter alle eingehenden Fremdschluessel: 0) — die Zahl war unpruefbar. Mig 285 §3b legt pattern_rule_belege nach, mit mandant_id am Beleg selbst, weil 84 von 117 Lern-Ereignissen schon heute aelter als JCs 90-Tage-Retention sind. ⚠️ ENTHALTUNG, ausdruecklich: solange Mig 285 nicht angewandt ist (JCs Hand), meldet er Exit 2 mit Begruendung statt gruen — ein Pruefer, der gruen meldet, weil es nichts zu pruefen gibt, ist die Luege, gegen die er gebaut wurde. Er urteilt ueber die Stimmigkeit der Zahl, nicht darueber, ob die Regel inhaltlich richtig ist. | 🔨 | TELEMETRIE#01 (#3255, 27.09.2026), Auftrag TOWER#60 / JC-Entscheid 3+5. Selbsttest 11 Faelle + 2 Kontrollen, netzfrei an eigenen Faellen (eine Positivkontrolle an lebenden Daten stirbt mit ihnen) — darunter „fuenf Belege EINES Mandanten ist k=1, nicht k=5“, also RAG#01s Defekt als Testfall. Mutationsprobe vier Richtungen, alle rot, u.a. set→list (der Defekt eingebaut). Im Tor mit == '1', nicht != '0' — er enthaelt sich jede Nacht, bis das Apply laeuft; mit != '0' waere der Nacht-Job dauerhaft rot (TOWER#57-Klasse, 116/116). LAUFORT-TRAEGER positiv geprueft: klassifiziere() ⇒ G_traegt_wirklich. PR #3136. |
08_OPS/check_abbruch_minuten.py (#3264, KOSTEN#02, Selbsttest-Traeger betriebs-crons.yml) | Wer bezahlt abgebrochene Laeufe, und wofuer? Ein abgebrochener Lauf wird VOLL bezahlt und liefert KEIN Urteil — am 27.09.2026 waren das 110 Laeufe / 495 min = 9,4 % der Tagesrechnung, und niemand konnte sagen, wessen Minuten das waren. Der Waechter gibt der Groesse eine ADRESSE: je Ablauf, je Zweig, mit Kopf-SHAs. ⛔⛔ ER SPART KEINE EINZIGE MINUTE, und das steht in seinem eigenen Kopf: wenn GitHub abbricht, ist die Zeit verbrannt — gemessen Median 235 s Laufzeit vor dem Tod. Eine Arbeitsgewohnheit ohne Adresse ist eine Ermahnung; mit Adresse ist sie ein Befund. ⭐⭐ Und er nennt den groesseren Hebel selbst mit, damit ihn niemand fuer die Loesung haelt: 232 der 495 abgebrochenen Minuten (47 %) sind schema-register-guards, dessen Abbrueche nach 396 s starben — davon 123 s allein checkout mit fetch-depth: 0. Abbruch-Kosten und Einrichtungs-Kosten sind eine Baustelle, nicht zwei. Ratsche auf den ANTEIL, nicht auf die Summe (die Tagessumme schwankt um Faktor 214, der Anteil nicht); Grundlinie 26 %, knapp ueber dem Wochen-Hoechstwert 25,2 %, mit Messdatum — eine Schwelle ohne Stichtag altert unsichtbar. | 🔨 | KOSTEN#02 (#3266, aus TOWER#61s Auftrag ③). ⛔ Status 🔨 und nicht ✅ — die Verdrahtung ist HALB, und zwar bewusst: in der CI laeuft nur --selftest (netzfrei, 8/8, beisst in beide Richtungen: 9,4 % gruen · 26,0 % gruen · 26,1 % rot · Tagesrechnung 0 ⇒ rc=2). Das URTEIL braucht die Tagesrechnung, und die misst mess_ci_minuten.py mit rund 1400 gh api-Aufrufen — ein Werkzeug, das die Groesse misst, die es selbst erzeugt, gehoert in keinen Ablauf, der achtmal am Tag faehrt. ⛔ Warum NICHT betriebs-waechter als Traeger, obwohl er der richtige Ort waere: er ist seit mindestens 21.09. jeden Tag rot (7 von 7 gemessen am 28.09.) — dort anzuhaengen hiesse, dass ein Waechter mehr schweigt, nicht dass einer mehr meldet. Der Echtlauf ist bis dahin Handarbeit: --tag (nur Abbrueche, ~110 Aufrufe) bzw. --befund <abgelegte Grundlinie> (netzfrei). ⭐ Gegengeprueft an echten Daten: --befund 08_OPS/ci_grundlinie_2026-09-27.json ⇒ 9,4 %, deckungsgleich mit der unabhaengigen Zaehlung ueber die Jobs-API (beide 495 min). |
08_OPS/check_bahn_faesst_sammeldateien_nicht_an.py (#1813/#3288, CI 51 konformitaets-guards.yml) | Fasst diese Bahn eine der SAMMELDATEIEN an — also eine Datei, an der jede andere Bahn auch haengt? Gemessen am 07.09.2026 servertreu gegen jeden offenen Vorgang: 8 von 10 standen auf CONFLICTING — 7 an ANFORDERUNGS_REGISTER_TIER_A.md, 8 an den erzeugten Spiegeln, keine an einer Zeile Quelltext. ⛔ Und der Schaden ist nicht die Reibung, sondern die PRUEFUNG: ein konfliktbehafteter Vorgang bekommt kein refs/pull/N/merge und damit ueberhaupt keinen Lauf — von aussen sieht das aus wie „läuft noch“. Liest nur git (merge-base + drei diff-Achsen, auch ungetrackte und gestagte Aenderungen), schreibt nichts. Beschafft sich in der CI den Abzweigpunkt selbst, weil das flache Auschecken kein origin/master hat — ohne das waere er rot aus dem falschen Grund. Die Liste traegt seit dem 28.09. sechs Dateien: 01_ITERATIONS/ROADMAP.md kam dazu, nachdem TOWER#62 sie in 7 von 7 Vorgaengen des Dienst-Fensters als Konfliktdatei gemessen hatte (Register-MD nur in 1 von 7). | ✅ vollstaendig — Selbsttest gruen (Liste vollstaendig, alle 6 Pfade existieren, Trefferrechnung schlaegt an UND schweigt in der Gegenrichtung); Beissprobe an einer echten Aenderung gefahren, die Meldung nennt den Rueckweg. | TOWER#53 → REGISTER#02 |
.github/workflows/register-nachzug.yml (#1813/#3288, push: master auf register/**) | Die Sammeldateien entstehen auf MASTER, nicht in der Bahn — damit nur EIN Schreiber sie anfasst. Die Bahn legt eine Scherbe in register/; dieser Lauf baut daraus die Register-MD, die vier Spiegel und ROADMAP.md und committet sie. ⭐ Nicht die Versionierung war das Problem, sondern die Zahl der SCHREIBER — und merge=ours half nie, weil github.com keine eigenen Merge-Treiber ausfuehrt (belegt an #2646: lokal sauber, auf GitHub CONFLICTING). Faehrt die SECHS Generatoren in der Reihenfolge aus check_generatoren_gelaufen.py — die zwei spaeteren (build_konzept_index 19.09., gen_roadmap_stand --schreibe 27.09.) fehlten bis zum 28.09. und machten master bei JEDEM Lauf rot (gemessen: gen_roadmap_stand --pruefe rc=1, 466 gegen 465 Adressen). Ausdruecklich ohne [skip ci], weil das deploy-doku.yml mit angehalten haette — die Seite waere gebaut, committet und nie ausgeliefert; Selbst-Ausloesung ist stattdessen ueber paths ausgeschlossen. | ✅ vollstaendig — Vorbedingung fail-closed VOR dem Bau und ein Torschluss aus vier Waechtern HINTER dem Bau und VOR dem Commit (rot heisst nichts committet), beide Kriterien in beide Richtungen gemessen. ⛔ Der Lauf selbst ist noch nie gefeuert — erster Lauf unmittelbar nach dem Merge, und das ist der letzte offene Punkt der Abnahme, nicht eine Nachbemerkung. | TOWER#53 → REGISTER#02 |
08_OPS/check_workflow_shell_unversehrt.py (#3288, CI 52 konformitaets-guards.yml) | Ist die Shell in einem run:-Block unversehrt — oder hat ein Fortsetzungs-Backslash sein Zeilenende verloren? ⛔ Anlass ist ein eigener Schaden, keine Vorsorge: am 29.09.2026 trug register-nachzug.yml ein literales Backslash-n in seiner git add-Liste, die Shell las daraus das Argument n (fatal: pathspec 'n' did not match any files, exit 128), der Nachzug brach beim Zurueckschreiben ab und DREI Waechter standen danach auf master rot. Ein zweiter Fall lag in familie-guard.yml und wurde vom Doppelpipe-Rueckfall (ODER-Verkettung) STILL gedeckt. ⭐⭐ Und warum ihn nichts fing: YAML war gueltig (im Block-Skalar eines run-Schritts ist die Zeile nur Text), der Schritt lief nur auf push: master — in keinem Vorgang — und der Torschluss lief davor. Der eine Schritt, den kein Vorgang messen kann, war der gebrochene. ⛔ ZWEI Achsen — und bash -n ist bei beiden Anlassfaellen blind (je rc=0: aus dem Backslash-n wird das WORT n, also ein falsches Argument, kein Syntaxfehler; Positivkontrolle if [ x rc=2 und Gegenprobe echo ok rc=0 im selben Lauf beweisen, dass das Werkzeug arbeitet). Die TEXT-Achse traegt den Anlassfall ALLEIN, bash -n bleibt als eigenes Tor fuer echte Syntaxfehler daneben. ⭐ Hier stand zuerst "faengt den einen Fall" — an einer printf-Zeile gemessen, die den Syntaxfehler selbst erzeugte; widerlegt von TOWER#62 (B-30). Die Text-Achse unterscheidet dabei IN von AUSSER Anfuehrungszeichen, weil 27 von 29 Vorkommen im Bestand legitime printf '%s\n' sind. | ✅ vollstaendig — Selbsttest gruen: beide ECHTEN Faelle als Positivkontrolle (Abschriften, keine Koeder), vier Gegenrichtungen, und ausdruecklich die Probe, dass Achse A beim zweiten Fall schweigt — faellt sie, ist A besser geworden und B zu ueberdenken. Positivkontrolle am echten Bestand: gegen origin/master 2 Befunde, gegen die Reparatur 0. | REGISTER#02 |
08_OPS/gen_eingangskette.py (#3750, FOLLOW#02 05.10.2026, LAUFORT ci, Träger register-nachzug.yml als achter Erzeuger) | Erzeugt die Eingangsketten-Seite, statt sie zu pflegen — und trägt dabei das Urteil WÖRTLICH mit. Anlass: die Kette war siebenmal dargestellt und keine Darstellung war die geltende; zwei waren überholt. Liest zwei Quellen, nie eine: 08_OPS/eingangskette.json (IST, 34 Checkpoints je mit Uhr, Fundstelle und Messgrenze) und EINGANGSKETTE_SOLL.md (URTEIL, von Hand, mit Urheber). ⛔ Fehlt das Urteil, wird nichts gebaut — sonst entstünde die Ablösung, die nur den rechenbaren Teil übernimmt (#3741). ⭐ --pruefe vergleicht die Substanz der Quellen gegen eine Marke im Spiegel, nicht die Bytes: die Seite trägt eine Uhr, ein Byte-Vergleich wäre bei jedem Lauf rot und damit wertlos — genau der Grund, aus dem roadmap_board.html gar keinen Wächter hat. | ✅ vollständig — --selbsttest fährt beide Richtungen: 8 grüne Achsen (Zahlen = gezählt · ungemessen bleibt ungemessen · Urteil wörtlich inkl. Fortsetzungszeilen · fehlende Kante benannt · Uhr+Messgrenze je Punkt · Reichweite mit · Escaping in Register UND Urteil · Substanz dreht sich mit dem Zustand) und 8 rote Fälle (Register fehlt · Urteil fehlt · Urteil leer · Register unvollständig · fremdes Schema · unbekannter Zustand · veralteter Spiegel · Spiegel ohne Marke). 8 Mutanten gesetzt, 8 gefangen — der Selbsttest ist nachweislich nicht blind. ⛔ Der Nachzugs-Lauf selbst ist lokal nicht fahrbar; erster Echtlauf nach dem Merge. ⭐ Das Bild der Seite kommt aus 08_OPS/eingangskette_graph.py und geht durch graph_projektion.reduziere — kein zweites Bild, dieselbe Projektion, die später den Core-Graph-Screen speist. | FOLLOW#02 |
07_TESTS/follow02/test_eingangskette_graph.py (#3750, FOLLOW#02 05.10.2026, LAUFORT ci, netzfrei) | Vertragstest für die PROZESS-Schicht des Core-Graphen (08_OPS/eingangskette_graph.eingangskette_kanten — der deploy-freie Baustein nach der Hausform von 08_OPS/fehlergrund.py; chat_engine/graph_adapter.py reicht in einem eigenen, dienstwirksamen Vorgang dorthin durch). ⛔ Er prüft nicht, ob die Eingangskette heute so läuft — er prüft, ob das Modul nur sagt, was im Register steht, ob es eine rot gemessene Verbindung nicht zeichnet, und ob das Ergebnis durch die Wand geht. Gebaut nach dem Vorbild von 07_TESTS/agent_chat01/test_graph_quellen.py, dieselben Achsen A/B — damit niemand zwei Testarten für dieselbe Schicht lesen muss. | ✅ 24 Achsen grün — A Vertrag (Quelle, Fassung, Messuhr je Prüfpunkt, Aussageart, Stufe, Kern-Prüfung) · B die Wand (sichtbare_ids=None ⇒ default-deny, die wichtigste Probe) · C die vier Zustands-Fälle (rot bekommt Knoten ohne Kante · ungemessen ≠ rot · roter Übergang nicht gezeichnet · Station ohne jede Messung hat einen eigenen Typ) · D vier Abweisungen · E Reichweite wandert mit. ⭐ Achse C hat beim ersten Lauf einen echten Entwurfsfehler gefunden (eine ungemessene Station sah aus wie eine teils grüne) — der Adapter wurde danach geändert, nicht der Test. | FOLLOW#02 |
08_OPS/loese_fassungsblock.py (#3296, LAUFORT hand, NAHT#02 29.09.2026) | Löst den SERVICE_VERSION-Konfliktblock in app.py BLOCKWEISE — masters Fassungskette übernehmen und die aktive Zeile demotieren, die eigene alte Zeile per Identität (--alt) als Kommentar dazu, oben die neue aktive Zeile mit dem EIGENEN Inhalt; Haupt- und Nebenblock; jede fremde Zeile im Block ⇒ ABBRUCH, nichts geschrieben. ⛔ Anlass: git checkout --theirs -- app.py verschluckte an #3029 eine eigene Zeile (kein Wächter rot), und „höchste Fassung unter live“ trug an #2957 den Inhalt einer fremden Bahn ein. Aufruf NACH git merge origin/master mit Konflikt in app.py, VOR git add. | ✅ gebaut, --selbsttest 21/21 (fünf gefahrene Gestalten + Gegenproben + Fahnen-Riegel), zwei echte Konfliktdateien (#3029, #2933) trocken grün | NAHT#02 → TOWER (Baustein der Naht; Nummer ziehen bleibt check_dienstfassung_eindeutig --ziehe --reserviere) |
08_OPS/check_r147_beisst.py (#3299, CI kanon-quellen-ssot.yml, RKA-UX#02 20.09.2026, Zeile nachgeholt NAHT#02 29.09.) | Beisst R-147 in drei Richtungen? — liest _R147_ZEICHEN aus engine.js (baut die Logik nicht nach) und fährt sie gegen 5 Positiv- und 7 Negativfälle: Chevron links (::before), KEIN Chevron (::after leer/none) und nativer <summary>-Marker (listStyleType). ⛔ Anlass: die alte Prüfung bestand „gar kein Chevron“ genauso wie „korrekt rechts“ — R-147 war seit Monaten erfüllt und die Form fehlte trotzdem. | ✅ vollständig — Selftest lässt alle 6 Zusagen einzeln rot werden | RKA-UX#02 (verwaist) → TOWER |
08_OPS/check_arbeitsfolge_kanon.py (#3301, CI konformitaets-guards.yml, RKA-UX#02 20.09.2026, Zeile nachgeholt NAHT#02 29.09.) | Hält K-42 fxArbeitsfolge den Kanon UND bleibt dock-frei? — fünf Zusagen an core_shell.html: Kanon-Klasse details.sec · <summary> (R-99) · Chevron ::after (R-147) · kein ::before · Dock-Freiheit (K-27 nicht vorausgesetzt, Auflage TOWER#58). Selftest mutiert eine Kopie und verlangt, dass jede Zusage einzeln rot wird — mit Wirkungs-Kontrolle, dass die Mutation im Funktionsrumpf landet. | ✅ vollständig — Selftest 5/5 beisst | RKA-UX#02 (verwaist) → TOWER |
08_OPS/check_stepper_gestalt_unveraendert.py (#2411, WERKZEUGE#02 21.09.2026, Zeile nachgeholt NAHT#02 29.09.) | Bildet fxStepper (K-05) die einzige LEBENDE Stufenreihe zeichengleich ab? — lädt core_shell.html über file:// in Chromium (Playwright, kein Netz, keine Secrets), rendert _ibSteps() vor und nach der Umstellung und vergleicht das Markup Zeichen für Zeichen. ⛔ Zusammenlegen erbt die engste Eigenschaft aller Kopien: schreibt der Baustein ein Attribut anders, verliert die einzige sichtbare Gestalt (#/eingang/inbox, sechs Stufen aus skills_done). | ✅ gebaut (--selftest), braucht Chromium — Laufort siehe Kopf | WERKZEUGE#02 (verwaist) → TOWER |
08_OPS/check_feedback_knopf_in_kopfleiste.py (#3877, CI konformitaets-guards.yml 54-B, UX#03 10.10.2026) | Steht der Feedback-Knopf in der Kopfleiste, und trägt der Fluss? — lädt core_shell.html?ia=min über einen erfundenen Ursprung http://tgv.test/ (Pfade über qa_harness_server.aufloesen, kein Netz, keine Secrets) und prüft sechs Achsen: ① genau ein #uxFbToggle in .ds-topbar__end · ② kein schwebender .ux-fb-toggle · ③ Enter/Esc/Eintrag mit Fokus und aria-expanded · ④ Knopf und Panel in allen sieben Sprachen am DOM · ⑤ Rückfall: ohne Kopfleiste schwebt er weiter · ⑥ ein Symbol, eine Bedeutung (kein 💬 am Feedback, kein theme im Einstellungs-Kopf). Selbsttest: 9 Mutanten im Speicher. | ✅ gebaut (--selftest), braucht Chromium — Laufort siehe Kopf | UX#03 → TOWER |
check_brett_traegt_den_boden.py (Workflow konformitaets-guards.yml 53, mit --selftest) | Steht jede aktive Bahn mit ihrem ARBEITSBODEN auf dem Auftraege-Brett? Zwei Fragen, beide laufen immer: ① zwei Zeilen auf EINEM boden ⇒ ROT · ② ein Zweig mit offenem PR ohne Brett-Zeile ⇒ ROT. ⛔⛔ Anlass (21.09.): acht Bahnen an einem Tag beauftragt, keine stand auf dem Brett, zwei landeten auf demselben Boden und vermischten unversionierte Arbeit. Die Kopfregel des Bretts gibt es seit dem 03.09. — sie stand nur im Text. ⭐⭐ Warum nicht git worktree list: git fuehrt jeden Pfad genau einmal — zwei Bahnen auf einem Boden sind fuer git EIN Eintrag, die Doppelbelegung ist strukturell unsichtbar. Erst die Deklaration macht sie messbar; der Waechter misst das Brett, nicht den Baum. ⚠️ ENTHALTUNG, ausdrücklich: ohne gh-Token enthaelt sich die PR-Achse und der Lauf endet auf KEIN URTEIL (rc=3), nie auf GRUEN — eine halbe Messung ist kein Bestehen. Nicht gesehen werden Zweige ohne PR (er sucht bei den PRs) und lokale Zweige ohne Fernzweig. | 🔨 | WAECHTER#03 (#2417, 21.09.2026, Auftrag TOWER#58). Beissprobe am echten Brett je Richtung: doppelter Boden ⇒ rc=1, Ruecknahme rc=0 · Zeile entfernt bei offenem PR ⇒ rc=1, Ruecknahme rc=0 · Block ganz weg ⇒ rc=3, ausdruecklich nicht gruen. BASELINE 40 offene PR-Zweige gestundet (Stundung, kein Freispruch) — offen: der Abbau. Erster echter Befund binnen Minuten: tower58/ci-sparen-pfadfilter. PR #3042. |
08_OPS/check_hinweis_nennt_keine_beschriftung.py (#2425, CI konformitaets-guards.yml ⑩-E, KLAERUNG#03 21.09.2026, Zeile nachgeholt NAHT#03 02.10.) | **Zitiert ein Hinweistext in 06_DASHBOARD/i18n/*.json das Etikett eines anderen Schlüssels wörtlich?** — zwei Achsen, beide laufen immer: ① ein Wert enthält den Wert eines anderen Schlüssels zwischen Anführungszeichen und das Paar steht nicht im Bestand ⇒ ROT · ② ein Bestandspaar ist nirgends mehr zu finden ⇒ ROT „Zeile streichen“. Bestand 08_OPS/hinweis_zitate_bestand.json = Ratsche mit NAMEN (20 Schlüsselpaare, je Grund und richtige Lösung: Interpolation statt Abschrift), nicht mit einer Zahl. ⛔ Anlass: #2422 benannte den Knopf der Freigabe-Karte um, der Hinweis darüber zitierte die alte Beschriftung in sieben Sprachdateien — nichts wurde rot, der Satz wurde bloss unwahr. Andere Achse als check_i18n_ssot.py (hält die Liste der Sprachen, sieht nie in die Werte). | ✅ gebaut, --selftest grün (vier ROT-Proben beissen), Beissprobe gegen origin/klaerung02/freigabe-sagt-was-sie-tut@7e64a2d25 zwei Befunde, HEAD grün | KLAERUNG#03 (#2425) → TOWER |
08_OPS/check_kanon_wert_ohne_wirkungssatz.py (#2211/#2204, CI konformitaets-guards.yml Schritt 54, RECHTE-UX#01 20.09.2026, scharf + Zeile nachgeholt NAHT#03 03.10.) | Gibt es im Server-Kanon einen Wert, für den die Fläche keinen Wirkungssatz hat? — schneidet _mgWirkung/den Rollen-Kanon aus core_shell.html und app.py heraus und fährt die echte Funktion in node: jeder Kanon-Wert muss einen nicht-leeren Satz bekommen; Sammel-Rückfall oder fehlender Anker ⇒ ENTHALTUNG (rc=2), die der Workflow-Schritt als ROT wertet. ⛔ Anlass (#2204): VERTRAULICH#01 nahm buchhalter in _MANDANT_ROLES auf, die Fläche reichte den Kanon ungefiltert durch und hatte für den neuen Wert kein Verhalten — beide Seiten für sich richtig, zusammen kaputt. Stand zwei Wochen als ENTWURF (Endbedingung im Kopf: schema-register-guards auf master wieder grün — erfüllt 02.10.). | ✅ gebaut, Selbsttest 5 Fälle (braucht node; lokal ohne node ENTHALTUNG), Gegenprobe am echten Anlassfall rot; eingehängt Schritt 54 | RECHTE-UX#01 (verwaist) → TOWER |
08_OPS/check_dunkle_testdateien.py (#3461, CI konformitaets-guards.yml Schritt 55 mit --selbsttest, KAI-BIND#02 03.10.2026, Auftrag TOWER#63) | Heisst eine Datei test_…, wird von pytest NICHT gesammelt UND ruft sie niemand auf? Drei Fragen in Reihe: ① Namensmuster unter 07_TESTS · ② statische Sammelbarkeit (test_, Test, unittest.TestCase) · ③ Aufruf-Fundstelle in DREI Ausfuehrungs-Regalen (CI-Ablaeufe · 08_OPS · 07_TESTS/**/.sh). ⛔ Anlass: zwei eigene Dateien hiessen test_… und trugen keine test_-Funktion ⇒ pytest sammelt null Faelle ⇒ no tests ran ⇒ gruen, ohne dass eine Probe lief. ⭐ Gezaehlt vor dem Bau: 416 Dateien mit Namensmuster · 336 gesammelt · 80 dunkel · 74 davon ohne Aufrufer. Die gefaehrliche Klasse ist leer — alle 80 sind Skripte mit eigenem __main__; der Befund ist damit niemand ruft sie, nicht sie luegen gruen. ⛔⛔ Liest die Prosa-Regale ausdruecklich NICHT: eine erste Messung ueber Register und Brett (2266 .md) meldete 75 von 80 werden genannt* — eine Entwarnung aus Prosa. Genannt ist nicht gerufen. ⚠️ Nicht gesehen werden Aufrufe aus n8n, cron auf Hetzner oder von Hand, und ob eine gesammelte Datei etwas WAHRES prueft. | ✅ gebaut, --selbsttest 13/13 (5 Proben + 4 Mutanten, jeder an einer NAMENTLICH genannten Probe); Stellvertreter fuer --collect-only beidseitig gegen den echten Laeufer belegt (0 falsch-hell, 0 falsch-rot — der erste Entwurf haette 54 Dateien falsch-rot gemeldet, weil pytest unittest.TestCase unabhaengig vom Klassennamen sammelt); eingehaengt Schritt 55. BASELINE 74 mit NAMEN, nie einer Zahl — eine Zahl kann man erreichen, indem man eine ANDERE Datei loescht; sie darf nur sinken. | KAI-BIND#02 (#3461) → TOWER · offen: die 28 ohne jede Erwaehnung durchsehen (namentlich in der Registerzeile #3461, ausdruecklich TOWER-Ball) |
08_OPS/check_eimer_passt_zur_entitaet.py (#3168/#3154, LAUFORT cron, ABLAGE-PERSONAL#01 26.09.2026, Zeile nachgeholt NAHT#03 03.10.) | Liegt das Core-Objekt im Eimer SEINER Entität? — Regel: storage_url gesetzt ⇒ storage_bucket == bucket_for_entity(entity); rein lesend gegen Supabase, kein Bucket-Zugriff. ⛔ Anlass (JCs Frage 7, 26.09.): 16 Belege lagen im Eimer einer fremden Entität, davon neun C3-Firmenbelege im privaten Eimer — der Zeiger stimmte, der ORT war falsch, und jede Prüfung, die nur die Zeile liest, sah es nicht. Die Ursache ist seit #2171 (19.09.) behoben; der Wächter hält die 15 Altlasten sichtbar und fängt Rückfälle. ⛔ Träger: bewusst noch keiner (Kopf: „LAUFORT-TRAEGER: NOCH KEINER" — eine Trägerzeile vor dem Aufruf wäre eine Behauptung, kein Vorsatz; Schwesterfall check_eimer_steht_in_der_zeile.py → NENNT_ABER_FAEHRT_NICHT). | 🔨 wird-vervollstaendigt — gebaut, ohne Träger; Einhängung in betriebs-waechter.yml offen | ABLAGE-PERSONAL#01 (verwaist, Zweig claude/goofy-heisenberg-86f188) → TOWER |
08_OPS/check_storage_schritt_hat_seine_buckets.py (#3630, CI konformitaets-guards.yml Schritt 56 mit --selftest, BETRIEB#01 04.10.2026, Auftrag TOWER#64) | Hat ein Schritt, der ALLE Mandanten-Buckets aufzählt, auch alle deren Zugänge? Zwei Achsen: (A) Aufzähler ohne Zugang UND ohne # ZUGANG-REICHWEITE:-Vermerk ⇒ ROT · (B) Aufzähler, der NUR den globalen HETZNER_S3_*-Satz trägt ⇒ ROT. ⛔⛔ Anlass — er hätte einen Auftrag abgefangen, bevor er gebaut wird: am 04.10. lautete der Auftrag, den env:-Block an JEDEN Storage-Schritt des betriebs-waechter nachzutragen, gezählt per git grep (2 von 16). ⭐ Die Zählung war richtig, der Schluss falsch: _mandant_saetze() liest nur HETZNER_S3__ mit DOPPELTEM Unterstrich (_storage.py:262), die globalen Secrets tragen EINEN — sie erreichen die drei blinden Prüfer nie, und mit ihnen sähen ⑨/㉘/⓪ 1 von 9 Buckets unter einer Gesamtaussage ⇒ falsches Rot statt ehrlichem „nicht messbar“. Achse B ist heute grün und wird rot, sobald jemand den Block nachträgt. ⛔ Die Trennlinie ist Bauform, nicht Vorsicht: wer _mandant_saetze() DIREKT ruft, zählt auf und braucht alle Sätze; wer je Zeile seinen Bucket öffnet (config_fuer_bucket, ㉛/㉜) darf mit dem globalen Satz fahren — deshalb ist _storage in der AST-Verfolgung ein BLATT, sonst 16 Fehlalarme. ⚠️ Nicht gesehen werden der Skills-Dienst auf Hetzner (systemd EnvironmentFile), die n8n-Instanz und alles außerhalb GitHub Actions. | ✅ gebaut, --selftest grün (8 Fälle in beide Richtungen + Vorspann-Biss + Gegenprobe an 5 ECHTEN Dateien: ⑨/㉘/⓪ als Aufzähler, ㉛/㉜ nicht); scharfer Lauf grün nach drei Vermerken; eingehängt Schritt 56. ⛔ Drei eigene Fehlalarme beim Bau gemessen und behoben: _env.py-Docstring als Fundstelle (16 Treffer), abgeschnittene oberste Vorspann-Zeile, und 4 Selbsttest-Schritte als Befund (der AUFRUF-MODUS gehört zur Messung). | BETRIEB#01 (#3630) → TOWER#64 · ⛔ JC-Ball offen: 32 Secret-Namen HETZNER_S3__<SLUG>__{ENDPOINT,BUCKET,ACCESS_KEY,SECRET_KEY} für die acht Slugs ODER Reichweiten-Logik in den drei Prüfern — ohne eins von beiden bleiben ⑨/㉘/⓪ dauerhaft blind, und das ist richtig so |
08_OPS/check_dispatch_liste_kennt_die_folgeablaeufe.py (#3806, CI laufort-traeger-guard.yml mit --selftest, ANSTOSS#01 07.10.2026, Auftrag TOWER#66) | Kennt die handgepflegte for-Liste in register-nachzug.yml alle Folge-Abläufe? Weil ein Push mit dem Vorgabe-GITHUB_TOKEN keine Push-Abläufe startet (29.09. MIT Kontrollgruppe gemessen: head_sha=2aa1b4bfa… → 0 Läufe gegen 5d34b0926… → 7), stösst der Nachzug seine Folge-Abläufe von Hand an. Der Schritt wird laut rot, wenn ein Anstoss misslingt — er fragte aber nie, ob die Liste vollständig ist. ⭐ Ein Ablauf, der nie angestossen wurde, ist in der Prüfliste nicht ROT, sondern GAR NICHT DA — er zeigt sich nie als Rot, immer nur als Abwesenheit. ZWEI Aufnahmegründe: ① paths trifft einen schreibbaren Pfad · ② paths FEHLT oder ein paths-ignore deckt nicht alles (feuert dann immer). ⛔ Ohne ② wächst die Klasse still: wer morgen einen Workflow ohne paths hinzufügt, erzeugt lautlos das nächste stumme Tor. ⭐⭐⭐ DIESELBE EIGENSCHAFT, ZWEI VORZEICHEN JE ACHSE — auf der FILTER-Achse (check_pfadlisten_beider_ausloeser) ist ein fehlendes paths ausdrücklich als „breiter, also kein Befund“ dokumentiert und dort richtig, weil dort GEFILTERT wird; hier ist dasselbe fehlende paths ein BEFUND, weil hier AUFGEZÄHLT wird. Eine dokumentierte Unbedenklichkeit wandert leichter auf die falsche Achse als eine undokumentierte — sie liest sich wie ein Urteil über die EIGENSCHAFT, ist aber ein Urteil über die ACHSE; deshalb steht dieser Satz im Code neben Zustand ②. Reichweite aus dem WORKFLOW, nicht aus einem Commit: die schreibbaren Pfade kommen aus der git add-Zeile (zehn), die Liste aus der for w in …-Zeile. ⛔ Beide Vormessungen rechneten gegen die acht Pfade EINES Commits — konzept_tower_zusagen.html und konzept_index.json fehlten darin. ⚠️ Nicht gesehen werden: GitHubs Glob-Semantik im Original (hier fnmatch mit Kandidaten-Varianten, geerbt aus pruefe_ci_zustaendigkeit), andere Auslöser als push, und ob die angestossenen Abläufe grün werden — nur, ob sie starten KÖNNTEN. | ✅ vollstaendig — --selftest grün: 19 Fälle + 2 Mutanten, eine je Achse (① ein Ablauf aus der Liste gestrichen · ② ein neuer Workflow OHNE paths dazugelegt), beide mit assert alt in text vor dem Schreiben. ⭐ Diese Disziplin hat sich selbst bewiesen: als die for-Liste im selben Vorgang wuchs, meldete Mutante ① „greift NICHT — KEINE Entwarnung“ statt grün zu bleiben; ihr Anker hängt jetzt an dispatch_liste() statt am Buchstaben. Instrument gegengeprüft, weil es eine Nachbildung ist und keine Plattform: gegen die 11 ECHTEN Läufe des Menschen-Merges d4aaa7743b8e 15 von 16 Workflows richtig, der eine Fehler in der SICHEREN Richtung (deploy-staging-shell, branches: [design02/…]) ⇒ branches wird mitgelesen. Fahnen-Riegel: --selftset → rc=2, nicht rc=0. ⛔ Drei eigene Fehler beim Bau gemessen und behoben: Prüffälle in Flow-Form, die es im Haus nicht gibt (vier Fälle bestanden aus dem falschen Grund) · branches in Blockform gab [] und hätte jeden solchen Workflow STILL entlastet · drei print()-Zeilen mit cp1252-Absturzzeichen, von R-155 gefangen. | ANSTOSS#01 (#3806) → TOWER#66 · ⛔ offen beim Eigner von iban-guard.yml: er hat keinen workflow_dispatch:-Auslöser, ist also gar nicht anstossbar — deshalb nennt der Wächter für solche Fälle eine ANDERE Kur (erst workflow_dispatch: ergänzen, DANN nachtragen), sonst hätte ein blindes „nachtragen“ den Nachzug bei JEDEM Lauf rot gemacht |
08_OPS/check_webfactory_adapter_spalte.py (#3822, CI konformitaets-guards.yml Schritt 57 mit --selftest und --nachziehen, WEBFACTORY#01 10.10.2026, Auftrag TOWER#67) | Trägt JEDE Katalog-Tabelle der Website-Factory den Adapter-Status? JC am 09.10.: „OK für Kontrakt-First als verbindliches Muster für ALLE Web-Fabrik-Bausteine — und der Adapter-Status als Pflichtspalte im Ernte-Katalog (§4.2).“ ⭐ Die Engstelle ist die BAUFORM, nicht die Abschnittsnummer: eine Tabelle, die je Baustein einen TGV-Status führt, IST der Ernte-Katalog — an welcher Nummer sie steht, ist Zufall. Hätte der Wächter an „§4.2“ gehangen, wäre der nächste Nachtrag ungeprüft daneben entstanden, und das ist nicht hypothetisch: gegen PR #3247s WIRKLICHE Fassung gefahren meldet er beide Tabellen, denn dessen neues §4.2a trägt acht Bausteine ohne Spalte. ⛔⛔ Darum trägt er seine HEILUNG mit (--nachziehen): ein roter Wächter, dessen Fundstelle in FREMDER Arbeit liegt, blockiert jeden PR und wird dann abgeschaltet statt befolgt (Narbe #2237). Ein Befund, dessen Kur einen Handgriff kostet, wird gemacht; einer, der eine Sitzung kostet, wird gestundet. ⚠️ Nicht geprüft: ob die WERTE stimmen (nur Anwesenheit und Form der drei Felder), Tabellen ohne TGV-Status-Spalte, und andere Dateien. | ✅ gebaut, --selftest grün (3 Mutanten: Spalte aus dem Kopf · Baustein ohne Kontrakt-Feld · eine NEUE Katalog-Tabelle ohne Spalte — jeder Anker wird vor dem Messen auf Treffer geprüft, weil eine Ersetzung ins Leere aus der Probe eine FALSCHE Aussage statt einer fehlenden macht); scharfer Lauf grün (23 von 23 Zeilen); Nachzug an #3247s Fassung gefahren: 31 Zeilen, 2 Befunde → 0, auf der echten Datei idempotent. ⛔ Zwei eigene Befunde beim Bau gemessen und behoben — R-155 beide Achsen: vier Symbole in print(), und der reconfigure-Vorspann, weil der Wächter GEPRÜFTES MATERIAL druckt (Baustein-Namen mit Stern) und sonst auf JCs Konsole GENAU beim Melden eines Befundes gestorben wäre. | WEBFACTORY#01 (#3822) → TOWER#67 · ⚠️ Tower-Ball: nach dem Merge von #3247 meldet er dessen 8 Nachtragszeilen — das ist beabsichtigt, und der Nachzug ist EIN Befehl |
08_OPS/check_webfactory_promotions_gate.py (#3822, CI konformitaets-guards.yml Schritt 57 mit --selftest, WEBFACTORY#01 10.10.2026, Auftrag TOWER#67) | Sind die vier Schwellen belegt, bevor ein Web-Fabrik-Kit Katalog-Modul wird? (§7.3: nativ lauffähig · Daten-/UX-Gate · Compliance/M0.5 · mehrfach nutzbar; Tower#24-Auflage, §8 wörtlich: „Ein Kanon, der nur im Katalog steht, wird vom nächsten Bau per Direktlink umgangen“). ⛔⛔ §7.3 zeigt auf den FALSCHEN ORT, gemessen: es nennt mandant_modules als Ort der Promotion — diese Tabelle hat DEFAULT = AN, belegt an drei unabhängigen Stellen (Mig 219:40 Tabellen-Kommentar · modul_kanon.json:_default_ist_AN · tgv_werkzeug_landkarte.py:module_an_aus_zeilen: nur enabled is False schaltet ab, eine fehlende oder kaputte Zeile tut es nicht). ⭐ Ein Kit, von dem niemand eine Zeile anlegt, wäre für JEDEN Mandanten AN — die Abwesenheit einer Zeile IST die Freigabe, und ein Gate, das das Anlegen verlangt, verlangt einen Schritt, der nur abschalten kann. ⭐⭐ Die echte Engstelle ist der Kanon-Eintrag, den check_modul_kanon.py BEREITS bewacht — dieser Wächter baut keinen zweiten Schalter, er ergänzt nur dessen BEDINGUNG. NUXIN: der Mechanismus war da, es fehlte die Bedingung. ⛔ Er meldet heute ausdrücklich „KEIN ANGRIFFSPUNKT“ und NICHT „grün“ — kein website- steht im Kanon (die 10 Module sind finanzen/dokumente/vertrag/rka/steuer/kontakte/mail/recherche/megabook/persona). Darum läuft sein Selbsttest in der CI scharf: er ist der einzige Beweis, dass er beisst, solange er nichts zu beissen hat. Ein Instrument, das 0 als Erfolg meldet, ist ein toter Meldeweg.* ⚠️ Nicht geprüft: die Datenbank (eine von Hand geschriebene mandant_modules-Zeile ist unsichtbar — dieselbe Grenze wie bei check_modul_kanon.py), ob eine Schwelle INHALTLICH erfüllt ist (die Promotion bleibt JCs Akt auf Tower-Empfehlung), und die 10 bestehenden Module — sie sind älter als §7.3 und werden nicht rückwirkend eingeklagt. | ✅ gebaut, --selftest grün (4 Mutanten: kein promotion-Feld · drei von vier Schwellen · vierte Schwelle LEER statt fehlend — der naheliegendste Weg, ein Gate zu umgehen · und die richtige Kur MUSS grün bleiben, sonst klagt der Wächter die Behebung an); scharfer Lauf endet auf „kein Angriffspunkt“. Die Kit-Erkennung liest die website-*-Ernte-Ziele aus dem Katalog (17 gefunden) — damit verdrahtet der Wächter §4.2 mit dem Kanon, statt eine zweite Namensliste zu führen. | WEBFACTORY#01 (#3822) → TOWER#67 |
08_OPS/check_sender_prueft_seinen_empfaenger.py (#3794, LAUFORT cron, CI betriebs-waechter.yml Schritt ㈞ mit --selftest, BETRIEB#02 06.10.2026, Auftrag TOWER#65) | Postet ein aktiver Sender an einen Empfaenger, dessen Zustand er nie prueft? Zwei Achsen: (A) URTEIL — aktiver, nicht archivierter n8n-Workflow nennt einen Webhook-Pfad, den kein aktiver Workflow bedient ⇒ ROT. (B) KEIN URTEIL, nur eine ZAHL — wie viele dieser Sender sehen die Antwort ihres Ziels ueberhaupt an. ⛔ Achse B hat bewusst keine Schwelle: aus dem Text ist nicht entscheidbar, ob ein catch eine Quittung ist oder ein Schlucken; wer das halb auswertet, erzeugt Fehlalarme oder deckt das Schlucken zu. ⛔⛔ Anlass (Register #3730 → #3794): TGV Cron Mail Failure-Notify v0.2 ist AKTIV, laeuft taeglich und postet an /webhook/tgv-skill-send-mail — seit dem 05.09. WEG. ⭐ Die Pointe ist nicht der tote Webhook: der Code faengt den Fehler (catch → ok:false), und ein gefangener Fehler laesst den n8n-Lauf status=success. Gruen nicht, weil der POST gelingt, sondern weil ein ok:false keinen Leser hat. ⭐⭐ PFLICHT-POSITIVKONTROLLE IM SCHARFEN LAUF, nicht nur im Selbsttest — und das ist die eigentliche Bauform: beim Messen entstanden zwei falsche „0 Befunde"-Aussagen, beide im EXTRAKTOR vor der Logik (//[^"\n] frisst die JSON-Zeile ab dem ersten //; //.$ frisst https://, weil ein Schema ein // enthaelt). Kein Selbsttest haette sie gefunden, denn die Logik war richtig. Deshalb beweist der Waechter bei JEDEM Lauf zuerst, dass er eine URL in der lebenden Zeilenform findet und einen Kommentar NICHT mitliest; faellt eine Kontrolle, gibt es rc 2 = KEIN URTEIL, nicht ein Gruen. ⚠️ Nicht gesehen (R-193): Sender ausserhalb von n8n (GitHub-Ablaeufe — gemessen 0 Webhook-Nennungen; Skills-Dienst; Python-Ports), dynamisch zusammengesetzte URLs, ob ein gemeldeter Zweig im Betrieb BETRETEN wird, und ob eine vorhandene Antwort-Pruefung den Fehler auch WEITERGIBT. | ✅ gebaut, --selftest gruen (14 Faelle, darunter die Gegenprobe „lebender Empfaenger ist KEIN Befund", Archiv traegt seinen Pfad nicht, eigener Webhook ist kein Empfaenger, Kommentar gegen Code in beiden Richtungen, unlesbares n8n ⇒ kein Urteil); scharfer Lauf live am 06.10.: rc 1, vier Befunde — deckungsgleich mit der unabhaengigen Handmessung derselben Frage, plus 9 nachrichtlich in inaktiven Sendern. ⛔ Der Beweis liegt NICHT in diesem PR: on: des betriebs-waechter ist schedule + workflow_dispatch, kein push — ㈞ wird erst im naechsten Zeitplan-Lauf oder per workflow_dispatch sichtbar. | BETRIEB#02 (#3794) → TOWER#65 · ⛔ Drei der vier Befunde gehoeren dem Skill Router v0.7.4 (Plan Dispatch → tgv-skill-extract-contract-pdf · tgv-skill-reconcile-payment · tgv-skill-upsert-contract-from-invoice, alle drei STILLGELEGT) — fremdes Revier, als Befund gemeldet, nicht angefasst; in 23.329 Zeichen dieses Knotens kommen statusCode/status/ok === 0x vor: kein Sender mit drei toten Zielen, sondern ein Sender ohne Quittung, der zufaellig drei tote Ziele hat |
08_OPS/check_kein_direkter_nicht_eu_aufruf.py (#3923, LAUFORT ci, CI konformitaets-guards.yml Schritt 58 mit --selftest, SOUVERAEN#01 10.10.2026, Auftrag TOWER#69) | Ruft eine Datei einen Modell-Anbieter ausserhalb der EU direkt — und steht sie dabei NICHT in der Liste, als benannte JC-Ausnahme oder als gezaehlter Umstell-Bestand? Vier Signaturen (URL-Literal · SDK-Fabrik · Kopfzeile anthropic-version · US-Schluessel gelesen); Liste MIT Klasse, JC-Fundstelle und Ablauf/Weg, darf nur schrumpfen; NEU/VERWAIST/FORM rot. Dritte Frage an dieselbe Flaeche neben check_eu_pflicht_riegel.py (Weg an der Policy vorbei im Gateway) und check_kein_modellname_im_code.py (Modellname im Code). | ✅ gebaut, --selftest gruen (15 Proben), Beissprobe am echten Baum rot | SOUVERAEN#01 (#3923) → TOWER#70 |
08_OPS/check_testdatei_ohne_importwirkung.py (#4024, LAUFORT ci, CI laufort-traeger-guard.yml mit --selftest, BETRIEB#05 11.10.2026, Auftrag TOWER#71) | Tut eine Datei unter 07_TESTS beim blossen IMPORT etwas — Netz, Schreiben, Unterprozess? Gemessen wird, was beim Import laeuft (Modulebene + transitiv gerufene lokale Funktionen, ohne __main__), mit der Signal-Erkennung von LAEUFER#01 (pruefer_fahrweise._datei_signale). Anlass 11.10. 01:10Z: pytest <dateiliste> importierte zwei Skripte, die an n8n posteten — 11 echte Ablagen. Ratsche 08_OPS/testdatei_importwirkung_grundlinie.json (112 Bestand) | ✅ gebaut, --selftest gruen (10 Proben), 5 Mutanten gefangen, Beissprobe am Anlass rot | BETRIEB#05 (#4024) → TOWER#71 |
08_OPS/check_policy_wand_klassen.py (#4067, LAUFORT ci + hand, CI schema-register-guards.yml ㈡ mit --selftest, SICHERHEIT#09 11.10.2026, Auftrag TOWER#71) | Haelt eine Policy das RICHTIGE? Drei Klassen, die das Haus bezahlt hat: K1 Lesen ohne Stufe (auch ein KIND einer gestuften Tabelle, das die Stufe nicht erbt — Mig 310) · K2 Schreiben ohne Schreibrolle (FOR ALL = Lesen — Mig 301) · K3 SECURITY DEFINER schreibt ohne Aufrufer-Pruefung (BUCHHALTUNG#14). Urteil per symbolischer UND/ODER-Auswertung (EXISTS wird betreten), nicht per Textgleichheit. Netzfrei ueber STAGED/PROPOSED, --live ueber pg_policies; Ratsche 08_OPS/grundlinie_policy_wand_klassen.json (live 25, Dateien 8). Gegenbeweis an echter RLS: 08_OPS/sicherheit09_gegenbeweis.py | ✅ gebaut, --selftest gruen (32 Proben inkl. Mutanten), beim Einschalten gruen (0 neu) | SICHERHEIT#09 (#4067) → TOWER#71 |