From b917c700c61ecd58a8109592b29ea9bee110de0f Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:28:03 +0800 Subject: [PATCH 01/14] feat: add one-command local Counterproof check runtime --- skill_factory/evolution/local_check.py | 234 +++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 skill_factory/evolution/local_check.py diff --git a/skill_factory/evolution/local_check.py b/skill_factory/evolution/local_check.py new file mode 100644 index 0000000..2db1a5a --- /dev/null +++ b/skill_factory/evolution/local_check.py @@ -0,0 +1,234 @@ +"""Local one-command Counterproof check for the current Git branch.""" +from __future__ import annotations + +import json +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from .integrity import ProofIntegrityReport, inspect_proof_integrity +from .onboarding import TestRunnerDetection, detect_test_runner +from .witness import RegressionWitness, run_regression_witness + + +@dataclass(frozen=True) +class LocalCheckResult: + repo_root: Path + base_ref: str + base_commit: str + detection: TestRunnerDetection + witness: RegressionWitness + integrity: ProofIntegrityReport + + @property + def ready(self) -> bool: + return self.witness.witnessed and self.integrity.status == "clean" + + @property + def status(self) -> str: + if self.integrity.status != "clean": + return "review-required" + if self.witness.witnessed: + return "verified" + return self.witness.status + + +def _git(repo_root: Path, *args: str, check: bool = True) -> str: + proc = subprocess.run( + ["git", *args], + cwd=repo_root, + text=True, + capture_output=True, + check=False, + ) + if check and proc.returncode != 0: + detail = proc.stderr.strip() or proc.stdout.strip() + raise RuntimeError(f"git {' '.join(args)} failed: {detail}") + return proc.stdout.strip() if proc.returncode == 0 else "" + + +def _ref_exists(repo_root: Path, ref: str) -> bool: + proc = subprocess.run( + ["git", "rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}"], + cwd=repo_root, + text=True, + capture_output=True, + check=False, + ) + return proc.returncode == 0 + + +def _remote_default_ref(repo_root: Path) -> str | None: + symbolic = _git( + repo_root, + "symbolic-ref", + "--quiet", + "--short", + "refs/remotes/origin/HEAD", + check=False, + ) + return symbolic or None + + +def resolve_base_ref(repo_root: Path, explicit: str | None = None) -> tuple[str, str]: + """Resolve a comparison ref and its merge-base with HEAD. + + Preference order: + 1. explicit --base + 2. origin/HEAD symbolic default + 3. origin/main, origin/master + 4. main, master + """ + repo_root = repo_root.resolve() + if not (repo_root / ".git").exists(): + raise ValueError(f"not a Git repository: {repo_root}") + + if explicit is not None: + candidates = [explicit] + else: + candidates: list[str] = [] + remote_default = _remote_default_ref(repo_root) + if remote_default: + candidates.append(remote_default) + candidates.extend(["origin/main", "origin/master", "main", "master"]) + + seen: set[str] = set() + for candidate in candidates: + if candidate in seen: + continue + seen.add(candidate) + if not _ref_exists(repo_root, candidate): + continue + merge_base = _git(repo_root, "merge-base", "HEAD", candidate, check=False) + if merge_base: + return candidate, merge_base + + if explicit is not None: + raise ValueError(f"could not resolve base ref: {explicit}") + raise ValueError( + "could not infer a base branch; pass --base explicitly " + "(for example origin/main)" + ) + + +def run_local_check( + repo_root: Path, + *, + base_ref: str | None = None, + test_command: str | None = None, + timeout_seconds: float = 300, + result_protocol: str = "exit-code", +) -> LocalCheckResult: + """Run Regression Witness + Proof Integrity with local auto-detection.""" + repo_root = repo_root.resolve() + resolved_ref, base_commit = resolve_base_ref(repo_root, base_ref) + detection = ( + TestRunnerDetection( + command=test_command, + runner="custom", + confidence="explicit", + evidence=("--test-command",), + ) + if test_command is not None + else detect_test_runner(repo_root) + ) + + witness = run_regression_witness( + repo_root, + base_ref=base_commit, + head_ref="HEAD", + test_command=detection.command, + timeout_seconds=timeout_seconds, + result_protocol=result_protocol, + ) + integrity = inspect_proof_integrity( + repo_root, + base_ref=base_commit, + head_ref="HEAD", + ) + return LocalCheckResult( + repo_root=repo_root, + base_ref=resolved_ref, + base_commit=base_commit, + detection=detection, + witness=witness, + integrity=integrity, + ) + + +def local_check_to_dict(result: LocalCheckResult) -> dict[str, Any]: + return { + "schema_version": 1, + "status": result.status, + "ready": result.ready, + "base_ref": result.base_ref, + "base_commit": result.base_commit, + "runner": { + "name": result.detection.runner, + "command": result.detection.command, + "confidence": result.detection.confidence, + "evidence": list(result.detection.evidence), + }, + "witness": { + "status": result.witness.status, + "mode": result.witness.mode, + "witnessed": result.witness.witnessed, + "changed_tests": list(result.witness.tests), + }, + "integrity": { + "status": result.integrity.status, + "findings": len(result.integrity.findings), + "high_risk": result.integrity.high_risk_count, + }, + } + + +def render_local_check(result: LocalCheckResult) -> str: + status = result.status.upper().replace("-", " ") + ready = "YES" if result.ready else "NO" + lines = [ + "Counterproof local check", + "", + f"Status {status}", + f"Proof ready {ready}", + f"Base {result.base_ref}", + f"Base commit {result.base_commit[:12]}", + f"Runner {result.detection.runner}", + f"Command {result.detection.command}", + "", + f"Regression {result.witness.status.upper().replace('-', ' ')}", + f"Evidence mode {result.witness.mode.upper()}", + f"Changed tests {len(result.witness.tests)}", + f"Proof integrity {result.integrity.status.upper().replace('-', ' ')}", + f"Integrity risks {len(result.integrity.findings)}", + ] + if result.ready: + lines.extend( + [ + "", + "The exact changed-test evidence distinguishes HEAD from BASE", + "and Counterproof did not detect a changed evidence surface.", + ] + ) + elif result.witness.status == "no-changed-tests": + lines.extend( + [ + "", + "No changed regression test was detected.", + "Add or modify a test that captures the fix, then run counterproof check again.", + ] + ) + elif result.integrity.status != "clean": + lines.extend( + [ + "", + "The PR changes evidence-producing surfaces.", + "Review those changes before treating the witness as independent proof.", + ] + ) + return "\n".join(lines) + "\n" + + +def local_check_json(result: LocalCheckResult) -> str: + return json.dumps(local_check_to_dict(result), indent=2, ensure_ascii=False) From 48574b89f0abe5f647a2ae816fc0b41eb13ebc17 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:28:18 +0800 Subject: [PATCH 02/14] feat: expose counterproof check for zero-config local proof --- skill_factory/evolution/cli.py | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/skill_factory/evolution/cli.py b/skill_factory/evolution/cli.py index 8214aec..1bc7803 100644 --- a/skill_factory/evolution/cli.py +++ b/skill_factory/evolution/cli.py @@ -28,6 +28,7 @@ render_integrity_markdown, write_integrity_json, ) +from .local_check import local_check_json, render_local_check, run_local_check from .models import ( CandidateMutation, Evidence, @@ -737,6 +738,66 @@ def doctor(json_output: bool) -> None: raise click.ClickException("Counterproof self-test failed") +@cli.command("check") +@click.option( + "--repo", + "repo_dir", + default=".", + show_default=True, + type=click.Path(file_okay=False), +) +@click.option( + "--base", + "base_ref", + default=None, + help="Override automatic base-branch detection.", +) +@click.option( + "--test-command", + default=None, + help="Override runner detection. Use {tests} for precise replay.", +) +@click.option("--timeout", "timeout_seconds", default=300.0, show_default=True, type=float) +@click.option( + "--result-protocol", + type=click.Choice(["exit-code", "json-v1"]), + default="exit-code", + show_default=True, +) +@click.option("--json-output", is_flag=True, help="Emit machine-readable JSON.") +@click.option( + "--strict", + is_flag=True, + help="Exit non-zero unless exact witness evidence exists and integrity is clean.", +) +def check_cmd( + repo_dir: str, + base_ref: str | None, + test_command: str | None, + timeout_seconds: float, + result_protocol: str, + json_output: bool, + strict: bool, +) -> None: + """Run a zero-config local proof check for the current branch.""" + try: + result = run_local_check( + Path(repo_dir), + base_ref=base_ref, + test_command=test_command, + timeout_seconds=timeout_seconds, + result_protocol=result_protocol, + ) + except (RuntimeError, ValueError) as exc: + raise click.ClickException(str(exc)) from exc + + click.echo(local_check_json(result) if json_output else render_local_check(result)) + if strict and not result.ready: + raise click.ClickException( + f"Counterproof strict check failed: status={result.status}" + ) + + @cli.command("integrity") @click.option( "--base", From 7107b4fd6f1086b3eb13d21c9f09a98cc0abaab6 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:28:36 +0800 Subject: [PATCH 03/14] test: prove counterproof check works against a real local Git branch --- tests/unit/test_local_check.py | 122 +++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/unit/test_local_check.py diff --git a/tests/unit/test_local_check.py b/tests/unit/test_local_check.py new file mode 100644 index 0000000..1d5638a --- /dev/null +++ b/tests/unit/test_local_check.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +from click.testing import CliRunner + +from skill_factory.evolution.cli import cli +from skill_factory.evolution.local_check import resolve_base_ref, run_local_check + + +def _git(repo: Path, *args: str) -> str: + proc = subprocess.run( + ["git", *args], + cwd=repo, + text=True, + capture_output=True, + check=False, + ) + assert proc.returncode == 0, proc.stderr + return proc.stdout.strip() + + +def _repo(tmp_path: Path) -> Path: + repo = tmp_path / "repo" + repo.mkdir() + _git(repo, "init", "-b", "main") + _git(repo, "config", "user.email", "counterproof@example.test") + _git(repo, "config", "user.name", "Counterproof Test") + + (repo / "app.py").write_text("VALUE = 1\n", encoding="utf-8") + _git(repo, "add", ".") + _git(repo, "commit", "-m", "base") + + _git(repo, "checkout", "-b", "feature/fix") + (repo / "app.py").write_text("VALUE = 2\n", encoding="utf-8") + tests = repo / "tests" + tests.mkdir() + (tests / "test_regression.py").write_text( + "from app import VALUE\n\n" + "def test_regression():\n" + " assert VALUE == 2\n", + encoding="utf-8", + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "fix plus regression test") + return repo + + +def test_resolve_base_falls_back_to_local_main(tmp_path): + repo = _repo(tmp_path) + + ref, commit = resolve_base_ref(repo) + + assert ref == "main" + assert commit == _git(repo, "rev-parse", "main") + + +def test_local_check_auto_detects_pytest_and_verifies_regression(tmp_path): + repo = _repo(tmp_path) + + result = run_local_check( + repo, + test_command=f"{sys.executable} -m pytest -q {{tests}}", + timeout_seconds=30, + ) + + assert result.status == "verified" + assert result.ready is True + assert result.base_ref == "main" + assert result.witness.status == "witnessed" + assert result.integrity.status == "clean" + + +def test_counterproof_check_cli_is_one_command_local_proof(tmp_path): + repo = _repo(tmp_path) + + result = CliRunner().invoke( + cli, + [ + "check", + "--repo", + str(repo), + "--test-command", + f"{sys.executable} -m pytest -q {{tests}}", + "--strict", + ], + ) + + assert result.exit_code == 0, result.output + assert "Status VERIFIED" in result.output + assert "Proof ready YES" in result.output + assert "Base main" in result.output + assert "Regression WITNESSED" in result.output + + +def test_counterproof_check_strict_rejects_weak_test(tmp_path): + repo = _repo(tmp_path) + # Rewrite the test so it already passes on base. + (repo / "tests" / "test_regression.py").write_text( + "def test_regression():\n" + " assert True\n", + encoding="utf-8", + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "weaken regression test") + + result = CliRunner().invoke( + cli, + [ + "check", + "--repo", + str(repo), + "--test-command", + f"{sys.executable} -m pytest -q {{tests}}", + "--strict", + ], + ) + + assert result.exit_code != 0 + assert "Counterproof strict check failed" in result.output From 2875a27f7b166ac8d5c718adabc6bec24127e7f4 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:28:51 +0800 Subject: [PATCH 04/14] docs: make counterproof check the instant first-run path From 05dda5c284d6b9d4f9bcabe73e8f0a29831b15f1 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:30:38 +0800 Subject: [PATCH 05/14] ci: prove counterproof check works from a clean wheel install --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbcc515..b1f3bfb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,6 +131,46 @@ jobs: grep -q 'Counterproof ยท Regression Witness' /tmp/REGRESSION_WITNESS.md grep -Fq 'Base code + PR tests: **FAIL**' /tmp/REGRESSION_WITNESS.md + - name: Run packaged zero-config local check + run: | + set -euo pipefail + rm -rf /tmp/counterproof-check-fixture + mkdir -p /tmp/counterproof-check-fixture + cd /tmp/counterproof-check-fixture + + git init -b main + git config user.email "counterproof@example.test" + git config user.name "Counterproof CI" + + printf 'VALUE = 1\n' > app.py + git add app.py + git commit -m "base" + + git checkout -b feature/fix + mkdir -p tests + printf 'VALUE = 2\n' > app.py + cat > tests/test_regression.py <<'PY' + from app import VALUE + + def test_regression(): + assert VALUE == 2 + PY + git add app.py tests/test_regression.py + git commit -m "fix plus regression test" + + /tmp/counterproof-clean/bin/pip install pytest + /tmp/counterproof-clean/bin/counterproof check \ + --repo /tmp/counterproof-check-fixture \ + --test-command "/tmp/counterproof-clean/bin/python -m pytest -q {tests}" \ + --strict \ + > /tmp/LOCAL_CHECK.txt + + grep -q 'Status VERIFIED' /tmp/LOCAL_CHECK.txt + grep -q 'Proof ready YES' /tmp/LOCAL_CHECK.txt + grep -q 'Base main' /tmp/LOCAL_CHECK.txt + grep -q 'Regression WITNESSED' /tmp/LOCAL_CHECK.txt + grep -q 'Proof integrity CLEAN' /tmp/LOCAL_CHECK.txt + - name: Run packaged Proof Integrity Guard run: | set -euo pipefail From d79e28c74bedf584126d9c445e4903a894bb70e9 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:31:51 +0800 Subject: [PATCH 06/14] audit: mark zero-config local proof check as tested --- skill_factory/evolution/capabilities.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/skill_factory/evolution/capabilities.py b/skill_factory/evolution/capabilities.py index f0f739f..76010a2 100644 --- a/skill_factory/evolution/capabilities.py +++ b/skill_factory/evolution/capabilities.py @@ -43,6 +43,13 @@ "evidence": "counterproof init detects common test runners and writes a pull-request workflow using the root CounterProof Action without overwriting existing config by default.", "limitation": "Runner detection is conservative; project-specific dependency setup may still need review, and strict mode refuses suite-only evidence.", }, + { + "id": "local-check", + "name": "One-command local branch proof check", + "status": "tested", + "evidence": "counterproof check resolves a base branch, detects or accepts a test runner, executes Regression Witness plus Proof Integrity, and is exercised from both unit tests and a clean installed wheel.", + "limitation": "Automatic base detection currently prefers origin/HEAD, main/master conventions, or an explicit --base; unusual repository topologies may need an override.", + }, { "id": "regression-witness", "name": "Regression Witness for agent pull requests", From e111463ba29ed26cec60f6b5ae8e7e64f2386f01 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:32:02 +0800 Subject: [PATCH 07/14] audit: sync local check capability to the public truth table --- site/data/capabilities.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/site/data/capabilities.json b/site/data/capabilities.json index c087ffb..419b0b2 100644 --- a/site/data/capabilities.json +++ b/site/data/capabilities.json @@ -43,6 +43,13 @@ "evidence": "counterproof init detects common test runners and writes a pull-request workflow using the root CounterProof Action without overwriting existing config by default.", "limitation": "Runner detection is conservative; project-specific dependency setup may still need review, and strict mode refuses suite-only evidence." }, + { + "id": "local-check", + "name": "One-command local branch proof check", + "status": "tested", + "evidence": "counterproof check resolves a base branch, detects or accepts a test runner, executes Regression Witness plus Proof Integrity, and is exercised from both unit tests and a clean installed wheel.", + "limitation": "Automatic base detection currently prefers origin/HEAD, main/master conventions, or an explicit --base; unusual repository topologies may need an override." + }, { "id": "regression-witness", "name": "Regression Witness for agent pull requests", @@ -220,7 +227,7 @@ } ], "summary": { - "tested": 22, + "tested": 23, "partial": 4, "demo": 1, "planned": 4 From e1781f45be9dd80a1edb7a22165a02bdff6f0467 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:02:58 +0800 Subject: [PATCH 08/14] fix: scope local check to submitted evidence --- skill_factory/evolution/local_check.py | 41 ++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/skill_factory/evolution/local_check.py b/skill_factory/evolution/local_check.py index 2db1a5a..3643650 100644 --- a/skill_factory/evolution/local_check.py +++ b/skill_factory/evolution/local_check.py @@ -1,4 +1,4 @@ -"""Local one-command Counterproof check for the current Git branch.""" +"""Local one-command CounterProof evidence check for the current Git branch.""" from __future__ import annotations import json @@ -22,15 +22,17 @@ class LocalCheckResult: integrity: ProofIntegrityReport @property - def ready(self) -> bool: + def strict_pass(self) -> bool: + """Whether the narrow local strict gate passes. + + This is intentionally not a product-correctness or merge-readiness verdict. + """ return self.witness.witnessed and self.integrity.status == "clean" @property def status(self) -> str: if self.integrity.status != "clean": return "review-required" - if self.witness.witnessed: - return "verified" return self.witness.status @@ -161,7 +163,15 @@ def local_check_to_dict(result: LocalCheckResult) -> dict[str, Any]: return { "schema_version": 1, "status": result.status, - "ready": result.ready, + "strict_gate": { + "passed": result.strict_pass, + "requires": [ + "exact changed-test witness", + "clean proof-integrity surface", + ], + }, + "evidence_scope": "submitted-judge", + "oracle_alignment": "unverified", "base_ref": result.base_ref, "base_commit": result.base_commit, "runner": { @@ -185,30 +195,31 @@ def local_check_to_dict(result: LocalCheckResult) -> dict[str, Any]: def render_local_check(result: LocalCheckResult) -> str: - status = result.status.upper().replace("-", " ") - ready = "YES" if result.ready else "NO" + strict_gate = "PASS" if result.strict_pass else "FAIL" lines = [ - "Counterproof local check", + "CounterProof local check", + "", + f"Regression {result.witness.status.upper().replace('-', ' ')}", + f"Evidence scope SUBMITTED JUDGE", + f"Proof integrity {result.integrity.status.upper().replace('-', ' ')}", + f"Strict gate {strict_gate}", + f"Product oracle UNVERIFIED", "", - f"Status {status}", - f"Proof ready {ready}", f"Base {result.base_ref}", f"Base commit {result.base_commit[:12]}", f"Runner {result.detection.runner}", f"Command {result.detection.command}", - "", - f"Regression {result.witness.status.upper().replace('-', ' ')}", f"Evidence mode {result.witness.mode.upper()}", f"Changed tests {len(result.witness.tests)}", - f"Proof integrity {result.integrity.status.upper().replace('-', ' ')}", f"Integrity risks {len(result.integrity.findings)}", ] - if result.ready: + if result.strict_pass: lines.extend( [ "", "The exact changed-test evidence distinguishes HEAD from BASE", - "and Counterproof did not detect a changed evidence surface.", + "and CounterProof did not detect a changed evidence surface.", + "This does not establish product-level correctness or merge readiness.", ] ) elif result.witness.status == "no-changed-tests": From 9c7e84c446712ecdc446ce9888bf232c9df65696 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:03:24 +0800 Subject: [PATCH 09/14] fix: keep local check evidence-scoped --- skill_factory/evolution/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skill_factory/evolution/cli.py b/skill_factory/evolution/cli.py index 1bc7803..12fd7cd 100644 --- a/skill_factory/evolution/cli.py +++ b/skill_factory/evolution/cli.py @@ -792,9 +792,9 @@ def check_cmd( raise click.ClickException(str(exc)) from exc click.echo(local_check_json(result) if json_output else render_local_check(result)) - if strict and not result.ready: + if strict and not result.strict_pass: raise click.ClickException( - f"Counterproof strict check failed: status={result.status}" + f"CounterProof strict check failed: status={result.status}" ) From 61bec530e60e3dbc5252f7c95413b36709440306 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:03:26 +0800 Subject: [PATCH 10/14] fix: keep local check evidence-scoped --- tests/unit/test_local_check.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_local_check.py b/tests/unit/test_local_check.py index 1d5638a..a88e59a 100644 --- a/tests/unit/test_local_check.py +++ b/tests/unit/test_local_check.py @@ -66,8 +66,8 @@ def test_local_check_auto_detects_pytest_and_verifies_regression(tmp_path): timeout_seconds=30, ) - assert result.status == "verified" - assert result.ready is True + assert result.status == "witnessed" + assert result.strict_pass is True assert result.base_ref == "main" assert result.witness.status == "witnessed" assert result.integrity.status == "clean" @@ -89,10 +89,13 @@ def test_counterproof_check_cli_is_one_command_local_proof(tmp_path): ) assert result.exit_code == 0, result.output - assert "Status VERIFIED" in result.output - assert "Proof ready YES" in result.output - assert "Base main" in result.output assert "Regression WITNESSED" in result.output + assert "Evidence scope SUBMITTED JUDGE" in result.output + assert "Proof integrity CLEAN" in result.output + assert "Strict gate PASS" in result.output + assert "Product oracle UNVERIFIED" in result.output + assert "Base main" in result.output + assert "does not establish product-level correctness or merge readiness" in result.output def test_counterproof_check_strict_rejects_weak_test(tmp_path): @@ -119,4 +122,4 @@ def test_counterproof_check_strict_rejects_weak_test(tmp_path): ) assert result.exit_code != 0 - assert "Counterproof strict check failed" in result.output + assert "CounterProof strict check failed" in result.output From 5fd4d4b2c2e092386205f5d3b1f299de625020f0 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:03:28 +0800 Subject: [PATCH 11/14] fix: keep local check evidence-scoped --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1f3bfb..d1dc6ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,10 +165,11 @@ jobs: --strict \ > /tmp/LOCAL_CHECK.txt - grep -q 'Status VERIFIED' /tmp/LOCAL_CHECK.txt - grep -q 'Proof ready YES' /tmp/LOCAL_CHECK.txt + grep -q 'Regression WITNESSED' /tmp/LOCAL_CHECK.txt + grep -q 'Strict gate PASS' /tmp/LOCAL_CHECK.txt grep -q 'Base main' /tmp/LOCAL_CHECK.txt grep -q 'Regression WITNESSED' /tmp/LOCAL_CHECK.txt + grep -q 'Product oracle UNVERIFIED' /tmp/LOCAL_CHECK.txt grep -q 'Proof integrity CLEAN' /tmp/LOCAL_CHECK.txt - name: Run packaged Proof Integrity Guard From dde16d0d224feb2ce3b21a91e669b51b1be863a5 Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:04:00 +0800 Subject: [PATCH 12/14] test: assert scoped local-check output --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1dc6ed..a465ca1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -168,7 +168,6 @@ jobs: grep -q 'Regression WITNESSED' /tmp/LOCAL_CHECK.txt grep -q 'Strict gate PASS' /tmp/LOCAL_CHECK.txt grep -q 'Base main' /tmp/LOCAL_CHECK.txt - grep -q 'Regression WITNESSED' /tmp/LOCAL_CHECK.txt grep -q 'Product oracle UNVERIFIED' /tmp/LOCAL_CHECK.txt grep -q 'Proof integrity CLEAN' /tmp/LOCAL_CHECK.txt From 200814ea0655fa1832ddf9f6ad0c2c4024e2e4dd Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:04:02 +0800 Subject: [PATCH 13/14] test: name local-check semantics precisely --- tests/unit/test_local_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_local_check.py b/tests/unit/test_local_check.py index a88e59a..ea6e91e 100644 --- a/tests/unit/test_local_check.py +++ b/tests/unit/test_local_check.py @@ -27,7 +27,7 @@ def _repo(tmp_path: Path) -> Path: repo.mkdir() _git(repo, "init", "-b", "main") _git(repo, "config", "user.email", "counterproof@example.test") - _git(repo, "config", "user.name", "Counterproof Test") + _git(repo, "config", "user.name", "CounterProof Test") (repo / "app.py").write_text("VALUE = 1\n", encoding="utf-8") _git(repo, "add", ".") @@ -57,7 +57,7 @@ def test_resolve_base_falls_back_to_local_main(tmp_path): assert commit == _git(repo, "rev-parse", "main") -def test_local_check_auto_detects_pytest_and_verifies_regression(tmp_path): +def test_local_check_auto_detects_pytest_and_witnesses_regression(tmp_path): repo = _repo(tmp_path) result = run_local_check( From 20462f86936217f74e68d924f2bdc1e3429fe3bc Mon Sep 17 00:00:00 2001 From: hippo <135493401+hippoley@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:04:42 +0800 Subject: [PATCH 14/14] fix: satisfy local-check lint --- skill_factory/evolution/local_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skill_factory/evolution/local_check.py b/skill_factory/evolution/local_check.py index 3643650..a53ff90 100644 --- a/skill_factory/evolution/local_check.py +++ b/skill_factory/evolution/local_check.py @@ -200,10 +200,10 @@ def render_local_check(result: LocalCheckResult) -> str: "CounterProof local check", "", f"Regression {result.witness.status.upper().replace('-', ' ')}", - f"Evidence scope SUBMITTED JUDGE", + "Evidence scope SUBMITTED JUDGE", f"Proof integrity {result.integrity.status.upper().replace('-', ' ')}", f"Strict gate {strict_gate}", - f"Product oracle UNVERIFIED", + "Product oracle UNVERIFIED", "", f"Base {result.base_ref}", f"Base commit {result.base_commit[:12]}",