Files
Pascal MichelandClaude Opus 5.5 0ed7d58e41 Regelwerk: Pruefaufwand nach Risiko, Paketgroesse, Drehbuch; Plan fuer Pruefung 7 (Steuerzeichen in Workflows)
Punkte 1 und 2 aus dem Paket Arbeitsweise (Pascals Freigabe 2026-09-26)
in codeworkflow.md; Plan fuer Punkt 4 unter .vault-tools/regelwerk-check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 20:51:29 +02:00

12 KiB

type, title, status, summary, tags, created, updated, modell, modell_begruendung
type title status summary tags created updated modell modell_begruendung
plan-task Task 01 — Prüfung 7 in regelwerk_check.py und ihre Fälle im Regressionslauf draft Neue Prüfung auf unsichtbare Zeichen in gespeicherten Workflows, dazu acht Angriffs- und zwei Gegenfälle.
plan-task
vault-tools
2026-09-26 2026-09-26 haiku Alle Blöcke stehen wörtlich im Plan und werden per Skript angewendet; Steuerzeichen entstehen nur über Codepunkte, keine Escape-Folge muss von Hand geschrieben werden.

Task 01 — Prüfung 7 in regelwerk_check.py und ihre Fälle

Globale Vorgaben: 00-uebersicht.md im selben Ordner, Abschnitt „Globale Vorgaben“.

Zieldateien

  • D:\Claude\.vault-tools\regelwerk-check\regelwerk_check.py (geändert)
  • D:\Claude\.vault-tools\regelwerk-check\regressionslauf.py (geändert)

Schritt 1 — Blöcke anwenden

python D:/Claude/.vault-tools/plan-kopie/plan_materialisiere.py D:/Claude/.vault-tools/regelwerk-check/docs/plans/2026-09-26-workflow-steuerzeichen D:/Claude task-01-pruefung-7.md

Erwartet: 0 Befund(e). und 'ersetzt': 13.

Geltungsbereich im Kopf von .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

Geprüft werden genau vier Orte:
  CLAUDE.md                     der Stamm
  AI/Regelwerk/*.md             die ausgelagerten Regeln
  .claude/skills/*/SKILL.md     die Skills
  .claude/agents/*.md           die Agentendefinitionen

Sechs Prüfungen, jede über eine GESCHLOSSENE Menge:

wird:

Geprüft werden genau fünf Orte:
  CLAUDE.md                     der Stamm
  AI/Regelwerk/*.md             die ausgelagerten Regeln
  .claude/skills/*/SKILL.md     die Skills
  .claude/agents/*.md           die Agentendefinitionen
  .claude/workflows/**/*.js     die gespeicherten Workflows, nur für Prüfung 7

Sieben Prüfungen, jede über eine GESCHLOSSENE Menge:

Prüfliste im Kopf von .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

  6 Planablage      Nennt eine Datei die Dateiform ohne die Ordnerform?

wird:

  6 Planablage      Nennt eine Datei die Dateiform ohne die Ordnerform?
  7 Steuerzeichen   Trägt ein gespeicherter Workflow ein unsichtbares Zeichen?

Benannte Lücken in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

  * effort in gespeicherten Workflows unter .claude/workflows/. Sie setzen
    den Effort mit agentType absichtlich nicht; eine Abweichung dort sieht
    diese Prüfung nicht.

wird:

  * effort in gespeicherten Workflows unter .claude/workflows/. Sie setzen
    den Effort mit agentType absichtlich nicht; eine Abweichung dort sieht
    diese Prüfung nicht.

  * Prüfung 7 meldet eine OBERMENGE dessen, was das Workflow-Werkzeug
    abweist. Belegt ist nur CR: plankritik.js mit CRLF scheiterte am
    2026-09-25 und 26 mit "script contains control characters that would be
    hidden in the approval dialog", mit LF lief sie. Die genaue Zeichenmenge
    des Werkzeugs steht im Programmabbild nicht eindeutig neben der Meldung
    (Claude Code 2.1.281, am Abbild gelesen 2026-09-26). Projektvorgabe ist
    deshalb: jedes Zeichen der Unicode-Kategorie Cc außer LF und jedes der
    Kategorie Cf. Darunter fallen CR, Tab, NUL, die Bidi-Steuerzeichen und
    die BOM. Ob das Werkzeug einen Tab abweist, ist unbelegt; gemeldet wird
    er trotzdem, eingerückt wird mit Leerzeichen.

  * Prüfung 7 liest nur .claude/workflows/. Die Kopie, die das Werkzeug je
    Lauf im Sitzungsordner ablegt, und ein Skript, das per scriptPath von
    anderswo kommt, sieht sie nicht.

Achsenzahl in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

Ein grüner Lauf belegt, dass keine der sechs formalen Achsen verletzt ist.

wird:

Ein grüner Lauf belegt, dass keine der sieben formalen Achsen verletzt ist.

Importe in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

import argparse
import re
import sys
from pathlib import Path

wird:

import argparse
import re
import sys
import unicodedata
from pathlib import Path

Konstanten in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

SKILLS = ".claude/skills"

