#!/usr/bin/env python3 """Extract policy-declared semantic surfaces without performing semantic judgment.""" from __future__ import annotations import argparse from dataclasses import dataclass import hashlib import json import os from pathlib import Path import re import sys from typing import Any, Iterable, Mapping from contract_markdown import as_list, parse_frontmatter DEFAULT_ROOT = Path(__file__).resolve().parents[2] DEFAULT_POLICY = Path("harness/source/document-semantic-surfaces.json") RESULT_SCHEMA = "semantic-surface-extractor-result/v1" POLICY_SCHEMA = "document-semantic-surfaces/v1" SECTION_MARKER_RE = re.compile(r"^\s*\s*$") HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*$") class SemanticSurfaceError(ValueError): """The policy or an input document cannot be interpreted safely.""" @dataclass(frozen=True) class Section: section_id: str | None heading: str level: int line_start: int line_end: int text: str def canonical_json_bytes(value: Any) -> bytes: return (json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True) + "\n").encode("utf-8") def _safe_rel(value: Any, location: str) -> str: if not isinstance(value, str) or not value or "\\" in value: raise SemanticSurfaceError(f"{location}: expected a non-empty repo-relative POSIX path") path = Path(value) if path.is_absolute() or ".." in path.parts: raise SemanticSurfaceError(f"{location}: path escapes repository") return path.as_posix() def load_policy(root: Path, policy_path: Path = DEFAULT_POLICY) -> Mapping[str, Any]: source = policy_path if policy_path.is_absolute() else root / policy_path try: data = json.loads(source.read_text(encoding="utf-8")) except (OSError, UnicodeError, json.JSONDecodeError) as exc: raise SemanticSurfaceError(f"surface policy error: {exc}") from exc if not isinstance(data, dict) or data.get("schema_version") != POLICY_SCHEMA: raise SemanticSurfaceError(f"expected {POLICY_SCHEMA}") if not isinstance(data.get("required_statuses"), list) or not all(isinstance(x, str) for x in data["required_statuses"]): raise SemanticSurfaceError("required_statuses must be a string list") if not isinstance(data.get("always_required_source_types"), list) or not all( isinstance(x, str) for x in data["always_required_source_types"] ): raise SemanticSurfaceError("always_required_source_types must be a string list") documents = data.get("documents") if not isinstance(documents, dict) or not documents: raise SemanticSurfaceError("documents must be a non-empty object") for source_type, config in documents.items(): if not isinstance(source_type, str) or not isinstance(config, dict): raise SemanticSurfaceError("invalid document policy") if config.get("mode") not in {"local", "hub"}: raise SemanticSurfaceError(f"documents.{source_type}.mode must be local or hub") roots = config.get("roots") surfaces = config.get("required_surfaces") if not isinstance(roots, list) or not roots: raise SemanticSurfaceError(f"documents.{source_type}.roots must be non-empty") for index, value in enumerate(roots): _safe_rel(value, f"documents.{source_type}.roots[{index}]") if not isinstance(surfaces, list) or not surfaces: raise SemanticSurfaceError(f"documents.{source_type}.required_surfaces must be non-empty") ids: set[str] = set() for index, surface in enumerate(surfaces): if not isinstance(surface, dict) or set(surface) not in ( {"section_id", "legacy_heading"}, {"section_id", "section_aliases", "legacy_heading"}, ): raise SemanticSurfaceError(f"documents.{source_type}.required_surfaces[{index}] has invalid fields") section_id = surface.get("section_id") pattern = surface.get("legacy_heading") if not isinstance(section_id, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]*", section_id): raise SemanticSurfaceError(f"invalid semantic section id: {section_id!r}") if section_id in ids: raise SemanticSurfaceError(f"duplicate semantic section id: {section_id}") ids.add(section_id) aliases = surface.get("section_aliases", []) if not isinstance(aliases, list) or any( not isinstance(alias, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]*", alias) for alias in aliases ): raise SemanticSurfaceError(f"invalid section_aliases for {section_id}") try: re.compile(str(pattern), re.IGNORECASE) except re.error as exc: raise SemanticSurfaceError(f"invalid legacy heading regex for {section_id}: {exc}") from exc return data def _sections(text: str) -> tuple[Section, ...]: lines = text.splitlines() headings: list[tuple[int, int, str, str | None]] = [] pending_id: str | None = None for line_number, line in enumerate(lines, 1): marker = SECTION_MARKER_RE.fullmatch(line) if marker: pending_id = marker.group(1) continue heading = HEADING_RE.match(line) if heading: headings.append((line_number, len(heading.group(1)), heading.group(2).strip(), pending_id)) pending_id = None elif line.strip() and not line.lstrip().startswith("