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>
89 lines
3.6 KiB
Python
89 lines
3.6 KiB
Python
"""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"
|
|
)
|