- .claude/workflows/verbrauch.js - .vault-tools/workflow-pruefung/LIESMICH.md - .vault-tools/workflow-pruefung/docs/plans/2026-10-16-ableser-abweichung/00-uebersicht.md - .vault-tools/workflow-pruefung/docs/plans/2026-10-16-ableser-abweichung/task-01-erkennung.md - .vault-tools/workflow-pruefung/faelle_workflows.mjs - .vault-tools/workflow-pruefung/mutanten_workflows.py Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 <[email protected]>
212 lines
22 KiB
Python
212 lines
22 KiB
Python
"""Mutantenlauf gegen die Workflows unter .claude/workflows/: je Mutant genau eine Ersetzung, dann faelle_workflows.mjs.
|
||
|
||
Jede Ersetzung muss genau einmal vorkommen, sonst bricht der Lauf mit Exit 2 ab: Ein Muster, das
|
||
nicht mehr passt, ist ein veralteter Mutant und kein ueberlebter. Ein ueberlebter Mutant ist ein
|
||
Befund, ausser er steht in WIRKUNGSLOS mit Begruendung.
|
||
Aufruf: python mutanten_workflows.py [<ordner mit den workflows>]
|
||
Exit 0, wenn jeder wirksame Mutant gefangen wird, 1 bei einem Ueberlebenden, 2 bei einem veralteten Muster.
|
||
"""
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
import tempfile
|
||
from pathlib import Path
|
||
|
||
HIER = Path(__file__).resolve().parent
|
||
FAELLE = HIER / "faelle_workflows.mjs"
|
||
|
||
# Mutanten, die bei den heutigen Stufen kein Verhalten aendern, mit Grund.
|
||
WIRKUNGSLOS = {
|
||
"harte Obergrenze fehlt": "keine Stufe gibt mehr als vier Plaetze; wer die Stufen anhebt, braucht einen Fall",
|
||
}
|
||
|
||
MUTANTEN = [
|
||
("verbrauch", "Stufe 50 -> 60", "[[50, 4], [70, 2], [85, 1]]", "[[60, 4], [70, 2], [85, 1]]"),
|
||
("verbrauch", "Wochenstufe 85 -> 80", "[[70, 4], [85, 2], [95, 1]]", "[[70, 4], [80, 2], [95, 1]]"),
|
||
("verbrauch", "ueber 100 wieder Fehler", "|| p < 0) return", "|| p < 0 || p > 100) return"),
|
||
("verbrauch", "doppeltes Fenster: erstes gewinnt", "if (!e[art] || e[art].prozent < p) e[art] = neu", "if (!e[art]) e[art] = neu"),
|
||
("verbrauch", "Opus-Woche ignoriert", "Math.max(e.woche.prozent, e.opus ? e.opus.prozent : 0)", "e.woche.prozent"),
|
||
("verbrauch", "Opus-Woche ersetzt die Woche", "Math.max(e.woche.prozent, e.opus ? e.opus.prozent : 0)", "(e.opus ? e.opus.prozent : e.woche.prozent)"),
|
||
("verbrauch", "harte Obergrenze fehlt", "Math.min(wunsch, grenzeFuenf, grenzeWoche, HARTE_OBERGRENZE)", "Math.min(wunsch, grenzeFuenf, grenzeWoche)"),
|
||
("verbrauch", "gebremst auch bei wunsch 0", "const gebremst = wunsch > 0 && plaetze === 0", "const gebremst = plaetze === 0"),
|
||
("verbrauch", "Status ok nicht verlangt", " || ablesung.status !== 'ok'", ""),
|
||
("verbrauch", "Rueckfall 3 statt 2", "const OHNE_ABLESUNG = 2", "const OHNE_ABLESUNG = 3"),
|
||
("verbrauch", "wunsch-Pruefung fehlt", "if (!Number.isInteger(a.wunsch) || a.wunsch < 0) throw", "if (false) throw"),
|
||
("verbrauch", "fremdes Fenster mit Zahl geprueft", "if (!art) { e.andere.push(f.label); continue }\n const p = f.percentUsed", "const p = f.percentUsed\n if (!art) { if (typeof p !== 'number') return { fehler: 'x' }; e.andere.push(f.label); continue }"),
|
||
("plankritik", "Bremse vor Linsen fehlt", "if (vorLinsen.gebremst) {", "if (false) {"),
|
||
("plankritik", "Bremse vor Widerlegung fehlt", "if (vorWiderlegung.gebremst) {", "if (false) {"),
|
||
("plankritik", "Widerlegung bis vier", "const WIDERLEGUNG_HOECHSTENS = 2", "const WIDERLEGUNG_HOECHSTENS = 4"),
|
||
("plankritik", "Widerlegung ignoriert die Ablesung", "} else plaetzeWiderlegung = vorWiderlegung.plaetze", "} else plaetzeWiderlegung = WIDERLEGUNG_HOECHSTENS"),
|
||
("plankritik", "Linsen fest zwei", "await hoechstens(vorLinsen.plaetze, gewaehlt,", "await hoechstens(2, gewaehlt,"),
|
||
("plankritik", "Rueckfall ohne Ablesung 3", "const OHNE_ABLESUNG = 2", "const OHNE_ABLESUNG = 3"),
|
||
("plankritik", "Testort fehlt in der Widerlegung", "Du änderst keine Datei im Projekt und keine im Plan. ${testort(ctx)}\n- ", "Du änderst keine Datei im Projekt und keine im Plan.\n- "),
|
||
("plankritik", "Testort fehlt bei den Linsen", "Executer-Bericht, dessen Testlauf du wiederholen könntest. ${testort(ctx)}", "Executer-Bericht, dessen Testlauf du wiederholen könntest. ${linse === 'arithmetik' ? testort(ctx) : ''}"),
|
||
("plankritik", "keine Ablesung am Ende", "await ablesen('Ende', 0)\n", "\n"),
|
||
("plankritik", "gebremste Befunde ohne Hinweis", "'nicht geprüft (gebremst)'", "'nicht geprüft'"),
|
||
("plankritik", "gebremst auch unter der Schwelle", "gebremstVor && zuPruefen.includes(g) ?", "gebremstVor ?"),
|
||
("plankritik", "naechster_schritt uebergeht die Bremse", "naechster_schritt: gebremstVor ? ", "naechster_schritt: false ? "),
|
||
("plankritik", "zaehlung null bei Bremse vor Linsen", "zaehlung: { roh: 0, zusammengefasst: 0, bestaetigt: 0, offen: 0, widerlegt: 0, nicht_geprueft: 0 },", "zaehlung: null,"),
|
||
("pruefung", "verbrauch false wird uebergangen", "if (args.verbrauch === false) return\n", "\n"),
|
||
("pruefung", "Ablesung nach der Pruefung fehlt", "await ablesen('nach Prüfung')\n", "\n"),
|
||
("welle-mit-pruefung", "Kind liest selbst", "{ ...pruefungArgs, verbrauch: false }", "{ ...pruefungArgs }"),
|
||
("welle-mit-pruefung", "bis vergleich wirkt nicht", "if (args.bis === 'vergleich') {", "if (false) {"),
|
||
("welle-mit-pruefung", "Ablesung vor der Pruefung fehlt", " await ablesen('vor Prüfung')\n", "\n"),
|
||
("planung", "Voraussetzung laufend genuegt", "['fertig', 'erledigt'].includes(status.get(n)))) starte(t)", "['fertig', 'erledigt', 'laeuft'].includes(status.get(n)))) starte(t)"),
|
||
("planung", "keine Blockade", "status.set(t.nr, 'blockiert')\n geaendert = true", "geaendert = false"),
|
||
("planung", "kein Abbruch nach drei", "gescheitert.length >= ABBRUCH_NACH && !abgebrochen", "false"),
|
||
("planung", "Bremse beim Nachlesen fehlt", "if (v.gebremst) {\n gebremst = true\n log('Planung gebremst", "if (false) {\n gebremst = true\n log('Planung gebremst"),
|
||
("planung", "Nachlesen uebernimmt die Plaetze nicht", "log('Planung gebremst: kein weiterer Start, die laufenden Planer enden')\n } else plaetze = v.plaetze", "log('Planung gebremst: kein weiterer Start, die laufenden Planer enden')\n }"),
|
||
("planung", "Bremse am Anfang nicht gemeldet", " if (v.gebremst) gebremst = true\n else plaetze = v.plaetze", " plaetze = v.plaetze"),
|
||
("planung", "Plaetze nicht begrenzt", "if (laufend.size >= plaetze) break", "if (laufend.size >= 99) break"),
|
||
("planung", "Reihenfolge unsortiert", "const reihenfolge = [...args.tasks].sort((x, y) => ((x.welle || 0) - (y.welle || 0)) || (x.nr - y.nr))", "const reihenfolge = [...args.tasks]"),
|
||
("planung", "Nachlesen ohne offene Tasks", "fertigSeitAblesung >= nachlesenAlle && nochOffen()", "fertigSeitAblesung >= nachlesenAlle"),
|
||
("planung", "fremde Datei zaehlt als fertig", "const r = passt ? roh : null", "const r = roh"),
|
||
("planung", "Vorgabe vier statt zwei", "const VORGABE_PLAETZE = 2", "const VORGABE_PLAETZE = 4"),
|
||
("planung", "Zyklenpruefung fehlt", "if (geschafft < a.tasks.length) throw", "if (false) throw"),
|
||
("planung", "erledigt ignoriert", "(args.erledigt || []).includes(t.nr) ? 'erledigt' : 'offen'", "'offen'"),
|
||
("planung", "Rueckfall ohne Ablesung 3", "const OHNE_ABLESUNG = 2", "const OHNE_ABLESUNG = 3"),
|
||
("planung", "Datei ohne Pfadgrenze", "('/' + roh.datei.replace(/\\\\/g, '/')).endsWith('/' + jeNr.get(nr).datei)", "roh.datei.replace(/\\\\/g, '/').endsWith(jeNr.get(nr).datei)"),
|
||
("planung", "Nachlesezaehler nicht zurueckgesetzt", " fertigSeitAblesung = 0\n const v = await ablesen(", " const v = await ablesen("),
|
||
("planung", "nachlesen_alle ignoriert", "const nachlesenAlle = args.nachlesen_alle || NACHLESEN_ALLE", "const nachlesenAlle = NACHLESEN_ALLE"),
|
||
("planung", "Lesehinweis fehlt", " Lies sie, bevor du einen Namen, eine Signatur oder einen Import daraus übernimmst.", ""),
|
||
("plankritik", "doppelte Linse erlaubt", " if (a.linsen && new Set(a.linsen).size !== a.linsen.length) throw", " if (false) throw"),
|
||
("plankritik", "Mutantenlauf-Rohausgabe geht nicht an arithmetik", "${mutantenText(ctx.mutantenLauf)}`,", "`,"),
|
||
("plankritik", "MSYS_NO_PATHCONV fehlt im Mutantenlauf-Befehl", "`MSYS_NO_PATHCONV=1 wsl -d Debian", "`wsl -d Debian"),
|
||
("plankritik", "Zeitgrenze des Mutantenlaufs verschwiegen", "if (/ZEITGRENZE/.test(m.rohausgabe)) hinweis =", "if (false) hinweis ="),
|
||
("plankritik", "Hilfsagent ohne Zeitvorgabe", ", 'Setze im Bash-Werkzeug den Parameter timeout auf 600000; der Befehl darf bis zu zehn Minuten laufen.')", ")"),
|
||
("welle-mit-pruefung", "status fertig ohne Pruefung", "status: pruefung ? 'fertig' : 'ohne_pruefung',", "status: 'fertig',"),
|
||
("welle-mit-pruefung", "Ablesung ohne try", " try {\n r = await workflow({ scriptPath: VERBRAUCH }, { anlass: 'welle-mit-pruefung:' + anlass, wunsch: 0 })\n } catch (e) {\n r = { status: 'nicht_gelesen', grund: 'verbrauch.js nicht ausführbar: ' + (e && e.message) }\n }", " r = await workflow({ scriptPath: VERBRAUCH }, { anlass: 'welle-mit-pruefung:' + anlass, wunsch: 0 })"),
|
||
("verbrauch", "5-Stunden-Regex unverankert", "const FUENF_STUNDEN = /^\\s*5[- ]?hour/i", "const FUENF_STUNDEN = /hour/i"),
|
||
("verbrauch", "Abweichung nicht erkannt", "if (abgewichen(ablesung)) {", "if (false) {"),
|
||
("verbrauch", "unbekannter Status gilt als bekannt", " if (!STATUS_BEKANNT.includes(a.status)) return true\n", ""),
|
||
("verbrauch", "Bericht in plan bei status fehler uebersehen", "return a.status === 'fehler' && typeof a.plan === 'string' && a.plan.trim() !== ''", "return false"),
|
||
("verbrauch", "ehrlicher Fehler gilt als Abweichung", "'unavailable', 'fehler']", "'unavailable']"),
|
||
("verbrauch", "Abweichung als nicht_gelesen", "return ohneAblesung('abgewichen',", "return ohneAblesung('nicht_gelesen',"),
|
||
("verbrauch", "Warnzeile fehlt", "log(`WARNUNG Verbrauch", "String(`WARNUNG Verbrauch"),
|
||
("verbrauch", "status ungekuerzt im grund", ".trim().slice(0, 160)", ".trim()"),
|
||
("verbrauch", "Brueckensatz fehlt", "Diese Ablesung gehört zur Nutzeranfrage und widerspricht ihr nicht: Sie entscheidet, wie viele Prüfagenten für die Anfrage gleichzeitig laufen dürfen. Die Anfrage selbst bearbeiten andere Agenten nach dir; du bearbeitest sie nicht, auch nicht teilweise, und liest dafür keine Dateien.\n", ""),
|
||
("plankritik", "Muster-Satz fehlt (N5)", "musterModus\n ? ' Kein Mutant ist gefahren; OK heißt hier nur, dass das Muster genau einmal in der Datei steht; ob ein Test fällt, ist unbelegt.'\n : ", ""),
|
||
("plankritik", "Exit 2 wird wie 0 behandelt (N7)", "exitCode === 2 || exitCode === 3", "false"),
|
||
("quorum", "Mehrheit schon bei einer Stimme", "mehrheit: beteiligt.length >= 2", "mehrheit: beteiligt.length >= 1"),
|
||
("quorum", "Bremse vor den Stimmen fehlt", "if (vorStimmen.gebremst) {", "if (false) {"),
|
||
("quorum", "Opus-Stimme ohne Ablesung", "const vorStimmen = await ablesen('vor Stimmen', 1)", "const vorStimmen = { gebremst: false, grund: '' }"),
|
||
("quorum", "keine Ablesung am Ende", "await ablesen('Ende', 0)\nconst zaehlung = {}", "const zaehlung = {}"),
|
||
("quorum", "Haiku auch bei gueltiger Codex-Stimme", "if (!codex.abgegeben) {\n hinweis(", "if (true) {\n hinweis("),
|
||
("quorum", "usage limit nicht geprueft", " if (e.usage_limit_treffer !== 0) return '\"usage limit\" im Log'\n", ""),
|
||
("quorum", "Lesebeleg nicht geprueft", " if (!String(e.lesebeleg).includes('Lesemarke: ' + p.lesemarke)) return 'Lesebeleg fehlt oder zitiert eine andere Lesemarke'\n", ""),
|
||
("quorum", "Status vor dem Lauf nicht geprueft", "if (!/EXIT status: 0(?!\\d)/.test(start)) {", "if (false) {"),
|
||
("quorum", "codex false wird uebergangen", "if (a.codex === false) {", "if (false) {"),
|
||
("quorum", "Codex-Runden fest drei", "const runden = a.codex_runden || CODEX_RUNDEN", "const runden = CODEX_RUNDEN"),
|
||
("quorum", "kein Abbruch nach Zeitueberschreitung", "await werkzeugAgent('codex:abbruch', 'Stimmen',", "await Promise.resolve('codex:abbruch', 'Stimmen',"),
|
||
("quorum", "Warten ohne Zeitvorgabe", "'auf das Ende des Codex-Auftrags warten', LANGE)", "'auf das Ende des Codex-Auftrags warten')"),
|
||
("quorum", "Codex darf schreiben", "task --background --json", "task --background --write --json"),
|
||
("quorum", "leerer Unterschied startet Stimmen", "if (paket.leer) {", "if (false) {"),
|
||
("quorum", "unbelegter Befund zaehlt", "belegt: String(b.beleg).trim() !== ''", "belegt: true"),
|
||
("quorum", "Zusammenfuehren sieht unbelegte Befunde", "zusammenfuehrenAuftrag(belegt)", "zusammenfuehrenAuftrag(alle)"),
|
||
("quorum", "Dateien nicht getrennt", "if (datei[i] === datei[j] && bereich[i] && bereich[j]", "if (bereich[i] && bereich[j]"),
|
||
("quorum", "Schwere der Gruppe ist die niedrigste", ".sort((x, y) => RANG[x] - RANG[y])[0]", ".sort((x, y) => RANG[y] - RANG[x])[0]"),
|
||
("quorum", "Hinweis wird nachgeprueft", "gruppen.filter(g => !g.mehrheit && g.schwere !== 'hinweis')", "gruppen.filter(g => !g.mehrheit)"),
|
||
("quorum", "Mehrheitsgruppe wird nachgeprueft", "gruppen.filter(g => !g.mehrheit && g.schwere !== 'hinweis')", "gruppen.filter(g => g.schwere !== 'hinweis')"),
|
||
("quorum", "ungepruefter blockierender Befund sperrt nicht", "(gilt(g) || g.status === 'nicht_geprueft')", "gilt(g)"),
|
||
("quorum", "Urteil schon mit einer Stimme", "if (abgegeben.length < 2) {", "if (abgegeben.length < 1) {"),
|
||
("quorum", "Bericht ohne Urteil", "return ergebnis('unvollstaendig', 'kein_urteil', grund, null,", "return ergebnis('unvollstaendig', 'kein_urteil', grund, 'x',"),
|
||
("quorum", "jobId aus der ganzen Ausgabe samt Status", "start.slice(start.lastIndexOf('EXIT status:'))", "start"),
|
||
("quorum", "Teilgruppen ignoriert, Gruppe bleibt ganz", "teile.forEach(t => gruppen.push(t))", "gruppen.push(mitglieder)"),
|
||
("quorum", "Gruppe bei Aufteilung ganz aufgeloest", "teile.forEach(t => gruppen.push(t))", "(teile.length > 1 ? mitglieder.map(b => [b]) : teile).forEach(t => gruppen.push(t))"),
|
||
("quorum", "Aufteilung ohne Hinweis", "if (teile.length > 1) meldungen.push(", "if (false) meldungen.push("),
|
||
("quorum", "Toleranz null", "const TOLERANZ = 3", "const TOLERANZ = 0"),
|
||
("quorum", "Toleranz zwei", "const TOLERANZ = 3", "const TOLERANZ = 2"),
|
||
("quorum", "Toleranz vier", "const TOLERANZ = 3", "const TOLERANZ = 4"),
|
||
("quorum", "Toleranz nur nach unten", "y[0] <= x[1] + TOLERANZ", "y[0] <= x[1]"),
|
||
("quorum", "keine transitive Huelle", "if (datei[i] === datei[j] && bereich[i]", "if (i === 0 && datei[i] === datei[j] && bereich[i]"),
|
||
("quorum", "Stelle ohne Zeilennummer bleibt nie bei ihrer Teilgruppe", "if (stuecke.size === 1) verbinde(", "if (false) verbinde("),
|
||
("quorum", "Stelle ohne Zeilennummer schliesst sich der ersten Teilgruppe an", "if (stuecke.size === 1) verbinde(", "if (stuecke.size >= 1) verbinde("),
|
||
("quorum", "Stellen ohne Zeilennummer getrennt", "else if (stuecke.size === 0 && gleich.length) verbinde(", "else if (false) verbinde("),
|
||
("quorum", "a.py:42 nicht gelesen", " || /\\.[a-z0-9]+:(\\d+)(?:\\s*(?:-|–)\\s*(\\d+))?/i.exec(s)\n", ""),
|
||
("quorum", "Zeilennummer nur am Anfang gelesen", "/(?:^|[^\\p{L}\\d])(?:zeilen?", "/^\\s*(?:zeilen?"),
|
||
("quorum", "L12 nicht gelesen", "|z\\.|lines?|l)\\s*(\\d+)", "|z\\.|lines?)\\s*(\\d+)"),
|
||
("quorum", "Gedankenstrich im Bereich nicht gelesen", "(?:-|–|bis|to)\\s*l?", "(?:-|bis|to)\\s*l?"),
|
||
("quorum", "Zeilenbereich nur erste Zeile", "const bis = m[2] === undefined ? von : Number(m[2])", "const bis = von"),
|
||
("quorum", "Stelle ohne Zeilen zaehlt als Zeile 0", "if (!m) return null", "if (!m) return [0, 0]"),
|
||
("quorum", "Befundzeilen der Gruppe fehlen", "for (const b of g.mitglieder) z.push(", "for (const b of []) z.push("),
|
||
("quorum", "Stelle fehlt in der Befundzeile", ", ${b.schwere}, Stelle ${klartext(b.stelle)}): ${klartext(b.aussage)}", ", ${b.schwere}): ${klartext(b.aussage)}"),
|
||
("quorum", "Nachpruefungszeile fehlt", "if (g.nachpruefung) z.push(''", "if (false) z.push(''"),
|
||
("quorum", "Nachpruefung ohne Fundstelle", "${g.nachpruefung.urteil}, ${klartext(g.nachpruefung.fundstelle)}.", "${g.nachpruefung.urteil}."),
|
||
("quorum", "Unbelegt-Liste leer", "for (const b of x.unbelegt)", "for (const b of [])"),
|
||
("quorum", "Nicht pruefbar leer", "(x.stimmen[n].nicht_pruefbar || [])", "([])"),
|
||
("quorum", "Hinweise des Laufs fehlen im Bericht", "const alleHinweise = [...p.hinweise, ...laufHinweise]", "const alleHinweise = []"),
|
||
("quorum", "Lesemarke der Claude-Stimmen nicht geprueft", "lesemarke_ok: String(e.lesemarke).includes(p.lesemarke)", "lesemarke_ok: true"),
|
||
("quorum", "Lesemarke exakt verglichen", "lesemarke_ok: String(e.lesemarke).includes(p.lesemarke)", "lesemarke_ok: String(e.lesemarke) === p.lesemarke"),
|
||
("quorum", "Stelle fehlt in der Gruppenueberschrift", ", Stelle ${klartext(erste.stelle)}", ""),
|
||
("quorum", "Stand statt PAKET im Frontmatter", "'stand: PAKET'", "'stand: ' + p.stand"),
|
||
("quorum", "ablegen_befehl fehlt", "ablegen_befehl: md ? ablegenBefehl(paket) : null", "ablegen_befehl: null"),
|
||
("quorum", "ablegen_befehl auch ohne Bericht", "ablegen_befehl: md ? ablegenBefehl(paket) : null", "ablegen_befehl: ablegenBefehl(paket)"),
|
||
("quorum", "anzahl_dateien nicht geprueft", "if (!Number.isInteger(p.anzahl_dateien) || p.anzahl_dateien < 0) return", "if (false) return"),
|
||
("quorum", "paket_summe nicht verlangt", "'hinweise', 'paket_summe']", "'hinweise']"),
|
||
("quorum", "paket_summe nicht geprueft", "if (!/^[0-9a-f]{64}$/.test(p.paket_summe)) return", "if (false) return"),
|
||
("quorum", "ablegen_befehl ohne summe", " --summe ${q(p.paket_summe)}", ""),
|
||
("quorum", "Exit des Pakets nicht geprueft", "if (!/EXIT paket: 0(?!\\d)/.test(aus) || !m) {", "if (!m) {"),
|
||
("quorum", "Vorgabe sechs Warterunden", "const CODEX_RUNDEN = 3", "const CODEX_RUNDEN = 6"),
|
||
("quorum", "codex_runden 6 abgelehnt", "a.codex_runden <= 6", "a.codex_runden < 6"),
|
||
("quorum", "Suite ohne Vergleich des Arbeitsbaums", '" ] || echo "BEFUND: Die Suite hat', '" ] && echo "BEFUND: Die Suite hat'),
|
||
("quorum", "Arbeitsbaum-Vergleich frischt den Index auf", "const baum = `GIT_OPTIONAL_LOCKS=0 git -C", "const baum = `git -C"),
|
||
("quorum", "ordner ausserhalb des Vaults erlaubt", "if (vaultPfad(a.ordner) === null) throw", "if (false) throw"),
|
||
("quorum", "Kundendokument im Code-Projekt ohne README-Link", "if (teile[0].toLowerCase() === 'code projekte' && teile.length >= 2) return", "if (false) return"),
|
||
("quorum", "README-Tiefe um eins daneben", "'../'.repeat(teile.length - 1)", "'../'.repeat(teile.length)"),
|
||
("quorum", ".md bleibt im Wikilink", "u.replace(/\\.md$/i, '')", "u"),
|
||
("quorum", "Backslash im Ordner bleibt", "String(ordner).replace(/\\\\/g, '/')", "String(ordner)"),
|
||
("quorum", "Stimmen kennen einander", "Die anderen Stimmen kennst du nicht.", "Die anderen Stimmen sind Opus, Sonnet und Codex."),
|
||
("quorum", "Zusammenfuehren darf Dateien lesen", "Lies keine Dateien und fahre keine Befehle; du arbeitest nur mit dieser Liste.", "Lies die Dateien, wenn du sie brauchst."),
|
||
("quorum", "Klartext: Klammerfolge bleibt", r".replace(/\[{2,}/g, folge => folge.replace(/\[/g, '\\['))", ""),
|
||
("quorum", "Klartext: nur die erste Klammer entschaerft", r"folge => folge.replace(/\[/g, '\\['))", r"folge => '\\' + folge)"),
|
||
("quorum", "Klartext: Einbettung ausgenommen", r".replace(/\[{2,}/g,", r".replace(/(?<!!)\[{2,}/g,"),
|
||
("quorum", "Klartext: Tag bleibt", r".replace(/(^|[^\p{L}\p{N}_\\])#(?=[^\s#])/gu, '$1\\#')", ""),
|
||
("quorum", "Klartext: Tag nur nach Leerraum", r"(^|[^\p{L}\p{N}_\\])#", r"(^|\s)#"),
|
||
("quorum", "Klartext: Gruppenueberschrift, Stelle roh", ", Stelle ${klartext(erste.stelle)}", ", Stelle ${erste.stelle}"),
|
||
("quorum", "Klartext: Befundzeile, Stelle roh", ", ${b.schwere}, Stelle ${klartext(b.stelle)})", ", ${b.schwere}, Stelle ${b.stelle})"),
|
||
("quorum", "Klartext: Befundzeile, Aussage roh", "}): ${klartext(b.aussage)}", "}): ${b.aussage}"),
|
||
("quorum", "Klartext: Beleg roh", " Beleg: ${klartext(b.beleg)}", " Beleg: ${b.beleg}"),
|
||
("quorum", "Klartext: Nachpruefung, Fundstelle roh", "${klartext(g.nachpruefung.fundstelle)}", "${g.nachpruefung.fundstelle}"),
|
||
("quorum", "Klartext: Nachpruefung, Begruendung roh", "${klartext(g.nachpruefung.begruendung)}", "${g.nachpruefung.begruendung}"),
|
||
("quorum", "Klartext: Unbelegt, Stelle roh", "Stelle ${klartext(b.stelle)}: ", "Stelle ${b.stelle}: "),
|
||
("quorum", "Klartext: Unbelegt, Aussage roh", ")}: ${klartext(b.aussage)}", ")}: ${b.aussage}"),
|
||
("quorum", "Klartext: Nicht pruefbar roh", "- ${NAMEN[n]}: ${klartext(t)}", "- ${NAMEN[n]}: ${t}"),
|
||
("quorum", "Klartext: Hinweise roh", "'- ' + klartext(h)", "'- ' + h"),
|
||
("quorum", "Klartext: Stimmentabelle, Grund roh", "'nein: ' + klartext(s.grund)", "'nein: ' + s.grund"),
|
||
("quorum", "Klartext: ganzer Bericht, Hub-Zeile entschaerft", r"return z.join('\n')", r"return klartext(z.join('\n'))"),
|
||
]
|
||
|
||
|
||
def main() -> int:
|
||
ordner = Path(sys.argv[1]) if len(sys.argv) > 1 else HIER.parent.parent / ".claude" / "workflows"
|
||
ueberlebt, veraltet = [], []
|
||
for datei, name, alt, neu in MUTANTEN:
|
||
with tempfile.TemporaryDirectory() as tmp:
|
||
ziel = Path(tmp) / "wf"
|
||
shutil.copytree(ordner, ziel)
|
||
pfad = ziel / (datei + ".js")
|
||
text = pfad.read_text(encoding="utf-8")
|
||
if text.count(alt) != 1:
|
||
veraltet.append(name)
|
||
print(f"VERALTET {datei}: {name} (Muster {text.count(alt)}-mal)")
|
||
continue
|
||
pfad.write_text(text.replace(alt, neu), encoding="utf-8", newline="\n")
|
||
lauf = subprocess.run(["node", str(FAELLE), str(ziel)], capture_output=True, text=True, encoding="utf-8", errors="replace")
|
||
rot = [z for z in lauf.stdout.splitlines() if z.startswith("FEHLER")]
|
||
if lauf.returncode == 0:
|
||
grund = WIRKUNGSLOS.get(name)
|
||
if grund:
|
||
print(f"WIRKUNGSLOS {datei}: {name} ({grund})")
|
||
else:
|
||
ueberlebt.append(name)
|
||
print(f"UEBERLEBT {datei}: {name}")
|
||
else:
|
||
print(f"GEFANGEN {datei}: {name} ({len(rot)} rot, z. B. {rot[0][7:90] if rot else 'Absturz'})")
|
||
print(f"{len(MUTANTEN)} Mutanten, {len(ueberlebt)} ueberlebt, {len(veraltet)} veraltet")
|
||
return 2 if veraltet else 1 if ueberlebt else 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.stdout.reconfigure(errors="replace")
|
||
sys.exit(main())
|