feat: docs reference generates a human command reference under docs/reference/ from the command records, one page per group plus exit codes and gates; toc never lands inside another generated region (#188)
CI / verify (push) Successful in 6m11s
CI / pwsh (push) Successful in 2m0s
Release / release (push) Successful in 35s

Files changed:
- CHANGES.md
- README.md
- VERSION
- docs/reference/README.md
- docs/reference/commands/README.md
- docs/reference/commands/catalog-and-log.md
- docs/reference/commands/content-migrations.md
- docs/reference/commands/distribution-and-versioning.md
- docs/reference/commands/finding-and-checking.md
- docs/reference/commands/git.md
- docs/reference/commands/guideline-export.md
- docs/reference/commands/instance-health.md
- docs/reference/commands/links-and-citations.md
- docs/reference/commands/pages.md
- docs/reference/commands/provenance.md
- docs/reference/commands/raw-material-and-uploads.md
- docs/reference/commands/telemetry.md
- docs/reference/commands/types-instructions-and-docs.md
- docs/reference/commands/workshop-runs-and-session-budget.md
- docs/reference/exit-codes.md
- instructions/dev/doc-pull-through.md
- tools/CONTRACT.md
- tools/README.md
- tools/chemenu/cli.py
- tools/chemenu/cli_contract.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/reference_doc.py
- tools/chemenu/tests/test_reference_doc.py
- tools/chemenu/toc.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
torbenandClaude Opus 5.5 committed 2026-10-06 21:29:01 +02:00
1 parent 01c888d3cd
commit 53383e7e9b
29 files changed
+5079 -29

No files matched your search

+288
View File
@@ -0,0 +1,288 @@
import re
import pytest
from typer.testing import CliRunner
from chemenu import blocks, cli, cli_contract, config, reference_doc, toc
from chemenu.cli import app
from chemenu.commands import dist_cmd, docs_verify
runner = CliRunner()
# --- the real tree --------------------------------------------------------
def _group_pages() -> dict[str, str]:
return {
title: (config.ROOT / reference_doc.group_page(title)).read_text(encoding="utf-8")
for title, _ in cli_contract.GROUPS
}
def test_the_real_reference_is_current():
"""What a session that changed a record and forgot `docs reference --apply`
is caught by."""
assert docs_verify.check_reference_regions() == []
def test_every_group_has_one_page_and_every_command_one_section():
pages = _group_pages()
for path in cli_contract.all_records():
heading = f"## `{path}`\n"
hits = [title for title, text in pages.items() if text.count(heading) == 1]
assert len(hits) == 1, path
assert sum(text.count(heading) for text in pages.values()) == 1, path
def test_group_slugs_are_unique():
slugs = [reference_doc.group_slug(title) for title, _ in cli_contract.GROUPS]
assert len(slugs) == len(set(slugs))
def test_no_page_carries_an_agent_addressed_field():
"""ON FAILURE reactions and NEVER tell an agent what to do; the human
reference keeps only the cause of each failure."""
text = "\n".join(_group_pages().values())
text += (config.ROOT / reference_doc.EXIT_CODES).read_text(encoding="utf-8")
for rec in cli_contract.all_records().values():
for failure in rec.failures:
if failure.reaction:
assert failure.reaction not in text, (rec.path, failure.reaction)
for line in rec.never:
assert line not in text, (rec.path, line)
def test_options_match_the_options_section_of_h():
texts = cli.options_texts(sorted(cli_contract.all_records()))
for path, options in texts.items():
result = runner.invoke(app, [*path.split(" "), "-h"])
assert result.exit_code == 0, (path, result.output)
if options.strip():
assert f"OPTIONS\n{options.rstrip()}\n\n" in result.output, path
else:
assert "\nOPTIONS\n" not in result.output, path
def _section(markdown: str, heading: str) -> str:
start = markdown.index(f"## {heading}\n")
end = markdown.find("\n## ", start + 1)
return markdown[start:end if end != -1 else None]
def test_exit_codes_names_every_gate_and_links_rather_than_repeats_agents_md():
page = (config.ROOT / reference_doc.EXIT_CODES).read_text(encoding="utf-8")
for rec in cli_contract.all_records().values():
for gate in rec.properties.gates:
assert f"### {gate}\n" in page, gate
assert "(../../AGENTS.md#tool-error-contract)" in page
assert "(../../AGENTS.md#gates)" in page
agents = (config.ROOT / "AGENTS.md").read_text(encoding="utf-8")
for heading in ("Tool error contract", "Gates"):
for sentence in re.split(r"(?<=[.:])\s+", _section(agents, heading)):
sentence = sentence.strip()
if len(sentence) >= 30:
assert sentence not in page, sentence
def test_the_reference_pages_survive_dist_export_unchanged():
"""`dist export` strips `dist:strip` spans from every shipped text file; a
record quoting the marker pair verbatim would be cut in the exported page
and the fresh instance's `docs verify` would call it stale."""
plan = dist_cmd.build_plan()
for region in reference_doc.regions():
assert plan[region.path].content == (config.ROOT / region.path).read_text(encoding="utf-8")
# --- a fixture tree -------------------------------------------------------
GROUPS = (("Fixture things", ("frobnicate",)), ("Other", ("gate keep",)))
def _record(path: str, gates: tuple[str, ...] = (), summary: str = "Does a thing.") -> cli_contract.CommandRecord:
failures = [cli_contract.Failure(cause="It broke", reaction="Show the user and stop")]
if gates:
failures.append(cli_contract.Failure(cause="Needs a person", reaction="Wait", code=42))
return cli_contract.CommandRecord(
path=path,
summary=summary,
synopsis=(cli_contract.Variant(usage=f"{path} [--flag]"),),
properties=cli_contract.Properties(
effect=cli_contract.Effect.WRITE,
idempotent=cli_contract.Idempotent.NO,
atomic="Yes",
budget=cli_contract.Budget.EXEMPT_WITHOUT_ARGS,
gates=gates,
),
notes=("Does a thing, mechanically.",),
failures=tuple(failures),
examples=("tools/wikitool frobnicate --flag",),
never=("Never frobnicate twice.",),
see_also=("`wikitool other` - a neighbour",),
)
HEADS = {
"docs/reference/commands/fixture-things.md": "# Fixture things\n\nThings, by hand.\n",
"docs/reference/commands/other.md": "# Other\n\nThe other one.\n",
"docs/reference/commands/README.md": "# Command reference\n\nAll of it.\n",
"docs/reference/exit-codes.md": "# Exit codes and gates\n\nWhere it stops.\n",
}
REGION_OF = {
"docs/reference/commands/fixture-things.md": reference_doc.COMMANDS_REGION,
"docs/reference/commands/other.md": reference_doc.COMMANDS_REGION,
"docs/reference/commands/README.md": reference_doc.INDEX_REGION,
"docs/reference/exit-codes.md": reference_doc.EXIT_CODES_REGION,
}
@pytest.fixture
def tree(tmp_path, monkeypatch):
monkeypatch.setattr(config, "ROOT", tmp_path)
records = {"frobnicate": _record("frobnicate"), "gate keep": _record("gate keep", ("keep",))}
monkeypatch.setattr(cli_contract, "all_records", lambda: dict(records))
monkeypatch.setattr(cli_contract, "GROUPS", GROUPS)
monkeypatch.setattr(cli, "options_texts", lambda paths: {p: " --flag A flag." for p in paths})
for relative, head in HEADS.items():
path = tmp_path / relative
path.parent.mkdir(parents=True, exist_ok=True)
name = REGION_OF[relative]
path.write_text(
f"{head}\n{blocks.open_marker(name)}\n{blocks.close_marker(name)}\n", encoding="utf-8"
)
return tmp_path, records
def _snapshot(root) -> dict[str, str]:
return {
p.relative_to(root).as_posix(): p.read_text(encoding="utf-8")
for p in sorted(root.rglob("*.md"))
}
def test_a_fresh_tree_is_reported_stale_until_applied(tree):
root, _ = tree
issues = docs_verify.check_reference_regions()
assert len(issues) == len(HEADS)
assert all("stale" in issue and "docs reference --apply" in issue for issue in issues)
dry = runner.invoke(app, ["docs", "reference"])
assert dry.exit_code == 0, dry.output
assert "would change" in dry.output
assert docs_verify.check_reference_regions() != []
result = runner.invoke(app, ["docs", "reference", "--apply"])
assert result.exit_code == 0, result.output
assert docs_verify.check_reference_regions() == []
def test_apply_twice_changes_nothing_the_second_time(tree):
root, _ = tree
runner.invoke(app, ["docs", "reference", "--apply"])
first = _snapshot(root)
second_run = runner.invoke(app, ["docs", "reference", "--apply"])
assert second_run.exit_code == 0
assert "already current" in second_run.output
assert _snapshot(root) == first
def test_the_head_survives_apply_byte_for_byte(tree):
root, _ = tree
runner.invoke(app, ["docs", "reference", "--apply"])
for relative, head in HEADS.items():
text = (root / relative).read_text(encoding="utf-8")
assert text.startswith(head), relative
def test_a_changed_record_makes_verify_name_the_page_and_the_command(tree, monkeypatch):
root, records = tree
runner.invoke(app, ["docs", "reference", "--apply"])
records["frobnicate"] = _record("frobnicate", summary="Does a different thing.")
monkeypatch.setattr(cli_contract, "all_records", lambda: dict(records))
issues = docs_verify.check_reference_regions()
assert any(
"docs/reference/commands/fixture-things.md" in issue and "docs reference --apply" in issue
for issue in issues
)
def test_a_generated_page_leaves_out_reactions_and_never(tree):
root, _ = tree
runner.invoke(app, ["docs", "reference", "--apply"])
text = (root / "docs/reference/commands/fixture-things.md").read_text(encoding="utf-8")
assert "It broke" in text
assert "Show the user and stop" not in text
assert "Never frobnicate twice." not in text
assert "- Counts toward the session call budget: only when called with arguments" in text
assert "```text\n --flag A flag.\n```" in text
def test_a_long_page_gets_its_toc_outside_the_generated_region(tree, monkeypatch):
"""The first `##` of a group page is generated; a TOC inserted before it
would sit inside the region and be wiped by the next regeneration."""
root, records = tree
many = tuple(f"cmd{i}" for i in range(12))
for path in many:
records[path] = _record(path)
monkeypatch.setattr(cli_contract, "all_records", lambda: dict(records))
monkeypatch.setattr(cli_contract, "GROUPS", (("Fixture things", many), GROUPS[1]))
assert runner.invoke(app, ["docs", "reference", "--apply"]).exit_code == 0
page = root / "docs/reference/commands/fixture-things.md"
text = page.read_text(encoding="utf-8")
assert toc.needs_toc(text)
assert text.index(blocks.close_marker(toc.REGION_NAME)) < text.index(
blocks.open_marker(reference_doc.COMMANDS_REGION)
)
assert toc.upsert(text) == text
assert docs_verify.check_reference_regions() == []
def test_a_group_without_a_page_fails_and_nothing_is_created(tree, monkeypatch):
root, _ = tree
monkeypatch.setattr(cli_contract, "GROUPS", GROUPS + (("New group", ()),))
issues = docs_verify.check_reference_regions()
assert any("docs/reference/commands/new-group.md is missing" in issue for issue in issues)
result = runner.invoke(app, ["docs", "reference", "--apply"])
assert result.exit_code == 1
assert "docs/reference/commands/new-group.md" in result.output
assert not (root / "docs/reference/commands/new-group.md").exists()
def test_a_page_without_its_marker_pair_fails_and_is_left_alone(tree):
root, _ = tree
page = root / "docs/reference/exit-codes.md"
page.write_text("# Exit codes and gates\n\nNo region here.\n", encoding="utf-8")
issues = docs_verify.check_reference_regions()
assert any("exit-codes.md has no" in issue for issue in issues)
result = runner.invoke(app, ["docs", "reference", "--apply"])
assert result.exit_code == 1
assert page.read_text(encoding="utf-8") == "# Exit codes and gates\n\nNo region here.\n"
def test_a_page_without_a_group_fails(tree):
root, _ = tree
(root / "docs/reference/commands/gone.md").write_text("# Gone\n", encoding="utf-8")
issues = docs_verify.check_reference_regions()
assert any("gone.md belongs to no group" in issue for issue in issues)
def test_a_tree_without_docs_passes(tmp_path, monkeypatch):
monkeypatch.setattr(config, "ROOT", tmp_path)
assert docs_verify.check_reference_regions() == []
def test_exit_codes_lists_each_gate_and_its_exit_42_causes(tree):
root, _ = tree
runner.invoke(app, ["docs", "reference", "--apply"])
page = (root / "docs/reference/exit-codes.md").read_text(encoding="utf-8")
assert "### keep\n\n- [`gate keep`](commands/other.md#gate-keep)" in page
assert "- [`gate keep`](commands/other.md#gate-keep): Needs a person" in page
assert "frobnicate" not in page
def test_the_index_links_every_command_to_its_section(tree):
root, _ = tree
runner.invoke(app, ["docs", "reference", "--apply"])
index = (root / "docs/reference/commands/README.md").read_text(encoding="utf-8")
assert "- [`frobnicate`](fixture-things.md#frobnicate) - Does a thing." in index
assert "- [`gate keep`](other.md#gate-keep) - Does a thing." in index