docs verify: DEVELOPMENT.md in STAGE_READMES; self-labelling release notes; prose corrections to 4.6.0 (#47 Block 3)
CI / verify (push) Successful in 54s
Release / release (push) Successful in 36s

Files changed:
- .gitea/workflows/release.yml
- CHANGES.md
- VERSION
- instructions/dev/stack-close/SKILL.md
- tools/CONTRACT.md
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/git_publish.py
- tools/chemenu/tests/test_docs_verify.py
This commit is contained in:
2026-09-04 15:55:04 +02:00
parent 91bd430ac8
commit 72b2b4424f
8 changed files with 137 additions and 12 deletions
+11 -1
View File
@@ -136,7 +136,17 @@ ROOT_README = config.ROOT / "README.md"
# tools/README.md is exactly the file it drifted in. INSTALL.md is here for the
# same reason: it is human-facing prose about installing an instance, and the
# command reference lives exactly once, in tools/CONTRACT.md.
STAGE_READMES = ("tools/README.md", "INSTALL.md")
#
# DEVELOPMENT.md joined them after it drifted the same way (Gitea #47): it grew
# a table describing what each verify command checks, which had to be removed by
# hand because nothing compared it to anything. It is not shipped - dist_cmd
# .ROOT_FILES excludes it - and that is not an argument against listing it here:
# `check_readmes_have_no_command_table` skips a file that does not exist, so in
# a distributed instance this entry is simply inert, while in the dev checkout
# (the only place the file exists, and the only place it can drift) it is
# checked. The name is now narrower than the tuple - these are the human-facing
# prose docs that must not re-list commands, stage README or not.
STAGE_READMES = ("tools/README.md", "INSTALL.md", "DEVELOPMENT.md")
# Docs that must not re-introduce the pre-migration bare-enum `type:` form.
# The per-collection contracts are appended at call time, since which ones exist
+12 -7
View File
@@ -441,13 +441,18 @@ STACK_MACHINERY_NOTE = (
def touches_stack_machinery(changed_files: list[str]) -> bool:
"""Whether `changed_files` includes a path under `version-parts.md`'s
scope for the stack version - `tools/`, `types/`, `instructions/`,
`AGENTS.md`, or any `<stage>/CONTRACT.md`. A publish in this class is,
by construction of the `stack-dev`/`stack-close` split, always followed
by the unchecked closing phase - `STACK_MACHINERY_NOTE` times a reminder
to land exactly there, for any session, not only one that read the
skill that names it."""
"""Whether `changed_files` includes a path under the stack version's own
scope - `tools/`, `types/`, `instructions/`, `AGENTS.md`, or a path ending
in `CONTRACT.md` at any depth. A publish in this class is, by construction
of the `stack-dev`/`stack-close` split, always followed by the unchecked
closing phase - `STACK_MACHINERY_NOTE` times a reminder to land exactly
there, for any session, not only one that read the skill that names it.
Deliberately a shade broader than CI's version gate, which matches
`<one-segment>/CONTRACT.md` only: this decides whether to print a sentence,
so over-matching costs a reminder nobody needed, while under-matching costs
the reminder in the one case it was built for. The two are not the same
predicate and should not be described as one."""
for path in changed_files:
if path in STACK_MACHINERY_NAMES:
return True
+24
View File
@@ -100,6 +100,30 @@ def test_install_md_is_checked_too(tmp_path, monkeypatch):
assert any("`doctor`" in issue for issue in issues)
def test_development_md_is_checked_too(tmp_path, monkeypatch):
"""DEVELOPMENT.md drifted exactly this way once (Gitea #47): a table
describing what each verify command checks, removed by hand because nothing
compared it to anything."""
root = tmp_path
(root / "DEVELOPMENT.md").write_text(
"| Command | Purpose |\n| `docs verify` | checks docs |\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("`docs verify`" in issue for issue in issues)
def test_an_absent_listed_doc_is_skipped_not_reported(tmp_path, monkeypatch):
"""The distributed-instance case: DEVELOPMENT.md is not shipped, so listing
it must stay inert where the file does not exist rather than failing a tree
that is correct."""
root = tmp_path
monkeypatch.setattr(docs_verify.config, "ROOT", root)
monkeypatch.setattr(docs_verify, "ROOT_README", root / "README.md")
assert docs_verify.check_readmes_have_no_command_table() == []
def test_legacy_type_blocks_are_absent():
assert docs_verify.check_legacy_type_blocks() == []