Source code for tg_model.execution.requirements

"""Requirement satisfaction reporting (Phase 7).

Acceptance criteria are compiled as dependency-graph constraint checks; results appear in
:class:`~tg_model.execution.run_context.RunContext` / :class:`~tg_model.execution.evaluator.RunResult`
alongside part-owned constraints, tagged with ``requirement_path`` when applicable.
"""

from __future__ import annotations

from dataclasses import dataclass

from tg_model.execution.evaluator import RunResult
from tg_model.execution.run_context import RunContext


[docs] @dataclass(frozen=True) class RequirementSatisfactionResult: """Outcome of one requirement acceptance check (one allocation x one compiled check).""" requirement_path: str allocation_target_path: str passed: bool evidence: str check_name: str
[docs] @dataclass(frozen=True) class RequirementSatisfactionSummary: """Aggregated requirement acceptance outcomes for one run. Use :attr:`check_count` to detect the case where **no** acceptance checks ran (no ``expr=`` requirements compiled into the graph for this configuration). :attr:`all_passed` is **False** when ``check_count == 0`` so callers do not treat "nothing to verify" as green compliance. """ results: tuple[RequirementSatisfactionResult, ...] @property def check_count(self) -> int: return len(self.results) @property def all_passed(self) -> bool: """True only when there is at least one acceptance check and every check passed.""" return len(self.results) > 0 and all(r.passed for r in self.results)
[docs] def iter_requirement_satisfaction( ctx: RunContext | RunResult, ) -> list[RequirementSatisfactionResult]: """Filter constraint rows that carry ``requirement_path`` (acceptance checks). Parameters ---------- ctx : RunContext or RunResult Object exposing ``constraint_results`` (same ordering as evaluation). Returns ------- list of RequirementSatisfactionResult One row per tagged constraint result. """ out: list[RequirementSatisfactionResult] = [] for cr in ctx.constraint_results: if cr.requirement_path is None: continue out.append( RequirementSatisfactionResult( requirement_path=cr.requirement_path, allocation_target_path=cr.allocation_target_path or "", passed=cr.passed, evidence=cr.evidence, check_name=cr.name, ) ) return out
[docs] def summarize_requirement_satisfaction( ctx: RunContext | RunResult, ) -> RequirementSatisfactionSummary: """Aggregate :func:`iter_requirement_satisfaction` into a summary tuple. Returns ------- RequirementSatisfactionSummary Includes :attr:`RequirementSatisfactionSummary.all_passed` semantics for zero checks. """ return RequirementSatisfactionSummary(tuple(iter_requirement_satisfaction(ctx)))
[docs] def all_requirements_satisfied(ctx: RunContext | RunResult) -> bool: """Return :attr:`RequirementSatisfactionSummary.all_passed` for ``ctx``. Returns ------- bool **False** when there are **zero** requirement acceptance checks (not vacuously true). See Also -------- summarize_requirement_satisfaction """ return summarize_requirement_satisfaction(ctx).all_passed