docs verify: nur als .template ausgeliefertes Linkziel gilt als aufgeloest (Defekt aus 0fb8fd6)
Files changed: - CHANGES.md - VERSION - tools/chemenu/commands/docs_verify.py - tools/chemenu/tests/test_docs_verify.py
This commit is contained in:
+27
-1
@@ -52,7 +52,7 @@ concern - readable here, never shipped as something to parse.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6.0.0-beta.1 - 2026-09-12 - docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
|
## 6.0.0-beta.2 - 2026-09-13 - docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
|
||||||
|
|
||||||
**Author:** Torben Nehmer
|
**Author:** Torben Nehmer
|
||||||
|
|
||||||
@@ -65,6 +65,9 @@ concern - readable here, never shipped as something to parse.
|
|||||||
- SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele
|
- SKILL.md: relative Links durch repo-root-relative Pfade ersetzt, docs verify/instructions verify pruefen Linkziele
|
||||||
- docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
|
- docs verify: der Linkziel-Check erreicht auch die instanz-eigenen kb/CONVENTIONS.md und COLLECTION.md - daher Grenzuebertritt
|
||||||
|
|
||||||
|
**Medium impact**
|
||||||
|
- docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
|
||||||
|
|
||||||
**Low impact**
|
**Low impact**
|
||||||
- gates.md/session-setup.md: die Budget-Ausnahme von version regrade haengt an der Aufrufform
|
- gates.md/session-setup.md: die Budget-Ausnahme von version regrade haengt an der Aufrufform
|
||||||
<!-- /wikitool:bumps -->
|
<!-- /wikitool:bumps -->
|
||||||
@@ -117,6 +120,29 @@ eigenen Regel in `tools/README.md`, nicht durch einen Check - was `docs/version-
|
|||||||
genau diese Stelle sagt ("a person looking at the diff ... not a validator"), hat sich hier
|
genau diese Stelle sagt ("a person looking at the diff ... not a validator"), hat sich hier
|
||||||
wiederholt.
|
wiederholt.
|
||||||
|
|
||||||
|
### docs verify: ein nur als .template ausgeliefertes Linkziel gilt als aufgeloest
|
||||||
|
|
||||||
|
Defektbehebung am Check aus den beiden Bumps darueber, gefunden unmittelbar nach deren Publish.
|
||||||
|
`check_reference_targets` meldete auf einem frisch exportierten Baum 13 tote Links - `kb/CONTRACT.md`
|
||||||
|
neunmal, dazu `german-terminology.md`, `kb-profiles.md` und `link-taxonomy.md` - und zwar dafuer,
|
||||||
|
dass der Export tut, was er soll. `kb/CONVENTIONS.md` und die vier `kb/<name>/COLLECTION.md` sind
|
||||||
|
instanz-eigen: die Distribution traegt `<name>.template`, und die Instanz uebernimmt sie erst im
|
||||||
|
Personalisierungsschritt von `instructions/setup-instance.md` durch Umbenennen. Zwischen
|
||||||
|
`dist export` und diesem Schritt existiert die fertige Datei berechtigterweise nicht, waehrend die
|
||||||
|
stack-eigenen Dateien sie unter ihrem kuenftigen Namen verlinken - korrekt, denn so wird sie heissen.
|
||||||
|
|
||||||
|
Ein Linkziel gilt jetzt auch dann als aufgeloest, wenn daneben `<ziel>.template` liegt. Die
|
||||||
|
Ausnahme ist eng: fehlt beides, bleibt es ein Befund. Damit beschreibt der Check nicht laenger
|
||||||
|
"noch nicht personalisiert" als "kaputter Link" - diesen Zustand meldet `doctor` unter
|
||||||
|
`conventions` praezise und zustaendig.
|
||||||
|
|
||||||
|
CI war davon nie rot: der Replay in `.gitea/workflows/ci.yml` uebernimmt die Templates, bevor er
|
||||||
|
`docs verify` aufruft, und der dokumentierte Weg in `setup-instance.md` stellt die Personalisierung
|
||||||
|
(Schritt 5/6) ebenfalls vor die Verifikation (Schritt 13). Getroffen haette es jeden, der nach dem
|
||||||
|
Export einmal zur Kontrolle `docs verify` aufruft. Aufgefallen ist es, weil die Verifikation des
|
||||||
|
vorherigen Publishes den Arbeitsbaum geprueft hatte und nicht den exportierten - ausgerechnet bei
|
||||||
|
einer Aenderung, deren ganzer Gegenstand Kopien in anderer Verzeichnistiefe sind.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
|
## 5.1.0 - 2026-09-12 - changelog: Kandidaten-Eintrag nach Impact gruppiert, version regrade zur Nachkorrektur, version release verlangt eine Zusammenfassung
|
||||||
|
|||||||
@@ -470,6 +470,13 @@ def check_toc_regions() -> list[str]:
|
|||||||
# escaping, which nothing here uses.
|
# escaping, which nothing here uses.
|
||||||
MARKDOWN_LINK_RE = re.compile(r"\[[^\]]*\]\(([^)\s]+)\)")
|
MARKDOWN_LINK_RE = re.compile(r"\[[^\]]*\]\(([^)\s]+)\)")
|
||||||
|
|
||||||
|
# The suffix `dist export` re-keys an instance-owned file to, and the one
|
||||||
|
# `setup-instance.md` renames away again. Spelled here rather than imported
|
||||||
|
# from `ownership`, whose own `.template` handling answers a different
|
||||||
|
# question (which side an upstream merge keeps) over a narrower scope
|
||||||
|
# (paths under a content stage).
|
||||||
|
TEMPLATE_SUFFIX = ".template"
|
||||||
|
|
||||||
|
|
||||||
def is_external_or_anchor(target: str) -> bool:
|
def is_external_or_anchor(target: str) -> bool:
|
||||||
"""A link this check does not resolve as a filesystem path: an absolute
|
"""A link this check does not resolve as a filesystem path: an absolute
|
||||||
@@ -498,6 +505,17 @@ def check_reference_targets() -> list[str]:
|
|||||||
a stale one anyway. Code fences and inline code spans are masked first
|
a stale one anyway. Code fences and inline code spans are masked first
|
||||||
(`markdown_code.strip_code_spans`), so a passage that shows link syntax
|
(`markdown_code.strip_code_spans`), so a passage that shows link syntax
|
||||||
as an example is not mistaken for a real reference.
|
as an example is not mistaken for a real reference.
|
||||||
|
|
||||||
|
**A target the stack ships only as a `.template` counts as resolving.**
|
||||||
|
`kb/CONVENTIONS.md` and every `kb/<name>/COLLECTION.md` are instance-owned:
|
||||||
|
a distribution carries `<name>.template` and the instance adopts it by
|
||||||
|
renaming, during `instructions/setup-instance.md`'s personalization step.
|
||||||
|
Between `dist export` and that step the real file legitimately does not
|
||||||
|
exist yet - while `kb/CONTRACT.md` and three flat instructions link to it
|
||||||
|
by its adopted name, correctly, because that is the name it will have.
|
||||||
|
Reporting those as dead links would fail a fresh export for doing exactly
|
||||||
|
what it is supposed to do, and would describe "not personalized yet" as a
|
||||||
|
broken link when `doctor`'s `conventions` check already says it precisely.
|
||||||
"""
|
"""
|
||||||
issues = []
|
issues = []
|
||||||
for path in toc.target_files():
|
for path in toc.target_files():
|
||||||
@@ -511,7 +529,11 @@ def check_reference_targets() -> list[str]:
|
|||||||
target_path = target.split("#", 1)[0]
|
target_path = target.split("#", 1)[0]
|
||||||
if not target_path:
|
if not target_path:
|
||||||
continue
|
continue
|
||||||
if not (path.parent / target_path).resolve().exists():
|
resolved = (path.parent / target_path).resolve()
|
||||||
|
if resolved.exists():
|
||||||
|
continue
|
||||||
|
if resolved.with_name(resolved.name + TEMPLATE_SUFFIX).exists():
|
||||||
|
continue
|
||||||
issues.append(
|
issues.append(
|
||||||
f"{rel_path(path)}:{line_number} links to `{target}`, which does not "
|
f"{rel_path(path)}:{line_number} links to `{target}`, which does not "
|
||||||
"resolve to an existing file"
|
"resolve to an existing file"
|
||||||
|
|||||||
@@ -586,6 +586,34 @@ def test_link_syntax_shown_as_an_example_in_a_fence_is_not_flagged(tmp_path, mon
|
|||||||
assert docs_verify.check_reference_targets() == []
|
assert docs_verify.check_reference_targets() == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_target_shipped_only_as_a_template_is_not_dead(tmp_path, monkeypatch):
|
||||||
|
"""Regression guard for a defect this check shipped with. A fresh
|
||||||
|
`dist export` carries `kb/CONVENTIONS.md.template`, not
|
||||||
|
`kb/CONVENTIONS.md` - the instance adopts it by renaming, during
|
||||||
|
`setup-instance.md`'s personalization step. `kb/CONTRACT.md` and three
|
||||||
|
flat instructions link to the adopted name, correctly. Before this
|
||||||
|
exemption the check reported 13 dead links on a just-exported tree, for
|
||||||
|
doing exactly what a fresh export is supposed to do."""
|
||||||
|
(tmp_path / "kb").mkdir()
|
||||||
|
(tmp_path / "kb" / "CONVENTIONS.md.template").write_text("# Conventions\n", encoding="utf-8")
|
||||||
|
fake = tmp_path / "kb" / "CONTRACT.md"
|
||||||
|
fake.write_text("What this instance decided: [kb/CONVENTIONS.md](CONVENTIONS.md).\n", encoding="utf-8")
|
||||||
|
monkeypatch.setattr(docs_verify.toc, "target_files", lambda: [fake])
|
||||||
|
assert docs_verify.check_reference_targets() == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_target_with_neither_the_file_nor_a_template_is_still_dead(tmp_path, monkeypatch):
|
||||||
|
"""The exemption is narrow: it covers a file the stack ships as a
|
||||||
|
template, not any missing target."""
|
||||||
|
(tmp_path / "kb").mkdir()
|
||||||
|
fake = tmp_path / "kb" / "CONTRACT.md"
|
||||||
|
fake.write_text("See [kb/CONVENTIONS.md](CONVENTIONS.md).\n", encoding="utf-8")
|
||||||
|
monkeypatch.setattr(docs_verify.toc, "target_files", lambda: [fake])
|
||||||
|
issues = docs_verify.check_reference_targets()
|
||||||
|
assert len(issues) == 1
|
||||||
|
assert "CONVENTIONS.md" in issues[0]
|
||||||
|
|
||||||
|
|
||||||
def test_every_reference_files_link_targets_resolve():
|
def test_every_reference_files_link_targets_resolve():
|
||||||
"""Forward direction, against the real tree: every relative link in a file
|
"""Forward direction, against the real tree: every relative link in a file
|
||||||
`toc.target_files()` covers must resolve - this is what a `../` count
|
`toc.target_files()` covers must resolve - this is what a `../` count
|
||||||
|
|||||||
Reference in New Issue
Block a user