review.py
python
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439
feat: K1-P1 complete — agent provenance, build-verification…
Sonnet 4.6
patch
56 days ago
| 1 | """``overseer review --freeze`` command (§K5.2).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import sys |
| 6 | from argparse import Namespace |
| 7 | from pathlib import Path |
| 8 | |
| 9 | from adapters.config import load_config |
| 10 | from adapters.errors import ConfigError, ReadError |
| 11 | from adapters.factory import create_adapter |
| 12 | from cli.context import CliContext |
| 13 | from cli.kit_root import kit_version |
| 14 | from cli.paths import PathEscapeError, confine_path, repo_relative, resolve_config_path, resolve_repo_root |
| 15 | from cli.sanitize import format_config_error |
| 16 | from tools.freeze_reviewer.checklist import builtin_checklist, load_checklist_file |
| 17 | from tools.freeze_reviewer.engine import ReviewOptions, resolve_exit_code, resolve_reviewer_settings, run_freeze_review |
| 18 | from tools.freeze_reviewer.labels import validate_reviewer_model |
| 19 | from tools.freeze_reviewer.report import build_report, render_human_report |
| 20 | from tools.substrate_health import check_substrate |
| 21 | |
| 22 | DISALLOWED_FLAGS = frozenset( |
| 23 | { |
| 24 | "--write-vcs", |
| 25 | "--commit", |
| 26 | "--push", |
| 27 | "--escalate-force-pass", |
| 28 | } |
| 29 | ) |
| 30 | |
| 31 | |
| 32 | def _validate_raw_argv(argv: list[str]) -> int | None: |
| 33 | """Reject disallowed flags with USAGE exit 1.""" |
| 34 | for token in argv: |
| 35 | if token in DISALLOWED_FLAGS: |
| 36 | return 1 |
| 37 | if token.startswith("--model") and "=" in token: |
| 38 | value = token.split("=", 1)[1] |
| 39 | if _looks_like_vendor_slug(value): |
| 40 | return 1 |
| 41 | if token == "--model": |
| 42 | return None |
| 43 | return None |
| 44 | |
| 45 | |
| 46 | def _looks_like_vendor_slug(value: str) -> bool: |
| 47 | lowered = value.lower() |
| 48 | return any(marker in lowered for marker in ("gpt-", "claude-", "composer-")) |
| 49 | |
| 50 | |
| 51 | def _resolve_effective_checklist(args: Namespace, repo_root: Path) -> tuple[list | None, int | None]: |
| 52 | if args.checklist is None: |
| 53 | return builtin_checklist(), None |
| 54 | try: |
| 55 | checklist_path = confine_path(repo_root, args.checklist) |
| 56 | except PathEscapeError: |
| 57 | return None, 4 |
| 58 | if not checklist_path.is_file(): |
| 59 | return None, 4 |
| 60 | try: |
| 61 | return load_checklist_file(checklist_path), None |
| 62 | except ConfigError: |
| 63 | return None, 2 |
| 64 | |
| 65 | |
| 66 | def _resolve_artifact(args: Namespace, repo_root: Path) -> tuple[Path | None, str | None, int | None]: |
| 67 | try: |
| 68 | artifact_path = confine_path(repo_root, args.freeze_path) |
| 69 | except PathEscapeError: |
| 70 | return None, None, 4 |
| 71 | if not artifact_path.is_file(): |
| 72 | return None, None, 4 |
| 73 | rel = repo_relative(repo_root, artifact_path) |
| 74 | return artifact_path, rel, None |
| 75 | |
| 76 | |
| 77 | def run_review(args: Namespace, ctx: CliContext, *, raw_argv: list[str] | None = None) -> int: |
| 78 | """Execute ``overseer review --freeze``.""" |
| 79 | argv = raw_argv or [] |
| 80 | disallowed = _validate_raw_argv(argv) |
| 81 | if disallowed is not None: |
| 82 | ctx.output.error("usage: invalid or disallowed flag") |
| 83 | return disallowed |
| 84 | |
| 85 | repo_root = resolve_repo_root(cwd=ctx.cwd, repo_arg=args.repo, command="review") |
| 86 | overseer_dir = repo_root / ".overseer" |
| 87 | if not overseer_dir.is_dir(): |
| 88 | ctx.output.error("not initialized — run overseer init first") |
| 89 | return 2 |
| 90 | |
| 91 | config_path = resolve_config_path(repo_root, args.config) |
| 92 | try: |
| 93 | config = load_config(config_path) |
| 94 | except ConfigError as exc: |
| 95 | ctx.output.error(format_config_error(exc, repo_root)) |
| 96 | return 2 |
| 97 | |
| 98 | checklist, checklist_code = _resolve_effective_checklist(args, repo_root) |
| 99 | if checklist_code is not None: |
| 100 | if checklist_code == 2: |
| 101 | ctx.output.error("invalid checklist file") |
| 102 | else: |
| 103 | ctx.output.error("refused: checklist path") |
| 104 | return checklist_code |
| 105 | |
| 106 | artifact_path, rel_path, artifact_code = _resolve_artifact(args, repo_root) |
| 107 | if artifact_code is not None or artifact_path is None or rel_path is None: |
| 108 | ctx.output.error("refused: artifact path") |
| 109 | return artifact_code or 4 |
| 110 | |
| 111 | # Validate model label when agent mode effective |
| 112 | effective_mode = args.mode or config.freeze_contract.reviewer.mode |
| 113 | effective_model = args.model or config.freeze_contract.reviewer.model |
| 114 | if effective_mode != "human": |
| 115 | if args.model and _looks_like_vendor_slug(args.model): |
| 116 | ctx.output.error("reviewer.model must be a label, not a vendor slug") |
| 117 | return 1 |
| 118 | try: |
| 119 | validate_reviewer_model(effective_model, ctx.kit) |
| 120 | except ConfigError as exc: |
| 121 | ctx.output.error(format_config_error(exc, repo_root)) |
| 122 | return 2 |
| 123 | if args.provider and args.provider not in {"local", "api"}: |
| 124 | ctx.output.error("invalid --provider value") |
| 125 | return 1 |
| 126 | if args.mode and args.mode not in {"agent", "human"}: |
| 127 | ctx.output.error("invalid --mode value") |
| 128 | return 1 |
| 129 | |
| 130 | adapter = create_adapter(config, repo_root, runner=ctx.runner) |
| 131 | substrate = check_substrate(config, repo_root) |
| 132 | if not substrate.ok: |
| 133 | ctx.output.error(f"substrate: {substrate.state} — {substrate.message}") |
| 134 | if substrate.remediation: |
| 135 | ctx.output.error(f"remediation: {substrate.remediation}") |
| 136 | return 2 |
| 137 | |
| 138 | status = adapter.status() |
| 139 | if isinstance(status, ReadError): |
| 140 | ctx.output.error(str(status)) |
| 141 | return 2 |
| 142 | |
| 143 | injected_provider = None |
| 144 | if ctx.review_provider_factory is not None: |
| 145 | injected_provider = ctx.review_provider_factory(config.freeze_contract.reviewer.provider) |
| 146 | |
| 147 | options = ReviewOptions( |
| 148 | dry_run=args.dry_run, |
| 149 | no_stamp=args.no_stamp, |
| 150 | mode=args.mode, |
| 151 | provider=args.provider, |
| 152 | model=args.model, |
| 153 | checklist=checklist, |
| 154 | kit_version=kit_version(), |
| 155 | kit_root=ctx.kit, |
| 156 | injected_provider=injected_provider, |
| 157 | ) |
| 158 | |
| 159 | try: |
| 160 | result = run_freeze_review( |
| 161 | artifact_path=artifact_path, |
| 162 | rel_path=rel_path, |
| 163 | config=config, |
| 164 | options=options, |
| 165 | ) |
| 166 | except ValueError as exc: |
| 167 | if str(exc) == "not-utf8": |
| 168 | ctx.output.error("refused: artifact not utf-8") |
| 169 | return 4 |
| 170 | raise |
| 171 | |
| 172 | reviewer = resolve_reviewer_settings(config.freeze_contract, options) |
| 173 | report = build_report( |
| 174 | freeze_path=rel_path, |
| 175 | result=result, |
| 176 | reviewer=reviewer, |
| 177 | config=config.freeze_contract, |
| 178 | enabled=config.freeze_contract.enabled, |
| 179 | ) |
| 180 | exit_code = resolve_exit_code( |
| 181 | result, |
| 182 | config_error=False, |
| 183 | refused=result.refused, |
| 184 | ) |
| 185 | report["exit_code"] = exit_code |
| 186 | |
| 187 | if ctx.output.json_mode: |
| 188 | ctx.output.emit_json(report) |
| 189 | else: |
| 190 | ctx.output.emit(render_human_report(freeze_path=rel_path, result=result)) |
| 191 | |
| 192 | return exit_code |
File History
1 commit
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439
feat: K1-P1 complete — agent provenance, build-verification…
Sonnet 4.6
patch
56 days ago