diff --git a/CHANGES.md b/CHANGES.md index 582dac7..af3f282 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,7 +35,7 @@ dev-checkout concern - readable here, never shipped as something to parse. --- -## 5.0.0-beta.2 - 2026-09-09 - tools/CONTRACT.md: docs toc im Fehlerkontrakt, docs-verify-Zeile nennt die TOC-Pruefung; tools/README.md korrigiert das --major-Kriterium +## 5.0.0-beta.3 - 2026-09-09 - dist export erzeugt die TOC-Region nach dem Marker-Strip neu (CI-Fund im Export-Replay) **Author:** Torben Nehmer @@ -55,6 +55,7 @@ dev-checkout concern - readable here, never shipped as something to parse. - Ausgelieferte Doku zitiert keine Issue-Nummern mehr, docs verify prueft es (schliesst #77) - TOC-Pflicht fuer Referenzdateien ueber 100 Zeilen; session-setup.md/gates.md nennen die tatsaechliche Budget-Ausnahmeliste (schliesst #73, #76) - tools/CONTRACT.md: docs toc im Fehlerkontrakt, docs-verify-Zeile nennt die TOC-Pruefung; tools/README.md korrigiert das --major-Kriterium +- dist export erzeugt die TOC-Region nach dem Marker-Strip neu (CI-Fund im Export-Replay) **Migration:** none required - Betrifft nur AGENTS.md, die Stage-/Collection-Contracts und instructions/; kb/-Inhalt bleibt unberuehrt, keine Migration noetig. @@ -917,6 +918,17 @@ zählt wie jedes mutierende Kommando. Beide Dateien verweisen jetzt auf die List in `tools/CONTRACT.md`, statt sie mit einer falschen Faustregel zu umschreiben. Kein Versionsbezug — reine Prosa-Korrektur, im selben Bump mitgeführt. +Nachgezogen in `-beta.3`: `dist export` erzeugt die TOC-Region jetzt **nach** +dem Marker-Strip neu. Ein ``-Block kann eine ganze +Sektion umschließen — der in `AGENTS.md` umschließt `## Developing this stack` —, +sodass die ausgelieferte Datei eine Überschrift weniger hat, als das im +Arbeitsbaum erzeugte Inhaltsverzeichnis auflistet. Die frische Instanz wäre +damit beim allerersten `docs verify` über eine Datei gefallen, die niemand +angefasst hat. Gefunden hat das die CI im Export-Replay („The distribution works +as a fresh instance"), nicht `pytest` und nicht `docs verify` im Arbeitsbaum — +beide sehen den gestrippten Text nie. Der Regressionstest sitzt jetzt in +`test_dist_cmd.py`. + Nachgezogen in `-beta.2`, weil `docs verify` die eigene Dokumentationstreue nur für die *Existenz* einer Kommandozeile prüft, nicht für deren Inhalt: die `docs verify`-Zeile in `tools/CONTRACT.md` nennt jetzt die TOC-Prüfung, die diff --git a/VERSION b/VERSION index 1ece2a2..f61653e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.0-beta.2 +5.0.0-beta.3 diff --git a/tools/chemenu/commands/dist_cmd.py b/tools/chemenu/commands/dist_cmd.py index 0e37edb..0a1f549 100644 --- a/tools/chemenu/commands/dist_cmd.py +++ b/tools/chemenu/commands/dist_cmd.py @@ -45,7 +45,16 @@ from typing import Callable, NamedTuple, Optional, Union import typer -from chemenu import config, conventions, kb_collections, kb_state, ownership, version as version_mod +from chemenu import ( + blocks, + config, + conventions, + kb_collections, + kb_state, + ownership, + toc, + version as version_mod, +) from chemenu.commands._util import console, fail, rel_path, success, today_iso app = typer.Typer(help="Build a distributable copy of the wiki machinery.") @@ -213,7 +222,18 @@ def _read_planned_file(path: Path, label: str) -> PlannedFile: if path.suffix != ".md": return PlannedFile(text, executable) _validate_markers(text, label) - return PlannedFile(strip_markers(text), executable) + stripped = strip_markers(text) + # A table-of-contents region describes the file it sits in, and stripping + # just changed that file: `AGENTS.md`'s marked block wraps the whole + # `## Developing this stack` section, so the shipped copy has one heading + # fewer than the working tree's TOC lists. Regenerating here - after the + # strip, on the text that actually ships - is what keeps a fresh + # instance's own `docs verify` green on the very first run. A file with + # no region, or one the strip pushed under the threshold, is handled by + # `toc.upsert` itself. + if blocks.find(stripped, toc.REGION_NAME) is not None: + stripped = toc.upsert(stripped) + return PlannedFile(stripped, executable) def _copy_tree( diff --git a/tools/chemenu/tests/test_dist_cmd.py b/tools/chemenu/tests/test_dist_cmd.py index 65cf050..1751633 100644 --- a/tools/chemenu/tests/test_dist_cmd.py +++ b/tools/chemenu/tests/test_dist_cmd.py @@ -12,7 +12,7 @@ from pathlib import Path import pytest import typer -from chemenu import config, version as version_mod +from chemenu import config, toc, version as version_mod from chemenu.commands import dist_cmd @@ -183,6 +183,36 @@ def test_plan_strips_markers_but_keeps_surrounding_content(repo): assert "Knowledge base (vendored)" not in agents +def test_plan_refreshes_a_table_of_contents_after_stripping(repo): + """A marked block can wrap a whole section - the real `AGENTS.md`'s wraps + `## Developing this stack` - so the shipped copy has one heading fewer + than the working tree's TOC lists. The plan regenerates the region on the + stripped text; without that, a fresh instance's very first `docs verify` + fails on a file nobody touched, which is how CI's export replay caught + this and pytest did not.""" + path = repo / "AGENTS.md" + filler = "\n".join(f"Line {i}." for i in range(120)) + path.write_text( + "# AGENTS\n\nCore rules.\n\n" + f"## Kept\n\n{filler}\n\n" + "\n" + "## Dev only\n\nNot shipped.\n" + "\n\n" + "## Changelog\n", + encoding="utf-8", + ) + # The region as `docs toc --apply` writes it in the working tree, where + # the stripped section is still present and so belongs in the TOC. + path.write_text(toc.upsert(path.read_text(encoding="utf-8")), encoding="utf-8") + assert "[Dev only](#dev-only)" in path.read_text(encoding="utf-8") + + agents = dist_cmd.build_plan()["AGENTS.md"].content + assert "## Dev only" not in agents + assert "[Dev only](#dev-only)" not in agents + assert "[Kept](#kept)" in agents + assert "[Changelog](#changelog)" in agents + + def test_plan_excludes_venv_and_pycache(repo): plan = dist_cmd.build_plan() assert not any(".venv" in relative for relative in plan)