lint liest Zitat-Syntax in Backticks und Codeblöcken als echte Referenz
#20
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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).