docs(ORCH-079): ORCH-52f — sync README with code + reviewer overview-docs axis
All checks were successful
CI / test (push) Successful in 29s
CI / test (pull_request) Successful in 30s

Layer 5 (final) of epic ORCH-52. Docs + prompt-only; src/ untouched.

- README.md «Известные ограничения»: fix numbering (was 1,2,3,4,3,4),
  move 6 resolved/obsolete items to «Закрыто (история)» trail with ORCH
  refs, keep only really-open limitations (Telegram-48h ORCH-087,
  task-deps intra-repo ORCH-026, serial-gate ORCH-088). Point-sync stage
  table (development → check_ci_green) and event-routing (ORCH-045).
- reviewer.md: overview-docs axis (axis 4 + constraints) — closing a
  README limitation without updating README → finding ≥P1 (canon 52d
  «»; verdict key + 5 XML sections + 6 schema fields byte-intact).
- tests: new tests/test_readme_limitations.py (numbering + no resolved
  items as open); test_agent_prompts_canon.py asserts the new axis.
- CLAUDE.md / CHANGELOG.md updated; epic ORCH-52 closed (52b→…→52f).

Refs: ORCH-079

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 16:23:17 +03:00
parent e75f8796f5
commit 131d002469
6 changed files with 141 additions and 9 deletions

View File

@@ -258,3 +258,24 @@ def test_claude_md_and_readme_reference_traceability_standard():
assert "TRACEABILITY.md" in _read_repo("docs", "architecture", "README.md"), (
"architecture README does not reference docs/_standards/TRACEABILITY.md"
)
# --------------------------------------------------------------------------- #
# ORCH-079 (ORCH-52f): reviewer overview-docs axis (layer 5 of epic ORCH-52).
# Pure-text anti-drift check (TRZ §FR-6 / AC-5), NO `src/` import.
# --------------------------------------------------------------------------- #
def test_reviewer_carries_overview_docs_axis():
"""ORCH-079 TC-01 (AC-5): reviewer.md covers the README overview-docs axis.
The reviewer must require README ("Известные ограничения") to be updated when
a PR closes a documented limitation. This guards the rule against silent drift
in a future prompt refactor, exactly like the traceability control axis.
"""
text = _read("reviewer")
assert "Известные ограничения" in text, (
"reviewer.md does not mention the README 'Известные ограничения' overview-docs axis"
)
assert "ORCH-079" in text, (
"reviewer.md does not anchor the overview-docs axis to ORCH-079"
)

View File

@@ -0,0 +1,88 @@
"""ORCH-079 (ORCH-52f): structural anti-drift for README "Известные ограничения".
Layer 5 (final) of epic ORCH-52: the root README overview showcase must not lie
about the project state. These are pure-text structural checks (NO `src/` import,
NO agent runs) guarding two invariants (TRZ §FR-1/FR-2, AC-1/AC-2):
* the OPEN limitations list is numbered strictly 1, 2, 3, … without repeats
(the historical bug was `1,2,3,4,3,4`);
* resolved/obsolete items (single-task worktree, in-process daemon, "Gitea CI
not configured", "no retry") are NOT listed as OPEN limitations — if mentioned
at all, only under the "Закрыто (история)" trail.
Covers test-plan TC-05.
"""
import os
import re
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_README = os.path.join(_REPO_ROOT, "README.md")
# Heading of the limitations section and the closed-history subsection that ends
# the OPEN portion.
_SECTION_HEAD = "## Известные ограничения"
_CLOSED_HEAD = "Закрыто (история)"
# Phrases that mark a RESOLVED/obsolete item. They must not appear in the OPEN
# portion of the section (only allowed under "Закрыто (история)").
_RESOLVED_MARKERS = (
"Single-task",
"shared `/repos`",
"daemon-потоки",
"не настроен", # "Gitea CI не настроен"
"No retry",
)
def _read_readme() -> str:
with open(_README, encoding="utf-8") as f:
return f.read()
def _limitations_section() -> str:
"""Text of the '## Известные ограничения' section up to the next '## ' heading."""
text = _read_readme()
idx = text.find(_SECTION_HEAD)
assert idx != -1, "README.md has no '## Известные ограничения' section"
rest = text[idx + len(_SECTION_HEAD):]
# Stop at the next top-level (##) heading, if any.
nxt = re.search(r"\n## ", rest)
return rest[: nxt.start()] if nxt else rest
def _open_portion(section: str) -> str:
"""The OPEN-limitations portion: everything before the 'Закрыто (история)' trail."""
cut = section.find(_CLOSED_HEAD)
return section[:cut] if cut != -1 else section
def test_open_limitations_numbered_sequentially():
"""AC-1: the OPEN limitations list is numbered 1, 2, 3, … with no repeats/gaps."""
open_part = _open_portion(_limitations_section())
# Leading numbered list items: lines like "1. **...".
numbers = [int(m) for m in re.findall(r"^(\d+)\.\s", open_part, flags=re.MULTILINE)]
assert numbers, "no numbered OPEN limitations found in README section"
assert numbers == list(range(1, len(numbers) + 1)), (
f"OPEN limitations numbering is not strictly sequential: {numbers}"
)
def test_resolved_items_not_listed_as_open():
"""AC-2: resolved/obsolete items are not present as OPEN limitations."""
open_part = _open_portion(_limitations_section())
leaked = [m for m in _RESOLVED_MARKERS if m in open_part]
assert not leaked, (
f"resolved items leaked into OPEN limitations (must be under 'Закрыто'): {leaked}"
)
def test_closed_history_trail_present_with_orch_refs():
"""AC-2: the 'Закрыто (история)' trail exists and carries ORCH references."""
section = _limitations_section()
assert _CLOSED_HEAD in section, (
"README limitations section lacks the 'Закрыто (история)' trail"
)
closed = section[section.find(_CLOSED_HEAD):]
assert re.search(r"ORCH-\d+", closed), (
"the 'Закрыто (история)' trail has no ORCH-NNN references"
)