stack: TOC-Pflicht fuer Referenzdateien ueber 100 Zeilen (docs toc); session-setup.md/gates.md nennen die tatsaechliche Budget-Ausnahmeliste (schliesst #73, #76)
CI / verify (push) Failing after 57s
Release / release (push) Successful in 37s

Files changed:
- AGENTS.md
- CHANGES.md
- VERSION
- instructions/CONTRACT.md
- instructions/capture-session.md
- instructions/claude-code-model-selection.md
- instructions/dev/issue-tracking.md
- instructions/dev/testing-conventions.md
- instructions/dev/version-parts.md
- instructions/evolve-subtypes.md
- instructions/gates.md
- instructions/german-terminology.md
- instructions/ingest-large-tree.md
- instructions/kb-profiles.md
- instructions/link-taxonomy.md
- instructions/mcp-read-server.md
- instructions/migrate-corpus.md
- instructions/migrations/3.0.0-authoring-conventions.md
- instructions/migrations/4.0.0-link-taxonomy.md
- instructions/private-instance.md
- instructions/session-setup.md
- instructions/setup-instance.md
- kb/CONTRACT.md
- kb/CONVENTIONS.md
- kb/concepts/COLLECTION.md
- raw/CONTRACT.md
- tools/CONTRACT.md
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/instructions_cmd.py
- tools/chemenu/tests/test_docs_verify.py
- tools/chemenu/tests/test_instructions_cmd.py
- tools/chemenu/tests/test_toc.py
- tools/chemenu/toc.py
- types/type-spec.md
This commit is contained in:
torben committed 2026-09-09 20:38:42 +02:00
1 parent a51d7a322f
commit 2c4c2b1c7c
34 files changed
+929 -15

No files matched your search

+58 -4
View File
@@ -41,7 +41,7 @@ from typing import Optional
import typer
from chemenu import config, conventions, kb_collections, version as version_mod
from chemenu import config, conventions, kb_collections, toc, version as version_mod
from chemenu.commands import dist_cmd
from chemenu.commands._util import fail, rel_path, success
@@ -351,6 +351,26 @@ def check_legacy_type_blocks() -> list[str]:
return issues
def check_toc_regions() -> list[str]:
"""Every reference file over the line threshold carries a current TOC.
`toc.upsert` is idempotent (`toc.py`'s own docstring), so comparing its
output against the file on disk catches both a missing region and a
stale one - a heading added, renamed or reordered without re-running
`wikitool docs toc --apply` - in one check, the same way `docs verify`
checks every other generated-from-code copy.
"""
issues = []
for path in toc.target_files():
text = path.read_text(encoding="utf-8")
if toc.upsert(text) != text:
issues.append(
f"{rel_path(path)} needs a table-of-contents region refreshed - "
"run `wikitool docs toc --apply`"
)
return issues
def command_table_free_readmes() -> list[Path]:
"""Every README that must not carry a copy of the command table.
@@ -400,9 +420,12 @@ def check_readmes_have_no_command_table() -> list[str]:
# The pattern knows nothing about Gitea - no client, no URL, no issue state -
# which is what keeps `instructions/dev/issue-tracking.md` § "What no tool
# checks" intact. It is a character pattern over shipped text, and `wikitool`
# stays as ignorant of the board as it was. Markdown anchors are word
# characters (`](#gates)`), so a link never matches.
ISSUE_REFERENCE_RE = re.compile(r"#\d+")
# stays as ignorant of the board as it was. Markdown anchors are usually word
# characters (`](#gates)`), but a numbered step's TOC entry is not
# (`](#2-fix-the-fidelity-before-writing-a-word)`) - the lookbehind excludes
# exactly the `](#...` link-fragment shape, not `#\d+` generally, so a real
# citation immediately after other punctuation still matches.
ISSUE_REFERENCE_RE = re.compile(r"(?<!\]\()#\d+")
# What counts as shipped prose: Markdown, plus the `.template` files an instance
# renames into place during setup. `tools/**/*.py` is deliberately outside it.
@@ -675,6 +698,7 @@ def verify():
+ check_migration_for_boundary()
+ check_breaking_change_for_boundary()
+ check_no_issue_references()
+ check_toc_regions()
)
if issues:
@@ -686,6 +710,36 @@ def verify():
f"{len(STAGE_CONTRACTS)} stage contract(s) present, no legacy type blocks, "
f"{len(IGNORE_CANARIES)} ignore canaries clear, "
f"no issue references in {len(shipped_prose())} shipped document(s), "
f"tables of contents current on {len(toc.target_files())} reference file(s), "
f"{version_mod.CHANGES_FILENAME} documents version "
f"{(config.ROOT / version_mod.VERSION_FILENAME).read_text(encoding='utf-8').strip()}."
)
@app.command("toc")
def toc_command(
apply: bool = typer.Option(False, "--apply", help="Write changes; default is dry-run (preview only)"),
):
"""Create, refresh or remove the generated table-of-contents region on
every reference file `toc.target_files()` covers - AGENTS.md, the stage
and collection contracts, and every flat `instructions/**.md` file."""
changed = []
for path in toc.target_files():
before = path.read_text(encoding="utf-8")
after = toc.upsert(before)
if after != before:
changed.append((path, after))
if not changed:
success("Every table of contents is already current.")
return
for path, after in changed:
typer.echo(rel_path(path))
if apply:
path.write_text(after, encoding="utf-8")
if apply:
success(f"Refreshed the table of contents on {len(changed)} file(s).")
else:
typer.echo(f"\n{len(changed)} file(s) would change. Re-run with --apply to write.")
+9 -1
View File
@@ -24,6 +24,7 @@ expected state of a clean checkout rather than a fault.
from __future__ import annotations
import filecmp
import re
import shutil
from pathlib import Path
@@ -300,7 +301,14 @@ def dev_only_forbidden_references(instructions_dir: Path | None = None) -> set[s
except OSError: # pragma: no cover - unreadable file
continue
for name in dev_names:
if name in text:
# Word-bounded, not a bare substring test: a generated TOC anchor
# like `#where-stack-development-happens` contains "stack-dev"
# as a raw substring without mentioning the skill at all. `\b`
# does not fire between "v" and "e" (both word characters), so
# "stack-development" is correctly not a match while a real
# mention (`` `stack-dev` ``, `instructions/dev/stack-dev/`) -
# bounded by punctuation on both sides - still is.
if re.search(rf"\b{re.escape(name)}\b", text):
referenced.add(name)
return referenced