wird:

SKILLS = ".claude/skills"
WORKFLOWS = ".claude/workflows"
# Die Zahl der Prüfungen steht an dieser einen Stelle; main() liest sie hier.
PRUEFUNGEN = 7

Neue Prüfung in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

# --------------------------------------------------------------------- Lauf

wird:

def pruefe_workflow_zeichen(vault: Path, befunde: list[Befund]) -> None:
    """Prüfung 7: unsichtbare Zeichen in den gespeicherten Workflows.

    Gemeldet wird je Datei einmal, mit Anzahl und erster Fundstelle, jedes Zeichen der
    Unicode-Kategorie Cc außer LF und jedes der Kategorie Cf. Gelesen wird als Bytes:
    Path.read_text übersetzt CRLF still in LF und wäre genau für den Fall blind, der am
    2026-09-25 plankritik.js lahmgelegt hat. Fehlt der Ordner, gibt es nichts zu prüfen,
    und Schweigen ist richtig.
    """
    ordner = vault / WORKFLOWS
    if not ordner.is_dir():
        return
    for datei in sorted(ordner.rglob("*.js")):
        if not datei.is_file():
            continue
        rel = datei.relative_to(vault).as_posix()
        try:
            text = datei.read_bytes().decode("utf-8")
        except UnicodeDecodeError as fehler:
            befunde.append(Befund("blockierend", rel, f"nicht als UTF-8 lesbar (ab Byte {fehler.start})"))
            continue
        funde = [
            stelle
            for stelle, zeichen in enumerate(text)
            if (zeichen != "\n" and unicodedata.category(zeichen) == "Cc")
            or unicodedata.category(zeichen) == "Cf"
        ]
        if not funde:
            continue
        erstes = funde[0]
        zeile = text.count("\n", 0, erstes) + 1
        spalte = erstes - (text.rfind("\n", 0, erstes) + 1) + 1
        befunde.append(
            Befund(
                "blockierend",
                rel,
                f"{len(funde)} unsichtbare(s) Zeichen, das erste in Zeile {zeile}, Spalte {spalte}: "
                f"U+{ord(text[erstes]):04X} (Kategorie {unicodedata.category(text[erstes])}); "
                "das Workflow-Werkzeug weist so eine Datei ab, also auf LF stellen und das Zeichen entfernen",
            )
        )


# --------------------------------------------------------------------- Lauf

Aufruf in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

        pruefe_planablage(vault, dateien, befunde); gelaufen += 1

wird:

        pruefe_planablage(vault, dateien, befunde); gelaufen += 1
        pruefe_workflow_zeichen(vault, befunde); gelaufen += 1

Schlusszeilen in .vault-tools/regelwerk-check/regelwerk_check.py

Aus:

        print(f"{len(befunde)} Befund(e) aus {gelaufen} von 6 Prüfungen.")
    else:
        print(f"Keine der {gelaufen} gelaufenen Prüfungen hat angeschlagen.")
    if gelaufen < 6 and not abbruch:
        print(f"ACHTUNG: nur {gelaufen} von 6 Prüfungen sind gelaufen — der Rest hatte keine Voraussetzung.")

wird:

        print(f"{len(befunde)} Befund(e) aus {gelaufen} von {PRUEFUNGEN} Prüfungen.")
    else:
        print(f"Keine der {gelaufen} gelaufenen Prüfungen hat angeschlagen.")
    if gelaufen < PRUEFUNGEN and not abbruch:
        print(f"ACHTUNG: nur {gelaufen} von {PRUEFUNGEN} Prüfungen sind gelaufen — der Rest hatte keine Voraussetzung.")

Grundlinie in .vault-tools/regelwerk-check/regressionslauf.py

Aus:

    for skill in (VAULT / ".claude" / "skills").glob("*/SKILL.md"):
        unter = ziel / ".claude" / "skills" / skill.parent.name
        unter.mkdir(parents=True, exist_ok=True)
        shutil.copy2(skill, unter / "SKILL.md")

wird:

    for skill in (VAULT / ".claude" / "skills").glob("*/SKILL.md"):
        unter = ziel / ".claude" / "skills" / skill.parent.name
        unter.mkdir(parents=True, exist_ok=True)
        shutil.copy2(skill, unter / "SKILL.md")
    # Prüfung 7 liest die gespeicherten Workflows. copy2 kopiert Bytes, die Grundlinie
    # trägt also dieselben Zeilenenden wie der Vault.
    (ziel / ".claude" / "workflows").mkdir(parents=True)
    for f in (VAULT / ".claude" / "workflows").glob("*.js"):
        shutil.copy2(f, ziel / ".claude" / "workflows" / f.name)

Beschädigungen für Prüfung 7 in .vault-tools/regelwerk-check/regressionslauf.py

Aus:

# Gegenfaelle: duerfen NICHT anschlagen. Ohne sie waere die Codeblock-

wird:

# Steuerzeichen entstehen nur ueber Codepunkte, nie als Escape-Folge im Quelltext: Die
# Schreibschicht hat solche Folgen schon still in echte Zeichen verwandelt
# (codeworkflow.md, "Escape-Folgen und unsichtbare Zeichen").
CR = bytes([13])
LF = bytes([10])


