plan-kopie: Zieldatei ohne Endung über ## Zieldateien, sonst Befund statt stillem Fehlziel

Anlass: Plankritik Paket R1 im Edge-Stack, Befunde 1 und 5. Die Überschrift
"Stelle 30 — `host/logrotate/edge-traefik`" setzte kein Ziel; der Block
"Die ganze Datei ist neu" überschrieb tests/systemd/test-units.sh mit Exit 0.

- ordne() in plan_materialisiere.py, von plan_bloecke.py importiert: Ziel ist
  der letzte PFAD-Treffer, ohne ihn der letzte Token, der unter ## Zieldateien
  steht (absolut relativ zu --projekt bzw. zum Ordner vor docs/plans).
- Überschrift ohne Ziel, die nach einer Datei aussieht (relativer Pfad mit /,
  Datei in Kopie/Projekt, Dateiname eines Eintrags): jeder schreibende Block
  danach ist Befund, Exit 2, nichts geschrieben.
- plan_ziel_treiber.py: 33 Fälle; Ursprung 22 rot, jetzt 33 grün;
  21 eingebaute Fehler gefangen.
- Vorher/nachher über 2 223 Plandateien: Abweichung nur R1 task-01 und
  Trading Task 90, beide jetzt ins richtige Ziel.
- LIESMICH und planer.md nachgezogen.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-17 11:10:42 +02:00
1 parent 07675bf541
commit 5cbe75c5e3
5 files changed
+433 -21

No files matched your search

+37 -8
View File
@@ -2,9 +2,19 @@
Koordinator-Werkzeug im Scratchpad. Geltungsbereich (Fangnetz, kein Nachweis):
- Sieht nur Codebloecke, deren letzte nicht leere Zeile vor dem Zaun genau "Aus:" oder
"wird:" ist (Projektkonvention der Planer-Taskdateien seit Teil 5). Die Zieldatei ist der
letzte Pfad in Backticks in einer Ueberschrift (##, ###, ####) oberhalb des Blocks, der
auf eine im Projekt vorhandene oder unter "## Zieldateien" genannte Datei zeigt.
"wird:" ist (Projektkonvention der Planer-Taskdateien seit Teil 5). Welche Datei Ziel ist, regelt
ordne() in plan_materialisiere.py (importiert, die Regel steht dort im Kopf): der letzte PFAD-Treffer
einer Ueberschrift (##, ###, ####) oberhalb des Blocks, ohne ihn ein Token, das genau so unter
"## Zieldateien" steht (absolute Eintraege relativ zu --projekt). Ob die Zieldatei im Projekt liegt,
prueft erst der Modus. Bis 2026-10-17 stand hier, das Ziel muesse unter Zieldateien stehen; der Code
tat das nie.
- Befund statt stillem Ziel (seit 2026-10-17, Plankritik Paket R1 im Edge-Stack): Nennt eine
Ueberschrift kein Ziel, aber einen Token, der nach einer Datei aussieht (relativer Pfad mit
Schraegstrich; Name, der direkt in --projekt als Datei liegt oder Dateiname eines Eintrags ist), ist
jeder Aus- und wird-Block danach ein Befund, bis eine Ueberschrift mit Ziel kommt; vorher wurde still
gegen das vorige Ziel geprueft. Luecken wie in plan_materialisiere.py; Nachweis: plan_ziel_treiber.py.
- Exit 3 hat Vorrang: Wurde in keiner Taskdatei ein Block gegen eine Datei geprueft, endet es mit 3,
auch wenn Befunde ausgegeben sind.
- Modus vor: jeder Aus-Block steht genau einmal in seiner Zieldatei (0 = Befund, >1 = Befund
"mehrdeutig"). Belegt, dass der Plan den heutigen Stand byte-genau zitiert.
- Modus nach: jeder wird-Block steht in der Zieldatei; jeder Aus-Block steht nicht mehr darin,
@@ -18,11 +28,15 @@ Exit: 0 kein Befund, 2 Befunde, 3 kein einziger Aus-/wird-Block gefunden (nichts
import argparse
import re
import sys
from pathlib import Path
from pathlib import Path, PureWindowsPath
# Die Zielregel (PFAD, Zieldateien, offene Token) steht einmal, in plan_materialisiere.py im selben Ordner;
# der Ordner kommt ausdruecklich in den Suchpfad, sonst scheitert der Import unter "python -I"
sys.path.insert(0, str(Path(__file__).resolve().parent))
from plan_materialisiere import ordne, zieldateien # noqa: E402
ZAUN = re.compile(r"^```")
UEBERSCHRIFT = re.compile(r"^#{2,4} ")
PFAD = re.compile(r"`([^`\s]+/[^`\s]+\.[A-Za-z0-9]+|[^`\s/]+\.(?:py|ts|tsx|toml|md|css|json))`")
def normal(text: str) -> str:
@@ -32,13 +46,20 @@ def normal(text: str) -> str:
def lies_bloecke(taskdatei: Path, projekt: Path) -> list[dict]:
zeilen = normal(taskdatei.read_text(encoding="utf-8")).split("\n")
bloecke: list[dict] = []
# Absolute Eintraege unter "## Zieldateien" zaehlen relativ zu --projekt, wie in welle_vergleich.py
geplant = zieldateien(zeilen, [PureWindowsPath(projekt.absolute()), PureWindowsPath(projekt.resolve())])
ziel: str | None = None
# (Zeile, Token) der letzten Ueberschrift ohne Ziel, die nach einer Datei aussieht; None, sobald ein Ziel kommt
ungeklaert: tuple[int, list[str]] | None = None
i = 0
while i < len(zeilen):
zeile = zeilen[i]
if UEBERSCHRIFT.match(zeile):
for treffer in PFAD.findall(zeile):
ziel = treffer
neu, offen = ordne(zeile, geplant, projekt)
if neu:
ziel, ungeklaert = neu, None
elif offen:
ungeklaert = (i + 1, offen)
if ZAUN.match(zeile):
start = i
# Ein Zaun aus n Backticks schliesst erst mit mindestens n Backticks: Ein
@@ -58,7 +79,8 @@ def lies_bloecke(taskdatei: Path, projekt: Path) -> list[dict]:
if k >= 0:
marke = zeilen[k].strip()
if marke in ("Aus:", "wird:"):
bloecke.append({"art": marke[:-1], "ziel": ziel, "zeile": start + 1, "text": "\n".join(inhalt)})
bloecke.append({"art": marke[:-1], "ziel": ziel, "zeile": start + 1, "text": "\n".join(inhalt),
"ungeklaert": ungeklaert})
i += 1
return bloecke
@@ -78,6 +100,13 @@ def main() -> int:
bloecke = lies_bloecke(taskdatei, projekt)
paare = 0
for nummer, block in enumerate(bloecke):
if block["ungeklaert"]:
zeile_u, pfade = block["ungeklaert"]
print(f"BEFUND {taskdatei.name}:{block['zeile']}: {block['art']}-Block unter Ueberschrift in Zeile {zeile_u}, "
f"die nennt {', '.join(f'`{p}`' for p in pfade)} und keine erkannte Zieldatei (fehlt sie unter ## Zieldateien?); "
f"geprueft wuerde gegen {block['ziel'] or 'nichts'}")
befunde += 1
continue
if block["ziel"] is None:
print(f"BEFUND {taskdatei.name}:{block['zeile']}: {block['art']}-Block ohne Zieldatei in einer Ueberschrift")
befunde += 1