Files
Pascal MichelandClaude Opus 5.5 b2b52dd0e8 Waisen und Quorum: Ausnahmemuster fuer Programm- und Vorlagendateien, Abschluss Waisen, Plan Quorum Task 06
waisen-ausnahmen.txt: Skills, Plugins, Testfixtures, Programmausgaben und Vorlagen
zaehlen nicht als Waisen. Waisen-Uebersicht: Abschluss mit Belegen je Fertig-heisst-Punkt.
Quorum: Task 06 entschaerft Freitext im Bericht (Fund aus dem ersten echten Lauf).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:07:31 +02:00

28 KiB

type, title, status, summary, tags, created, updated, modell, effort, modell_begruendung
type title status summary tags created updated modell effort modell_begruendung
plan-task Task 06 — Nachbesserung: Bericht entschärft Obsidian-Syntax aus Stimmen und Nachprüfung draft quorum.js schickt jeden Freitext aus Stimmen, Zusammenführen und Nachprüfung durch eine neue Funktion klartext(), damit zitierte Wikilinks, Einbettungen und Tags im Bericht lesbarer Klartext bleiben; Hub-Zeile und Frontmatter bleiben unverändert.
plan-task
vault-tools
codeworkflow
2026-10-05 2026-10-05 sonnet low Abschrift per plan_materialisiere.py mit genannten Zieldateien und Verboten, ohne Fließtext und ohne typografische Zeichen in Codeblöcken; sonnet mit effort low statt haiku, weil der Mutantenlauf länger dauert als ein Aufruf des Bash-Werkzeugs und im Hintergrund gestartet und abgefragt werden muss.

Übersicht: 00-uebersicht.md

Task 06 — Nachbesserung: Bericht entschärft Obsidian-Syntax aus Stimmen und Nachprüfung

Die globalen Vorgaben stehen in 00-uebersicht.md im selben Ordner, Abschnitt „Globale Vorgaben“. Arbeitsverzeichnis ist D:/Claude.

Voraussetzung: Task 01 bis 05 sind angewendet und committet. Dieser Task ist ein Delta gegen den heutigen Baum; die Taskdateien 01 bis 05 bleiben unverändert.

Anlass

  • quorum.js übernimmt Freitext aus Stimmen, Zusammenführen und Nachprüfung ungefiltert in bericht_md.
  • Zitiert eine Stimme Obsidian-Syntax, wird sie im Vault lebendig. Beleg: Der erste echte Bericht enthält zwei tote Wikilinks, einen unter „Nicht prüfbar“, einen in einer Befundaussage; der Linkcheck meldet beide, im Graphen entstehen Geisterknoten.
  • Ein zitierter Tag wie #needs-review würde den Bericht als Review-Notiz zählen lassen, eine Einbettung wie ![[c]] eine fremde Datei einbetten.
  • Der erste echte Bericht selbst wird hier nicht angefasst (offener Punkt unten).

Entscheidung: Backslash vor Klammer und Raute

Eine neue Funktion klartext() setzt einen Backslash vor jede öffnende eckige Klammer in einer Folge von zwei oder mehr und vor jede Raute, die einen Tag beginnen könnte. Die Leseansicht zeigt den Text wie zitiert.

