fix: dist export erzeugt die TOC-Region nach dem Marker-Strip neu (CI-Fund im Export-Replay)
CI / verify (push) Successful in 1m0s
Release / release (push) Successful in 36s

Files changed:
- CHANGES.md
- VERSION
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/tests/test_dist_cmd.py
This commit is contained in:
2026-09-09 20:47:29 +02:00
parent 53e3527e0b
commit bb8f956719
4 changed files with 67 additions and 5 deletions
+13 -1
View File
@@ -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)
<!-- /wikitool:bumps -->
**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 `<!-- dist:strip-start/end -->`-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
+1 -1
View File
@@ -1 +1 @@
5.0.0-beta.2
5.0.0-beta.3
+22 -2
View File
@@ -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(
+31 -1
View File
@@ -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"
"<!-- dist:strip-start -->\n"
"## Dev only\n\nNot shipped.\n"
"<!-- dist:strip-end -->\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)