CITE_REF_RE ist ein reiner Textmatch (\[\^(...)\]) ohne Kenntnis von Markdown-Codespannen. Eine Seite, die Zitat-Syntax erwähnt statt sie zu benutzen, produziert damit Undefined Footnote References — und das ist ein Hard-Error, der lint --fail-on-error und damit CI rot macht.
Nachgestellt:
body='Prosa mit dem Token `[^cite-id]` in Backticks, und einem Codeblock:\n\n```\n[^s-beispiel]: [[Source - X]]\n```\n'[m.group(1)forminCITE_REF_RE.finditer(body)]# -> ['cite-id', 's-beispiel']
Beide sind Erwähnungen. Keine ist eine Referenz.
Beobachtet
Beim Ingest am 2026-08-31 (c28f8ce). Der ausführende Agent schrieb Concept-Seiten über den Zitat-Mechanismus — das Thema der Quelle war ein Datenverlust im Fußnoten-Block — und setzte die Syntax dabei in Backticks. Ergebnis: fünf undefinierte Referenzen, Hard-Error. Er hat es vor dem Publish gefangen und die Prosa umformuliert („Zitatdefinitionszeilen", „Fußnotenreferenz").
Der Ausweg war also, über die eigene Syntax nicht in ihrer eigenen Schreibweise zu schreiben. Das ist die falsche Richtung: eine Wissensbasis über einen Stack muss dessen Notation zitieren können.
Warum das mehr als kosmetisch ist
Es trifft ausgerechnet die Seiten, die den Stack dokumentieren — also genau das, was dieses Wiki über sich selbst weiß.
Der Fehler ist hart, nicht advisory: er blockiert CI.
Die Ausweichformulierung ist unsichtbar. Niemand, der die Seite später liest, erkennt, dass dort eine Umschreibung steht, weil der Linter das Original nicht zulässt.
Betroffene Prüfungen
undefined_footnote_refs sicher. Zu prüfen, ob dieselbe Blindheit auch gilt für:
orphan_footnote_defs (Gegenrichtung: eine Definition in einem Codeblock zählt als echte Definition)
citation_frontmatter_drift
legacy_citation_markers
extract_inline_cites in provenance.py — dort entscheidet es mit, was als belegt gilt, also potenziell auch über Konfidenz
Ebenso zu prüfen: [[wikilinks]] in Codeblöcken. Wenn broken_links denselben Textmatch macht, ist ein Beispiel-Wikilink in einer Doku-Seite ein kaputter Link — vermutlich derselbe Defekt an einer zweiten Stelle.
Lösungsrichtung
Codespannen und eingezäunte Blöcke vor dem Matchen maskieren, an genau einer Stelle. Eine Hilfsfunktion in provenance.py (strip_code_spans(body)), die
eingezäunte Blöcke (``` und ~~~, inklusive Sprach-Info),
eingerückte Codeblöcke,
Inline-Codespannen (auch mehrfache Backticks)
durch Leerraum gleicher Länge ersetzt, sodass Offsets erhalten bleiben, falls ein Aufrufer sie braucht.
Die Alternative — den Regexen einzeln Kontextlogik beibringen — wäre die zweite Kopie derselben Regel und genau das, was Invariante 8 verbietet.
Akzeptanzkriterien
Eine Seite, die [^cite-id] in Backticks und eine Definitionszeile in einem Codeblock enthält, erzeugt keinen undefined_footnote_refs- und keinen orphan_footnote_defs-Befund. Das ist der Test, und er muss zuerst rot sein.
Eine echte Referenz direkt neben einer erwähnten wird weiterhin gefunden — die Maskierung darf nicht zu viel schlucken.
Geprüft und dokumentiert, welche der oben genannten Prüfungen betroffen waren; jede behobene bekommt ihren eigenen Testfall.
[[wikilink]]-Beispiele in Codeblöcken sind untersucht; falls betroffen, im selben Zug behoben oder als eigenes Issue abgetrennt.
Die Seiten, die heute umformuliert wurden, dürfen die Syntax wieder direkt schreiben. Ob sie zurückgeändert werden, ist eine Inhaltsentscheidung und nicht Teil dieses Issues.
Changelog, PATCH — kein Kommando-Interface ändert sich.
Hinweis
Passt in die Reihe von Green Suite Blind Spot: auch dieser Fall war von keinem Test abgedeckt, weil bisher niemand eine Seite über die Zitat-Notation geschrieben hatte.
## Der Defekt
`CITE_REF_RE` ist ein reiner Textmatch (`\[\^(...)\]`) ohne Kenntnis von Markdown-Codespannen. Eine Seite, die Zitat-Syntax **erwähnt** statt sie zu benutzen, produziert damit `Undefined Footnote References` — und das ist ein Hard-Error, der `lint --fail-on-error` und damit CI rot macht.
Nachgestellt:
```python
body = 'Prosa mit dem Token `[^cite-id]` in Backticks, und einem Codeblock:\n\n```\n[^s-beispiel]: [[Source - X]]\n```\n'
[m.group(1) for m in CITE_REF_RE.finditer(body)]
# -> ['cite-id', 's-beispiel']
```
Beide sind Erwähnungen. Keine ist eine Referenz.
## Beobachtet
Beim Ingest am 2026-08-31 (`c28f8ce`). Der ausführende Agent schrieb Concept-Seiten **über** den Zitat-Mechanismus — das Thema der Quelle war ein Datenverlust im Fußnoten-Block — und setzte die Syntax dabei in Backticks. Ergebnis: fünf undefinierte Referenzen, Hard-Error. Er hat es vor dem Publish gefangen und die Prosa umformuliert („Zitatdefinitionszeilen", „Fußnotenreferenz").
Der Ausweg war also, **über die eigene Syntax nicht in ihrer eigenen Schreibweise zu schreiben.** Das ist die falsche Richtung: eine Wissensbasis über einen Stack muss dessen Notation zitieren können.
## Warum das mehr als kosmetisch ist
- Es trifft ausgerechnet die Seiten, die den Stack dokumentieren — also genau das, was dieses Wiki über sich selbst weiß.
- Der Fehler ist **hart**, nicht advisory: er blockiert CI.
- Die Ausweichformulierung ist unsichtbar. Niemand, der die Seite später liest, erkennt, dass dort eine Umschreibung steht, weil der Linter das Original nicht zulässt.
## Betroffene Prüfungen
`undefined_footnote_refs` sicher. Zu prüfen, ob dieselbe Blindheit auch gilt für:
- `orphan_footnote_defs` (Gegenrichtung: eine Definition in einem Codeblock zählt als echte Definition)
- `citation_frontmatter_drift`
- `legacy_citation_markers`
- `extract_inline_cites` in `provenance.py` — dort entscheidet es mit, was als belegt gilt, also potenziell auch über Konfidenz
Ebenso zu prüfen: `[[wikilinks]]` in Codeblöcken. Wenn `broken_links` denselben Textmatch macht, ist ein Beispiel-Wikilink in einer Doku-Seite ein kaputter Link — vermutlich derselbe Defekt an einer zweiten Stelle.
## Lösungsrichtung
Codespannen und eingezäunte Blöcke vor dem Matchen maskieren, an genau einer Stelle. Eine Hilfsfunktion in `provenance.py` (`strip_code_spans(body)`), die
- eingezäunte Blöcke (```` ``` ```` und `~~~`, inklusive Sprach-Info),
- eingerückte Codeblöcke,
- Inline-Codespannen (auch mehrfache Backticks)
durch Leerraum gleicher Länge ersetzt, sodass Offsets erhalten bleiben, falls ein Aufrufer sie braucht.
Die Alternative — den Regexen einzeln Kontextlogik beibringen — wäre die zweite Kopie derselben Regel und genau das, was Invariante 8 verbietet.
## Akzeptanzkriterien
- [ ] Eine Seite, die `[^cite-id]` in Backticks und eine Definitionszeile in einem Codeblock enthält, erzeugt keinen `undefined_footnote_refs`- und keinen `orphan_footnote_defs`-Befund. Das ist der Test, und er muss zuerst rot sein.
- [ ] Eine *echte* Referenz direkt neben einer erwähnten wird weiterhin gefunden — die Maskierung darf nicht zu viel schlucken.
- [ ] Geprüft und dokumentiert, welche der oben genannten Prüfungen betroffen waren; jede behobene bekommt ihren eigenen Testfall.
- [ ] `[[wikilink]]`-Beispiele in Codeblöcken sind untersucht; falls betroffen, im selben Zug behoben oder als eigenes Issue abgetrennt.
- [ ] Die Seiten, die heute umformuliert wurden, dürfen die Syntax wieder direkt schreiben. Ob sie zurückgeändert werden, ist eine Inhaltsentscheidung und nicht Teil dieses Issues.
- [ ] Changelog, **PATCH** — kein Kommando-Interface ändert sich.
## Hinweis
Passt in die Reihe von `Green Suite Blind Spot`: auch dieser Fall war von keinem Test abgedeckt, weil bisher niemand eine Seite über die Zitat-Notation geschrieben hatte.
Die Hilfsfunktion liegt nicht in provenance.py, sondern in einem neuen Modul tools/wiki_tools/markdown_code.py. Grund: kb_scan.extract_wikilinks() braucht dieselbe Regel, und einen Wikilink-Belang aus provenance zu importieren wäre die falsche Richtung. Der Name aus dem Issue bleibt (strip_code_spans).
Sie ersetzt eingezäunte Blöcke (``` und ~~~, inklusive der Fence-Zeilen selbst) und Inline-Codespannen durch Leerzeichen gleicher Länge — Offsets, Zeilenstruktur und Gesamtlänge bleiben erhalten, sodass ein Aufrufer gegen den maskierten Text matchen und den echten schneiden kann. Genau das tut split_cite_block().
Durchgereicht an: provenance.iter_cite_refs() (neu, der eine Einstiegspunkt für Referenz-Scans), split_cite_block() für Definitionen, legacy_citation_markers(), kb_scan.extract_wikilinks() und count_wikilinks().
Zwei bewusste Grenzen
Eingerückte Codeblöcke werden nicht maskiert. In diesem Korpus ist eine Vier-Leerzeichen-Einrückung weit öfter eine Listenfortsetzung als Code: von drei eingerückten kb/-Zeilen mit Wiki-Notation sind zwei Aufzählungspunkte, deren [[wikilink]] ein echter Link ist (Event-Driven Automation). Eine Maskierung nach Einrückung hätte sie stumm aus dem Linkgraphen gelöscht. CommonMarks eigene Regel dafür braucht den Listenkontext, nicht die Zeile.
Inline-Codespannen werden zeilenlokal gematcht. Ein fehlender schließender Backtick ist ein häufiger Tippfehler, und ein Matcher über Zeilengrenzen macht daraus einen stumm maskierten Absatz. Zu viel zu maskieren lässt Befunde verschwinden — die Richtung, die niemandem auffällt.
Aus demselben Grund sucht split_cite_block() die Fußnoten-Überschrift weiterhin im unmaskierten Body: ein einziger unclosed Fence irgendwo in der Prosa würde sonst die Überschrift mitschwärzen, die Seite hätte keine Definitionen mehr, und jede Zitat-Referenz darauf wäre undefiniert. Ein gezeigtes ## Fußnoten im Codeblock ist der seltenere und billigere Unfall.
Betroffene Prüfungen — geprüft und dokumentiert
Prüfung
Betroffen
Behoben
undefined_footnote_refs
ja
ja, über iter_cite_refs()
orphan_footnote_defs
ja, beide Richtungen
ja
citation_frontmatter_drift
ja, transitiv über extract_inline_cites
ja
legacy_citation_markers
ja
ja
extract_inline_cites (provenance.py)
ja — entschied mit, was als belegt gilt
ja
broken_links / extract_wikilinks
ja, derselbe Defekt an zweiter Stelle
ja, im selben Zug
count_wikilinks (corpus_diff)
ja
ja
Der Fund, der eine Entscheidung brauchte
Der Korpus hatte die spiegelbildliche Gewohnheit: 21 [^s-…]-Marker standen innerhalb eingezäunter Blöcke, hinter einer Kommando- oder YAML-Zeile, auf vier Seiten. Sie haben dort nie als Fußnote gerendert — GFM zeigt sie wörtlich, wer den Befehl kopiert, kopiert den Marker mit. Mit der Maskierung wurden zwei davon zu verwaisten Definitionen, also Hard-Error.
Entscheidung des Nutzers: Fences mitmaskieren und die Seiten korrigieren. Die Marker stehen jetzt auf je einer Quelle: [^s-…]-Zeile unter ihrem Block — atlantis (5 Blöcke), docker.nehmer.net (2), hephaestus.nehmer.net (2), External Ingress Model (1). Kein Befehl und keine Konfiguration wurde inhaltlich geändert; modified: über wikitool touch, Eintrag in kb/log.md.
Die Regel dazu steht jetzt in kb/CONTRACT.md bei der Zitat-Sektion — sonst wäre sie nur im Linter kodiert, und ein Autor, der den Marker künftig in einen Fence setzt, verlöre das Zitat stumm.
Akzeptanzkriterien
Eine Seite mit [^cite-id] in Backticks und einer Definitionszeile im Codeblock erzeugt weder undefined_footnote_refs noch orphan_footnote_defs — test_lint_ignores_citation_syntax_shown_as_code. Zuerst rot: gegen eine No-op-Maskierung schlagen die drei maskierungsabhängigen Lint-Tests und die drei Provenance-Tests fehl (verifiziert).
Eine echte Referenz direkt neben einer erwähnten wird weiterhin gefunden — test_lint_still_sees_a_real_citation_beside_a_mentioned_one, plus test_real_notation_next_to_a_mentioned_one_survives auf Modulebene.
Betroffene Prüfungen geprüft und dokumentiert (Tabelle oben); jede behobene hat einen eigenen Testfall.
[[wikilink]]-Beispiele in Codeblöcken untersucht — betroffen, im selben Zug behoben (test_lint_ignores_wikilink_examples_in_code). Gemessen: 7 Beispiel-Links fallen aus dem Graphen, null neue Orphans, null neue Broken Links.
Die heute umformulierten Seiten dürfen die Syntax wieder direkt schreiben. Ob sie zurückgeändert werden, bleibt wie vorgesehen eine Inhaltsentscheidung außerhalb dieses Issues.
Changelog, PATCH — 1.7.1 → 1.7.2.
Neu
tools/wiki_tools/tests/test_markdown_code.py (11 Tests) pinnt die Eigenschaften, auf die sich alle sechs Prüfungen verlassen: Offset-Erhalt, Tilde-Fences, längerer Fence wird nicht von einem kürzeren Lauf geschlossen, unclosed Fence läuft bis Dateiende, mehrfache Backticks, unclosed Backtick maskiert nichts, eingerückte Zeilen bleiben unangetastet.
Umgesetzt in 1.7.2 (`49bd7d4`).
## Was gebaut wurde
Die Hilfsfunktion liegt **nicht** in `provenance.py`, sondern in einem neuen Modul `tools/wiki_tools/markdown_code.py`. Grund: `kb_scan.extract_wikilinks()` braucht dieselbe Regel, und einen Wikilink-Belang aus `provenance` zu importieren wäre die falsche Richtung. Der Name aus dem Issue bleibt (`strip_code_spans`).
Sie ersetzt eingezäunte Blöcke (``` und `~~~`, inklusive der Fence-Zeilen selbst) und Inline-Codespannen durch Leerzeichen gleicher Länge — Offsets, Zeilenstruktur und Gesamtlänge bleiben erhalten, sodass ein Aufrufer gegen den maskierten Text matchen und den echten schneiden kann. Genau das tut `split_cite_block()`.
Durchgereicht an: `provenance.iter_cite_refs()` (neu, der eine Einstiegspunkt für Referenz-Scans), `split_cite_block()` für Definitionen, `legacy_citation_markers()`, `kb_scan.extract_wikilinks()` und `count_wikilinks()`.
## Zwei bewusste Grenzen
**Eingerückte Codeblöcke werden nicht maskiert.** In diesem Korpus ist eine Vier-Leerzeichen-Einrückung weit öfter eine Listenfortsetzung als Code: von drei eingerückten `kb/`-Zeilen mit Wiki-Notation sind zwei Aufzählungspunkte, deren `[[wikilink]]` ein echter Link ist (`Event-Driven Automation`). Eine Maskierung nach Einrückung hätte sie stumm aus dem Linkgraphen gelöscht. CommonMarks eigene Regel dafür braucht den Listenkontext, nicht die Zeile.
**Inline-Codespannen werden zeilenlokal gematcht.** Ein fehlender schließender Backtick ist ein häufiger Tippfehler, und ein Matcher über Zeilengrenzen macht daraus einen stumm maskierten Absatz. Zu viel zu maskieren lässt Befunde *verschwinden* — die Richtung, die niemandem auffällt.
Aus demselben Grund sucht `split_cite_block()` die Fußnoten-Überschrift weiterhin im **unmaskierten** Body: ein einziger unclosed Fence irgendwo in der Prosa würde sonst die Überschrift mitschwärzen, die Seite hätte keine Definitionen mehr, und jede Zitat-Referenz darauf wäre undefiniert. Ein gezeigtes `## Fußnoten` im Codeblock ist der seltenere und billigere Unfall.
## Betroffene Prüfungen — geprüft und dokumentiert
| Prüfung | Betroffen | Behoben |
|---|---|---|
| `undefined_footnote_refs` | ja | ja, über `iter_cite_refs()` |
| `orphan_footnote_defs` | ja, beide Richtungen | ja |
| `citation_frontmatter_drift` | ja, transitiv über `extract_inline_cites` | ja |
| `legacy_citation_markers` | ja | ja |
| `extract_inline_cites` (`provenance.py`) | ja — entschied mit, was als belegt gilt | ja |
| `broken_links` / `extract_wikilinks` | ja, derselbe Defekt an zweiter Stelle | ja, im selben Zug |
| `count_wikilinks` (`corpus_diff`) | ja | ja |
## Der Fund, der eine Entscheidung brauchte
Der Korpus hatte die spiegelbildliche Gewohnheit: **21 `[^s-…]`-Marker standen innerhalb eingezäunter Blöcke**, hinter einer Kommando- oder YAML-Zeile, auf vier Seiten. Sie haben dort nie als Fußnote gerendert — GFM zeigt sie wörtlich, wer den Befehl kopiert, kopiert den Marker mit. Mit der Maskierung wurden zwei davon zu verwaisten Definitionen, also Hard-Error.
Entscheidung des Nutzers: Fences mitmaskieren **und** die Seiten korrigieren. Die Marker stehen jetzt auf je einer `Quelle: [^s-…]`-Zeile unter ihrem Block — `atlantis` (5 Blöcke), `docker.nehmer.net` (2), `hephaestus.nehmer.net` (2), `External Ingress Model` (1). Kein Befehl und keine Konfiguration wurde inhaltlich geändert; `modified:` über `wikitool touch`, Eintrag in `kb/log.md`.
Die Regel dazu steht jetzt in `kb/CONTRACT.md` bei der Zitat-Sektion — sonst wäre sie nur im Linter kodiert, und ein Autor, der den Marker künftig in einen Fence setzt, verlöre das Zitat stumm.
## Akzeptanzkriterien
- [x] Eine Seite mit `[^cite-id]` in Backticks und einer Definitionszeile im Codeblock erzeugt weder `undefined_footnote_refs` noch `orphan_footnote_defs` — `test_lint_ignores_citation_syntax_shown_as_code`. Zuerst rot: gegen eine No-op-Maskierung schlagen die drei maskierungsabhängigen Lint-Tests und die drei Provenance-Tests fehl (verifiziert).
- [x] Eine echte Referenz direkt neben einer erwähnten wird weiterhin gefunden — `test_lint_still_sees_a_real_citation_beside_a_mentioned_one`, plus `test_real_notation_next_to_a_mentioned_one_survives` auf Modulebene.
- [x] Betroffene Prüfungen geprüft und dokumentiert (Tabelle oben); jede behobene hat einen eigenen Testfall.
- [x] `[[wikilink]]`-Beispiele in Codeblöcken untersucht — betroffen, im selben Zug behoben (`test_lint_ignores_wikilink_examples_in_code`). Gemessen: 7 Beispiel-Links fallen aus dem Graphen, null neue Orphans, null neue Broken Links.
- [x] Die heute umformulierten Seiten dürfen die Syntax wieder direkt schreiben. Ob sie zurückgeändert werden, bleibt wie vorgesehen eine Inhaltsentscheidung außerhalb dieses Issues.
- [x] Changelog, PATCH — 1.7.1 → 1.7.2.
## Neu
`tools/wiki_tools/tests/test_markdown_code.py` (11 Tests) pinnt die Eigenschaften, auf die sich alle sechs Prüfungen verlassen: Offset-Erhalt, Tilde-Fences, längerer Fence wird nicht von einem kürzeren Lauf geschlossen, unclosed Fence läuft bis Dateiende, mehrfache Backticks, unclosed Backtick maskiert nichts, eingerückte Zeilen bleiben unangetastet.
Korrektur zur eigenen Zusammenfassung oben: 12, nicht 21, In-Fence-Zitatmarker.
Nachgezählt am Diff von 49bd7d4: git show 49bd7d4 -- <die vier Seiten> | grep -c "^-.*\[\^s-" → 12 (atlantis: 6, docker.nehmer.net: 2, hephaestus.nehmer.net: 3, External Ingress Model: 1). Die 21 war eine Verwechslung während der Sitzung: 9 Beispiel-Wikilinks in Codeblöcken (unverändert, kein Zitat-Fund, nur zur Illustration im Scan mitgezählt) plus 12 echte Zitatmarker wurden addiert und das Ergebnis fälschlich als „Zitatmarker" bezeichnet.
Die vier Seiten und der Fix selbst sind davon nicht betroffen — nur die Zahl in der Prosa oben, in CHANGES.md (jetzt korrigiert) und in kb/log.md (dort per neuem Log-Eintrag korrigiert, da log.md append-only ist).
Korrektur zur eigenen Zusammenfassung oben: **12**, nicht 21, In-Fence-Zitatmarker.
Nachgezählt am Diff von `49bd7d4`: `git show 49bd7d4 -- <die vier Seiten> | grep -c "^-.*\[\^s-"` → 12 (atlantis: 6, docker.nehmer.net: 2, hephaestus.nehmer.net: 3, External Ingress Model: 1). Die 21 war eine Verwechslung während der Sitzung: 9 Beispiel-Wikilinks in Codeblöcken (unverändert, kein Zitat-Fund, nur zur Illustration im Scan mitgezählt) plus 12 echte Zitatmarker wurden addiert und das Ergebnis fälschlich als „Zitatmarker" bezeichnet.
Die vier Seiten und der Fix selbst sind davon nicht betroffen — nur die Zahl in der Prosa oben, in `CHANGES.md` (jetzt korrigiert) und in `kb/log.md` (dort per neuem Log-Eintrag korrigiert, da `log.md` append-only ist).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Der Defekt
CITE_REF_REist ein reiner Textmatch (\[\^(...)\]) ohne Kenntnis von Markdown-Codespannen. Eine Seite, die Zitat-Syntax erwähnt statt sie zu benutzen, produziert damitUndefined Footnote References— und das ist ein Hard-Error, derlint --fail-on-errorund damit CI rot macht.Nachgestellt:
Beide sind Erwähnungen. Keine ist eine Referenz.
Beobachtet
Beim Ingest am 2026-08-31 (
c28f8ce). Der ausführende Agent schrieb Concept-Seiten über den Zitat-Mechanismus — das Thema der Quelle war ein Datenverlust im Fußnoten-Block — und setzte die Syntax dabei in Backticks. Ergebnis: fünf undefinierte Referenzen, Hard-Error. Er hat es vor dem Publish gefangen und die Prosa umformuliert („Zitatdefinitionszeilen", „Fußnotenreferenz").Der Ausweg war also, über die eigene Syntax nicht in ihrer eigenen Schreibweise zu schreiben. Das ist die falsche Richtung: eine Wissensbasis über einen Stack muss dessen Notation zitieren können.
Warum das mehr als kosmetisch ist
Betroffene Prüfungen
undefined_footnote_refssicher. Zu prüfen, ob dieselbe Blindheit auch gilt für:orphan_footnote_defs(Gegenrichtung: eine Definition in einem Codeblock zählt als echte Definition)citation_frontmatter_driftlegacy_citation_markersextract_inline_citesinprovenance.py— dort entscheidet es mit, was als belegt gilt, also potenziell auch über KonfidenzEbenso zu prüfen:
[[wikilinks]]in Codeblöcken. Wennbroken_linksdenselben Textmatch macht, ist ein Beispiel-Wikilink in einer Doku-Seite ein kaputter Link — vermutlich derselbe Defekt an einer zweiten Stelle.Lösungsrichtung
Codespannen und eingezäunte Blöcke vor dem Matchen maskieren, an genau einer Stelle. Eine Hilfsfunktion in
provenance.py(strip_code_spans(body)), die```und~~~, inklusive Sprach-Info),durch Leerraum gleicher Länge ersetzt, sodass Offsets erhalten bleiben, falls ein Aufrufer sie braucht.
Die Alternative — den Regexen einzeln Kontextlogik beibringen — wäre die zweite Kopie derselben Regel und genau das, was Invariante 8 verbietet.
Akzeptanzkriterien
[^cite-id]in Backticks und eine Definitionszeile in einem Codeblock enthält, erzeugt keinenundefined_footnote_refs- und keinenorphan_footnote_defs-Befund. Das ist der Test, und er muss zuerst rot sein.[[wikilink]]-Beispiele in Codeblöcken sind untersucht; falls betroffen, im selben Zug behoben oder als eigenes Issue abgetrennt.Hinweis
Passt in die Reihe von
Green Suite Blind Spot: auch dieser Fall war von keinem Test abgedeckt, weil bisher niemand eine Seite über die Zitat-Notation geschrieben hatte.Umgesetzt in 1.7.2 (
49bd7d4).Was gebaut wurde
Die Hilfsfunktion liegt nicht in
provenance.py, sondern in einem neuen Modultools/wiki_tools/markdown_code.py. Grund:kb_scan.extract_wikilinks()braucht dieselbe Regel, und einen Wikilink-Belang ausprovenancezu importieren wäre die falsche Richtung. Der Name aus dem Issue bleibt (strip_code_spans).Sie ersetzt eingezäunte Blöcke (``` und
~~~, inklusive der Fence-Zeilen selbst) und Inline-Codespannen durch Leerzeichen gleicher Länge — Offsets, Zeilenstruktur und Gesamtlänge bleiben erhalten, sodass ein Aufrufer gegen den maskierten Text matchen und den echten schneiden kann. Genau das tutsplit_cite_block().Durchgereicht an:
provenance.iter_cite_refs()(neu, der eine Einstiegspunkt für Referenz-Scans),split_cite_block()für Definitionen,legacy_citation_markers(),kb_scan.extract_wikilinks()undcount_wikilinks().Zwei bewusste Grenzen
Eingerückte Codeblöcke werden nicht maskiert. In diesem Korpus ist eine Vier-Leerzeichen-Einrückung weit öfter eine Listenfortsetzung als Code: von drei eingerückten
kb/-Zeilen mit Wiki-Notation sind zwei Aufzählungspunkte, deren[[wikilink]]ein echter Link ist (Event-Driven Automation). Eine Maskierung nach Einrückung hätte sie stumm aus dem Linkgraphen gelöscht. CommonMarks eigene Regel dafür braucht den Listenkontext, nicht die Zeile.Inline-Codespannen werden zeilenlokal gematcht. Ein fehlender schließender Backtick ist ein häufiger Tippfehler, und ein Matcher über Zeilengrenzen macht daraus einen stumm maskierten Absatz. Zu viel zu maskieren lässt Befunde verschwinden — die Richtung, die niemandem auffällt.
Aus demselben Grund sucht
split_cite_block()die Fußnoten-Überschrift weiterhin im unmaskierten Body: ein einziger unclosed Fence irgendwo in der Prosa würde sonst die Überschrift mitschwärzen, die Seite hätte keine Definitionen mehr, und jede Zitat-Referenz darauf wäre undefiniert. Ein gezeigtes## Fußnotenim Codeblock ist der seltenere und billigere Unfall.Betroffene Prüfungen — geprüft und dokumentiert
undefined_footnote_refsiter_cite_refs()orphan_footnote_defscitation_frontmatter_driftextract_inline_citeslegacy_citation_markersextract_inline_cites(provenance.py)broken_links/extract_wikilinkscount_wikilinks(corpus_diff)Der Fund, der eine Entscheidung brauchte
Der Korpus hatte die spiegelbildliche Gewohnheit: 21
[^s-…]-Marker standen innerhalb eingezäunter Blöcke, hinter einer Kommando- oder YAML-Zeile, auf vier Seiten. Sie haben dort nie als Fußnote gerendert — GFM zeigt sie wörtlich, wer den Befehl kopiert, kopiert den Marker mit. Mit der Maskierung wurden zwei davon zu verwaisten Definitionen, also Hard-Error.Entscheidung des Nutzers: Fences mitmaskieren und die Seiten korrigieren. Die Marker stehen jetzt auf je einer
Quelle: [^s-…]-Zeile unter ihrem Block —atlantis(5 Blöcke),docker.nehmer.net(2),hephaestus.nehmer.net(2),External Ingress Model(1). Kein Befehl und keine Konfiguration wurde inhaltlich geändert;modified:überwikitool touch, Eintrag inkb/log.md.Die Regel dazu steht jetzt in
kb/CONTRACT.mdbei der Zitat-Sektion — sonst wäre sie nur im Linter kodiert, und ein Autor, der den Marker künftig in einen Fence setzt, verlöre das Zitat stumm.Akzeptanzkriterien
[^cite-id]in Backticks und einer Definitionszeile im Codeblock erzeugt wederundefined_footnote_refsnochorphan_footnote_defs—test_lint_ignores_citation_syntax_shown_as_code. Zuerst rot: gegen eine No-op-Maskierung schlagen die drei maskierungsabhängigen Lint-Tests und die drei Provenance-Tests fehl (verifiziert).test_lint_still_sees_a_real_citation_beside_a_mentioned_one, plustest_real_notation_next_to_a_mentioned_one_survivesauf Modulebene.[[wikilink]]-Beispiele in Codeblöcken untersucht — betroffen, im selben Zug behoben (test_lint_ignores_wikilink_examples_in_code). Gemessen: 7 Beispiel-Links fallen aus dem Graphen, null neue Orphans, null neue Broken Links.Neu
tools/wiki_tools/tests/test_markdown_code.py(11 Tests) pinnt die Eigenschaften, auf die sich alle sechs Prüfungen verlassen: Offset-Erhalt, Tilde-Fences, längerer Fence wird nicht von einem kürzeren Lauf geschlossen, unclosed Fence läuft bis Dateiende, mehrfache Backticks, unclosed Backtick maskiert nichts, eingerückte Zeilen bleiben unangetastet.Korrektur zur eigenen Zusammenfassung oben: 12, nicht 21, In-Fence-Zitatmarker.
Nachgezählt am Diff von
49bd7d4:git show 49bd7d4 -- <die vier Seiten> | grep -c "^-.*\[\^s-"→ 12 (atlantis: 6, docker.nehmer.net: 2, hephaestus.nehmer.net: 3, External Ingress Model: 1). Die 21 war eine Verwechslung während der Sitzung: 9 Beispiel-Wikilinks in Codeblöcken (unverändert, kein Zitat-Fund, nur zur Illustration im Scan mitgezählt) plus 12 echte Zitatmarker wurden addiert und das Ergebnis fälschlich als „Zitatmarker" bezeichnet.Die vier Seiten und der Fix selbst sind davon nicht betroffen — nur die Zahl in der Prosa oben, in
CHANGES.md(jetzt korrigiert) und inkb/log.md(dort per neuem Log-Eintrag korrigiert, dalog.mdappend-only ist).