refactor(frontmatter): unified frontmatter contract + handoff spec (ORCH-52c)

src/frontmatter.py grows from a single-key reader into the full machine
contract: reader (read_frontmatter_value, unchanged), one parse primitive
(parse_frontmatter), writer (render/write_frontmatter), schema validator
(validate_schema/REQUIRED_FIELDS, warning-only by default) and a shared
strip_frontmatter helper. The five verdict gates (check_reviewer_verdict,
_parse_tests_verdict, _parse_deploy_status, _parse_staging_status,
parse_security_status) now read through the single parse_frontmatter point
instead of duplicated ad-hoc YAML logic; review_parse._strip_frontmatter and
security_gate.extract_security_findings reuse the shared helper.

Strictly backward compatible + never-raise: STAGE_TRANSITIONS, the QG_CHECKS
composition, verdict semantics (incl. ORCH-047 three-field tester + negative
token priority), reason-strings and worktree->origin/main fallback are 1:1.
The schema validator never influences a gate verdict by default; hard-fail is
reserved behind the frontmatter_validation_strict kill-switch (default False).

New formal handoff spec docs/_standards/HANDOFF_PROTOCOL.md ("stage -> required
output" + required frontmatter schema), aligned 1:1 with PIPELINE_DOCS.md.

Tests: test_frontmatter.py (TC-01..07), test_qg_verdicts.py (TC-08..15),
test_security_gate.py (TC-12), test_stages_invariants.py (TC-16). Full
tests/ green (1212).

Refs: ORCH-076

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 14:03:49 +03:00
committed by orchestrator-deployer
parent 2030d1627a
commit 92961d1d32
14 changed files with 1043 additions and 109 deletions

View File

@@ -559,19 +559,19 @@ def parse_security_status(content: str) -> tuple[bool, str]:
* ``security_status: FAIL`` -> ``(False, "Security status: FAIL")``
* missing field / no frontmatter / bad YAML -> ``(False, <reason>)`` (fail-closed
on the verdict read, AC-9).
"""
import yaml
ORCH-52c: parse delegated to the unified ``frontmatter.parse_frontmatter``
primitive (single source of YAML-frontmatter logic); the security_status
semantics (FAIL authoritative) are UNCHANGED (1:1).
"""
from .frontmatter import parse_frontmatter
parse = parse_frontmatter(content)
if parse.yaml_error is not None:
return False, f"Invalid YAML frontmatter in security report: {parse.yaml_error}"
status = None
if content.startswith("---"):
parts = content.split("---", 2)
if len(parts) >= 3:
try:
fm = yaml.safe_load(parts[1]) or {}
except yaml.YAMLError as e:
return False, f"Invalid YAML frontmatter in security report: {e}"
if isinstance(fm, dict):
status = str(fm.get("security_status", "")).upper().strip()
if parse.has_block and not parse.malformed:
status = str(parse.data.get("security_status", "")).upper().strip()
if status == "FAIL":
return False, "Security status: FAIL"
if status == "PASS":
@@ -593,11 +593,9 @@ def extract_security_findings(report_path: str) -> str:
return ""
with open(report_path, "r", encoding="utf-8") as f:
content = f.read()
# Drop the frontmatter; keep the human body.
if content.startswith("---"):
parts = content.split("---", 2)
if len(parts) >= 3:
content = parts[2]
# Drop the frontmatter; keep the human body (ORCH-52c: shared helper).
from .frontmatter import strip_frontmatter
content = strip_frontmatter(content)
wanted = ("## Verdict", "## Secrets", "## Dependencies (blocking)")
lines = content.splitlines()
out = []