Contracts
review-regression-gate-contract.md
Contract — auto-scoped review-time regression gate (Phase 1; #572 + #1979 facet + #2283 Phase 1)
review/pre_review_gate.py: derive_test_scope(changed_files) → affected tests, keyed on dorny group SHAPE — per-shard groups' tests/ globs (via _gate_coverage.aggregate_filter_groups) + composite groups' _COMPOSITE_ROUTING cone_roots; catch-alls (core_misc/e2e/any_src + any src/-probe group like windows_critical) EXCLUDED. evaluate_pre_review_gate derives the scope then delegates to evaluate_with_scope(scope, ...) — the shared verdict tail (head-side run, net-new runner; diffs vs base via baseline.py's _parse_junit_xml+diff_baseline, reused unchanged) → new-failure verdict. evaluate_with_scope is the SAME entrypoint tasks_move_task._mt_pre_review_gate_with_override_scope (FR-004's explicit-override tier) drives for its own non-empty scope — a pre-merge finding removed that tier's hand-mirrored copy of the tail so the two precedence tiers can never diverge. Empty affected set / empty-cone composite / excluded-only / uncomputable baseline → no_coverage/unverified_baseline WARN, NEVER a green "verified".tasks_move_task.py: on --to for_review (only), before any status commit, runs the gate. Warn-default; opt-in block on review.fail_on_pre_review_regression (raises typer.Exit(1) before commit); --force bypass recorded. Evidence on StatusEvent.policy_metadata["pre_review_gate"]. Override precedence: frontmatter pre_review_test_scope > config review.pre_review_test_command > census default.- Prerequisite (pre-merge finding, #572/#1979/#2283):
review.fail_on_pre_review_regression is only EFFECTIVE when review.test_command is also configured. The block can only ever fire on a NEW_FAILURES verdict, which requires a computed baseline; baseline.py's capture_baseline (run at implement time) returns None — no baseline artifact is ever written — when review.test_command is unset. Without a baseline, every for_review move degrades to no_coverage/unverified_baseline, and the opt-in block can NEVER engage — silently, unless surfaced. tasks_move_task._mt_pre_review_gate_console_warning escalates that specific combination (block_enabled=True + no_coverage/unverified_baseline) from the routine dim advisory to an explicit, non-dim warning naming this prerequisite; the warn-vs-block semantics themselves are unchanged (still non-blocking — a block can't fire on data that doesn't exist). - FR-006 arch invariant (
test_pre_review_scope_singlesource.py) guards BOTH group shapes + the exclusion against the live authorities (mutation-bites).