def _workflows(v: Path) -> list[Path]:
    dateien = sorted((v / ".claude" / "workflows").glob("*.js"))
    assert len(dateien) >= 2, "die Grundlinie braucht mindestens zwei gespeicherte Workflows"
    return dateien


def _haenge_an(f: Path, zusatz: bytes) -> None:
    f.write_bytes(f.read_bytes() + zusatz)


def s_workflow_crlf_erste_datei(v: Path) -> None:
    f = _workflows(v)[0]
    f.write_bytes(f.read_bytes().replace(LF, CR + LF))


def s_workflow_cr_letzte_datei_letzte_zeile(v: Path) -> None:
    f = _workflows(v)[-1]
    f.write_bytes(f.read_bytes().rstrip(LF) + CR + LF)


def s_workflow_nul(v: Path) -> None:
    _haenge_an(_workflows(v)[0], b"// " + bytes([0]) + LF)


def s_workflow_bidi(v: Path) -> None:
    _haenge_an(_workflows(v)[0], ("// " + chr(0x202E) + "x").encode("utf-8") + LF)


def s_workflow_bom(v: Path) -> None:
    f = _workflows(v)[-1]
    f.write_bytes(chr(0xFEFF).encode("utf-8") + f.read_bytes())


def s_workflow_tab(v: Path) -> None:
    _haenge_an(_workflows(v)[0], b"//" + bytes([9]) + b"x" + LF)


def s_workflow_kein_utf8(v: Path) -> None:
    _haenge_an(_workflows(v)[0], b"// " + bytes([0xFF]) + LF)


def s_workflow_unterordner(v: Path) -> None:
    unter = v / ".claude" / "workflows" / "unter"
    unter.mkdir()
    (unter / "tief.js").write_bytes(b"a" + CR + LF)


def g_workflow_typografie(v: Path) -> None:
    """Kein Schaden: typografische Zeichen und Umlaute sind keine Steuerzeichen."""
    zeichen = "".join(chr(c) for c in (0x201E, 0x201C, 0x2013, 0x2014, 0x2026, 0xE4, 0x20AC))
    _haenge_an(_workflows(v)[0], ("// " + zeichen).encode("utf-8") + LF)


def g_workflow_ordner_fehlt(v: Path) -> None:
    """Kein Schaden: ohne Ordner gibt es nichts zu pruefen."""
    shutil.rmtree(v / ".claude" / "workflows")


# Gegenfaelle: duerfen NICHT anschlagen. Ohne sie waere die Codeblock-

Gegenfälle in .vault-tools/regelwerk-check/regressionslauf.py

Aus:

    ("zweiter-backtick | Effort-Zelle mit zweitem Backtick-Wert", g_zweiter_backtick),
]

wird:

    ("zweiter-backtick | Effort-Zelle mit zweitem Backtick-Wert", g_zweiter_backtick),
    ("typografie | Anführung, Striche, Umlaut im Workflow", g_workflow_typografie),
    ("ordner-fehlt | kein Workflow-Ordner", g_workflow_ordner_fehlt),
]

Angriffsfälle in .vault-tools/regelwerk-check/regressionslauf.py

Aus:

    ("freigabe   | status: pending-review", s_effort_freigabe_pending, "Freigabe steht aus"),
]

wird:

    ("freigabe   | status: pending-review", s_effort_freigabe_pending, "Freigabe steht aus"),
    ("zeichen    | CRLF in der ERSTEN Workflowdatei", s_workflow_crlf_erste_datei, "U+000D"),
    ("zeichen    | CR in der LETZTEN Zeile der LETZTEN Workflowdatei", s_workflow_cr_letzte_datei_letzte_zeile, "U+000D"),
    ("zeichen    | NUL", s_workflow_nul, "U+0000"),
    ("zeichen    | Bidi-Umkehr", s_workflow_bidi, "U+202E"),
    ("zeichen    | BOM am Dateianfang", s_workflow_bom, "Zeile 1, Spalte 1: U+FEFF"),
    ("zeichen    | Tab", s_workflow_tab, "U+0009"),
    ("kodierung  | Workflow ist kein UTF-8", s_workflow_kein_utf8, "nicht als UTF-8 lesbar"),
    ("zusaetzlich| CR in einem Unterordner", s_workflow_unterordner, ".claude/workflows/unter/tief.js"),
]

Schritt 2 — Verifikation

python D:/Claude/.vault-tools/regelwerk-check/regressionslauf.py

Erwartet: Exit 0, die Grundlinie grün, jede Zeile unter ANGRIFFSLISTE beginnt mit ANGESCHLAGEN, jede unter GEGENFÄLLE mit STUMM.

python D:/Claude/.vault-tools/regelwerk-check/regelwerk_check.py --vault D:/Claude

Erwartet: Exit 0 und Keine der 7 gelaufenen Prüfungen hat angeschlagen.

Melde beide Ausgaben vollständig. Läuft etwas anders: nichts anpassen, melden.