Zitat der Stimme im Quelltext des Berichts Regel
[[a/b]] \[\[a/b]] Klammerfolge
![[c]] !\[\[c]] Klammerfolge, das ! bleibt
#needs-review \#needs-review Raute am Wortanfang vor einem Zeichen, das kein Leerraum und keine Raute ist
[[#x]] \[\[\#x]] beides; vor der Raute steht eine Klammer, kein Buchstabe
x = 1 # Kommentar unverändert Raute vor Leerzeichen
C#, seite.md#abschnitt unverändert am Wortende bzw. Buchstabe davor

Warum diese Form:

  • Backslash ist das Markdown-Escape für [ und # (CommonMark erlaubt es für jedes ASCII-Satzzeichen). Es wirkt je Zeichen, braucht keine Grenzen eines Links und ändert bei zweiter Anwendung nichts mehr.
  • Backticks um das Zitat scheiden aus: Freitext trägt selbst Backticks, dann bricht die Code-Spanne, und Anfang und Ende jedes Links müssten erkannt werden.
  • Gelesen im Quelltext der Vault-Werkzeuge: linkcheck.py und waisen.py sehen in \[\[x]] keinen Link (Befund des Koordinators). sitzungsstart.py zählt #needs-review nur nach Leerraum oder am Zeilenanfang (Regex (?<!\S)#needs-review, Zeile 66), \#needs-review also nicht.
  • Nicht gemessen: Ob Obsidian 1.13.7 den Backslash vor Wikilink, Einbettung und Tag beachtet (Leseansicht, Live-Vorschau, Graph, Tag-Index, Review-Queue.base). Das bestätigen Pascal oder der Koordinator in der App, siehe „Offene Punkte“.
  • Die Tag-Regel ist bewusst weiter als die, die Obsidian vermutlich anwendet (nur nach Leerraum): Sie entschärft auch eine Raute hinter [, ( oder einem Anführungszeichen. Kostet nur einen Backslash im Quelltext.

Wo klartext() greift und wo nicht:

Stelle in bericht_md Quelle entschärft
Stimmentabelle, Spalte abgegeben, Grund Codex-Hilfsagent (job_status), Absturzmeldung ja
Gruppenüberschrift, Stelle Stimme ja
Befundzeile, Stelle und Aussage; Beleg Stimme ja
Nachprüfung, Fundstelle und Begründung Nachprüfung ja
Unbelegt, Stelle und Aussage Stimme ja
Nicht prüfbar Stimme ja
Hinweise des Laufs Paket, Zusammenführen (IDs, Stellen), Codex-Grund ja
Hub-Zeile (Projekt: … bzw. Übersicht: …) Workflow nein, bleibt Link
Frontmatter, Urteil-Zeile, Basis und Stand Workflow, Paket nein
Datei in Backticks Stimme nein, Obsidian lässt Code-Spannen in Ruhe
Felder des JSON-Ergebnisses (gruppen, hinweise) alle nein, sie gehen nicht in den Vault

Die Fälle und Mutanten unten binden genau diese Form. Fällt die Bestätigung in Obsidian negativ aus, braucht es eine neue Nachbesserung mit anderer Form, anderen Erwartungstexten und anderen Mutanten.

Zieldateien

  • D:\Claude\.vault-tools\workflow-pruefung\faelle_workflows.mjs (geändert)
  • D:\Claude\.vault-tools\workflow-pruefung\mutanten_workflows.py (geändert)
  • D:\Claude\.claude\workflows\quorum.js (geändert)

Verbote

  • Keine andere Datei im Vault, auch nicht attrappe.mjs, die übrigen Workflows, die Taskdateien 01 bis 05 und kein Bericht unter docs/quorum/ eines Projekts.
  • Außerhalb des Vaults nur der Arbeitsordner /tmp/quorum-task06 in der Git-Bash. Ihn nicht löschen; der Koordinator räumt ihn nach der Prüfung.
  • Kein Lauf eines echten Workflows, kein Aufruf des Codex-Plugins.
  • Nichts von Hand schreiben oder umformulieren; die Blöcke wendet nur plan_materialisiere.py an, in den Baum kommen sie nur per cp aus dem Arbeitsordner. Ein Widerspruch zwischen Plan und Baum wird gemeldet.
  • Während der Mutantenlauf läuft, keine Datei unter .claude/workflows/ und .vault-tools/workflow-pruefung/ ändern.
  • Kein Commit, kein git add.

Schritt 1 — Vorprüfung

python D:/Claude/.vault-tools/plan-kopie/plan_bloecke.py vor D:/Claude/.vault-tools/workflow-pruefung/docs/plans/2026-09-29-quorum/task-06-bericht-entschaerfen.md --projekt D:/Claude

Erwartet: 0 Befund(e). Merk dir die Zahl hinter geprueft:, Schritt 2 vergleicht damit. Bei einem Befund anhalten und melden.

GIT_OPTIONAL_LOCKS=0 git -C D:/Claude status --porcelain -- .claude/workflows .vault-tools/workflow-pruefung; echo "ENDE"; node D:/Claude/.vault-tools/workflow-pruefung/faelle_workflows.mjs | tail -n 1; echo "EXIT ${PIPESTATUS[0]}"

Erwartet: direkt ENDE (beide Ordner sauber), dann eine Zeile mit 0 gescheitert, dann EXIT 0. Steht vor ENDE eine Zeile oder scheitert ein Fall: anhalten und melden.

Schritt 2 — Blöcke in einem Arbeitsordner anwenden

Der Arbeitsordner trägt Kopien der drei Zieldateien. So kommen Fälle und Mutanten in Schritt 3 vor der Umsetzung in Schritt 4 in den Baum.

A="$(cygpath -m -l /tmp)/quorum-task06" && mkdir "$A" && mkdir -p "$A/k/.claude/workflows" "$A/k/.vault-tools/workflow-pruefung" && cp D:/Claude/.claude/workflows/quorum.js "$A/k/.claude/workflows/" && cp D:/Claude/.vault-tools/workflow-pruefung/faelle_workflows.mjs D:/Claude/.vault-tools/workflow-pruefung/mutanten_workflows.py "$A/k/.vault-tools/workflow-pruefung/" && python D:/Claude/.vault-tools/plan-kopie/plan_materialisiere.py D:/Claude/.vault-tools/workflow-pruefung/docs/plans/2026-09-29-quorum "$A/k" task-06-bericht-entschaerfen.md; echo "EXIT $?"

Erwartet: In der Zählerzeile 'neu': 0, 'angehaengt': 0, 'geloescht': 0 und 'ersetzt' gleich der Zahl hinter geprueft: aus Schritt 1; dann 0 Befund(e). und EXIT 0. Scheitert mkdir "$A", weil der Ordner schon da ist: anhalten und melden, nichts löschen.

Fälle in .vault-tools/workflow-pruefung/faelle_workflows.mjs

Neue Fälle am Ende des Abschnitts quorum. Jeder lässt Stimmen, Nachprüfung oder Hilfsagenten Wikilink, Einbettung, Tag und Abschnittslink zitieren und verlangt: im Bericht keins davon, der Text steht lesbar in der Form aus der Tabelle oben, die Linkzeile des Workflows bleibt ein Link.

Aus:

// ---------------------------------------------------------------- Lauf

wird:

// Klartext im Bericht (Nachbesserung Task 06): Stimmen, Nachprüfung und Hilfsagenten zitieren
// Wikilink, Einbettung, Tag und Abschnittslink. Q_KLAR ist derselbe Text, wie er im Bericht
// stehen muss: lesbar, aber ohne Link, Einbettung und Tag.
const Q_ZITATE = '[[a/b]] ![[c]] #needs-review [[#x]]'
const Q_KLAR = '\\[\\[a/b]] !\\[\\[c]] \\#needs-review \\[\\[\\#x]]'
const qZ = schwere => ({ datei: 'src/a.py', stelle: Q_ZITATE, schwere, aussage: Q_ZITATE, beleg: Q_ZITATE })
// Was Obsidian als Wikilink, Einbettung oder Tag lesen könnte. Der Tag zählt hier schon hinter
// jedem Zeichen außer Buchstabe, Ziffer, Unterstrich und Backslash, nicht erst nach Leerraum.
function qObsidian(text) {
  const funde = []
  if (/(^|[^!])\[\[/.test(text)) funde.push('Wikilink')
  if (text.includes('![[')) funde.push('Einbettung')
  if (/(^|[^\p{L}\p{N}_\\])#[^\s#]/u.test(text)) funde.push('Tag')
  return funde
}
const qSauber = (soll, text) => { const f = qObsidian(text); soll(f.length === 0, 'Obsidian-Syntax im Bericht: ' + f.join(', ')) }

fall('quorum', 'Klartext im Bericht: Stelle, Aussage und Beleg einer Gruppe ohne Wikilink, Einbettung und Tag, README-Link bleibt', async () => {
  const { fehler, soll } = pruefe()
  const { ergebnis: e } = await QR(Q_ARGS, { stimmen: { opus: [qZ('blockierend')], sonnet: [qZ('blockierend')], codex: [] } })
  const md = (e && e.bericht_md) || ''
  qSauber(soll, md)
  for (const z of [
    '### G1: blockierend, Mehrheit (Opus, Sonnet)\n\n`src/a.py`, Stelle ' + Q_KLAR + '\n',
    '- O1 (Opus, blockierend, Stelle ' + Q_KLAR + '): ' + Q_KLAR + '\n  Beleg: ' + Q_KLAR + '\n',
    '\n\nProjekt: [README](../../README.md)\n\n',
  ]) soll(md.includes(z), 'Bericht ohne ' + JSON.stringify(z))
  return fehler
})

fall('quorum', 'Klartext im Bericht: Fundstelle und Begründung der Nachprüfung ohne Wikilink, Einbettung und Tag', async () => {
  const { fehler, soll } = pruefe()
  const stimmen = { opus: [qB('src/a.py', '10', 'blockierend')], sonnet: [qB('src/b.py', '5', 'hinweis')], codex: [] }
  const urteile = pr => ({ urteile: [...pr.matchAll(/^Befund ([A-Z]\d+):$/gm)].map(m => ({ id: m[1], urteil: 'bestaetigt', fundstelle: Q_ZITATE, begruendung: Q_ZITATE })) })
  const { ergebnis: e } = await QR(Q_ARGS, { stimmen, urteile })
  const md = (e && e.bericht_md) || ''
  qSauber(soll, md)
  const z = '\nNachprüfung: bestaetigt, ' + Q_KLAR + '. ' + Q_KLAR + '\n'
  soll(md.includes(z), 'Bericht ohne ' + JSON.stringify(z))
  return fehler
})

fall('quorum', 'Klartext im Bericht: Unbelegt und Nicht prüfbar ohne Wikilink, Einbettung und Tag', async () => {
  const { fehler, soll } = pruefe()
  const stimmen = { opus: [{ ...qZ('wichtig'), beleg: '' }], sonnet: [qB('src/b.py', '5', 'hinweis')], codex: [] }
  const { ergebnis: e } = await QR(Q_ARGS, { stimmen, nichtPruefbar: { sonnet: [Q_ZITATE] } })
  const md = (e && e.bericht_md) || ''
  qSauber(soll, md)
  for (const z of [
    '## Unbelegt\n\n- O1 (Opus, wichtig) `src/a.py`, Stelle ' + Q_KLAR + ': ' + Q_KLAR + '\n',
    '## Nicht prüfbar\n\n- Sonnet: ' + Q_KLAR + '\n',
  ]) soll(md.includes(z), 'Bericht ohne ' + JSON.stringify(z))
  return fehler
})

fall('quorum', 'Klartext im Bericht: Hinweise des Laufs und Grund der Codex-Stimme ohne Wikilink, Einbettung und Tag', async () => {
  const { fehler, soll } = pruefe()
  const stimmen = { opus: [qB('src/a.py', '10', 'hinweis')], sonnet: [qB('src/b.py', '5', 'hinweis')] }
  const k = { stimmen, paket: { hinweise: [Q_ZITATE] }, codexEnde: { job_status: Q_ZITATE }, gruppen: () => ({ gruppen: [{ ids: [Q_ZITATE], begruendung: 'zitiert' }] }) }
  const { ergebnis: e } = await QR(Q_ARGS, k)
  const md = (e && e.bericht_md) || ''
  qSauber(soll, md)
  for (const z of [
    '| Codex | nein: Codex-Auftrag endete mit ' + Q_KLAR + ' |',
    '## Hinweise des Laufs\n\n- ' + Q_KLAR + '\n',
    '\n- Codex ohne gültige Stimme (Codex-Auftrag endete mit ' + Q_KLAR + '); Haiku ist eingesprungen\n',
    '\n- Zusammenführen: unbekannte ID ' + Q_KLAR + ' verworfen\n',
  ]) soll(md.includes(z), 'Bericht ohne ' + JSON.stringify(z))
  return fehler
})

fall('quorum', 'Klartext im Bericht eines Kundendokuments: Hub-Zeile bleibt Wikilink, Frontmatter bleibt, zitierte Syntax nicht', async () => {
  const { fehler, soll } = pruefe()
  const K = 'D:/Claude/IT Workspace/Beispiel/abgabe'
  const HUB = 'Übersicht: [[IT Workspace/Beispiel/abgabe/00 Übersicht|00 Übersicht.md]]'
  const args = { anlass: 'kundendokument', ordner: K, dateien: ['kapitel/01.md'], uebersicht: '00 Übersicht.md' }
  const paket = { anlass: 'kundendokument', art: 'doku', wurzel: K, basis: 'erstes Quorum', stand: 'sha256:' + 'c'.repeat(64), vorher: null, nummer: 1 }
  const { ergebnis: e } = await QR(args, { paket, stimmen: { opus: [qZ('blockierend')], sonnet: [qZ('blockierend')], codex: [] } })
  const md = (e && e.bericht_md) || ''
  qSauber(soll, md.replace(HUB, ''))
  soll(md.includes('\n\n' + HUB + '\n\n') && md.split('[[').length === 2, 'Hub-Zeile nicht genau einmal als Wikilink: ' + JSON.stringify(md.split('\n').filter(x => x.includes('Übersicht'))))
  for (const z of ['---\ntype: note\n', '\ntags: [quorum]\n', '- O1 (Opus, blockierend, Stelle ' + Q_KLAR + '): ' + Q_KLAR + '\n']) soll(md.includes(z), 'Bericht ohne ' + JSON.stringify(z))
  return fehler
})

// ---------------------------------------------------------------- Lauf

Mutanten in .vault-tools/workflow-pruefung/mutanten_workflows.py

Drei bestehende Mutanten zielen auf Stellen, die jetzt klartext() tragen; ohne Anpassung meldet der Lauf sie als veraltet.

Aus:

    ("quorum", "Stelle fehlt in der Befundzeile", ", ${b.schwere}, Stelle ${b.stelle}): ${b.aussage}", ", ${b.schwere}): ${b.aussage}"),

wird:

    ("quorum", "Stelle fehlt in der Befundzeile", ", ${b.schwere}, Stelle ${klartext(b.stelle)}): ${klartext(b.aussage)}", ", ${b.schwere}): ${klartext(b.aussage)}"),

Aus:

    ("quorum", "Nachpruefung ohne Fundstelle", "${g.nachpruefung.urteil}, ${g.nachpruefung.fundstelle}.", "${g.nachpruefung.urteil}."),

wird:

    ("quorum", "Nachpruefung ohne Fundstelle", "${g.nachpruefung.urteil}, ${klartext(g.nachpruefung.fundstelle)}.", "${g.nachpruefung.urteil}."),

Aus:

    ("quorum", "Stelle fehlt in der Gruppenueberschrift", ", Stelle ${erste.stelle}", ""),

wird:

    ("quorum", "Stelle fehlt in der Gruppenueberschrift", ", Stelle ${klartext(erste.stelle)}", ""),

Neue Mutanten, alle mit dem Namensanfang Klartext:. Die ersten bauen eine falsche Regel in klartext() ein (Klammerfolge, erste Klammer, Einbettung, Tag, Abschnittslink), die folgenden lassen je eine Stelle roh, der letzte entschärft den ganzen Bericht samt Hub-Zeile. Muster mit Backslash stehen als Python-Raw-String mit Präfix r, damit der Backslash wörtlich bleibt.

Aus:

    ("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."),
]

wird:

    ("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'))"),
]

Welcher Fall welchen Mutanten fängt:

Mutant rot wird mindestens
Regel-Mutanten (Klammerfolge, erste Klammer, Einbettung, Tag, Tag nach Leerraum) jeder neue Fall
Gruppenüberschrift, Befundzeile, Beleg Stelle, Aussage und Beleg einer Gruppe
Nachprüfung, Fundstelle und Begründung Fundstelle und Begründung der Nachprüfung
Unbelegt, Nicht prüfbar Unbelegt und Nicht prüfbar
Hinweise, Stimmentabelle Hinweise des Laufs und Grund der Codex-Stimme
ganzer Bericht Bericht eines Kundendokuments (Hub-Zeile), dazu die bestehenden Hub-Fälle mit Wikilink

Umsetzung in .claude/workflows/quorum.js

Kopfkommentar:

Aus:

// Laufzeit: Date.now() und new Date() gibt es im Laufzeitsystem nicht. Die Codex-Laufzeit kommt
// aus dem Job, die der Claude-Stimmen steht im Bericht als nicht gemessen.

wird:

// Laufzeit: Date.now() und new Date() gibt es im Laufzeitsystem nicht. Die Codex-Laufzeit kommt
// aus dem Job, die der Claude-Stimmen steht im Bericht als nicht gemessen.
// Bericht: Freitext aus Stimmen, Zusammenführen und Nachprüfung geht nur durch klartext() in
// bericht_md. Ein zitierter Wikilink, eine Einbettung oder ein Tag würde im Vault sonst lebendig;
// Hub-Zeile und Frontmatter baut der Workflow selbst, sie bleiben echte Links bzw. Felder.

Neue Funktion klartext() vor berichtVoll():

Aus:

function berichtVoll(a, p, x) {

wird:

// Freitext als Klartext für Obsidian: Jede öffnende eckige Klammer in einer Folge von zwei oder
// mehr bekommt einen Backslash, damit kein Wikilink und keine Einbettung entsteht. Eine Raute
// bekommt einen Backslash, wenn ihr kein Buchstabe, keine Ziffer, kein Unterstrich und kein
// Backslash vorausgeht und weder Leerraum noch eine zweite Raute folgt; so entsteht kein Tag.
// Die Leseansicht zeigt den Text wie zitiert. Zweimal angewendet ändert sich nichts mehr.
function klartext(s) {
  return String(s)
    .replace(/\[{2,}/g, folge => folge.replace(/\[/g, '\\['))
    .replace(/(^|[^\p{L}\p{N}_\\])#(?=[^\s#])/gu, '$1\\#')
}

function berichtVoll(a, p, x) {

Stimmentabelle, Grund einer nicht abgegebenen Stimme:

Aus:

    z.push(`| ${name} | ${s.abgegeben ? 'ja' : 'nein: ' + s.grund} | ${c.gesamt} | ${c.belegt} | ${c.bestaetigt} | ${c.alleinfunde} | ${laufzeit(s.dauer_s)} |`)

wird:

    z.push(`| ${name} | ${s.abgegeben ? 'ja' : 'nein: ' + klartext(s.grund)} | ${c.gesamt} | ${c.belegt} | ${c.bestaetigt} | ${c.alleinfunde} | ${laufzeit(s.dauer_s)} |`)

Gruppenüberschrift, Befundzeilen, Beleg und Nachprüfung:

Aus:

    z.push(`### G${g.nr}: ${g.schwere}, ${STATUS_TEXT[g.status]} (${g.stimmen.map(n => NAMEN[n]).join(', ')})`, '', `\`${erste.datei}\`, Stelle ${erste.stelle}`, '')
    for (const b of g.mitglieder) z.push(`- ${b.id} (${NAMEN[b.stimme]}, ${b.schwere}, Stelle ${b.stelle}): ${b.aussage}`, `  Beleg: ${b.beleg}`)
    if (g.nachpruefung) z.push('', `Nachprüfung: ${g.nachpruefung.urteil}, ${g.nachpruefung.fundstelle}. ${g.nachpruefung.begruendung}`)

wird:

    z.push(`### G${g.nr}: ${g.schwere}, ${STATUS_TEXT[g.status]} (${g.stimmen.map(n => NAMEN[n]).join(', ')})`, '', `\`${erste.datei}\`, Stelle ${klartext(erste.stelle)}`, '')
    for (const b of g.mitglieder) z.push(`- ${b.id} (${NAMEN[b.stimme]}, ${b.schwere}, Stelle ${klartext(b.stelle)}): ${klartext(b.aussage)}`, `  Beleg: ${klartext(b.beleg)}`)
    if (g.nachpruefung) z.push('', `Nachprüfung: ${g.nachpruefung.urteil}, ${klartext(g.nachpruefung.fundstelle)}. ${klartext(g.nachpruefung.begruendung)}`)

Unbelegt:

Aus:

  for (const b of x.unbelegt) z.push(`- ${b.id} (${NAMEN[b.stimme]}, ${b.schwere}) \`${b.datei}\`, Stelle ${b.stelle}: ${b.aussage}`)

wird:

  for (const b of x.unbelegt) z.push(`- ${b.id} (${NAMEN[b.stimme]}, ${b.schwere}) \`${b.datei}\`, Stelle ${klartext(b.stelle)}: ${klartext(b.aussage)}`)

Nicht prüfbar:

Aus:

  const offen = Object.keys(x.stimmen).flatMap(n => (x.stimmen[n].nicht_pruefbar || []).map(t => `- ${NAMEN[n]}: ${t}`))

wird:

  const offen = Object.keys(x.stimmen).flatMap(n => (x.stimmen[n].nicht_pruefbar || []).map(t => `- ${NAMEN[n]}: ${klartext(t)}`))

Hinweise des Laufs:

Aus:

  z.push(...(alleHinweise.length ? alleHinweise.map(h => '- ' + h) : ['keine']), '')

wird:

  z.push(...(alleHinweise.length ? alleHinweise.map(h => '- ' + klartext(h)) : ['keine']), '')

Schritt 3 — Fälle und Mutanten in den Baum, Rot im Wortlaut

quorum.js im Baum ist noch die alte Fassung; sie ist die falsche Umsetzung, an der jeder neue Fall rot werden muss.

A="$(cygpath -m -l /tmp)/quorum-task06" && cp "$A/k/.vault-tools/workflow-pruefung/faelle_workflows.mjs" "$A/k/.vault-tools/workflow-pruefung/mutanten_workflows.py" D:/Claude/.vault-tools/workflow-pruefung/ && node D:/Claude/.vault-tools/workflow-pruefung/faelle_workflows.mjs > "$A/rot.txt"; echo "EXIT $?"; grep -A 1 --no-group-separator '^FEHLER' "$A/rot.txt"; grep '^HINWEIS' "$A/rot.txt"; echo "ENDE"

Erwartet genau diese Ausgabe, Zeile für Zeile:

EXIT 1
FEHLER quorum: Klartext im Bericht: Stelle, Aussage und Beleg einer Gruppe ohne Wikilink, Einbettung und Tag, README-Link bleibt
       Obsidian-Syntax im Bericht: Wikilink, Einbettung, Tag
FEHLER quorum: Klartext im Bericht: Fundstelle und Begründung der Nachprüfung ohne Wikilink, Einbettung und Tag
       Obsidian-Syntax im Bericht: Wikilink, Einbettung, Tag
FEHLER quorum: Klartext im Bericht: Unbelegt und Nicht prüfbar ohne Wikilink, Einbettung und Tag
       Obsidian-Syntax im Bericht: Wikilink, Einbettung, Tag
FEHLER quorum: Klartext im Bericht: Hinweise des Laufs und Grund der Codex-Stimme ohne Wikilink, Einbettung und Tag
       Obsidian-Syntax im Bericht: Wikilink, Einbettung, Tag
FEHLER quorum: Klartext im Bericht eines Kundendokuments: Hub-Zeile bleibt Wikilink, Frontmatter bleibt, zitierte Syntax nicht
       Obsidian-Syntax im Bericht: Wikilink, Einbettung, Tag
ENDE
  • Jede Zeile unter einer FEHLER-Zeile beginnt mit sieben Leerzeichen.
  • Keine Zeile HINWEIS: Die Grundlinie des Quorums bleibt grün.
  • Weicht die Ausgabe ab: nichts anpassen, anhalten, rot.txt vollständig melden.

Schritt 4 — Umsetzung in den Baum, grün, Byte-Vergleich

A="$(cygpath -m -l /tmp)/quorum-task06" && cp "$A/k/.claude/workflows/quorum.js" D:/Claude/.claude/workflows/ && node D:/Claude/.vault-tools/workflow-pruefung/faelle_workflows.mjs > "$A/gruen.txt"; echo "EXIT $?"; grep -E '^(FEHLER|HINWEIS)' "$A/gruen.txt"; tail -n 1 "$A/gruen.txt"; echo "ENDE"

Erwartet: EXIT 0, dann genau eine Zeile, die 0 gescheitert enthält, dann ENDE.

A="$(cygpath -m -l /tmp)/quorum-task06" && cmp "$A/k/.claude/workflows/quorum.js" D:/Claude/.claude/workflows/quorum.js && cmp "$A/k/.vault-tools/workflow-pruefung/faelle_workflows.mjs" D:/Claude/.vault-tools/workflow-pruefung/faelle_workflows.mjs && cmp "$A/k/.vault-tools/workflow-pruefung/mutanten_workflows.py" D:/Claude/.vault-tools/workflow-pruefung/mutanten_workflows.py; echo "EXIT $?"; GIT_OPTIONAL_LOCKS=0 git -C D:/Claude status --porcelain -- .claude/workflows .vault-tools/workflow-pruefung; echo "ENDE"

Erwartet: EXIT 0 ohne Ausgabe von cmp, dann je eine Zeile M .claude/workflows/quorum.js, M .vault-tools/workflow-pruefung/faelle_workflows.mjs und M .vault-tools/workflow-pruefung/mutanten_workflows.py in der Reihenfolge, die git ausgibt, keine weitere, dann ENDE.

python D:/Claude/.vault-tools/plan-kopie/plan_bloecke.py nach D:/Claude/.vault-tools/workflow-pruefung/docs/plans/2026-09-29-quorum/task-06-bericht-entschaerfen.md --projekt D:/Claude

Erwartet: 0 Befund(e).

Schritt 5 — Mutantenlauf im Hintergrund

Der Lauf fährt für jeden Mutanten alle Fälle; zuletzt dauerte er etwa 25 Minuten, mit den neuen Mutanten etwas länger. Das ist länger als ein Aufruf des Bash-Werkzeugs, deshalb im Hintergrund.

Starten, im Bash-Werkzeug mit run_in_background: true:

A="$(cygpath -m -l /tmp)/quorum-task06" && PYTHONIOENCODING=utf-8 python -u D:/Claude/.vault-tools/workflow-pruefung/mutanten_workflows.py > "$A/mutanten.txt" 2>&1; echo "EXIT $?" >> "$A/mutanten.txt"

Abfragen, im Bash-Werkzeug mit timeout 400000; wiederholen, bis die letzte Zeile mit EXIT beginnt:

sleep 300; A="$(cygpath -m -l /tmp)/quorum-task06" && tail -n 2 "$A/mutanten.txt"

Auswerten:

A="$(cygpath -m -l /tmp)/quorum-task06" && grep -E '^(UEBERLEBT|VERALTET)' "$A/mutanten.txt"; echo "---"; grep -E 'quorum: (Klartext: |Stelle fehlt in der Befundzeile|Nachpruefung ohne Fundstelle|Stelle fehlt in der Gruppenueberschrift)' "$A/mutanten.txt"; echo "---"; tail -n 2 "$A/mutanten.txt"

Erwartet:

  • Vor dem ersten --- keine Zeile.
  • Zwischen den beiden --- je eine Zeile für jeden Mutanten mit dem Namensanfang Klartext: aus dem Block oben und für die drei angepassten; jede beginnt mit GEFANGEN.
  • Danach eine Zeile, die auf 0 ueberlebt, 0 veraltet endet, und EXIT 0.

Meldung

Melde alle Ausgaben der Schritte 1 bis 5 vollständig, dazu die Zahl hinter geprueft: aus Schritt 1 und den Wert von 'ersetzt' aus Schritt 2. Läuft etwas anders als erwartet: nichts anpassen, melden, mit jeder FEHLER-, UEBERLEBT- oder VERALTET-Zeile und ihrem Text.

Offene Punkte

  • Obsidian-Bestätigung (Pascal oder Koordinator, vor der Abnahme): In Obsidian 1.13.7 bestätigen, dass die gewählte Form keinen Link, keine Einbettung und keinen Tag erzeugt. Vorschlag: eine Probenotiz mit den vier Zeilen \[\[a/b]], !\[\[c]], \#needs-review, \[\[\#x]] als Fließtext anlegen, dann Leseansicht und Live-Vorschau ansehen, ausgehende Links, Tag-Bereich, Graph und Review-Queue.base prüfen und die Notiz wieder löschen. Fällt die Probe negativ aus, ist eine neue Nachbesserung nötig (andere Form, andere Erwartungstexte, andere Mutanten).
  • Der erste echte Bericht behält seine zwei toten Wikilinks. Ob sie von Hand in dieselbe Form gebracht werden, entscheidet der Koordinator; dieser Task fasst keinen Bericht an.
  • Lauf mit echten Daten: Beim nächsten echten Quorum den abgelegten Bericht mit linkcheck.py prüfen; erwartet ist kein toter Link aus dem Bericht. Zitiert dort eine Stimme Obsidian-Syntax, ist das zugleich die Probe in der App.
  • Übersicht: Tasktabelle um Task 06 ergänzen; das zieht der Koordinator nach.

Risiken

  • Weitere Markdown-Syntax im Freitext bleibt lebendig: ein Markdown-Link der Form Klammer-Text-Klammer mit Pfad dahinter (relativ zum Berichtsordner meist tot), eine Raute mit Leerzeichen am Zeilenanfang eines mehrzeiligen Texts (Überschrift), ein senkrechter Strich im Grund der Stimmentabelle (zerlegt die Zeile). Nicht im Umfang; der Linkcheck meldet tote Markdown-Links nicht.
  • Backslash in Code-Spannen sichtbar: Steht zitierte Syntax im Freitext selbst zwischen Backticks, zeigt die Leseansicht dort den Backslash mit. Lesbar, aber nicht wörtlich.
  • Lauf über 10 Minuten: Bricht die Abfrage ab oder endet mutanten.txt ohne EXIT-Zeile, ist der Lauf nicht fertig oder gestorben; dann nicht neu starten, sondern melden.