Chemenu 2.1.0 - deterministischer Wissenskompiler
CI / verify (push) Failing after 32s
Release / release (push) Successful in 38s

Chemenu kompiliert Rohnotizen zu einem verlinkten, quellengebundenen Wiki:
raw/ -> types/ + tools/ -> kb/ -> reports/. Was mechanisch ist, macht
tools/wikitool; was Urteil braucht, macht ein Agent unter Contracts, deren
Grenzen in Code durchgesetzt sind statt im Prompt.

Dieser Commit ist der Startpunkt der oeffentlichen Historie. Die vorherige
Entwicklung fand in einer privaten Instanz statt und ist nicht Teil dieses
Repositorys; ihre Erzaehlung steht vollstaendig in CHANGES.md, das mit 44
Eintraegen von 0.1.0 bis 2.1.0 erhalten geblieben ist.

Der mitgelieferte Korpus ist ein Testbett und eine Demo: 170 Seiten ueber den
Stack selbst - Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er
dokumentiert das Werkzeug mit den eigenen Mitteln des Werkzeugs.

Lizenz: AGPL-3.0 fuer den Stack (tools/, types/), CC-BY-4.0 fuer die Inhalte.
Die Grenze zwischen beiden ist der Dateiplan, den dist export berechnet -
siehe NOTICE.
This commit is contained in:
2026-09-01 16:24:34 +02:00
commit 18ae28f918
368 changed files with 50628 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
import pytest
import typer
from chemenu.commands import docs_verify
def test_every_registered_command_is_documented():
"""Forward direction: a command added to the CLI without a README row is
exactly the drift this check exists to catch."""
assert docs_verify.check_cli_readme() == []
def test_registered_commands_include_groups_and_top_level():
commands = docs_verify.registered_commands()
assert "new" in commands
assert "touch" in commands
assert "xref add" in commands
assert "confidence init-base" in commands
assert "docs verify" in commands
def test_undocumented_command_is_reported(monkeypatch):
monkeypatch.setattr(
docs_verify, "registered_commands", lambda: {"new", "frobnicate"}
)
monkeypatch.setattr(docs_verify, "top_level_names", lambda: {"new", "frobnicate"})
issues = docs_verify.check_cli_readme()
assert any("frobnicate" in issue for issue in issues)
def test_documented_but_nonexistent_command_is_reported(monkeypatch):
monkeypatch.setattr(docs_verify, "registered_commands", lambda: set())
monkeypatch.setattr(docs_verify, "top_level_names", lambda: set())
issues = docs_verify.check_cli_readme()
assert any("is not a wikitool command" in issue for issue in issues)
def test_invented_subcommand_under_a_real_group_is_caught(tmp_path, monkeypatch):
"""Regression guard: checking only the first token (`xref`) let a typo'd
or invented subcommand sit undetected forever next to a real command
group. The reverse check must match the full registered path, not just
the top-level word."""
fake = tmp_path / "README.md"
fake.write_text("| `xref frobnicate --a X --b Y` | does not exist |\n", encoding="utf-8")
monkeypatch.setattr(docs_verify, "CLI_README", fake)
monkeypatch.setattr(docs_verify, "registered_commands", lambda: {"xref add", "xref remove"})
issues = docs_verify.check_cli_readme()
assert any("xref frobnicate" in issue for issue in issues)
def test_collection_contracts_exist():
assert docs_verify.check_collection_contracts() == []
def test_readmes_carry_no_command_table():
"""A derived copy is checked or absent: the command table is checked in
tools/CONTRACT.md, so no README may hold a second one."""
assert docs_verify.check_readmes_have_no_command_table() == []
def test_a_command_table_in_the_root_readme_is_reported(tmp_path, monkeypatch):
fake = tmp_path / "README.md"
fake.write_text("| Command | Purpose |\n| `lint` | does things |\n", encoding="utf-8")
monkeypatch.setattr(docs_verify, "ROOT_README", fake)
issues = docs_verify.check_readmes_have_no_command_table()
assert any("`lint`" in issue for issue in issues)
def test_non_command_tables_in_the_root_readme_are_allowed(tmp_path, monkeypatch):
fake = tmp_path / "README.md"
fake.write_text("| Skill | Purpose |\n| `wiki-ingest` | ingests |\n", encoding="utf-8")
monkeypatch.setattr(docs_verify, "ROOT_README", fake)
assert docs_verify.check_readmes_have_no_command_table() == []
def test_stage_readmes_are_checked_too(tmp_path, monkeypatch):
"""tools/README.md is the file the command table actually drifted in - a
stage README is allowed to exist, but not to hold a second copy."""
root = tmp_path
(root / "tools").mkdir()
(root / "tools" / "README.md").write_text(
"| Command | Purpose |\n| `publish` | pushes |\n", encoding="utf-8"
)
monkeypatch.setattr(docs_verify.config, "ROOT", root)
monkeypatch.setattr(docs_verify, "ROOT_README", root / "README.md")
issues = docs_verify.check_readmes_have_no_command_table()
assert any("`publish`" in issue for issue in issues)
def test_install_md_is_checked_too(tmp_path, monkeypatch):
"""INSTALL.md is human-facing prose about installing an instance - the
command reference lives exactly once, in tools/CONTRACT.md."""
root = tmp_path
(root / "INSTALL.md").write_text(
"| Command | Purpose |\n| `doctor` | checks things |\n", encoding="utf-8"
)
monkeypatch.setattr(docs_verify.config, "ROOT", root)
monkeypatch.setattr(docs_verify, "ROOT_README", root / "README.md") # doesn't exist here
issues = docs_verify.check_readmes_have_no_command_table()
assert any("`doctor`" in issue for issue in issues)
def test_legacy_type_blocks_are_absent():
assert docs_verify.check_legacy_type_blocks() == []
def test_legacy_type_regex_matches_pre_migration_form():
assert docs_verify.LEGACY_TYPE_RE.search("---\ntype: comparison\ntags: []\n---")
assert not docs_verify.LEGACY_TYPE_RE.search("---\ntype: types/comparison.md\n---")
def test_no_content_is_gitignored():
"""The regression guard for the 2026-08-13 `.gitignore` rewrite: patterns
like `*temp*` and `bin/` were silently excluding files under raw/, so the
wiki reported them as covered while `publish` never committed them."""
assert docs_verify.check_ignored_content() == []
def test_ignore_canaries_are_clear():
assert docs_verify.ignored_canaries() == []
def test_a_swallowed_canary_is_reported():
"""`tools/.wikitool_session/` is legitimately ignored, so it stands in for
a content path that a bad pattern would swallow."""
swallowed = docs_verify.ignored_canaries(("tools/.wikitool_session/budget.json",))
assert swallowed == ["tools/.wikitool_session/budget.json"]
def test_the_environment_note_is_ignored_but_its_template_is_not():
"""The pattern has to split a file from its own template. `ENVIRONMENT.md`
describes one checkout and must never be committed; `ENVIRONMENT.md.template`
is tracked machinery that `dist export` ships, and the careless pattern
(`ENVIRONMENT.md*`) would swallow both."""
assert docs_verify.ignored_canaries(("ENVIRONMENT.md",)) == ["ENVIRONMENT.md"]
assert docs_verify.ignored_canaries(("ENVIRONMENT.md.template",)) == []
def test_coverage_output_is_ignored():
"""`pytest --cov` writes into tools/, and `publish` runs `git add -A`."""
paths = ("tools/coverage.xml", "tools/htmlcov/index.html", "tools/.coverage")
assert docs_verify.ignored_canaries(paths) == list(paths)
def test_ignore_checks_degrade_when_git_is_unavailable(monkeypatch):
"""Without git the ignore rules are unknowable, not wrong - `docs verify`
must stay usable rather than reporting a false positive."""
monkeypatch.setattr(docs_verify, "_git", lambda *a, **k: None)
assert docs_verify.check_ignored_content() == []
def test_this_repos_version_and_changelog_agree():
assert docs_verify.check_version_changelog() == []
def _versioned_tree(tmp_path, monkeypatch, version: str, changes: str):
(tmp_path / "VERSION").write_text(version, encoding="utf-8")
(tmp_path / "CHANGES.md").write_text(changes, encoding="utf-8")
monkeypatch.setattr(docs_verify.config, "ROOT", tmp_path)
def test_a_bump_with_no_changelog_entry_is_reported(tmp_path, monkeypatch):
"""The check that gives `version bump` its teeth: a version raised with
nothing written about it would ship release notes describing the
previous release."""
_versioned_tree(tmp_path, monkeypatch, "0.2.0\n", "# Changelog\n\n## 0.1.0 - 2026-08-29 - Old\n")
issues = docs_verify.check_version_changelog()
assert any("0.2.0" in issue and "0.1.0" in issue for issue in issues)
def test_a_changelog_with_no_versioned_entry_is_accepted(tmp_path, monkeypatch):
"""A fresh distribution ships an empty changelog, and this repo's own
pre-versioning entries are dated rather than versioned. Neither claims to
describe the current version."""
_versioned_tree(
tmp_path, monkeypatch, "0.1.0\n", "# Changelog\n\n## 2026-08-01 - Before versioning\n"
)
assert docs_verify.check_version_changelog() == []
def test_a_missing_or_malformed_version_is_reported(tmp_path, monkeypatch):
monkeypatch.setattr(docs_verify.config, "ROOT", tmp_path)
assert any("VERSION" in issue for issue in docs_verify.check_version_changelog())
_versioned_tree(tmp_path, monkeypatch, "not-a-version\n", "# Changelog\n")
assert any("semantic version" in issue for issue in docs_verify.check_version_changelog())
def test_this_repos_boundary_is_accounted_for():
assert docs_verify.check_migration_for_boundary() == []
def _boundary_tree(tmp_path, monkeypatch, current: str, previous: str, marker: str = ""):
(tmp_path / "VERSION").write_text(f"{current}\n", encoding="utf-8")
(tmp_path / "CHANGES.md").write_text(
"# Changelog\n\n---\n\n"
f"## {current} - 2026-09-01 - New\n\n{marker}Body.\n\n---\n\n"
f"## {previous} - 2026-08-30 - Old\n\nBody.\n",
encoding="utf-8",
)
instructions = tmp_path / "instructions"
(instructions / "migrations").mkdir(parents=True, exist_ok=True)
monkeypatch.setattr(docs_verify.config, "ROOT", tmp_path)
monkeypatch.setattr(docs_verify.config, "INSTRUCTIONS_DIR", instructions)
return tmp_path
def test_a_breaking_release_without_a_migration_is_reported(tmp_path, monkeypatch):
"""`version check` tells an instance it must migrate; without this, that is
where the trail ends."""
_boundary_tree(tmp_path, monkeypatch, "2.0.0", "1.4.0")
issues = docs_verify.check_migration_for_boundary()
assert any("2.0.0" in issue and "must migrate" in issue for issue in issues)
def test_a_compatible_release_needs_no_migration(tmp_path, monkeypatch):
_boundary_tree(tmp_path, monkeypatch, "1.5.0", "1.4.0")
assert docs_verify.check_migration_for_boundary() == []
def test_an_explicit_none_required_marker_satisfies_the_check(tmp_path, monkeypatch):
from chemenu import version as version_mod
_boundary_tree(
tmp_path, monkeypatch, "2.0.0", "1.4.0",
marker=f"{version_mod.MIGRATION_NONE_MARKER} - nothing to change.\n\n",
)
assert docs_verify.check_migration_for_boundary() == []
def test_a_migration_document_satisfies_the_check(tmp_path, monkeypatch):
root = _boundary_tree(tmp_path, monkeypatch, "2.0.0", "1.4.0")
(root / "instructions" / "migrations" / "2.0.0-retype.md").write_text(
"---\ntype: types/instruction.md\nname: 2.0.0-retype\n"
"description: Retype.\nmanual: true\nmigrates_to: 2.0.0\n---\n",
encoding="utf-8",
)
assert docs_verify.check_migration_for_boundary() == []
def test_verify_raises_when_a_boundary_has_no_migration(monkeypatch):
monkeypatch.setattr(docs_verify, "check_migration_for_boundary", lambda: ["unbridged"])
with pytest.raises(typer.Exit):
docs_verify.verify()
def test_verify_raises_when_the_version_is_undocumented(monkeypatch):
monkeypatch.setattr(docs_verify, "check_version_changelog", lambda: ["undocumented"])
with pytest.raises(typer.Exit):
docs_verify.verify()
def test_verify_raises_when_issues_exist(monkeypatch):
monkeypatch.setattr(docs_verify, "check_cli_readme", lambda: ["boom"])
with pytest.raises(typer.Exit):
docs_verify.verify()
def test_verify_raises_when_content_is_ignored(monkeypatch):
monkeypatch.setattr(docs_verify, "check_ignored_content", lambda: ["swallowed"])
with pytest.raises(typer.Exit):
docs_verify.verify()