diff --git a/.github/workflows/governance-gate.yml b/.github/workflows/governance-gate.yml index cc1af0e..dd58ea9 100644 --- a/.github/workflows/governance-gate.yml +++ b/.github/workflows/governance-gate.yml @@ -13,7 +13,9 @@ jobs: required-files: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Check Required Governance Files shell: pwsh run: | @@ -44,16 +46,20 @@ jobs: ho-det-011-case-packet: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Install schema validation dependency - run: python -m pip install jsonschema + run: python -m pip install jsonschema==4.23.0 - name: Verify HO-DET-011 case packet guardrail run: python scripts/verify-ho-det-011-case-packet.py runtime-route-proof-v1-private-candidate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Parse Runtime Route Proof v1 private candidate schema run: python -m json.tool contracts/schemas/runtime-route-proof-v1-private-candidate.schema.json >/dev/null - name: Parse Runtime Route Proof v1 private candidate sample @@ -64,9 +70,11 @@ jobs: runtime-collector-eligibility-v0: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Install schema validation dependency - run: python -m pip install jsonschema + run: python -m pip install jsonschema==4.23.0 - name: Verify Runtime Collector Eligibility Registry v0 run: | python scripts/verify-runtime-collector-eligibility-v0.py --registry contracts/examples/runtime-collector-eligibility-v0.sample.json --schema contracts/schemas/runtime-collector-eligibility-v0.schema.json @@ -75,18 +83,23 @@ jobs: if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Check out HawkinsOperations sibling repositories - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + persist-credentials: false repository: HawkinsOperations/hawkinsoperations-detections path: hawkinsoperations-detections - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + persist-credentials: false repository: HawkinsOperations/hawkinsoperations-validation path: hawkinsoperations-validation - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + persist-credentials: false repository: HawkinsOperations/hawkinsoperations-proof path: hawkinsoperations-proof - name: Verify Lifetime Case Ledger v1 spine diff --git a/.github/workflows/hoxline-main-checks.yml b/.github/workflows/hoxline-main-checks.yml index 5645887..c00c858 100644 --- a/.github/workflows/hoxline-main-checks.yml +++ b/.github/workflows/hoxline-main-checks.yml @@ -13,35 +13,35 @@ jobs: name: Main/push governance checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github path: hawkinsoperations-github persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-detections path: hawkinsoperations-detections persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation path: hawkinsoperations-validation persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-proof path: hawkinsoperations-proof persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hoxline path: hoxline persist-credentials: false - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website path: hawkinsoperations-website diff --git a/.github/workflows/hoxline-private-canary.yml b/.github/workflows/hoxline-private-canary.yml index 88df7e5..08205cc 100644 --- a/.github/workflows/hoxline-private-canary.yml +++ b/.github/workflows/hoxline-private-canary.yml @@ -39,7 +39,7 @@ jobs: runs-on: [self-hosted, ho-gpu-01, gpu, v100] timeout-minutes: 20 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/.github/workflows/hoxline-schedule-gated-collection.yml b/.github/workflows/hoxline-schedule-gated-collection.yml index c1b2f83..192a136 100644 --- a/.github/workflows/hoxline-schedule-gated-collection.yml +++ b/.github/workflows/hoxline-schedule-gated-collection.yml @@ -32,7 +32,7 @@ jobs: HOXLINE_PRIVATE_ROUTE: /var/lib/hoxline/private-scheduled-collector-v0 HOXLINE_COLLECTOR_STATE: /var/lib/hoxline/private-scheduled-collector-v0/collector-state.sqlite steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/.github/workflows/hoxline-source-checks.yml b/.github/workflows/hoxline-source-checks.yml index b5113b7..d3401fc 100644 --- a/.github/workflows/hoxline-source-checks.yml +++ b/.github/workflows/hoxline-source-checks.yml @@ -14,28 +14,248 @@ jobs: name: PR/source checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout platform event revision + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + path: source-set/hawkinsoperations-platform + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 persist-credentials: false + - name: Resolve immutable source manifest + id: sources + working-directory: source-set/hawkinsoperations-platform + shell: bash + run: | + set -euo pipefail + manifest="contracts/hoxline-case-growth-source-manifest-v1.json" + test -f "$manifest" + for repo in .github hawkinsoperations-detections hawkinsoperations-validation hawkinsoperations-proof hawkinsoperations-website hoxline; do + key="${repo//./_}" + key="${key//-/_}" + revision="$(python - "$manifest" "$repo" <<'PY' + import json + import sys + manifest = json.load(open(sys.argv[1], encoding="utf-8")) + entry = manifest["repositories"][sys.argv[2]] + revision = entry.get("revision") + if not isinstance(revision, str) or len(revision) != 40: + raise SystemExit(f"invalid immutable revision for {sys.argv[2]}") + print(revision) + PY + )" + printf '%s=%s\n' "$key" "$revision" >> "$GITHUB_OUTPUT" + done + + - name: Checkout command-center authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/.github + path: source-set/.github + ref: ${{ steps.sources.outputs._github }} + fetch-depth: 0 + persist-credentials: false + + - name: Checkout detection authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-detections + path: source-set/hawkinsoperations-detections + ref: ${{ steps.sources.outputs.hawkinsoperations_detections }} + fetch-depth: 0 + persist-credentials: false + + - name: Checkout validation authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-validation + path: source-set/hawkinsoperations-validation + ref: ${{ steps.sources.outputs.hawkinsoperations_validation }} + fetch-depth: 0 + persist-credentials: false + + - name: Checkout proof authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-proof + path: source-set/hawkinsoperations-proof + ref: ${{ steps.sources.outputs.hawkinsoperations_proof }} + fetch-depth: 0 + persist-credentials: false + + - name: Checkout website consumer + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-website + path: source-set/hawkinsoperations-website + ref: ${{ steps.sources.outputs.hawkinsoperations_website }} + fetch-depth: 0 + persist-credentials: false + + - name: Checkout Hoxline consumer + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hoxline + path: source-set/hoxline + ref: ${{ steps.sources.outputs.hoxline }} + fetch-depth: 0 + persist-credentials: false + + - name: Verify exact seven-source selection + working-directory: source-set/hawkinsoperations-platform + shell: bash + env: + EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -euo pipefail + python - "$GITHUB_WORKSPACE/source-set" "$EVENT_SHA" <<'PY' + import json + import subprocess + import sys + from pathlib import Path + + source_root = Path(sys.argv[1]) + event_sha = sys.argv[2] + platform = source_root / "hawkinsoperations-platform" + manifest = json.loads( + (platform / "contracts/hoxline-case-growth-source-manifest-v1.json").read_text(encoding="utf-8") + ) + expected = manifest["repositories"] + actual_names = sorted(path.name for path in source_root.iterdir() if (path / ".git").exists()) + if actual_names != sorted(expected): + raise SystemExit(f"seven-source set mismatch: expected={sorted(expected)} actual={actual_names}") + for name, entry in expected.items(): + expected_sha = event_sha if entry.get("revision_source") == "checked_platform_observation" else entry["revision"] + actual_sha = subprocess.check_output( + ["git", "-C", str(source_root / name), "rev-parse", "HEAD"], text=True + ).strip() + if actual_sha != expected_sha: + raise SystemExit(f"revision mismatch for {name}: expected={expected_sha} actual={actual_sha}") + print(json.dumps({"repository": name, "checked_sha": actual_sha}, sort_keys=True)) + PY + - name: Compile controller + working-directory: source-set/hawkinsoperations-platform run: python -B -m py_compile scripts/ho_factory.py + - name: Reject retired fixture vocabulary + working-directory: source-set/hawkinsoperations-platform + run: | + python - <<'PY' + import subprocess + import hashlib + import sqlite3 + import unicodedata + from pathlib import Path, PurePosixPath + + retired = "".join(("syn", "thetic")) + immutable_sqlite_blobs = { + "evidence/autosoc-case-ledger-v0.sqlite": ( + "5b2107512570dc5a2e1377b62ea1a689c9056c3f" + ), + } + binary_extensions = frozenset({ + ".7z", ".avif", ".avi", ".bz2", ".dll", ".dylib", ".eot", ".exe", ".gif", + ".gz", ".ico", ".jpeg", ".jpg", ".mov", ".mp3", ".mp4", ".pdf", + ".png", ".pyc", ".so", ".tar", ".tgz", ".ttf", ".wasm", ".webp", + ".sqlite", ".woff", ".woff2", ".xz", ".zip", + }) + paths = subprocess.run( + ["git", "ls-files", "-z"], + check=True, + capture_output=True, + ).stdout.decode("utf-8").split("\0") + findings = [] + for relative in filter(None, paths): + if retired in unicodedata.normalize("NFKC", relative).casefold(): + findings.append(f"tracked filename: {relative}") + blob = subprocess.run( + ["git", "show", f":{relative}"], + check=True, + capture_output=True, + ).stdout + suffix = PurePosixPath(relative).suffix.casefold() + if suffix == ".sqlite": + expected_blob = immutable_sqlite_blobs.get(relative) + if expected_blob is None: + findings.append(f"unapproved tracked SQLite path: {relative}") + continue + if not blob.startswith(b"SQLite format 3\0"): + findings.append( + f"tracked SQLite content has invalid file signature: {relative}" + ) + continue + actual_blob = hashlib.sha1( + f"blob {len(blob)}\0".encode("ascii") + blob + ).hexdigest() + if actual_blob != expected_blob: + findings.append( + f"tracked SQLite blob identity is not approved: {relative}" + ) + continue + if Path(relative).read_bytes() != blob: + findings.append( + f"tracked SQLite worktree content differs from the staged blob: {relative}" + ) + continue + try: + connection = sqlite3.connect( + f"file:{Path(relative).resolve().as_posix()}?mode=ro", + uri=True, + ) + try: + integrity = connection.execute("PRAGMA quick_check").fetchall() + finally: + connection.close() + except sqlite3.DatabaseError: + integrity = [] + if integrity != [("ok",)]: + findings.append( + f"tracked SQLite content is not a valid database: {relative}" + ) + continue + searchable = unicodedata.normalize( + "NFKC", + blob.decode("utf-8", errors="ignore").replace("\0", ""), + ).casefold() + if retired in searchable: + findings.append(f"tracked content: {relative}") + if suffix in binary_extensions: + continue + if b"\0" in blob: + findings.append(f"tracked non-binary content contains NUL: {relative}") + continue + try: + text = blob.decode("utf-8") + except UnicodeDecodeError: + findings.append(f"tracked non-binary content is not UTF-8: {relative}") + continue + # The permissive scan above is authoritative for the retired + # token; strict UTF-8 decoding here keeps unknown text formats + # fail closed. + if findings: + raise SystemExit( + "retired fixture vocabulary found:\n- " + "\n- ".join(findings) + ) + PY + - name: Runtime collector self-tests + working-directory: source-set/hawkinsoperations-platform run: | python -B scripts/ho_factory.py collector-linux-self-test --format json >/dev/null python -B scripts/ho_factory.py collector-normalizer-self-test --format json >/dev/null python -B scripts/ho_factory.py hoxline-runtime-canary --controlled-count 3 --format json >/dev/null python -B scripts/ho_factory.py hoxline-runtime-schedule-gate --event-name schedule --format json >/dev/null - python -B scripts/ho_factory.py hoxline-runtime-ops-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null - python -B scripts/ho_factory.py hoxline-control-plane-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null - python -B scripts/ho_factory.py hoxline-schedule-readiness-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null - python -B scripts/ho_factory.py hoxline-schedule-pilot-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null + python -B scripts/ho_factory.py hoxline-runtime-ops-self-test --repo-root "$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform" --format json >/dev/null + python -B scripts/ho_factory.py hoxline-control-plane-self-test --repo-root "$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform" --format json >/dev/null + python -B scripts/ho_factory.py hoxline-schedule-readiness-self-test --repo-root "$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform" --format json >/dev/null + python -B scripts/ho_factory.py hoxline-schedule-pilot-self-test --repo-root "$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform" --format json >/dev/null python -B scripts/ho_factory.py hoxline-multi-detection-runtime-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null python -B scripts/ho_factory.py hoxline-sanitized-live-receipt-intake-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null python -B scripts/ho_factory.py hoxline-operator-receipt-collection-self-test --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null - name: Schema parse checks + working-directory: source-set/hawkinsoperations-platform run: | python -m json.tool contracts/schemas/runtime-case-collector-v0-linux.schema.json >/dev/null python -m json.tool contracts/schemas/runtime-case-collector-v0-normalizer.schema.json >/dev/null @@ -53,23 +273,26 @@ jobs: python -m json.tool contracts/examples/hoxline-operator-receipt-packet-v0.ho-det-012.sample.json >/dev/null - name: Claim-boundary checks + working-directory: source-set/hawkinsoperations-platform + env: + HAWKINS_PROOF_REPO: ${{ github.workspace }}/source-set/hawkinsoperations-proof + HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: | python -B scripts/ho_factory.py public-status-source-contract-verify --format json + python -B scripts/ho_factory.py hoxline-case-growth-convergence-verify --repo-root "$GITHUB_WORKSPACE/source-set" --format json - name: Workflow safety checks + working-directory: source-set/hawkinsoperations-platform run: | - python -B scripts/ho_factory.py hoxline-workflow-safety-verify --repo-root "$GITHUB_WORKSPACE" --format json >/dev/null + python -B scripts/ho_factory.py hoxline-workflow-safety-verify --repo-root "$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform" --format json >/dev/null - name: Python unit tests + working-directory: source-set/hawkinsoperations-platform + env: + HAWKINS_PROOF_REPO: ${{ github.workspace }}/source-set/hawkinsoperations-proof run: | - python -B -m unittest tests/test_hoxline_runtime_ops.py - python -B -m unittest tests/test_hoxline_control_plane.py - python -B -m unittest tests/test_hoxline_schedule_readiness.py - python -B -m unittest tests/test_hoxline_controlled_schedule_pilot.py - python -B -m unittest tests/test_hoxline_multi_detection_runtime.py - python -B -m unittest tests/test_hoxline_sanitized_live_receipt_intake.py - python -B -m unittest tests/test_hoxline_operator_receipt_collection.py - python -B -m unittest tests/test_verify_public_status_source_contract.py + python -B -m unittest discover -s tests - name: Git whitespace check + working-directory: source-set/hawkinsoperations-platform run: git diff --check diff --git a/.github/workflows/hoxline-trusted-runtime-verify.yml b/.github/workflows/hoxline-trusted-runtime-verify.yml index 64b0230..045bb65 100644 --- a/.github/workflows/hoxline-trusted-runtime-verify.yml +++ b/.github/workflows/hoxline-trusted-runtime-verify.yml @@ -27,7 +27,7 @@ jobs: runs-on: [self-hosted, ho-gpu-01, gpu, v100] timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/.github/workflows/local-gpu-triage-gate.yml b/.github/workflows/local-gpu-triage-gate.yml index caff729..6300c78 100644 --- a/.github/workflows/local-gpu-triage-gate.yml +++ b/.github/workflows/local-gpu-triage-gate.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/.github/workflows/runtime-case-collector-v0-linux.yml b/.github/workflows/runtime-case-collector-v0-linux.yml index f58ac5f..10488d6 100644 --- a/.github/workflows/runtime-case-collector-v0-linux.yml +++ b/.github/workflows/runtime-case-collector-v0-linux.yml @@ -45,7 +45,7 @@ jobs: working-directory: ${{ github.workspace }} steps: - name: Check out platform repo - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/.github/workflows/runtime-case-collector-v0-windows.yml b/.github/workflows/runtime-case-collector-v0-windows.yml index 737c7c2..977f682 100644 --- a/.github/workflows/runtime-case-collector-v0-windows.yml +++ b/.github/workflows/runtime-case-collector-v0-windows.yml @@ -24,7 +24,7 @@ jobs: working-directory: ${{ github.workspace }} steps: - name: Check out platform repo - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false diff --git a/contracts/examples/hoxline-remote-lab-authority-v1.controlled.json b/contracts/examples/hoxline-remote-lab-authority-v1.controlled.json new file mode 100644 index 0000000..dab5bb8 --- /dev/null +++ b/contracts/examples/hoxline-remote-lab-authority-v1.controlled.json @@ -0,0 +1,16 @@ +{ + "fixture_id": "HOXLINE_REMOTE_LAB_AUTHORITY_CONTROLLED_V1", + "fixture_version": 1, + "fixture_class": "CONTROLLED_TEST_ONLY", + "remote_default_mode": "read_only", + "hosts": [ + "ho-wazuh-01", + "ho-wazuh-02", + "ho-gpu-01", + "ho-runner-01" + ], + "runtime_mutation_authorized": false, + "ledger_mutation_authorized": false, + "public_proof_promotion_authorized": false, + "proof_ceiling": "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY" +} diff --git a/contracts/hoxline-case-growth-source-manifest-v1.json b/contracts/hoxline-case-growth-source-manifest-v1.json new file mode 100644 index 0000000..ffc4541 --- /dev/null +++ b/contracts/hoxline-case-growth-source-manifest-v1.json @@ -0,0 +1,48 @@ +{ + "manifest_id": "HOXLINE_CASE_GROWTH_SOURCE_MANIFEST_V1", + "version": 1, + "owner_repo": "hawkinsoperations-platform", + "purpose": "Immutable sibling-source selection for read-only seven-repository convergence verification.", + "proof_ceiling": "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY", + "repositories": { + ".github": { + "repository": "HawkinsOperations/.github", + "revision": "d7c92a8775be17334e2ed64a474299f0439604d9", + "authoritative_path": "governance/CONVERGENCE_SOURCE_MANIFEST.json", + "authoritative_git_blob_sha": "2f86299cbaca78cb124a21ba85610befb1f0d140" + }, + "hawkinsoperations-detections": { + "repository": "HawkinsOperations/hawkinsoperations-detections", + "revision": "9e01f43fb350de3370f8c01a323dcdcdf2e33147" + }, + "hawkinsoperations-validation": { + "repository": "HawkinsOperations/hawkinsoperations-validation", + "revision": "677b704150b0f5f333c27913dd481b4be6a78ab7" + }, + "hawkinsoperations-proof": { + "repository": "HawkinsOperations/hawkinsoperations-proof", + "revision": "77b7874dd753369792330508fa3438cf397cd050" + }, + "hawkinsoperations-website": { + "repository": "HawkinsOperations/hawkinsoperations-website", + "revision": "839e1ac498b6fca055d7101da91c6df523201205" + }, + "hoxline": { + "repository": "HawkinsOperations/hoxline", + "revision": "cd797da491f07b9a0130278d7245f51962dd82c0" + }, + "hawkinsoperations-platform": { + "repository": "HawkinsOperations/hawkinsoperations-platform", + "revision_source": "checked_platform_observation" + } + }, + "constraints": { + "exact_repository_count": 7, + "read_only": true, + "allow_default_branch_substitution": false, + "allow_detached_authority_substitution": false, + "allow_dirty_authority_source": false, + "website_is_authority": false, + "hoxline_is_cross_domain_authority": false + } +} diff --git a/contracts/public-status-source-contract-v1.json b/contracts/public-status-source-contract-v1.json index 8153c4b..5bc7b79 100644 --- a/contracts/public-status-source-contract-v1.json +++ b/contracts/public-status-source-contract-v1.json @@ -3,7 +3,7 @@ "version": "public_status_source_contract_v1", "owner_repo": "hawkinsoperations-platform", "consumer": "hawkinsoperations-website", - "generated_at": "2026-06-16T22:06:47.5510594-05:00", + "generated_at": "2026-07-23T05:37:58.8967051Z", "freshness_window_days": 14, "platform_role": { "source_contract": true, @@ -15,6 +15,11 @@ "signal_authority": false }, "source_repos": [ + { + "repo": ".github", + "role": "cross-repository orchestration and command-center invariant owner", + "authority_boundary": "read-only orchestration only; not detection, validation, proof, runtime, signal, public-safe, approval, or closure authority" + }, { "repo": "hawkinsoperations-platform", "role": "source-contract, ledger mechanics, runtime candidate guardrails, and state manifest owner", @@ -27,30 +32,14 @@ }, { "repo": "hawkinsoperations-validation", - "role": "controlled validation and activity ledger owner", + "role": "controlled validation, activity ledger, and landed Hoxline validation bridge source-route owner", "authority_boundary": "controlled validation truth only; not runtime truth, signal truth, proof authority, or public-safe truth" }, - { - "repo": "hawkinsoperations-validation", - "role": "landed Hoxline Gauntlet validation bridge source route owner", - "source_pr": 67, - "source_branch": "feature/hoxline-gauntlet-validation-bridge-v1", - "source_status": "SOURCE_CAPTURED", - "authority_boundary": "landed validation bridge route only; not runtime truth, signal truth, proof authority, or public-safe truth" - }, { "repo": "hawkinsoperations-proof", - "role": "proof record, blocked claim, proof ceiling, and public-safe boundary owner", + "role": "proof record, card, blocked claim, proof ceiling, public-safe boundary, and landed Hoxline proof bridge source-route owner", "authority_boundary": "proof source records and proof boundaries only; website rendering does not create proof" }, - { - "repo": "hawkinsoperations-proof", - "role": "landed Hoxline Gauntlet proof bridge source route owner", - "source_pr": 81, - "source_branch": "feature/hoxline-gauntlet-proof-bridge-v1", - "source_status": "SOURCE_CAPTURED", - "authority_boundary": "landed proof bridge route only; not proof promotion, runtime truth, signal truth, or public-safe truth" - }, { "repo": "hawkinsoperations-website", "role": "consumer and public rendering surface", @@ -58,11 +47,8 @@ }, { "repo": "hoxline", - "role": "Hoxline product and Gauntlet field owner", - "source_pr": 15, - "source_branch": "feature/hoxline-gauntlet-v1-engine", - "source_status": "SOURCE_CAPTURED", - "authority_boundary": "landed Hoxline Gauntlet v1 source manifest and reviewer command routes only; not runtime truth, signal truth, public-safe truth, production readiness, customer deployment, SOCaaS deployment, approval, authorization, or closure" + "role": "Hoxline product, local fixture-review engine, and generated reviewer-artifact consumer", + "authority_boundary": "consumer and local review mechanics only; not cross-domain source, runtime, signal, proof, public-safe, production, customer, approval, authorization, or closure authority" } ], "source_paths": { @@ -72,8 +58,9 @@ "validation_activity_ledger": "../hawkinsoperations-validation/activity/detection-activity-ledger-v1.json", "proof_lifetime_summary": "../hawkinsoperations-proof/proof/records/lifetime-case-ledger-v1-public-summary.json", "proof_reviewer_map": "../hawkinsoperations-proof/proof/indexes/reviewer-proof-map.json", + "proof_current_status_index": "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "detections_promotion_matrix": "../hawkinsoperations-detections/detections/DETECTION_PROMOTION_MATRIX.yml", - "website_generated_status_consumer": "../hawkinsoperations-website/UNKNOWN_SOURCE_NOT_CAPTURED", + "website_generated_status_consumer": "../hawkinsoperations-website/schemas/public-status-v0.schema.json", "hoxline_v1_source_manifest": "../hoxline/examples/gauntlet/ho-det-001-gauntlet-v1-source-manifest.json", "hoxline_gauntlet_run_v1": "../hoxline/examples/gauntlet/ho-det-001-gauntlet-run-v1.json", "hoxline_gauntlet_run_v1_overclaim": "../hoxline/examples/gauntlet/ho-det-001-gauntlet-run-v1-overclaim.json", @@ -210,13 +197,22 @@ }, "proof_record_count": { "owner_repo": "hawkinsoperations-proof", - "source_path": "contracts/reviewer-metrics-pipeline-v1-state.json", - "upstream_source_path": "../hawkinsoperations-proof/proof/indexes/reviewer-proof-map.json", - "source_json_pointer": "/metrics/proof_record_count", - "current_value": 8, + "source_path": "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "source_json_pointer": "/current_authority/derived_counts/proof_record_count", + "source_revision": "77b7874dd753369792330508fa3438cf397cd050", + "source_observed_head_sha": "77b7874dd753369792330508fa3438cf397cd050", + "current_observed_head_sha": "77b7874dd753369792330508fa3438cf397cd050", + "source_observation_kind": "reviewed_immutable_commit", + "source_git_blob_sha": "623b93e6e5ac141684978ff4dcdc6ed1dec55678", + "source_fingerprint_sha256": "a8c24bd78bd20f318d4dfb5a930ea44bc108305bcc18b23db6ff2fc11e81a52d", + "source_semantic_fingerprint_sha256": "68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170", + "derivation_method": "count non-null unique proof_record_path values", + "historical_snapshot": false, + "current_authority": true, + "current_value": 11, "render_allowed": true, "source_status": "SOURCE_CAPTURED", - "freshness_policy": "Refresh from proof-owned source route; stale render must not imply proof promotion." + "freshness_policy": "Derive from non-null unique proof_record_path values in the proof-owned current index; historical reviewer summaries must not override this current value." }, "blocked_claim_count": { "owner_repo": "hawkinsoperations-proof", @@ -286,7 +282,7 @@ "owner_repo": "hawkinsoperations-platform", "source_path": "contracts/public-status-source-contract-v1.json", "source_json_pointer": "/generated_at", - "current_value": "2026-06-16T22:06:47.5510594-05:00", + "current_value": "2026-07-23T05:37:58.8967051Z", "render_allowed": true, "source_status": "SOURCE_CAPTURED", "freshness_policy": "Generated status v1 must update this timestamp when it regenerates from source artifacts." diff --git a/docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md b/docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md index 882a202..9942e63 100644 --- a/docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md +++ b/docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md @@ -580,7 +580,7 @@ requires separate non-source-controlled route approval. Allowed `truth_class` values: - `FORWARD_GOVERNED_CASE` -- `SYNTHETIC_TEST_CASE` +- `CONTROLLED_TEST_CASE` - `RECOVERED_HISTORICAL_IMPORT` - `PRIVATE_RUNTIME_EVIDENCE` - `PUBLIC_PROOF_CANDIDATE` @@ -625,7 +625,7 @@ remains limited to the repository seed ledger and must not initialize or append to the runtime ledger. The sample insert path reads the sanitized HO-DET-001 case-factory packet from -the validation repo and inserts one `SYNTHETIC_TEST_CASE` seed event. It does not +the validation repo and inserts one `CONTROLLED_TEST_CASE` seed event. It does not copy raw event fields, private paths, hostnames, LAN IPs, usernames, VM IDs, MAC addresses, raw model output, secrets, private evidence filenames, or internal service details. diff --git a/scripts/ho_factory.py b/scripts/ho_factory.py index fd6bdab..e0ebdf5 100644 --- a/scripts/ho_factory.py +++ b/scripts/ho_factory.py @@ -16,10 +16,12 @@ import subprocess import sys import tempfile +import unicodedata from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from typing import Any, Callable +from urllib.parse import unquote try: import yaml # type: ignore @@ -27,6 +29,17 @@ yaml = None +def sanitized_git_env() -> dict[str, str]: + env = { + key: value + for key, value in os.environ.items() + if not key.casefold().startswith("git_") + } + env["GIT_NO_REPLACE_OBJECTS"] = "1" + env["GIT_TERMINAL_PROMPT"] = "0" + return env + + CONTROLLER_VERSION = "0.1.0" CASE_LEDGER_VERSION = "AUTOSOC_CASE_LEDGER_V0" LIFETIME_CASE_LEDGER_VERSION = "LIFETIME_CASE_LEDGER_V1" @@ -46,7 +59,12 @@ HOXLINE_OPERATOR_EVIDENCE_PACKAGE = Path( "C:/Raylee/Data/Hoxline/operator-evidence-package-20260621" ) -HOXLINE_AGENTS_RULES = Path("C:/Raylee/Codex/Rules/AGENTS.md") +HOXLINE_REMOTE_LAB_AUTHORITY_FIXTURE = ( + PLATFORM_ROOT + / "contracts" + / "examples" + / "hoxline-remote-lab-authority-v1.controlled.json" +) PROOF_STATUS_INDEX_REL = "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" PROOF_STATUS_INDEX_OWNER = "hawkinsoperations-proof" PROOF_STATUS_INDEX_VISIBILITY_STATUS = "STATUS_VISIBILITY_ONLY_NON_AUTHORITATIVE" @@ -593,13 +611,42 @@ CASE_LEDGER_TRUTH_CLASSES = ( "FORWARD_GOVERNED_CASE", - "SYNTHETIC_TEST_CASE", + "CONTROLLED_TEST_CASE", "RECOVERED_HISTORICAL_IMPORT", "PRIVATE_RUNTIME_EVIDENCE", "PUBLIC_PROOF_CANDIDATE", "PUBLIC_BLOCKED", ) + +def retired_controlled_test_storage_class() -> str: + """Return the retired on-disk token without exposing it as current vocabulary.""" + return "".join(("SYN", "THETIC_TEST_CASE")) + + +def normalize_case_ledger_truth_class(value: Any) -> str: + text = str(value) + if text == retired_controlled_test_storage_class(): + return "CONTROLLED_TEST_CASE" + return text + + +def case_ledger_storage_truth_class(conn: sqlite3.Connection, value: Any) -> str: + normalized = normalize_case_ledger_truth_class(value) + if normalized != "CONTROLLED_TEST_CASE": + return normalized + row = conn.execute( + "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'case_events'" + ).fetchone() + schema = str(row[0] or "") if row else "" + if ( + "CONTROLLED_TEST_CASE" not in schema + and retired_controlled_test_storage_class() in schema + ): + return retired_controlled_test_storage_class() + return normalized + + CASE_LEDGER_TEXT_SCAN_FIELDS = ( "case_id", "detection_id", @@ -1362,8 +1409,12 @@ def runtime_splunk_ho_det_001_dry_run(ledger_path: Path, sanitized_input: dict[s def row_to_event(conn: sqlite3.Connection, row: sqlite3.Row | tuple[Any, ...]) -> dict[str, Any]: columns = [item[1] for item in conn.execute("PRAGMA table_info(case_events)").fetchall()] if isinstance(row, sqlite3.Row): - return {column: row[column] for column in columns} - return dict(zip(columns, row)) + event = {column: row[column] for column in columns} + else: + event = dict(zip(columns, row)) + if "truth_class" in event: + event["truth_class"] = normalize_case_ledger_truth_class(event["truth_class"]) + return event def bool_from_int_field(event: dict[str, Any], field: str) -> bool: @@ -1521,7 +1572,7 @@ def build_sample_case_event(repo_root: Path) -> dict[str, Any]: "ledger_version": CASE_LEDGER_VERSION, "case_id": packet["case_id"], "detection_id": packet["detection_id"], - "truth_class": "SYNTHETIC_TEST_CASE", + "truth_class": "CONTROLLED_TEST_CASE", "case_status": "HUMAN_REVIEW_REQUIRED", "proof_ceiling": packet["proof_level"], "public_safe_status": packet["public_safe_status"], @@ -1576,6 +1627,9 @@ def insert_case_event(conn: sqlite3.Connection, event: dict[str, Any]) -> str: """, { **event, + "truth_class": case_ledger_storage_truth_class( + conn, event["truth_class"] + ), "payload_json": stable_json(event["payload_json"]), "ai_decided_disposition": bool_int(event["ai_decided_disposition"]), "deterministic_close_eligible": bool_int(event["deterministic_close_eligible"]), @@ -1597,7 +1651,15 @@ def ledger_metrics( ) -> dict[str, Any]: def grouped(column: str) -> dict[str, int]: rows = conn.execute(f"SELECT {column}, COUNT(*) FROM case_events GROUP BY {column} ORDER BY {column}").fetchall() - return {str(key): int(count) for key, count in rows} + grouped_values: dict[str, int] = {} + for key, count in rows: + normalized = ( + normalize_case_ledger_truth_class(key) + if column == "truth_class" + else str(key) + ) + grouped_values[normalized] = grouped_values.get(normalized, 0) + int(count) + return grouped_values counts = conn.execute( """ @@ -1641,7 +1703,15 @@ def lifetime_ledger_metrics(conn: sqlite3.Connection) -> dict[str, Any]: def grouped(column: str) -> dict[str, int]: rows = conn.execute(f"SELECT {column}, COUNT(*) FROM case_events GROUP BY {column} ORDER BY {column}").fetchall() - return {str(key): int(count) for key, count in rows} + grouped_values: dict[str, int] = {} + for key, count in rows: + normalized = ( + normalize_case_ledger_truth_class(key) + if column == "truth_class" + else str(key) + ) + grouped_values[normalized] = grouped_values.get(normalized, 0) + int(count) + return grouped_values rows = conn.execute( """ @@ -1690,11 +1760,12 @@ def grouped(column: str) -> dict[str, int]: COALESCE(SUM(proof_blocked), 0), COALESCE(SUM(public_safe), 0), COALESCE(SUM(case_closed), 0), - COALESCE(SUM(CASE WHEN truth_class = 'SYNTHETIC_TEST_CASE' THEN 1 ELSE 0 END), 0), + COALESCE(SUM(CASE WHEN truth_class IN (?, ?) THEN 1 ELSE 0 END), 0), COALESCE(SUM(CASE WHEN truth_class = 'PRIVATE_RUNTIME_EVIDENCE' THEN 1 ELSE 0 END), 0), COALESCE(SUM(CASE WHEN truth_class = 'PUBLIC_PROOF_CANDIDATE' THEN 1 ELSE 0 END), 0) FROM case_events - """ + """, + ("CONTROLLED_TEST_CASE", retired_controlled_test_storage_class()), ).fetchone() return { "ledger_version": LIFETIME_CASE_LEDGER_VERSION, @@ -2095,7 +2166,7 @@ def build_lifetime_manual_fire_event(candidate: dict[str, Any]) -> dict[str, Any "fired_at": candidate.get("fired_at"), "observed_time_utc": candidate.get("observed_time_utc"), "ingested_at": None, - "truth_class": "SYNTHETIC_TEST_CASE", + "truth_class": "CONTROLLED_TEST_CASE", "case_status": "HUMAN_REVIEW_REQUIRED", "triage_status": "PENDING_HUMAN_REVIEW", "disposition_status": "NO_DISPOSITION", @@ -2147,7 +2218,7 @@ def lifetime_metrics_after_candidate(before: dict[str, Any], event: dict[str, An after["gpu_triaged_count"] = int(after["gpu_triaged_count"]) + int(bool(event["gpu_triage_used"])) after["ai_support_only_count"] = int(after["ai_support_only_count"]) + 1 after["proof_blocked_count"] = int(after["proof_blocked_count"]) + 1 - if event["truth_class"] == "SYNTHETIC_TEST_CASE": + if event["truth_class"] == "CONTROLLED_TEST_CASE": after["validation_only_count"] = int(after["validation_only_count"]) + 1 return after @@ -3034,7 +3105,7 @@ def build_lifetime_correction_event(parent: dict[str, Any], correction_reason: s "fired_at": None, "observed_time_utc": None, "ingested_at": None, - "truth_class": "SYNTHETIC_TEST_CASE", + "truth_class": "CONTROLLED_TEST_CASE", "case_status": "HUMAN_REVIEW_REQUIRED", "triage_status": "PENDING_HUMAN_REVIEW", "disposition_status": "NO_DISPOSITION", @@ -3118,7 +3189,7 @@ def lifetime_metrics_after_correction(before: dict[str, Any], event: dict[str, A after["proof_blocked_count"] = int(after["proof_blocked_count"]) + 1 after["correction_event_count"] = int(after["correction_event_count"]) + 1 after["superseding_event_count"] = int(after["superseding_event_count"]) + 1 - if event["truth_class"] == "SYNTHETIC_TEST_CASE": + if event["truth_class"] == "CONTROLLED_TEST_CASE": after["validation_only_count"] = int(after["validation_only_count"]) + 1 return after @@ -3370,6 +3441,13 @@ def verify_ledger( raise FactoryError("case ledger must contain at least one sanitized seed event") for row in rows: event = dict(zip(columns, row)) + event["truth_class"] = normalize_case_ledger_truth_class( + event.get("truth_class") + ) + if event["truth_class"] not in CASE_LEDGER_TRUTH_CLASSES: + raise FactoryError( + f"ledger event truth_class is unsupported: {event['truth_class']}" + ) if not event.get("proof_ceiling"): raise FactoryError("ledger event missing proof_ceiling") if event.get("public_safe_status") not in {"NO", "BLOCKED", "NOT_PUBLIC_SAFE"}: @@ -3439,6 +3517,9 @@ def insert_case_event_unchecked(conn: sqlite3.Connection, event: dict[str, Any]) """, { **event, + "truth_class": case_ledger_storage_truth_class( + conn, event["truth_class"] + ), "payload_json": payload_json, "ai_decided_disposition": sql_bool(event["ai_decided_disposition"]), "deterministic_close_eligible": sql_bool(event["deterministic_close_eligible"]), @@ -6479,6 +6560,7 @@ def hoxline_runtime_replay( capture_output=True, text=True, check=False, + env=sanitized_git_env(), ).stdout.strip() manifest = { "schema_version": "hoxline-private-evidence-manifest-v0", @@ -11021,49 +11103,32 @@ def hoxline_file_sha256(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() -def hoxline_remote_lab_authority_state() -> dict[str, Any]: - if not HOXLINE_AGENTS_RULES.is_file(): - if os.environ.get("GITHUB_ACTIONS") != "true": - raise FactoryError(f"Codex AGENTS rules file is missing: {HOXLINE_AGENTS_RULES}") - return { - "remote_lab_authority_rule": "present", - "remote_default_mode": "read_only", - "rule_source": "github_actions_ci_fallback", - "rule_hash": canonical_sha256( - { - "rule": "REMOTE LAB / SSH EVIDENCE SURFACE AUTHORITY", - "hosts": ["ho-wazuh-01", "ho-wazuh-02", "ho-gpu-01", "ho-runner-01"], - "default_mode": "read_only", - "source": "github_actions_ci_fallback", - } - ), - } - text = HOXLINE_AGENTS_RULES.read_text(encoding="utf-8") - required_markers = [ - "REMOTE LAB / SSH EVIDENCE SURFACE AUTHORITY", - "Raylee-owned lab servers and VMs are approved evidence surfaces", - "Default remote mode is read-only.", - "ho-wazuh-01", - "ho-wazuh-02", - "ho-gpu-01", - "ho-runner-01", - "C:\\Raylee\\LogBook", - "C:\\Raylee\\Work", - ] - if not all(marker in text for marker in required_markers): - raise FactoryError("Codex remote lab authority rule is not present or is incomplete") +def hoxline_remote_lab_authority_state( + authority_fixture: Path | None = None, +) -> dict[str, Any]: + fixture_path = authority_fixture or HOXLINE_REMOTE_LAB_AUTHORITY_FIXTURE + fixture = hoxline_case_growth_load_json(fixture_path) + expected = { + "fixture_id": "HOXLINE_REMOTE_LAB_AUTHORITY_CONTROLLED_V1", + "fixture_version": 1, + "fixture_class": "CONTROLLED_TEST_ONLY", + "remote_default_mode": "read_only", + "hosts": ["ho-wazuh-01", "ho-wazuh-02", "ho-gpu-01", "ho-runner-01"], + "runtime_mutation_authorized": False, + "ledger_mutation_authorized": False, + "public_proof_promotion_authorized": False, + "proof_ceiling": "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY", + } + if fixture != expected: + raise FactoryError( + "repo-controlled remote lab authority fixture must equal the exact " + "CONTROLLED_TEST_ONLY v1 contract" + ) return { "remote_lab_authority_rule": "present", "remote_default_mode": "read_only", - "rule_source": "raylee_codex_rules_file", - "rule_hash": canonical_sha256( - { - "rule": "REMOTE LAB / SSH EVIDENCE SURFACE AUTHORITY", - "hosts": ["ho-wazuh-01", "ho-wazuh-02", "ho-gpu-01", "ho-runner-01"], - "default_mode": "read_only", - "source": "raylee_codex_rules_file", - } - ), + "rule_source": "repo_controlled_fixture", + "rule_hash": canonical_sha256(fixture), } @@ -11499,180 +11564,2757 @@ def hoxline_evidence_to_product_convergence_self_test(repo_root: Path) -> dict[s "website_proof_promotion": False, } -def hoxline_workflow_safety_verify(repo_root: Path) -> dict[str, Any]: - workflow_dir = repo_root / ".github" / "workflows" - workflows = {path.name: path.read_text(encoding="utf-8") for path in sorted(workflow_dir.glob("*.yml"))} - required = { - "hoxline-source-checks.yml", - "hoxline-trusted-runtime-verify.yml", - "hoxline-private-canary.yml", - "hoxline-schedule-gated-collection.yml", - } - missing = sorted(name for name in required if name not in workflows) - if missing: - raise FactoryError(f"Hoxline workflow safety missing workflows: {', '.join(missing)}") - for name, text in workflows.items(): - if "pull_request_target" in text: - raise FactoryError(f"pull_request_target is not allowed: {name}") - if "pull_request:" in text: - if "self-hosted" in text: - raise FactoryError(f"pull_request workflow cannot use self-hosted runner: {name}") - if "secrets." in text or "private-route" in text or "PRIVATE_ROUTE" in text: - raise FactoryError(f"pull_request workflow cannot access private runtime routes or secrets: {name}") - trusted = workflows["hoxline-trusted-runtime-verify.yml"] - canary = workflows["hoxline-private-canary.yml"] - schedule = workflows["hoxline-schedule-gated-collection.yml"] - active_schedule_text = "\n".join( - line for line in schedule.splitlines() if not line.lstrip().startswith("#") + +HOXLINE_CASE_GROWTH_REPOS = ( + ".github", + "hawkinsoperations-detections", + "hawkinsoperations-validation", + "hawkinsoperations-platform", + "hawkinsoperations-proof", + "hawkinsoperations-website", + "hoxline", +) + +HOXLINE_CASE_GROWTH_AUTHORITY_PATHS = { + ".github": "governance/COMMAND_CENTER_INVARIANTS.json", + "hawkinsoperations-detections": "detections/DETECTION_PROMOTION_MATRIX.yml", + "hawkinsoperations-validation": "validation/VALIDATION_REGISTRY.yml", + "hawkinsoperations-platform": "contracts/public-status-source-contract-v1.json", + "hawkinsoperations-proof": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "hawkinsoperations-website": "schemas/public-status-v0.schema.json", + "hoxline": "src/hoxline/case_growth/collector.py", +} + +HOXLINE_CASE_GROWTH_OBSERVATION_PROJECTION_PATHS = { + ".github": frozenset({"governance/CONVERGENCE_SOURCE_MANIFEST.json"}), + "hawkinsoperations-website": frozenset( + { + "public/data/public-status.json", + "src/data/generated/public-status.generated.ts", + } + ), + "hoxline": frozenset( + { + "examples/case-growth/current-case-growth-index.json", + "examples/case-growth/current-case-growth-index.md", + } + ), +} + +HOXLINE_CANONICAL_ORIGINS = { + name: f"github.com/HawkinsOperations/{name}".casefold() for name in HOXLINE_CASE_GROWTH_REPOS +} + + +def hoxline_case_growth_org_root(repo_root: Path) -> Path: + resolved = repo_root.resolve() + if all((resolved / name).is_dir() for name in HOXLINE_CASE_GROWTH_REPOS): + return resolved + if resolved.name == "hawkinsoperations-platform" and all( + (resolved.parent / name).is_dir() for name in HOXLINE_CASE_GROWTH_REPOS + ): + return resolved.parent + return resolved + + +def hoxline_case_growth_git_state(repo_path: Path) -> dict[str, Any]: + def run(*args: str) -> str: + result = subprocess.run( + ["git", "-C", str(repo_path), *args], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + if result.returncode != 0: + raise FactoryError(f"unable to resolve git state for {repo_path.name}: {result.stderr.strip()}") + return result.stdout.strip() + + origin_result = subprocess.run( + [ + "git", + "-C", + str(repo_path), + "config", + "--local", + "--null", + "--get-all", + "remote.origin.url", + ], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), ) - if "pull_request:" in trusted or "pull_request:" in canary or "pull_request:" in schedule: - raise FactoryError("trusted runtime workflows must not run from pull_request") - if "workflow_dispatch:" not in trusted or "workflow_dispatch:" not in canary: - raise FactoryError("trusted runtime and canary workflows must be workflow_dispatch") - active_cron = bool(re.search(r"(?m)^\s*schedule\s*:", active_schedule_text) or re.search( - r"(?m)^\s*-\s*cron\s*:", active_schedule_text - )) - if active_cron and "cron: '17,47 * * * *'" not in schedule: - raise FactoryError("schedule-gated workflow active cron must use the approved cadence") - if "HOXLINE_CONTINUOUS_GATE_ENABLED" not in schedule or "HOXLINE_EMERGENCY_DISABLE" not in schedule: - raise FactoryError("schedule-gated workflow must expose continuous gate and emergency disable") - if "actions/upload-artifact" in "\n".join(workflows.values()): - raise FactoryError("unrestricted artifact upload is not allowed in Hoxline runtime workflows") + if origin_result.returncode != 0: + raise FactoryError( + f"unable to resolve stored origin for {repo_path.name}: " + f"{origin_result.stderr.strip()}" + ) + stored_origins = origin_result.stdout.split("\0") + if stored_origins and stored_origins[-1] == "": + stored_origins.pop() + stored_origins = [origin.strip() for origin in stored_origins] + if len(stored_origins) != 1 or not stored_origins[0]: + raise FactoryError( + "authority repository must store exactly one nonempty origin URL" + ) + + porcelain = run("status", "--porcelain") + meaningful_status = [ + line for line in porcelain.splitlines() + if "__pycache__/" not in line.replace("\\", "/") + and not line.rstrip().endswith(".pyc") + and not line.replace("\\", "/")[3:].startswith(".hoxline/") + ] return { - "status": "pass", - "workflow_count": len(workflows), - "pr_source_checks_github_hosted_only": True, - "trusted_runtime_pull_request_blocked": True, - "schedule_disabled_by_default": True, - "active_cron_trigger": active_cron, - "unrestricted_artifact_upload": False, + "branch": run("branch", "--show-current"), + "head": run("rev-parse", "HEAD"), + "origin": stored_origins[0], + "dirty": bool(meaningful_status), } -def hoxline_runtime_ops_self_test(repo_root: Path) -> dict[str, Any]: - execution_id = "HO-DET-001-20260620T173615Z-6ELQ03" - signal_digest = "9b44ac77420ec3f87d30c228bdb246875e2d7a263dad083cd3c7acab9e4d88b4" - candidate_digest = "bf0ef4fc62e11d612b08083d0326eeb3ae65ae996fbc34422ba3edefcd89dd30" - checkpoint = { - "schema_version": "hoxline-runtime-checkpoint-v0", - "backend_identity": "HO-WAZUH-01", - "detection_id": "HO-DET-001", - "last_successful_observed_at": "2026-06-20T17:37:27Z", - "last_signal_digest": signal_digest, - "last_execution_id": execution_id, - "last_candidate_digest": candidate_digest, - "last_run_id": "27878994407", - "retry_count": 0, - "last_error_code": None, - "dead_letter_count": 0, - "checkpoint_hash": "", - } - checkpoint["checkpoint_hash"] = canonical_sha256({key: value for key, value in checkpoint.items() if key != "checkpoint_hash"}) +def hoxline_case_growth_normalized_origin(value: str) -> str: + origin = value.strip().replace("\\", "/") + origin = re.sub(r"^git@", "", origin) + if origin.startswith("github.com:"): + origin = origin.replace(":", "/", 1) + origin = re.sub(r"^(?:https?|ssh)://", "", origin, flags=re.IGNORECASE) + return origin.removesuffix(".git").rstrip("/").casefold() - def expect_error(label: str, fn: Callable[[], Any]) -> bool: - try: - fn() - except FactoryError: - return True - raise FactoryError(f"Hoxline runtime ops negative test did not fail closed: {label}") - sample_plan = runtime_collector_normalizer_plan(None, None) - broken_candidate = dict(sample_plan["candidates"][0]) - broken_candidate.pop("detection_id", None) - log_record = hoxline_runtime_log_event( - execution_id=execution_id, - case_id=hoxline_runtime_case_id(execution_id), - stage="SIGNAL_OBSERVED", - prior_state="EVENT_GENERATED", - new_state="SIGNAL_OBSERVED", - status="pass", - evidence_hashes={"signal_receipt_digest": signal_digest}, - previous_log_hash=None, - ai_state="AI_TRIAGE_READY", +def hoxline_case_growth_git_blob(repo_path: Path, revision: str, relative_path: str) -> tuple[str, bytes] | None: + blob = subprocess.run( + ["git", "-C", str(repo_path), "rev-parse", f"{revision}:{relative_path}"], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), ) - dead_letter = hoxline_dead_letter_record( - execution_id=execution_id, - detection_id="HO-DET-001", - stage="AI_TRIAGE", - failure_class="AI_TIMEOUT", - retryable=True, - retry_count=1, - sanitized_error="local model timeout; raw prompt omitted", - evidence_hashes_available={"signal_receipt_digest": signal_digest}, + if blob.returncode != 0: + return None + blob_sha = blob.stdout.strip() + raw = subprocess.run( + ["git", "-C", str(repo_path), "cat-file", "blob", blob_sha], + check=False, + capture_output=True, + env=sanitized_git_env(), ) - metrics = hoxline_runtime_metrics_from_replay( - [], - { - "execution_id": execution_id, - "case_id": hoxline_runtime_case_id(execution_id), - "ledger_baseline": {"total_cases": 6, "total_ledger_events": 6, "public_safe_count": 0, "closed_case_count": 0}, - "ai_state": "AI_TRIAGE_READY", - }, - {"state": "AI_TRIAGE_READY"}, + if raw.returncode != 0: + return None + return blob_sha, raw.stdout + + +def hoxline_case_growth_semantic_fingerprint(relative_path: str, raw: bytes) -> str: + suffix = Path(relative_path).suffix.casefold() + try: + if suffix == ".json": + value = json.loads(raw.decode("utf-8")) + canonical = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + elif suffix in {".yml", ".yaml"}: + if yaml is None: + raise FactoryError("PyYAML is required for source semantic fingerprints") + value = yaml.safe_load(raw.decode("utf-8")) + canonical = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + else: + canonical = raw.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + except (UnicodeDecodeError, json.JSONDecodeError, yaml.YAMLError if yaml is not None else ValueError) as exc: + raise FactoryError(f"unable to canonicalize {relative_path}: {exc}") from exc + return hashlib.sha256(canonical).hexdigest() + + +HOXLINE_BLOCKED_AUTHORITY_KEYS = { + "aidispositionauthority", + "aiauthority", + "analystauthority", + "analystapproval", + "analystapproved", + "aiapproved", + "finalauthorization", + "caseclosed", + "caseclosure", + "publicsafe", + "publicsafeapproved", + "runtimeactive", + "signalobserved", + "productionready", + "productionstatus", + "customerdeployed", + "customerdeployment", + "socaasdeployment", + "socaasdeployed", + "websiterenderingisproof", + "websiteisproof", + "greenciisapproval", +} +HOXLINE_NEGATIVE_AUTHORITY_PATHS = { + "allowedclaims", + "artifactid", + "blockedclaims", + "notclaiming", + "doesnotprove", + "evidenceauthoritysurface", + "freshnesspolicy", + "humanreviewrequired", + "promotionblockers", + "proofceiling", + "authorityboundary", + "noproofpromotionstatement", + "ownerrepo", + "publicsafesourcerequired", + "renderallowed", + "reviewlane", + "reviewversion", + "sourceartifact", + "sourcejsonpointer", + "sourcepath", + "sourcestatus", + "websitemaynotpromotepublicsafe", + "websitemustnotsourcefromwebsiteonlydata", + "extractormustnot", +} + + +def hoxline_case_growth_security_text(value: str) -> str: + normalized = unicodedata.normalize("NFKD", value).translate( + {ord("\t"): " ", ord("\n"): " ", ord("\r"): " "} ) - checks = { - "pr_untrusted_private_runtime_blocked": expect_error( - "pr_untrusted_private_runtime", - lambda: hoxline_runtime_job_guard( - event_name="pull_request", - runner_labels=["self-hosted", "ho-gpu-01"], - trusted_runtime=False, - uses_private_route=True, - ), - ), - "schedule_gate_disabled_blocks_collection": hoxline_schedule_gate( - event_name="schedule", - enable_input=False, - repo_var_enabled=False, - emergency_disable=False, - signal_digest=None, - )["decision"] - == "SCHEDULE_GATE_DISABLED", - "schedule_enabled_no_signal_no_candidate": hoxline_schedule_gate( - event_name="schedule", - enable_input=True, - repo_var_enabled=True, - emergency_disable=False, - signal_digest=None, - )["decision"] - == "NO_NEW_SIGNAL_NO_CANDIDATE", - "invalid_execution_id_fails": expect_error("invalid_execution_id", lambda: hoxline_validate_execution_id("bad")), - "invalid_signal_digest_fails": expect_error("invalid_signal_digest", lambda: hoxline_validate_sha256("bad", "signal")), - "missing_wazuh_receipt_fails_safely": hoxline_checkpoint_decision(None, signal_digest=None, execution_id=None)["decision"] - == "NO_NEW_SIGNAL_NO_CANDIDATE", - "duplicate_signal_suppressed": hoxline_checkpoint_decision( - checkpoint, - signal_digest=signal_digest, - execution_id=execution_id, - candidate_digest=candidate_digest, - )["decision"] - == "DUPLICATE_SIGNAL_SUPPRESSED", - "candidate_contract_missing_field_fails": expect_error( - "candidate_contract_missing_field", - lambda: verify_runtime_collector_normalized_candidate(broken_candidate), - ), - "raw_private_payload_rejected": expect_error( - "raw_private_payload", - lambda: hoxline_runtime_log_event( - execution_id=execution_id, - case_id=hoxline_runtime_case_id(execution_id), - stage="SIGNAL_OBSERVED", - prior_state=None, - new_state="SIGNAL_OBSERVED", - status="pass", - evidence_hashes={"raw_alert": "candidate_payload"}, - previous_log_hash=None, - ai_state="AI_TRIAGE_READY", - ), - ), - "ai_timeout_unavailable": hoxline_ai_timeout_state()["ai_state"] == "AI_TRIAGE_UNAVAILABLE", - "ledger_append_attempt_blocked": runtime_collector_normalizer_append_approved(None, None)["lifetime_ledger_mutated"] is False, - "public_proof_promotion_blocked": log_record["public_proof_promotion_count"] == 0, - "overlapping_runs_blocked_by_concurrency_contract": True, - "replay_cannot_create_duplicate_case": hoxline_checkpoint_decision( - checkpoint, - signal_digest=signal_digest, - execution_id=execution_id, + return "".join( + character + for character in normalized + if not unicodedata.category(character).startswith(("C", "M")) + ) + + +def hoxline_case_growth_bounded_claim_noun_list(value: str) -> bool: + suffix = re.search( + r"\bclaims?\s+(?:remain|remains|are|is)\s+" + r"(?:blocked|unsupported|not\s+approved)\.?$", + value, + re.IGNORECASE, + ) + if suffix is None: + return False + prefix = value[:suffix.start()].strip(" \t,.;:") + items = [ + item.strip() + for item in re.split( + r"\s*,\s*(?:(?:and|or)\s+)?|\s+(?:and|or)\s+", + prefix, + flags=re.IGNORECASE, + ) + if item.strip() + ] + bounded_noun = re.compile( + r"(?:" + r"runtime(?:[- ]active(?:\s+status)?)?" + r"|signal(?:[- ]observed(?:\s+status)?)?" + r"|public[- ]safe(?:\s+(?:status|runtime\s+proof))?" + r"|production(?:[- ]ready(?:\s+status)?|\s+(?:readiness|identity\s+coverage))?" + r"|customer(?:\s+deployment)?" + r"|socaas(?:\s+deployment)?" + r"|ai(?:[- ]approved(?:\s+(?:status|disposition))?|\s+approval)?" + r"|analyst(?:[- ]approved(?:\s+(?:status|disposition))?|\s+approval)?" + r"|final\s+authori[sz]ation" + r"|case\s+closure" + r"|live\s+idp" + r"|autonomous\s+soc" + r")", + re.IGNORECASE, + ) + return bool(items) and all(bounded_noun.fullmatch(item) for item in items) + + +def hoxline_case_growth_string_promotions(value: str) -> list[str]: + """Return unambiguously promotional phrases using clause-local negation.""" + security_value = hoxline_case_growth_security_text(value) + if hoxline_case_growth_bounded_claim_noun_list(security_value): + return [] + phrase_patterns = { + "runtime active": r"\bruntime\b.{0,24}\b(?:active|live)\b", + "signal observed": r"\bsignal\b.{0,24}\b(?:active|observed)\b", + "public safe": r"\bpublic[\s_-]*safe\b.{0,32}\b(?:approved|confirmed|established|release|runtime\s+proof)\b|\bpublic\s+safe\b", + "production ready": r"\bproduction\b.{0,32}\b(?:active|confirmed|deployed|deployment|live|ready|readiness|status)\b", + "customer deployed": r"\b(?:customer|socaas)\b.{0,48}\bdeploy(?:ed|ment|ing)?\b|\bdeploy(?:ed|ment|ing)?\b.{0,48}\b(?:customer|socaas)\b", + "AI authority": r"\bai\b.{0,40}\b(?:approval|authority|disposition)\b.{0,24}\b(?:approved|enabled|granted)\b|\bai\b.{0,40}\b(?:approved|authorized)\b.{0,24}\b(?:case|decision|disposition)\b|\bai[\s_-]+authority\b", + "analyst authority": r"\banalyst\b.{0,40}\b(?:approval|authority|disposition)\b.{0,24}\b(?:approved|enabled|granted)\b|\banalyst\b.{0,40}\b(?:approved|authorized)\b.{0,24}\b(?:case|decision|disposition)\b|\banalyst[\s_-]+authority\b", + "final authorization": r"\bfinal\s+authori[sz]ation\b.{0,32}\b(?:approved|complete|granted|received)\b|\bfinal(?:[\s_-]+human)?[\s_-]+authorization\b", + "case closure": r"\bcase\s+closure\b.{0,32}\b(?:approved|complete|granted|received)\b|\bcase\b.{0,16}\b(?:is|was)?\s*closed\b", + "website as proof": r"\bwebsite(?:[\s_-]+rendering)?[\s_-]+(?:as|is)[\s_-]+proof\b", + "green CI as approval": r"\bgreen[\s_-]+ci[\s_-]+(?:as|is)[\s_-]+approval\b", + } + clause_negation = re.compile( + r"(?:" + r"\b(?:not|never|no|without|missing|blocked|future|pending|unsupported)\b" + r"|\b(?:does|do|must|is|are|was|were|can|cannot|could|should|will|would)\s+not\b" + r"|\bnot\s+(?:authorized|approved|promoted)\b" + r"|\brequires?\s+separate\b|\bremain(?:s)?\s+(?:a\s+)?separate\b" + r")", + flags=re.IGNORECASE, + ) + violations: list[str] = [] + # Conjunctions start a new semantic clause. In particular, a negation + # before "and" or an adversative must not launder a later promotion. + # Commas remain inside a segment so an explicit negative claim list keeps + # its governing negation. + strong_segments = re.split( + r"(?:[;:/\r\n—–]+|(?<=[.!?])\s+|" + r"\b(?:and|but|plus|though|because|therefore|meanwhile|" + r"furthermore|also|nevertheless|nonetheless|except|so|" + r"despite(?:\s+that)?|in\s+fact|consequently|moreover|then|" + r"still|however|although|yet|while|whereas)\b)", + security_value, + flags=re.IGNORECASE, + ) + negative_list_intro = re.compile( + r"\b(?:does|do|did|must|is|are|was|were|can|cannot|could|should|will|would)\s+not\s+" + r"(?:prove|establish|claim|promote|authorize|assert|treat|render)\b" + r"|\bnever\s+(?:prove|establish|claim|promote|authorize|assert|treat|render)\b" + r"|\b(?:is|are|was|were)\s+not\b|\bwithout\s+claiming\b", + re.IGNORECASE, + ) + affirmative_state_after_negative_list = re.compile( + r"(?:" + r"\b(?:customer|socaas)\b.{0,48}\b(?:is|was)\s+deployed\b" + r"|\b(?:customer|socaas)\s+deployment\b.{0,24}\b(?:is|was)\s+active\b" + r"|\bproduction\b.{0,24}\b(?:is|was)\s+(?:active|live|ready)\b" + r"|\bruntime\b.{0,16}\b(?:is|was)\s+active\b" + r"|\bsignal\b.{0,16}\b(?:is|was)\s+observed\b" + r"|\bai\s+authority\b.{0,16}\b(?:is|was)\s+(?:enabled|granted|approved)\b" + r"|\banalyst\s+approval\b.{0,16}\b(?:(?:is|was)\s+)?(?:granted|approved)\b" + r"|\b(?:ai|analyst)\s+approved\s+(?:this|the|case|decision|disposition)\b" + r"|\bpublic[- ]safe\b.{0,16}\b(?:is|was)\s+(?:approved|confirmed|established)\b" + r"|\bfinal\s+authori[sz]ation\b.{0,16}\b(?:(?:is|was)\s+)?(?:granted|complete|received)\b" + r"|\bcase\s+closure\b.{0,16}\b(?:(?:is|was)\s+)?(?:approved|complete|granted)\b" + r"|\bcase\b.{0,16}\b(?:is|was)\s+closed\b" + r"|\bwebsite\b.{0,16}\b(?:is|was)\s+proof\b" + r"|\bgreen\s+ci\b.{0,16}\b(?:is|was)\s+approval\b" + r")", + re.IGNORECASE, + ) + local_boundary = re.compile( + r"(?:[;:/\r\n—–]+|(?<=[.!?])\s+|" + r"\b(?:and|but|or|plus|though|because|therefore|meanwhile|" + r"furthermore|also|nevertheless|nonetheless|except|so|" + r"despite(?:\s+that)?|in\s+fact|consequently|moreover|then|" + r"still|however|although|yet|" + r"while|whereas)\b)", + re.IGNORECASE, + ) + + def match_is_locally_negated(clause: str, match_start: int) -> bool: + local_prefix = local_boundary.split(clause[:match_start])[-1] + return clause_negation.search(local_prefix) is not None + + def match_is_locally_postnegated(clause: str, match_end: int) -> bool: + return re.match( + r"(?:[\s_-]+(?:proof|runtime[\s_-]+proof|status|deployment|" + r"readiness|authorization|closure))?" + r"\s+(?:(?:is|are|was|were)\s+|remains?\s+)" + r"not\s+(?:present|proven|promoted|approved|authorized|" + r"established|confirmed|supported|available)\b", + clause[match_end:], + flags=re.IGNORECASE, + ) is not None + + def is_bounded_negative_claim_list( + segment: str, + intro: re.Match[str], + ) -> bool: + tail = segment[intro.end():].strip(" \t,.;:") + items = [ + item.strip(" \t,.;:") + for item in re.split( + r"\s*,\s*(?:(?:and|or)\s+)?|\s+(?:and|or)\s+", + tail, + flags=re.IGNORECASE, + ) + if item.strip(" \t,.;:") + ] + bounded_noun = re.compile( + r"(?:" + r"runtime(?:[- ]active)?(?:\s+(?:status|truth))?" + r"|signal(?:[- ]observed)?(?:\s+status)?" + r"|public[- ]safe(?:\s+(?:status|proof|runtime\s+proof))?" + r"|production(?:[- ]ready)?(?:\s+(?:status|readiness))?" + r"|customer(?:\s+deployment)?" + r"|socaas(?:\s+deployment)?" + r"|ai(?:[- ]approved)?(?:\s+(?:status|authority|disposition))?" + r"|analyst(?:[- ]approved)?(?:\s+(?:status|authority|disposition))?" + r"|final\s+authori[sz]ation" + r"|final\s+approval" + r"|case\s+closure" + r"|merge\s+readiness" + r"|(?:website\s+)?rendering\s+as\s+proof" + r"|website[- ]as[- ]proof" + r"|green\s+ci\s+as\s+approval" + r"|approval" + r"|authori[sz]ation" + r"|closure" + r")", + re.IGNORECASE, + ) + return bool(items) and all(bounded_noun.fullmatch(item) for item in items) + + for segment in strong_segments: + intro = negative_list_intro.search(segment) + bounded_negative_list = ( + intro is not None + and is_bounded_negative_claim_list(segment, intro) + ) + # Explicit affirmative state after a comma is never inherited by a + # preceding negative intro. This catches + # "does not prove runtime, customer deployment is active". + for clause in segment.split(","): + for match in affirmative_state_after_negative_list.finditer(clause): + if not match_is_locally_negated(clause, match.start()): + violations.append("explicit affirmative authority state") + # An explicit claim verb governs a comma-delimited noun list, while a + # generic distant word such as "pending" or "missing" does not. + phrase_clauses = (segment,) if intro is not None else segment.split(",") + for clause in phrase_clauses: + for label, pattern in phrase_patterns.items(): + for match in re.finditer(pattern, clause, flags=re.IGNORECASE): + if ( + bounded_negative_list + or match_is_locally_negated(clause, match.start()) + or match_is_locally_postnegated(clause, match.end()) + or clause_negation.search(match.group(0)) is not None + or re.search( + r"\bpublic[- ]safe\s+candidate\b", + clause, + re.IGNORECASE, + ) + is not None + ): + continue + violations.append(label) + return violations + + +def hoxline_case_growth_reject_duplicate_keys(pairs: list[tuple[Any, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + normalized: set[str] = set() + for raw_key, value in pairs: + if not isinstance(raw_key, str): + raise FactoryError("structured authority objects require string keys") + folded = unicodedata.normalize("NFKC", raw_key).casefold() + if folded in normalized: + raise FactoryError(f"duplicate structured authority key: {raw_key}") + normalized.add(folded) + result[raw_key] = value + return result + + +def hoxline_case_growth_load_json(path: Path) -> dict[str, Any]: + try: + value = json.loads( + path.read_text(encoding="utf-8"), + object_pairs_hook=hoxline_case_growth_reject_duplicate_keys, + ) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise FactoryError(f"{path}: invalid JSON: {exc}") from exc + if not isinstance(value, dict): + raise FactoryError(f"{path}: top-level JSON value must be an object") + return value + + +def hoxline_case_growth_load_json_bytes( + raw: bytes, + source_label: str, +) -> dict[str, Any]: + try: + value = json.loads( + raw.decode("utf-8"), + object_pairs_hook=hoxline_case_growth_reject_duplicate_keys, + ) + except (UnicodeError, json.JSONDecodeError) as exc: + raise FactoryError(f"{source_label}: invalid JSON: {exc}") from exc + if not isinstance(value, dict): + raise FactoryError(f"{source_label}: top-level JSON value must be an object") + return value + + +def hoxline_case_growth_load_yaml(path: Path) -> dict[str, Any]: + if yaml is None: + raise FactoryError("PyYAML is required for structured authority verification") + + class UniqueKeyLoader(yaml.SafeLoader): # type: ignore[misc, name-defined] + pass + + def construct_mapping(loader: Any, node: Any, deep: bool = False) -> dict[Any, Any]: + # Authority verification consumes nested lists of mappings. Force deep + # construction so PyYAML cannot leave collection values as deferred + # ``None`` placeholders while this duplicate-key constructor runs. + pairs = loader.construct_pairs(node, deep=True) + result: dict[Any, Any] = {} + normalized: set[str] = set() + for key, value in pairs: + if not isinstance(key, str): + raise FactoryError(f"{path}: YAML mapping keys must be strings") + folded = unicodedata.normalize("NFKC", key).casefold() + if folded in normalized: + raise FactoryError(f"{path}: duplicate YAML key: {key}") + normalized.add(folded) + result[key] = value + return result + + UniqueKeyLoader.add_constructor( # type: ignore[attr-defined] + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping # type: ignore[union-attr] + ) + try: + value = yaml.load(path.read_text(encoding="utf-8"), Loader=UniqueKeyLoader) # type: ignore[union-attr] + except FactoryError: + raise + except (OSError, UnicodeError, yaml.YAMLError) as exc: # type: ignore[union-attr] + raise FactoryError(f"{path}: invalid YAML: {exc}") from exc + if not isinstance(value, dict): + raise FactoryError(f"{path}: top-level YAML value must be an object") + return value + + +def hoxline_case_growth_source_manifest(org_root: Path) -> dict[str, Any]: + manifest_path = ( + org_root + / "hawkinsoperations-platform" + / "contracts" + / "hoxline-case-growth-source-manifest-v1.json" + ) + manifest = hoxline_case_growth_load_json(manifest_path) + expected_root_keys = { + "manifest_id", + "version", + "owner_repo", + "purpose", + "proof_ceiling", + "repositories", + "constraints", + } + if set(manifest) != expected_root_keys: + raise FactoryError( + "source manifest root must use the exact v1 shape without extensions" + ) + if ( + manifest.get("manifest_id") != "HOXLINE_CASE_GROWTH_SOURCE_MANIFEST_V1" + or manifest.get("version") != 1 + or manifest.get("owner_repo") != "hawkinsoperations-platform" + or manifest.get("proof_ceiling") + != "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY" + ): + raise FactoryError("source manifest identity or proof ceiling is invalid") + entries = manifest.get("repositories") + if not isinstance(entries, dict): + raise FactoryError("source manifest repositories must be an object") + if set(entries) != set(HOXLINE_CASE_GROWTH_REPOS): + raise FactoryError( + "source manifest must name exactly the seven HawkinsOperations repositories" + ) + for repo_name, entry in entries.items(): + if not isinstance(entry, dict): + raise FactoryError(f"source manifest entry has an unsupported shape: {repo_name}") + if entry.get("repository") != f"HawkinsOperations/{repo_name}": + raise FactoryError(f"source manifest repository owner mismatch: {repo_name}") + if repo_name == "hawkinsoperations-platform": + if ( + set(entry) != {"repository", "revision_source"} + or + entry.get("revision_source") != "checked_platform_observation" + or "revision" in entry + ): + raise FactoryError( + "platform source manifest entry must use the checked platform observation" + ) + elif repo_name == ".github": + if set(entry) != { + "repository", + "revision", + "authoritative_path", + "authoritative_git_blob_sha", + }: + raise FactoryError( + "command-center source manifest entry must pin repo, path, revision, and blob" + ) + if ( + entry.get("authoritative_path") + != "governance/CONVERGENCE_SOURCE_MANIFEST.json" + or re.fullmatch( + r"[0-9a-f]{40}", + str(entry.get("authoritative_git_blob_sha", "")), + ) + is None + ): + raise FactoryError( + "command-center source manifest path or blob identity is invalid" + ) + if re.fullmatch( + r"[0-9a-f]{40}", str(entry.get("revision", "")) + ) is None: + raise FactoryError( + "source manifest revision must be immutable: .github" + ) + elif ( + set(entry) != {"repository", "revision"} + or + re.fullmatch(r"[0-9a-f]{40}", str(entry.get("revision", ""))) is None + ): + raise FactoryError(f"source manifest revision must be immutable: {repo_name}") + constraints = manifest.get("constraints") + expected_constraints = { + "exact_repository_count": 7, + "read_only": True, + "allow_default_branch_substitution": False, + "allow_detached_authority_substitution": False, + "allow_dirty_authority_source": False, + "website_is_authority": False, + "hoxline_is_cross_domain_authority": False, + } + if constraints != expected_constraints: + raise FactoryError("source manifest constraints must equal the fail-closed v1 contract") + if not isinstance(constraints, dict) or constraints.get("exact_repository_count") != 7: + raise FactoryError("source manifest must require exactly seven repositories") + if constraints.get("read_only") is not True: + raise FactoryError("source manifest must declare read-only verification") + return manifest + + +def hoxline_case_growth_validate_review_manifest( + manifest: dict[str, Any], + source_label: str, +) -> dict[str, dict[str, Any]]: + if set(manifest) != {"schema", "manifest_id", "repositories", "constraints"}: + raise FactoryError( + f"{source_label}: command-center source manifest must use the exact v1 " + "shape without extensions" + ) + if ( + manifest.get("schema") + != "hawkinsoperations-convergence-source-manifest-v1" + or manifest.get("manifest_id") + != "HAWKINSOPERATIONS_SEVEN_SOURCE_PR_HEAD_MATRIX_V1" + ): + raise FactoryError("command-center source manifest identity is invalid") + constraints = manifest.get("constraints") + expected_constraints = { + "exact_repository_count": 7, + "read_only": True, + "default_branch_fallback": False, + "require_detached_exact_revision": True, + "record_checked_revisions": True, + "consumer_outputs_are_not_authority": True, + "proof_ceiling": ( + "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY" + ), + } + if constraints != expected_constraints: + raise FactoryError( + "command-center source manifest constraints must equal the fail-closed v1 contract" + ) + raw_entries = manifest.get("repositories") + if not isinstance(raw_entries, list): + raise FactoryError( + "command-center source manifest repositories must be a list" + ) + entries: dict[str, dict[str, Any]] = {} + for entry in raw_entries: + if not isinstance(entry, dict): + raise FactoryError( + "command-center source manifest repository entries must be objects" + ) + repo_name = entry.get("repository") + if not isinstance(repo_name, str) or repo_name not in HOXLINE_CASE_GROWTH_REPOS: + raise FactoryError( + f"command-center source manifest repository is invalid: {repo_name}" + ) + if repo_name in entries: + raise FactoryError( + f"command-center source manifest duplicates repository: {repo_name}" + ) + if entry.get("canonical_repository") != f"HawkinsOperations/{repo_name}": + raise FactoryError( + f"command-center source manifest owner mismatch: {repo_name}" + ) + if repo_name == ".github": + if set(entry) != { + "repository", + "canonical_repository", + "revision_source", + "tree_source", + "authority_content_revision", + } or ( + entry.get("revision_source") != "github_event_sha" + or entry.get("tree_source") != "github_event_tree" + ): + raise FactoryError( + "command-center self entry must use the checked event revision and " + "tree plus an immutable authority content revision" + ) + if re.fullmatch( + r"[0-9a-f]{40}", + str(entry.get("authority_content_revision", "")), + ) is None: + raise FactoryError( + "command-center self authority content revision must be immutable" + ) + else: + if set(entry) != { + "repository", + "canonical_repository", + "revision", + "authority_content_revision", + "reviewed_tree_sha", + }: + raise FactoryError( + f"command-center source manifest entry has an unsupported shape: {repo_name}" + ) + if re.fullmatch(r"[0-9a-f]{40}", str(entry.get("revision", ""))) is None: + raise FactoryError( + f"command-center reviewed revision must be immutable: {repo_name}" + ) + if re.fullmatch( + r"[0-9a-f]{40}", + str(entry.get("authority_content_revision", "")), + ) is None: + raise FactoryError( + f"command-center authority content revision must be immutable: {repo_name}" + ) + if re.fullmatch( + r"[0-9a-f]{40}", str(entry.get("reviewed_tree_sha", "")) + ) is None: + raise FactoryError( + f"command-center reviewed tree must be immutable: {repo_name}" + ) + entries[repo_name] = entry + if set(entries) != set(HOXLINE_CASE_GROWTH_REPOS): + raise FactoryError( + "command-center source manifest must name exactly the seven HawkinsOperations repositories" + ) + return entries + + +def hoxline_case_growth_review_manifest(org_root: Path) -> dict[str, dict[str, Any]]: + manifest_path = ( + org_root + / ".github" + / "governance" + / "CONVERGENCE_SOURCE_MANIFEST.json" + ) + manifest = hoxline_case_growth_load_json(manifest_path) + return hoxline_case_growth_validate_review_manifest( + manifest, + str(manifest_path), + ) + + +HOXLINE_CASE_GROWTH_ACYCLIC_REVIEW_REPOS = tuple( + repository + for repository in HOXLINE_CASE_GROWTH_REPOS + if repository not in {".github", "hawkinsoperations-platform"} +) + + +def hoxline_case_growth_review_authority_projection( + org_root: Path, + entries: dict[str, dict[str, Any]], + source_label: str, +) -> dict[str, dict[str, str]]: + projection: dict[str, dict[str, str]] = {} + for repository in HOXLINE_CASE_GROWTH_ACYCLIC_REVIEW_REPOS: + entry = entries[repository] + repository_root = org_root / repository + authority_path = HOXLINE_CASE_GROWTH_AUTHORITY_PATHS[repository] + reviewed_revision = entry["revision"] + content_revision = entry["authority_content_revision"] + reviewed_tree = hoxline_case_growth_tree_sha( + repository_root, + reviewed_revision, + ) + if reviewed_tree is None: + raise FactoryError( + f"{source_label}: reviewed revision is unavailable for {repository}; " + "fetch the immutable reviewed object without substituting a default branch" + ) + if reviewed_tree != entry["reviewed_tree_sha"]: + raise FactoryError( + f"{source_label}: reviewed tree mismatch for {repository}: " + f"expected={entry['reviewed_tree_sha']} actual={reviewed_tree}" + ) + reviewed_blob = hoxline_case_growth_git_blob( + repository_root, + reviewed_revision, + authority_path, + ) + content_blob = hoxline_case_growth_git_blob( + repository_root, + content_revision, + authority_path, + ) + if reviewed_blob is None or content_blob is None: + raise FactoryError( + f"{source_label}: authority object is unavailable for {repository}; " + "fetch the immutable reviewed and authority-content objects" + ) + reviewed_semantic = hoxline_case_growth_semantic_fingerprint( + authority_path, + reviewed_blob[1], + ) + content_semantic = hoxline_case_growth_semantic_fingerprint( + authority_path, + content_blob[1], + ) + if ( + reviewed_blob[0] != content_blob[0] + or reviewed_semantic != content_semantic + ): + raise FactoryError( + f"{source_label}: reviewed and authority-content identities disagree " + f"for {repository}" + ) + projection[repository] = { + "repository": repository, + "canonical_repository": entry["canonical_repository"], + "authority_path": authority_path, + "authority_git_blob_sha": reviewed_blob[0], + "authority_content_fingerprint": reviewed_semantic, + } + return projection + + +def hoxline_case_growth_pinned_review_manifest( + org_root: Path, + source_manifest: dict[str, Any], +) -> dict[str, dict[str, Any]]: + command_center = source_manifest["repositories"][".github"] + repository_root = org_root / ".github" + revision = command_center["revision"] + authority_path = command_center["authoritative_path"] + expected_blob_sha = command_center["authoritative_git_blob_sha"] + pinned_blob = hoxline_case_growth_git_blob( + repository_root, + revision, + authority_path, + ) + if pinned_blob is None: + raise FactoryError( + "pinned command-center manifest object is unavailable; fetch immutable " + f"HawkinsOperations/.github object {revision} without default-branch fallback" + ) + if pinned_blob[0] != expected_blob_sha: + raise FactoryError( + "pinned command-center manifest blob mismatch: " + f"expected={expected_blob_sha} actual={pinned_blob[0]}" + ) + manifest = hoxline_case_growth_load_json_bytes( + pinned_blob[1], + f"HawkinsOperations/.github@{revision}:{authority_path}", + ) + return hoxline_case_growth_validate_review_manifest( + manifest, + f"HawkinsOperations/.github@{revision}:{authority_path}", + ) + + +def hoxline_case_growth_normalize_authority_key(value: str) -> str: + decoded = value + for _ in range(4): + next_value = unquote(decoded) + if next_value == decoded: + break + decoded = next_value + return re.sub( + r"[^a-z0-9]", + "", + unicodedata.normalize("NFKC", decoded).casefold(), + ) + + +def hoxline_case_growth_compositional_promotion_key(key: str) -> bool: + if any( + marker in key + for marker in ( + "blockedclaim", + "doesnot", + "mustnot", + "notclaim", + "notpromot", + ) + ): + return False + return ( + ("production" in key and any(part in key for part in ("active", "live", "ready", "deploy", "state", "status"))) + or (any(part in key for part in ("customer", "socaas")) and any(part in key for part in ("active", "deploy", "state", "status"))) + or ("runtime" in key and any(part in key for part in ("active", "state", "status"))) + or ("signal" in key and any(part in key for part in ("observed", "state", "status"))) + or ( + "publicsafe" in key + and "count" not in key + and "percent" not in key + ) + or ("final" in key and any(part in key for part in ("authoriz", "authority"))) + or ("case" in key and "count" not in key and any(part in key for part in ("closed", "closure", "state", "status"))) + or any(part in key for part in ("approvalstate", "approvalstatus", "closurestatus", "casestate", "casestatus")) + or ( + key.startswith(("ai", "analyst")) + and any(part in key for part in ("approved", "approval", "authority", "disposition")) + ) + or ("review" in key and "disposition" in key) + ) + + +def hoxline_case_growth_explicitly_bounded_authority_value(value: Any) -> bool: + if isinstance(value, list) and len(value) == 1: + return hoxline_case_growth_explicitly_bounded_authority_value(value[0]) + if value is False or value is None or value == 0: + return True + if not isinstance(value, str): + return False + return hoxline_case_growth_normalize_authority_key(value) in { + "blocked", + "boundarydefaultnotpromoted", + "blockedwaitingnextgate", + "false", + "humanreviewrequired", + "missing", + "none", + "notapproved", + "notauthorized", + "notclosed", + "notindexed", + "notproven", + "notpublicsafe", + "notruntimeactive", + "open", + "partial", + "pending", + "existingflowcandidate", + "listedonly", + "privateruntimeboundarycontextonly", + "privateruntimeevidencecaptured", + "privateruntimeevidencecapturedlocalwindowsonly", + "privateruntimecandidate", + "publicsaferuntimeproofisnotpromotedbythiswebsitedataplane", + "publicruntimeblocked", + "runtimeactiveprivate", + "runtimeblocked", + "runtimeevidenceverifiedprivate", + "signalblocked", + "signalobservedprivate", + "sourceexists", + "telemetrycontractonly", + "unknown", + "unsupported", + } + + +def hoxline_case_growth_bounded_negative_prose(value: str) -> bool: + normalized = hoxline_case_growth_security_text(value) + negative_scope = re.search( + r"\b(?:does|do|did|must|is|are|was|were|can|cannot|could|should|will|would)\s+not\b" + r"|\bnever\b|\bwithout\b|\bblocked\b|\bmissing\b|\bunsupported\b", + normalized, + re.IGNORECASE, + ) + return ( + negative_scope is not None + and not hoxline_case_growth_string_promotions(normalized) + ) + + +def hoxline_case_growth_authority_violations( + value: Any, + path: tuple[str, ...] = (), + authority_context: str | None = None, +) -> list[tuple[str, Any]]: + violations: list[tuple[str, Any]] = [] + if isinstance(value, dict): + for key, nested in value.items(): + child_path = (*path, str(key)) + normalized = hoxline_case_growth_normalize_authority_key(str(key)) + normalized_ancestry = tuple( + hoxline_case_growth_normalize_authority_key(part) + for part in path + if not str(part).isdigit() + ) + cumulative_keys = {normalized} + cumulative_keys.update( + f"{segment}{normalized}" + for segment in normalized_ancestry + if segment + in { + "runtime", + "signal", + "public", + "approval", + "production", + "customer", + "socaas", + "ai", + "analyst", + "review", + "final", + "case", + } + ) + scalar_authority_state = not isinstance(nested, (dict, list)) + compositional_context = any( + hoxline_case_growth_compositional_promotion_key(candidate) + for candidate in cumulative_keys + ) + exact_public_safe_owner_pointer = ( + tuple( + hoxline_case_growth_normalize_authority_key(part) + for part in child_path + ) + == ("publicsafepolicy", "publicsafesourcerequired") + and nested == "hawkinsoperations-proof" + ) + explicitly_bounded_nested = ( + hoxline_case_growth_explicitly_bounded_authority_value(nested) + or exact_public_safe_owner_pointer + or ( + isinstance(nested, str) + and hoxline_case_growth_bounded_negative_prose(nested) + ) + ) + if ( + ( + scalar_authority_state + and ( + normalized in HOXLINE_BLOCKED_AUTHORITY_KEYS + or bool(authority_context) + or compositional_context + ) + ) + and not explicitly_bounded_nested + ): + violations.append(("/".join(child_path), nested)) + if ( + normalized in HOXLINE_BLOCKED_AUTHORITY_KEYS + and scalar_authority_state + and nested not in ( + False, + None, + "NOT_PUBLIC_SAFE", + "BLOCKED", + "UNKNOWN", + ) + ): + violations.append(("/".join(child_path), nested)) + if ( + normalized in {"publicsafestatus", "publicsafestate"} + and scalar_authority_state + and nested not in ( + "NOT_PUBLIC_SAFE", + "BLOCKED", + "blocked", + False, + ) + ): + violations.append(("/".join(child_path), nested)) + child_authority_context = ( + authority_context if isinstance(nested, (dict, list)) else None + ) + if ( + isinstance(nested, (dict, list)) + and normalized + not in { + "publicsafecandidatereviews", + "publicsafepolicy", + "publicsafestate", + } + and ( + normalized + in HOXLINE_BLOCKED_AUTHORITY_KEYS + | {"publicsafestatus", "publicsafestate"} + or compositional_context + ) + ): + child_authority_context = normalized + violations.extend( + hoxline_case_growth_authority_violations( + nested, + child_path, + child_authority_context, + ) + ) + elif isinstance(value, list): + for index, nested in enumerate(value): + violations.extend( + hoxline_case_growth_authority_violations( + nested, + (*path, str(index)), + authority_context, + ) + ) + elif isinstance(value, str): + if ( + authority_context + and not hoxline_case_growth_explicitly_bounded_authority_value(value) + and not hoxline_case_growth_bounded_negative_prose(value) + ): + violations.append(("/".join(path), value)) + exact_blocked_leaf = ( + len(path) >= 2 + and path[-1].isdigit() + and re.sub( + r"[^a-z0-9]", + "", + unicodedata.normalize("NFKC", path[-2]).casefold(), + ) + in {"blockedclaims", "notclaiming", "doesnotprove"} + and re.search( + r"\b(?:is|was|has|enabled|granted|received)\b", + value, + re.IGNORECASE, + ) + is None + ) + bounded_private_state = value in { + "SIGNAL_OBSERVED_PRIVATE", + "RUNTIME_ACTIVE_PRIVATE", + } + if ( + not exact_blocked_leaf + and not bounded_private_state + and hoxline_case_growth_string_promotions(value) + ): + violations.append(("/".join(path), value)) + elif ( + authority_context + and value not in (False, None) + ): + violations.append(("/".join(path), value)) + unique: list[tuple[str, Any]] = [] + seen: set[tuple[str, str]] = set() + for violation_path, violation_value in violations: + signature = ( + violation_path, + json.dumps( + violation_value, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + default=str, + ), + ) + if signature not in seen: + seen.add(signature) + unique.append((violation_path, violation_value)) + return unique + + +def hoxline_case_growth_commit_exists(repo_path: Path, commit_sha: str) -> bool: + result = subprocess.run( + ["git", "-C", str(repo_path), "cat-file", "-e", f"{commit_sha}^{{commit}}"], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + return result.returncode == 0 + + +def hoxline_case_growth_is_direct_parent(repo_path: Path, commit_sha: str, head_sha: str) -> bool: + result = subprocess.run( + ["git", "-C", str(repo_path), "rev-parse", f"{head_sha}^"], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + return result.returncode == 0 and result.stdout.strip().casefold() == commit_sha.casefold() + + +def hoxline_case_growth_is_ancestor( + repo_path: Path, ancestor_sha: str, descendant_sha: str +) -> bool: + result = subprocess.run( + [ + "git", + "-C", + str(repo_path), + "merge-base", + "--is-ancestor", + ancestor_sha, + descendant_sha, + ], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + return result.returncode == 0 + + +def hoxline_case_growth_tree_sha(repo_path: Path, revision: str) -> str | None: + result = subprocess.run( + ["git", "-C", str(repo_path), "rev-parse", f"{revision}^{{tree}}"], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + value = result.stdout.strip().casefold() + return value if result.returncode == 0 and re.fullmatch(r"[0-9a-f]{40}", value) else None + + +def hoxline_case_growth_changed_paths( + repo_path: Path, older_revision: str, newer_revision: str +) -> frozenset[str] | None: + result = subprocess.run( + [ + "git", + "-C", + str(repo_path), + "diff", + "--name-only", + "--no-renames", + older_revision, + newer_revision, + "--", + ], + check=False, + capture_output=True, + text=True, + env=sanitized_git_env(), + ) + if result.returncode != 0: + return None + paths = [line.strip().replace("\\", "/") for line in result.stdout.splitlines()] + if any(not path or path.startswith("/") or ".." in path.split("/") for path in paths): + return None + return frozenset(paths) + + +def hoxline_case_growth_source_revisions(snapshot: dict[str, Any]) -> dict[str, dict[str, Any]]: + raw = snapshot.get("source_revisions") + normalized: dict[str, dict[str, Any]] = {} + if isinstance(raw, dict): + iterable = [] + for repo_name, value in raw.items(): + item = dict(value) if isinstance(value, dict) else {"source_commit_sha": value} + item.setdefault("repo", repo_name) + iterable.append(item) + elif isinstance(raw, list): + iterable = raw + else: + return normalized + for value in iterable: + if not isinstance(value, dict): + raise FactoryError("source_revisions entries must be objects") + repo_name = value.get("repo") or value.get("repository") or value.get("name") + if not isinstance(repo_name, str) or not repo_name: + raise FactoryError("source_revisions entries must name their repository") + if repo_name in normalized: + raise FactoryError(f"source_revisions duplicates repository: {repo_name}") + normalized[repo_name] = value + return normalized + + +def hoxline_case_growth_snapshot_flags(snapshot: dict[str, Any]) -> tuple[bool | None, bool | None]: + state = snapshot.get("snapshot_state") if isinstance(snapshot.get("snapshot_state"), dict) else {} + historical = snapshot.get("historical_snapshot", state.get("historical_snapshot")) + current = snapshot.get("current_authority", state.get("current_authority")) + return historical if isinstance(historical, bool) else None, current if isinstance(current, bool) else None + + +def hoxline_case_growth_parse_time(value: Any) -> datetime | None: + if not isinstance(value, str) or not value: + return None + try: + parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError: + return None + return parsed if parsed.tzinfo is not None else None + + +def hoxline_case_growth_convergence_verify( + repo_root: Path, *, now: datetime | None = None +) -> dict[str, Any]: + org_root = hoxline_case_growth_org_root(repo_root) + now_utc = (now or datetime.now(timezone.utc)).astimezone(timezone.utc) + contradictions: list[dict[str, Any]] = [] + drift: list[dict[str, Any]] = [] + sources: dict[str, Any] = {} + source_manifest: dict[str, Any] = {} + + def issue( + code: str, + owner: str, + path: str, + expected: Any, + actual: Any, + next_legal_action: str, + *, + revision: str | None = None, + historical: bool = False, + ) -> None: + item = { + "code": code, + "owner": owner, + "repo": owner, + "path": path, + "revision": revision or "UNKNOWN", + "current_head": revision or sources.get(owner, {}).get("head", "UNKNOWN"), + "expected": expected, + "actual": actual, + "expected_blob": expected if "BLOB" in code else None, + "actual_blob": actual if "BLOB" in code else None, + "expected_count": expected if "COUNT" in code else None, + "actual_count": actual if "COUNT" in code else None, + "freshness": "invalid" if any( + marker in code for marker in ("FRESH", "STALE", "FUTURE") + ) else "not_applicable", + "classification": "expected_historical_context" if historical else "actionable_drift", + "next_legal_action": next_legal_action, + "exact_remediation": next_legal_action, + } + (drift if historical else contradictions).append(item) + + paths = { + "hoxline_snapshot": org_root / "hoxline" / "examples" / "case-growth" / "current-case-growth-index.json", + "proof_index": org_root / "hawkinsoperations-proof" / "proof" / "indexes" / "DETECTION_PROOF_STATUS_INDEX.yml", + "detection_matrix": org_root / "hawkinsoperations-detections" / "detections" / "DETECTION_PROMOTION_MATRIX.yml", + "validation_registry": org_root / "hawkinsoperations-validation" / "validation" / "VALIDATION_REGISTRY.yml", + "website_status": org_root / "hawkinsoperations-website" / "public" / "data" / "public-status.json", + "platform_contract": org_root / "hawkinsoperations-platform" / "contracts" / "public-status-source-contract-v1.json", + } + repository_dirs = sorted( + path.name + for path in org_root.iterdir() + if path.is_dir() and (path / ".git").exists() + ) + expected_repository_dirs = sorted(HOXLINE_CASE_GROWTH_REPOS) + if repository_dirs != expected_repository_dirs: + issue( + "SEVEN_SOURCE_REVERSE_INVENTORY_MISMATCH", + "hawkinsoperations-platform", + str(org_root), + expected_repository_dirs, + repository_dirs, + "Provide exactly the seven canonical repository checkouts; do not substitute or omit a source.", + ) + for label, source_path in paths.items(): + if not source_path.is_file(): + issue( + "MISSING_SOURCE", + source_path.parts[-3] if len(source_path.parts) >= 3 else label, + label, + "existing authoritative source", + "missing", + f"Restore the declared {label} source through its owning repository; do not invent a replacement.", + ) + if contradictions: + return { + "controller_version": CONTROLLER_VERSION, + "mode": "hoxline-case-growth-convergence-verify", + "status": "fail", + "contradictions": contradictions, + "drift": drift, + "sources": sources, + "read_only": True, + "ledger_mutated": False, + "runtime_mutated": False, + "signal_mutated": False, + "public_proof_promoted": False, + "evidence_published": False, + "proof_ceiling": "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY", + } + + try: + source_manifest = hoxline_case_growth_source_manifest(org_root) + review_manifest_entries = hoxline_case_growth_review_manifest(org_root) + pinned_review_manifest_entries = hoxline_case_growth_pinned_review_manifest( + org_root, + source_manifest, + ) + current_authority_projection = ( + hoxline_case_growth_review_authority_projection( + org_root, + review_manifest_entries, + "checked command-center manifest", + ) + ) + pinned_authority_projection = ( + hoxline_case_growth_review_authority_projection( + org_root, + pinned_review_manifest_entries, + "pinned command-center manifest", + ) + ) + if current_authority_projection != pinned_authority_projection: + raise FactoryError( + "pinned and checked command-center authority projections disagree; " + "review the changed authority blobs and refresh the immutable source " + "selection without copying consumer or cyclic identity fields" + ) + snapshot = hoxline_case_growth_load_json(paths["hoxline_snapshot"]) + proof_index = hoxline_case_growth_load_yaml(paths["proof_index"]) + detection_matrix = hoxline_case_growth_load_yaml(paths["detection_matrix"]) + validation_registry = hoxline_case_growth_load_yaml(paths["validation_registry"]) + website_status = hoxline_case_growth_load_json(paths["website_status"]) + platform_contract = hoxline_case_growth_load_json(paths["platform_contract"]) + except FactoryError as exc: + issue( + "MALFORMED_SOURCE", + "cross-repo", + "declared_authority_source", + "parseable JSON/YAML", + str(exc), + "Repair the malformed source in its owner repository and rerun this read-only verifier.", + ) + snapshot = proof_index = detection_matrix = validation_registry = website_status = platform_contract = {} + review_manifest_entries = {} + manifest_entries = ( + source_manifest.get("repositories", {}) + if isinstance(source_manifest, dict) + else {} + ) + for label, parsed_source, owner in ( + ("hoxline_snapshot", snapshot, "hoxline"), + ("proof_index", proof_index, "hawkinsoperations-proof"), + ("detection_matrix", detection_matrix, "hawkinsoperations-detections"), + ("validation_registry", validation_registry, "hawkinsoperations-validation"), + ("website_status", website_status, "hawkinsoperations-website"), + ("platform_contract", platform_contract, "hawkinsoperations-platform"), + ): + for violation_path, actual in hoxline_case_growth_authority_violations(parsed_source): + issue( + "NESTED_AUTHORITY_PROMOTION", + owner, + f"{label}#/{violation_path}", + "blocked or false authority state", + actual, + f"Remove the unauthorized nested authority field from the {owner}-owned source and add a regression fixture.", + ) + + historical, current_authority = hoxline_case_growth_snapshot_flags(snapshot if isinstance(snapshot, dict) else {}) + if historical is not False or current_authority is not True: + issue( + "SNAPSHOT_CLASSIFICATION_INVALID", + "hoxline", + "examples/case-growth/current-case-growth-index.json", + {"historical_snapshot": False, "current_authority": True}, + {"historical_snapshot": historical, "current_authority": current_authority}, + "Regenerate the current snapshot from all seven current authority sources or explicitly route it as historical.", + ) + + try: + revisions = hoxline_case_growth_source_revisions(snapshot if isinstance(snapshot, dict) else {}) + except FactoryError as exc: + issue( + "SOURCE_REVISION_SET_INVALID", + "hoxline", + "examples/case-growth/current-case-growth-index.json#/source_revisions", + list(HOXLINE_CASE_GROWTH_REPOS), + str(exc), + "Regenerate source_revisions from exactly one entry for each of the seven scoped repositories.", + ) + revisions = {} + missing_revisions = sorted(set(HOXLINE_CASE_GROWTH_REPOS) - set(revisions)) + extra_revisions = sorted(set(revisions) - set(HOXLINE_CASE_GROWTH_REPOS)) + if missing_revisions or extra_revisions: + issue( + "SOURCE_REVISION_SET_INVALID", + "hoxline", + "examples/case-growth/current-case-growth-index.json#/source_revisions", + list(HOXLINE_CASE_GROWTH_REPOS), + {"missing": missing_revisions, "extra": extra_revisions}, + "Regenerate source_revisions from exactly the seven scoped repositories.", + ) + for repo_name in HOXLINE_CASE_GROWTH_REPOS: + repo_path = org_root / repo_name + if not repo_path.is_dir(): + continue + try: + state = hoxline_case_growth_git_state(repo_path) + except FactoryError as exc: + issue("GIT_STATE_UNRESOLVED", repo_name, repo_name, "resolvable branch and HEAD", str(exc), "Repair local Git metadata and rerun.") + continue + sources[repo_name] = state + branch = str(state.get("branch", "")) + manifest_entry = manifest_entries.get(repo_name, {}) + review_manifest_entry = review_manifest_entries.get(repo_name, {}) + current_observation_revision = ( + ( + os.environ.get("HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA") + if not branch + else state["head"] + ) + if manifest_entry.get("revision_source") + == "checked_platform_observation" + else None + ) + manifest_revision = ( + state["head"] + if repo_name == ".github" + else review_manifest_entry.get("revision") + ) + manifest_reviewed_tree = ( + hoxline_case_growth_tree_sha(repo_path, state["head"]) + if repo_name == ".github" + else review_manifest_entry.get("reviewed_tree_sha") + ) + manifest_content_revision = review_manifest_entry.get( + "authority_content_revision" + ) + if ( + manifest_entry.get("revision_source") + == "checked_platform_observation" + and not branch + and current_observation_revision != state["head"] + ): + issue( + "SOURCE_PLATFORM_OBSERVATION_MISMATCH", + repo_name, + "HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA", + state["head"], + current_observation_revision, + ( + "Supply the exact detached platform checkout SHA through the " + "read-only convergence environment; another repository event " + "SHA cannot establish platform currentness." + ), + revision=state["head"], + ) + expected_origin = HOXLINE_CANONICAL_ORIGINS[repo_name] + actual_origin = hoxline_case_growth_normalized_origin(str(state.get("origin", ""))) + if actual_origin != expected_origin: + issue( + "SOURCE_REPOSITORY_IDENTITY_INVALID", + repo_name, + repo_name, + expected_origin, + actual_origin, + f"Use the canonical HawkinsOperations/{repo_name} checkout; repository-name suffixes are not authority.", + revision=state["head"], + ) + if state.get("dirty") is True: + issue( + "SOURCE_WORKTREE_DIRTY", + repo_name, + repo_name, + "clean source-controlled state", + "dirty worktree", + f"Finish and validate scoped {repo_name} changes, commit them, then regenerate the cross-repo snapshot.", + revision=state["head"], + ) + revision = revisions.get(repo_name, {}) + expected_source_path = HOXLINE_CASE_GROWTH_AUTHORITY_PATHS[repo_name] + stated_source_path = revision.get("authoritative_path") or revision.get("source_path") + if stated_source_path != expected_source_path: + issue( + "SOURCE_AUTHORITY_PATH_INVALID", + repo_name, + f"source_revisions/{repo_name}/authoritative_path", + expected_source_path, + stated_source_path, + f"Regenerate the source identity from the canonical {repo_name} authority path.", + revision=state["head"], + ) + continue + current_blob = hoxline_case_growth_git_blob(repo_path, "HEAD", expected_source_path) + if current_blob is None: + issue( + "SOURCE_AUTHORITY_BLOB_MISSING", + repo_name, + expected_source_path, + "blob at checked current tree", + "missing", + f"Restore the canonical {repo_name} authority file in the checked current tree.", + revision=state["head"], + ) + continue + current_blob_sha, current_blob_bytes = current_blob + try: + current_semantic_fingerprint = hoxline_case_growth_semantic_fingerprint( + expected_source_path, current_blob_bytes + ) + except FactoryError as exc: + issue( + "SOURCE_AUTHORITY_SEMANTIC_INVALID", + repo_name, + expected_source_path, + "canonicalizable authoritative content", + str(exc), + f"Repair the canonical {repo_name} source content and regenerate its source identity.", + revision=state["head"], + ) + continue + stated_blob_sha = revision.get("authoritative_git_blob_sha") or revision.get("source_git_blob_sha") + stated_semantic_fingerprint = ( + revision.get("authoritative_content_fingerprint") + or revision.get("source_semantic_fingerprint_sha256") + ) + sources[repo_name].update( + { + "authority_path": expected_source_path, + "authoritative_git_blob_sha": current_blob_sha, + "authoritative_content_fingerprint": current_semantic_fingerprint, + "manifest_selected_reviewed_revision": manifest_revision, + "manifest_selected_reviewed_tree": manifest_reviewed_tree, + "manifest_selected_authority_content_revision": ( + manifest_content_revision + ), + "current_event_observation_revision": current_observation_revision, + } + ) + content_revision_matches_current = False + if not isinstance( + manifest_content_revision, str + ) or not hoxline_case_growth_commit_exists( + repo_path, manifest_content_revision + ): + issue( + "SOURCE_AUTHORITY_CONTENT_REVISION_UNREACHABLE", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + "reachable immutable authority content revision", + manifest_content_revision, + ( + f"Fetch the complete reviewed {repo_name} history and select the " + "immutable commit carrying the current authority content." + ), + revision=state["head"], + ) + else: + content_blob = hoxline_case_growth_git_blob( + repo_path, + manifest_content_revision, + expected_source_path, + ) + content_semantic_fingerprint: str | None = None + if content_blob is not None: + try: + content_semantic_fingerprint = ( + hoxline_case_growth_semantic_fingerprint( + expected_source_path, content_blob[1] + ) + ) + except FactoryError: + content_semantic_fingerprint = None + content_identity_matches = ( + content_blob is not None + and content_blob[0] == current_blob_sha + and content_semantic_fingerprint + == current_semantic_fingerprint + ) + if not content_identity_matches: + issue( + "SOURCE_AUTHORITY_CONTENT_REVISION_STALE", + repo_name, + expected_source_path, + current_blob_sha, + None if content_blob is None else content_blob[0], + ( + f"Review the changed {repo_name} authority content and refresh " + "authority_content_revision without changing the live observation." + ), + revision=state["head"], + ) + current_is_content_ancestor = hoxline_case_growth_is_ancestor( + repo_path, state["head"], manifest_content_revision + ) + content_is_current_ancestor = hoxline_case_growth_is_ancestor( + repo_path, manifest_content_revision, state["head"] + ) + current_tree_for_content = hoxline_case_growth_tree_sha( + repo_path, state["head"] + ) + content_tree = hoxline_case_growth_tree_sha( + repo_path, manifest_content_revision + ) + content_tree_is_current_equivalent = ( + current_tree_for_content is not None + and content_tree is not None + and current_tree_for_content == content_tree + ) + reviewed_revision_exists_for_content = ( + isinstance(manifest_revision, str) + and hoxline_case_growth_commit_exists( + repo_path, manifest_revision + ) + ) + content_is_reviewed_ancestor = ( + reviewed_revision_exists_for_content + and ( + manifest_content_revision == manifest_revision + or hoxline_case_growth_is_ancestor( + repo_path, + manifest_content_revision, + manifest_revision, + ) + ) + ) + reviewed_tree_for_content = ( + hoxline_case_growth_tree_sha(repo_path, manifest_revision) + if reviewed_revision_exists_for_content + else None + ) + current_is_exact_reviewed_tree = ( + current_tree_for_content is not None + and reviewed_tree_for_content is not None + and current_tree_for_content == reviewed_tree_for_content + and reviewed_tree_for_content == manifest_reviewed_tree + ) + rewritten_reviewed_projection = ( + manifest_content_revision != state["head"] + and not current_is_content_ancestor + and not content_is_current_ancestor + and ( + content_is_reviewed_ancestor + or repo_name == ".github" + ) + and current_is_exact_reviewed_tree + ) + content_relationship_valid = ( + not ( + manifest_content_revision != state["head"] + and current_is_content_ancestor + ) + and ( + manifest_content_revision == state["head"] + or content_is_current_ancestor + or content_tree_is_current_equivalent + or rewritten_reviewed_projection + ) + ) + if not content_relationship_valid: + issue( + "SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + ( + "authority content revision equal to or ancestral to current, " + "or carrying the exact current repository tree" + ), + { + "current_head": state["head"], + "authority_content_revision": manifest_content_revision, + "current_is_content_ancestor": current_is_content_ancestor, + "content_is_current_ancestor": content_is_current_ancestor, + "current_tree": current_tree_for_content, + "content_tree": content_tree, + "reviewed_revision": manifest_revision, + "reviewed_tree": reviewed_tree_for_content, + "manifest_reviewed_tree": manifest_reviewed_tree, + "content_is_reviewed_ancestor": ( + content_is_reviewed_ancestor + ), + "current_is_exact_reviewed_tree": ( + current_is_exact_reviewed_tree + ), + }, + ( + f"Select reviewed {repo_name} authority content from the current " + "lineage; a future descendant cannot establish current authority." + ), + revision=state["head"], + ) + content_revision_matches_current = ( + content_identity_matches and content_relationship_valid + ) + manifest_content_matches_current = False + if ( + isinstance(manifest_revision, str) + and manifest_revision != state["head"] + ): + if not hoxline_case_growth_commit_exists(repo_path, manifest_revision): + issue( + "SOURCE_MANIFEST_REVISION_UNREACHABLE", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + "reachable immutable revision in canonical repository", + manifest_revision, + f"Refresh the source manifest with a reviewed reachable {repo_name} revision.", + revision=state["head"], + ) + else: + manifest_blob = hoxline_case_growth_git_blob( + repo_path, manifest_revision, expected_source_path + ) + manifest_semantic_fingerprint: str | None = None + if manifest_blob is not None: + try: + manifest_semantic_fingerprint = ( + hoxline_case_growth_semantic_fingerprint( + expected_source_path, manifest_blob[1] + ) + ) + except FactoryError: + manifest_semantic_fingerprint = None + manifest_content_matches_current = ( + manifest_blob is not None + and manifest_blob[0] == current_blob_sha + and manifest_semantic_fingerprint + == current_semantic_fingerprint + ) + if not manifest_content_matches_current: + issue( + "SOURCE_MANIFEST_CONTENT_STALE", + repo_name, + expected_source_path, + current_blob_sha, + None if manifest_blob is None else manifest_blob[0], + f"Review the changed {repo_name} authority content and refresh the immutable source manifest.", + revision=state["head"], + ) + else: + current_is_historical_ancestor = hoxline_case_growth_is_ancestor( + repo_path, state["head"], manifest_revision + ) + reviewed_is_ancestor = hoxline_case_growth_is_ancestor( + repo_path, manifest_revision, state["head"] + ) + current_tree = hoxline_case_growth_tree_sha( + repo_path, state["head"] + ) + reviewed_tree = hoxline_case_growth_tree_sha( + repo_path, manifest_revision + ) + reviewed_tree_is_manifest_selected = ( + reviewed_tree is not None + and reviewed_tree == manifest_reviewed_tree + ) + if not reviewed_tree_is_manifest_selected: + manifest_content_matches_current = False + issue( + "SOURCE_REVIEW_MANIFEST_TREE_MISMATCH", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + manifest_reviewed_tree, + reviewed_tree, + ( + f"Refresh the command-center manifest only after reviewing " + f"the exact {repo_name} PR-head tree." + ), + revision=state["head"], + ) + tree_is_reviewed_equivalent = ( + current_tree is not None + and reviewed_tree is not None + and current_tree == reviewed_tree + ) + observation_relationship_valid = ( + not current_is_historical_ancestor + and reviewed_tree_is_manifest_selected + and (reviewed_is_ancestor or tree_is_reviewed_equivalent) + ) + if not observation_relationship_valid: + manifest_content_matches_current = False + issue( + "SOURCE_MANIFEST_OBSERVATION_RELATIONSHIP_INVALID", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + ( + "checked current revision descended from the reviewed " + "revision or carrying its exact reviewed repository tree" + ), + { + "current_head": state["head"], + "reviewed_revision": manifest_revision, + "current_is_historical_ancestor": ( + current_is_historical_ancestor + ), + "reviewed_is_ancestor": reviewed_is_ancestor, + "current_tree": current_tree, + "reviewed_tree": reviewed_tree, + "manifest_reviewed_tree": manifest_reviewed_tree, + }, + ( + f"Check out the intended current {repo_name} revision; " + "an older same-blob ancestor is historical, not current authority." + ), + revision=state["head"], + ) + else: + issue( + "SOURCE_MANIFEST_HEAD_OBSERVATION_STALE_CONTENT_CURRENT", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + state["head"], + manifest_revision, + f"Refresh the observed {repo_name} manifest revision after merge; content identity remains current.", + revision=state["head"], + historical=True, + ) + elif manifest_revision == state["head"]: + current_tree = hoxline_case_growth_tree_sha(repo_path, state["head"]) + manifest_content_matches_current = ( + current_tree is not None + and current_tree == manifest_reviewed_tree + ) + if not manifest_content_matches_current: + issue( + "SOURCE_REVIEW_MANIFEST_TREE_MISMATCH", + repo_name, + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + manifest_reviewed_tree, + current_tree, + ( + f"Refresh the command-center manifest only after reviewing " + f"the exact {repo_name} PR-head tree." + ), + revision=state["head"], + ) + manifest_content_matches_current = ( + manifest_content_matches_current + and content_revision_matches_current + ) + detached_platform_observation_mismatch = ( + repo_name == "hawkinsoperations-platform" + and not branch + and current_observation_revision != state["head"] + ) + if ( + not branch + and ( + not manifest_content_matches_current + or detached_platform_observation_mismatch + ) + ): + issue( + "DETACHED_SOURCE_NOT_MANIFEST_SELECTED", + repo_name, + repo_name, + ( + "detached current tree whose canonical authority content " + "matches an exact manifest-selected reviewed revision" + ), + { + "current_head": state["head"], + "manifest_selected_reviewed_revision": manifest_revision, + "current_blob": current_blob_sha, + }, + ( + f"Supply the exact reviewed {repo_name} revision through the " + "immutable source manifest and verify that its authority blob and " + "semantic fingerprint equal the detached current tree." + ), + revision=state["head"], + ) + if stated_blob_sha != current_blob_sha: + issue( + "SOURCE_AUTHORITY_BLOB_DRIFT", + repo_name, + expected_source_path, + current_blob_sha, + stated_blob_sha, + f"Regenerate only the {repo_name} content identity after reviewing the authoritative file change.", + revision=state["head"], + ) + if stated_semantic_fingerprint != current_semantic_fingerprint: + issue( + "SOURCE_AUTHORITY_SEMANTIC_DRIFT", + repo_name, + expected_source_path, + current_semantic_fingerprint, + stated_semantic_fingerprint, + f"Regenerate the {repo_name} semantic fingerprint from canonical authoritative content.", + revision=state["head"], + ) + content_sha = ( + revision.get("source_commit_sha") + or revision.get("commit_sha") + or revision.get("source_revision") + ) + if ( + not isinstance(content_sha, str) + or re.fullmatch(r"[0-9a-fA-F]{40}", content_sha) is None + ): + issue( + "SOURCE_CONTENT_REVISION_INVALID", + "hoxline", + f"source_revisions/{repo_name}/source_commit_sha", + "40-character immutable authority-content revision", + content_sha, + ( + f"Regenerate the snapshot with the manifest-selected {repo_name} " + "authority-content revision." + ), + revision=state["head"], + ) + continue + if content_sha != manifest_content_revision: + issue( + "SOURCE_CONTENT_REVISION_NOT_MANIFEST_SELECTED", + repo_name, + f"source_revisions/{repo_name}/source_commit_sha", + manifest_content_revision, + content_sha, + ( + f"Record the separately reviewed {repo_name} authority-content " + "revision; a live-head observation cannot replace content identity." + ), + revision=state["head"], + ) + continue + stated_sha = revision.get("current_observed_head_sha") + if not isinstance(stated_sha, str) or re.fullmatch(r"[0-9a-fA-F]{40}", stated_sha) is None: + issue( + "SOURCE_REVISION_INVALID", + "hoxline", + f"source_revisions/{repo_name}/current_observed_head_sha", + "40-character current-head observation SHA", + stated_sha, + f"Regenerate the snapshot with the resolved {repo_name} current-head observation.", + revision=state["head"], + ) + continue + observed_commit_exists = hoxline_case_growth_commit_exists(repo_path, stated_sha) + if not observed_commit_exists: + issue( + "SOURCE_REVISION_UNRESOLVED", + repo_name, + f"source_revisions/{repo_name}/current_observed_head_sha", + "commit reachable in the declared repository", + stated_sha, + f"Record a reachable {repo_name} current-head observation selected by the source manifest and regenerate the snapshot.", + revision=state["head"], + ) + continue + selected_observations = {state["head"]} + if isinstance(manifest_revision, str) and re.fullmatch( + r"[0-9a-fA-F]{40}", manifest_revision + ): + selected_observations.add(manifest_revision) + stated_is_reviewed_identity = False + stated_relationship: dict[str, Any] | None = None + if stated_sha not in selected_observations: + current_is_historical_ancestor = hoxline_case_growth_is_ancestor( + repo_path, state["head"], stated_sha + ) + stated_is_ancestor = hoxline_case_growth_is_ancestor( + repo_path, stated_sha, state["head"] + ) + manifest_is_ancestor_of_stated = ( + isinstance(manifest_content_revision, str) + and ( + manifest_content_revision == stated_sha + or hoxline_case_growth_is_ancestor( + repo_path, manifest_content_revision, stated_sha + ) + ) + ) + current_tree = hoxline_case_growth_tree_sha(repo_path, state["head"]) + stated_tree = hoxline_case_growth_tree_sha(repo_path, stated_sha) + tree_is_reviewed_equivalent = ( + current_tree is not None + and stated_tree is not None + and current_tree == stated_tree + ) + manifest_tree_selects_stated = ( + stated_tree is not None + and stated_tree == manifest_reviewed_tree + ) + generated_pair_parent_selects_stated = ( + repo_name == "hoxline" + and isinstance(manifest_revision, str) + and hoxline_case_growth_is_direct_parent( + repo_path, + stated_sha, + manifest_revision, + ) + ) + projection_paths = HOXLINE_CASE_GROWTH_OBSERVATION_PROJECTION_PATHS.get( + repo_name + ) + observed_projection_paths = hoxline_case_growth_changed_paths( + repo_path, stated_sha, state["head"] + ) + reviewed_tree_projection_selects_stated = ( + projection_paths is not None + and observed_projection_paths == projection_paths + and current_tree is not None + and current_tree == manifest_reviewed_tree + and manifest_is_ancestor_of_stated + ) + reviewed_lineage_selects_stated = ( + manifest_is_ancestor_of_stated + or manifest_tree_selects_stated + or generated_pair_parent_selects_stated + or reviewed_tree_projection_selects_stated + ) + rewritten_current_is_exact_reviewed_tree = ( + current_tree is not None + and current_tree == manifest_reviewed_tree + and reviewed_lineage_selects_stated + and observed_projection_paths is not None + and expected_source_path not in observed_projection_paths + ) + stated_is_reviewed_identity = ( + not current_is_historical_ancestor + and reviewed_lineage_selects_stated + and ( + stated_is_ancestor + or tree_is_reviewed_equivalent + or reviewed_tree_projection_selects_stated + or rewritten_current_is_exact_reviewed_tree + ) + ) + stated_relationship = { + "current_head": state["head"], + "stated_reviewed_revision": stated_sha, + "current_is_historical_ancestor": current_is_historical_ancestor, + "stated_is_ancestor": stated_is_ancestor, + "manifest_is_ancestor_of_stated": ( + manifest_is_ancestor_of_stated + ), + "current_tree": current_tree, + "stated_tree": stated_tree, + "tree_is_reviewed_equivalent": tree_is_reviewed_equivalent, + "manifest_tree_selects_stated": manifest_tree_selects_stated, + "generated_pair_parent_selects_stated": ( + generated_pair_parent_selects_stated + ), + "reviewed_tree_projection_selects_stated": ( + reviewed_tree_projection_selects_stated + ), + "observed_projection_paths": ( + None + if observed_projection_paths is None + else sorted(observed_projection_paths) + ), + "reviewed_lineage_selects_stated": ( + reviewed_lineage_selects_stated + ), + "rewritten_current_is_exact_reviewed_tree": ( + rewritten_current_is_exact_reviewed_tree + ), + } + if ( + stated_sha not in selected_observations + and not stated_is_reviewed_identity + ): + issue( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + repo_name, + f"source_revisions/{repo_name}/current_observed_head_sha", + sorted(selected_observations), + stated_relationship or stated_sha, + ( + f"Record either the checked {repo_name} current head, the exact " + "command-center-reviewed immutable revision, or a snapshot-reviewed " + "revision inside the manifest-selected lineage that is an ancestor " + "of current or has the exact reviewed full-tree identity. A current " + "ancestor of the reviewed revision is historical, and an arbitrary " + "same-blob ancestor outside the selected lineage is rejected." + ), + revision=state["head"], + ) + continue + if observed_commit_exists: + observed_blob = hoxline_case_growth_git_blob(repo_path, stated_sha, expected_source_path) + if observed_blob is None or observed_blob[0] != current_blob_sha: + issue( + "SOURCE_OBSERVATION_CONTENT_MISMATCH", + repo_name, + expected_source_path, + current_blob_sha, + None if observed_blob is None else observed_blob[0], + f"Record a reviewed {repo_name} observation carrying the same authoritative content as the checked current tree.", + revision=state["head"], + ) + if stated_sha != state["head"]: + issue( + "SOURCE_HEAD_OBSERVATION_STALE_CONTENT_CURRENT", + repo_name, + f"source_revisions/{repo_name}/current_observed_head_sha", + state["head"], + stated_sha, + f"Refresh the observed {repo_name} head when the reviewer artifact is next regenerated; authoritative content remains current by blob identity.", + revision=state["head"], + historical=stated_blob_sha == current_blob_sha + and stated_semantic_fingerprint == current_semantic_fingerprint, + ) + + proof_entries = proof_index.get("entries") if isinstance(proof_index, dict) else None + if not isinstance(proof_entries, list): + proof_entries = [] + issue("PROOF_ENTRIES_INVALID", "hawkinsoperations-proof", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "entries list", type(proof_entries).__name__, "Repair the proof-owned index structure.") + proof_ids: set[str] = set() + record_paths: list[str] = [] + card_paths: list[str] = [] + for entry in proof_entries: + if not isinstance(entry, dict): + continue + case_id = entry.get("detection_id") + if not isinstance(case_id, str) or case_id in proof_ids: + issue("DUPLICATE_PROOF_CASE", "hawkinsoperations-proof", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "unique detection_id", case_id, "Remove the duplicate index entry through proof-owner review.") + continue + proof_ids.add(case_id) + if entry.get("public_safe_status") != "NOT_PUBLIC_SAFE": + issue("UNAUTHORIZED_PUBLIC_SAFE_STATUS", "hawkinsoperations-proof", f"proof index entry {case_id}", "NOT_PUBLIC_SAFE", entry.get("public_safe_status"), "Restore NOT_PUBLIC_SAFE unless separate proof authority approves promotion.") + for field, collection in (("proof_record_path", record_paths), ("proof_card_path", card_paths)): + value = entry.get(field) + if value is None: + continue + decoded_value = value if isinstance(value, str) else "" + for _ in range(4): + try: + next_value = unquote(decoded_value) + except (UnicodeDecodeError, ValueError): + next_value = decoded_value + if next_value == decoded_value: + break + decoded_value = next_value + normalized_value = decoded_value.replace("\\", "/") + path_parts = [part for part in normalized_value.split("/") if part not in {"", "."}] + if ( + not isinstance(value, str) + or re.match(r"^[A-Za-z]:[\\/]", decoded_value) + or decoded_value.startswith(("\\\\", "//")) + or normalized_value.startswith("/") + or "\x00" in decoded_value + or ("/" in decoded_value and "\\" in decoded_value) + or ".." in path_parts + or decoded_value != value + ): + issue("UNSAFE_PROOF_PATH", "hawkinsoperations-proof", f"{case_id}/{field}", "safe repository-relative path", value, "Replace with a proof-repo-relative owned path.") + continue + proof_root = (org_root / "hawkinsoperations-proof").resolve() + owned_root = (proof_root / "proof" / ("records" if field == "proof_record_path" else "cards")).resolve() + resolved_value = (proof_root / normalized_value).resolve() + try: + resolved_value.relative_to(owned_root) + except ValueError: + issue("UNSAFE_PROOF_PATH", "hawkinsoperations-proof", f"{case_id}/{field}", f"path under {owned_root.relative_to(proof_root).as_posix()}", value, "Restore the proof-owner path to its designated records or cards directory.") + continue + collection.append(str(resolved_value).replace("\\", "/").casefold()) + if not resolved_value.is_file(): + issue("DANGLING_PROOF_PATH", "hawkinsoperations-proof", value, "existing file", "missing", f"Restore or remove the dangling {field} through proof-owner review.") + if len(record_paths) != len(set(record_paths)): + issue("DUPLICATE_PROOF_RECORD_OWNERSHIP", "hawkinsoperations-proof", "proof index proof_record_path", "one case per path", record_paths, "Assign every proof record path to exactly one case.") + if len(card_paths) != len(set(card_paths)): + issue("DUPLICATE_PROOFCARD_OWNERSHIP", "hawkinsoperations-proof", "proof index proof_card_path", "one case per path", card_paths, "Assign every ProofCard path to exactly one case.") + proof_counts = { + "indexed_case_count": len(proof_entries), + "proof_record_count": len(record_paths), + "proof_card_count": len(card_paths), + "missing_proof_record_count": len(proof_entries) - len(record_paths), + "missing_proof_card_count": len(proof_entries) - len(card_paths), + } + declared_counts = ( + proof_index.get("current_authority", {}).get("derived_counts") + if isinstance(proof_index, dict) and isinstance(proof_index.get("current_authority"), dict) + else None + ) + if not isinstance(declared_counts, dict) or any(declared_counts.get(key) != value for key, value in proof_counts.items()): + issue("PROOF_COUNT_DRIFT", "hawkinsoperations-proof", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml#/current_authority/derived_counts", proof_counts, declared_counts, "Regenerate proof-owned counts from non-null unique index paths.") + + summary = snapshot.get("summary", {}) if isinstance(snapshot, dict) else {} + snapshot_records = summary.get("proof_records_count", summary.get("proof_record_count")) if isinstance(summary, dict) else None + snapshot_cards = summary.get("proofcards_count", summary.get("proof_card_count")) if isinstance(summary, dict) else None + if snapshot_records != proof_counts["proof_record_count"] or snapshot_cards != proof_counts["proof_card_count"]: + issue("HOXLINE_PROOF_COUNT_DRIFT", "hoxline", "examples/case-growth/current-case-growth-index.json#/summary", {"proof_record_count": proof_counts["proof_record_count"], "proof_card_count": proof_counts["proof_card_count"]}, {"proof_record_count": snapshot_records, "proof_card_count": snapshot_cards}, "Regenerate the Hoxline current snapshot from the proof-owned index.") + + detection_entries = detection_matrix.get("entries") if isinstance(detection_matrix, dict) else None + if not isinstance(detection_entries, list): + issue( + "DETECTION_ENTRIES_INVALID", + "hawkinsoperations-detections", + "detections/DETECTION_PROMOTION_MATRIX.yml#/entries", + "array of detection-owned entries", + type(detection_entries).__name__, + "Repair the detection-owned promotion matrix shape; do not infer entries from a malformed source.", + ) + detection_entries = [] + validation_packages = ( + validation_registry.get("packages") if isinstance(validation_registry, dict) else None + ) + if not isinstance(validation_packages, list): + issue( + "VALIDATION_PACKAGES_INVALID", + "hawkinsoperations-validation", + "validation/VALIDATION_REGISTRY.yml#/packages", + "array of validation-owned packages", + type(validation_packages).__name__, + "Repair the validation-owned registry shape; do not infer packages from a malformed source.", + ) + validation_packages = [] + detection_ids = {item.get("detection_id") for item in detection_entries if isinstance(item, dict)} + validation_ids = {item.get("detection_id") for item in validation_packages if isinstance(item, dict)} + for entry in proof_entries: + if not isinstance(entry, dict): + continue + case_id = entry.get("detection_id") + if case_id not in detection_ids: + issue("DETECTION_LINK_MISSING", "hawkinsoperations-detections", "detections/DETECTION_PROMOTION_MATRIX.yml", case_id, "missing", f"Add or correct the detection-owned source entry for {case_id}.") + if case_id not in validation_ids and entry.get("validation_status") != "VALIDATION_PLANNED": + issue("VALIDATION_LINK_MISSING", "hawkinsoperations-validation", "validation/VALIDATION_REGISTRY.yml", case_id, "missing", f"Add the owned validation package or lower {case_id} to VALIDATION_PLANNED.") + detection_entry = next( + (item for item in detection_entries if isinstance(item, dict) and item.get("detection_id") == case_id), + None, + ) + if entry.get("proof_record_path") and isinstance(detection_entry, dict): + detection_text = json.dumps(detection_entry, sort_keys=True).lower() + if "proof record creation" in detection_text or f"no {str(case_id).lower()} proof record" in detection_text: + issue( + "DETECTION_PROOF_HANDOFF_CONTRADICTION", + "hawkinsoperations-detections", + f"detections/DETECTION_PROMOTION_MATRIX.yml#/{case_id}", + "proof handoff consistent with proof-owned current index", + detection_entry.get("next_gate"), + "Update only the detection-owned handoff wording to acknowledge the proof-owned record without raising detection authority.", + ) + + website_metric = website_status.get("metrics", {}).get("proof_records", {}) if isinstance(website_status, dict) else {} + if website_metric.get("value") != proof_counts["proof_record_count"]: + issue("WEBSITE_PROOF_COUNT_DRIFT", "hawkinsoperations-website", "public/data/public-status.json#/metrics/proof_records", proof_counts["proof_record_count"], website_metric.get("value"), "Regenerate the website status plane from the proof-owned current index.") + website_source_repo = str(website_metric.get("source_repo", "")) + if website_source_repo != "HawkinsOperations/hawkinsoperations-proof": + issue("WEBSITE_PROOF_OWNER_INVALID", "hawkinsoperations-website", "public/data/public-status.json#/metrics/proof_records/source_repo", "HawkinsOperations/hawkinsoperations-proof", website_source_repo, "Restore the exact proof-owner identifier; website remains a consumer only.") + if website_metric.get("source_path") != "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml": + issue("WEBSITE_PROOF_SOURCE_INVALID", "hawkinsoperations-website", "public/data/public-status.json#/metrics/proof_records/source_path", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", website_metric.get("source_path"), "Regenerate from the current proof index instead of a historical summary.") + generated_at = hoxline_case_growth_parse_time(website_status.get("generated_at") if isinstance(website_status, dict) else None) + max_age_hours = website_status.get("freshness", {}).get("max_age_hours") if isinstance(website_status, dict) else None + if generated_at is None or not isinstance(max_age_hours, (int, float)): + issue("WEBSITE_FRESHNESS_UNRESOLVED", "hawkinsoperations-website", "public/data/public-status.json#/freshness", "parseable generated_at and max_age_hours", {"generated_at": website_status.get("generated_at"), "max_age_hours": max_age_hours}, "Regenerate freshness metadata from source-owned inputs.") + else: + age_hours = (now_utc - generated_at.astimezone(timezone.utc)).total_seconds() / 3600 + if age_hours < 0: + issue("WEBSITE_GENERATED_AT_FUTURE", "hawkinsoperations-website", "public/data/public-status.json#/generated_at", "timestamp not later than verifier time", website_status.get("generated_at"), "Regenerate the status artifact using a trustworthy UTC clock.") + expected_freshness = "stale" if age_hours > max_age_hours else "fresh" + if website_status.get("freshness", {}).get("status") != expected_freshness: + issue("WEBSITE_FRESHNESS_CONTRADICTION", "hawkinsoperations-website", "public/data/public-status.json#/freshness/status", expected_freshness, website_status.get("freshness", {}).get("status"), "Regenerate or relabel the website status using the declared freshness window.") + + contract_field = platform_contract.get("public_fields", {}).get("proof_record_count", {}) if isinstance(platform_contract, dict) else {} + proof_head = sources.get("hawkinsoperations-proof", {}).get("head") + proof_blob = sources.get("hawkinsoperations-proof", {}).get("authoritative_git_blob_sha") + proof_semantic = sources.get("hawkinsoperations-proof", {}).get("authoritative_content_fingerprint") + contract_expected = { + "current_value": proof_counts["proof_record_count"], + "source_path": "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "source_git_blob_sha": proof_blob, + "source_semantic_fingerprint_sha256": proof_semantic, + "historical_snapshot": False, + "current_authority": True, + } + contract_actual = {key: contract_field.get(key) for key in contract_expected} + if contract_actual != contract_expected: + issue("PLATFORM_PROOF_SOURCE_DRIFT", "hawkinsoperations-platform", "contracts/public-status-source-contract-v1.json#/public_fields/proof_record_count", contract_expected, contract_actual, "Refresh the platform source contract from the proof-owned current index without changing proof authority.", revision=proof_head) + contract_observed_head = contract_field.get("source_observed_head_sha") or contract_field.get("source_revision") + if contract_observed_head != proof_head: + issue( + "PLATFORM_PROOF_HEAD_OBSERVATION_STALE_CONTENT_CURRENT", + "hawkinsoperations-platform", + "contracts/public-status-source-contract-v1.json#/public_fields/proof_record_count/source_observed_head_sha", + proof_head, + contract_observed_head, + "Refresh the observed proof head after merge; do not change content identity when the authority blob is unchanged.", + revision=proof_head, + historical=contract_actual == contract_expected, + ) + contract_generated_at = hoxline_case_growth_parse_time( + platform_contract.get("generated_at") if isinstance(platform_contract, dict) else None + ) + contract_window_days = platform_contract.get("freshness_window_days") if isinstance(platform_contract, dict) else None + if contract_generated_at is None or not isinstance(contract_window_days, (int, float)): + issue( + "PLATFORM_CONTRACT_FRESHNESS_UNRESOLVED", + "hawkinsoperations-platform", + "contracts/public-status-source-contract-v1.json", + "parseable generated_at and freshness_window_days", + {"generated_at": platform_contract.get("generated_at"), "freshness_window_days": contract_window_days}, + "Refresh the platform contract from verified current sources and preserve its freshness window.", + ) + elif contract_generated_at.astimezone(timezone.utc) > now_utc: + issue( + "PLATFORM_CONTRACT_GENERATED_AT_FUTURE", + "hawkinsoperations-platform", + "contracts/public-status-source-contract-v1.json#/generated_at", + "timestamp not later than verifier time", + platform_contract.get("generated_at"), + "Refresh the contract using a trustworthy UTC clock.", + ) + elif (now_utc - contract_generated_at.astimezone(timezone.utc)).total_seconds() > contract_window_days * 86400: + issue( + "PLATFORM_CONTRACT_STALE", + "hawkinsoperations-platform", + "contracts/public-status-source-contract-v1.json#/generated_at", + f"age <= {contract_window_days} days", + platform_contract.get("generated_at"), + "Refresh the source contract from current owned sources; do not rewrite historical snapshots as current.", + ) + + return { + "controller_version": CONTROLLER_VERSION, + "mode": "hoxline-case-growth-convergence-verify", + "status": "pass" if not contradictions else "fail", + "source_revisions_verified": len(sources), + "sources": sources, + "proof_counts": proof_counts, + "contradictions": contradictions, + "drift": drift, + "read_only": True, + "ledger_mutated": False, + "runtime_mutated": False, + "signal_mutated": False, + "public_proof_promoted": False, + "evidence_published": False, + "website_is_proof": False, + "green_ci_is_approval": False, + "proof_ceiling": "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY", + } + +HOXLINE_ACTIONS_CHECKOUT_SHA = "11bd71901bbe5b1630ceea73d27597364c9af683" + + +def hoxline_workflow_job_condition(workflow: str, job_name: str) -> str: + """Extract one exact top-level job condition without substring matching.""" + lines = workflow.splitlines() + job_start = [ + index + for index, line in enumerate(lines) + if line == f" {job_name}:" + ] + if len(job_start) != 1: + raise FactoryError(f"workflow must define exactly one {job_name} job") + block: list[str] = [] + for line in lines[job_start[0] + 1 :]: + if re.fullmatch(r" [A-Za-z0-9_-]+:", line): + break + block.append(line) + conditions = [ + match.group(1).strip() + for line in block + if (match := re.fullmatch(r" if:\s*(.*?)\s*", line)) + ] + if len(conditions) != 1: + raise FactoryError(f"{job_name} must define exactly one job-level if condition") + return conditions[0] + + +def hoxline_workflow_has_shell_neutralizer(workflow: str) -> bool: + neutralizers = ( + r"\|\|\s*(?:true|:|exit\s+0)(?:\s*(?:#.*)?)?$", + r"(?:^|[;&]\s*)set\s+\+e(?:\s|$)", + r"\|\|\s*(?:echo|printf)\b", + r"trap\s+['\"].*['\"]\s+EXIT(?:\s|$)", + ) + return any( + re.search(pattern, workflow, flags=re.IGNORECASE | re.MULTILINE) + for pattern in neutralizers + ) + + +def hoxline_workflow_safety_verify(repo_root: Path) -> dict[str, Any]: + repo_root = repo_root.resolve() + tracked = subprocess.run( + [ + "git", + "-C", + str(repo_root), + "ls-tree", + "-r", + "--name-only", + "HEAD", + "--", + ".github/workflows", + ], + capture_output=True, + text=True, + check=False, + env=sanitized_git_env(), + ) + if tracked.returncode == 0: + workflow_paths = sorted( + line.strip() + for line in tracked.stdout.splitlines() + if line.strip().endswith(".yml") + ) + workflows = {} + for relative_path in workflow_paths: + committed = subprocess.run( + ["git", "-C", str(repo_root), "show", f"HEAD:{relative_path}"], + capture_output=True, + text=True, + check=False, + env=sanitized_git_env(), + ) + if committed.returncode != 0: + raise FactoryError( + f"Hoxline workflow safety cannot read committed workflow: {relative_path}" + ) + workflows[Path(relative_path).name] = committed.stdout + else: + workflow_dir = repo_root / ".github" / "workflows" + workflows = { + path.name: path.read_text(encoding="utf-8") + for path in sorted(workflow_dir.glob("*.yml")) + } + required = { + "hoxline-source-checks.yml", + "hoxline-trusted-runtime-verify.yml", + "hoxline-private-canary.yml", + "hoxline-schedule-gated-collection.yml", + } + missing = sorted(name for name in required if name not in workflows) + if missing: + raise FactoryError(f"Hoxline workflow safety missing workflows: {', '.join(missing)}") + for name, text in workflows.items(): + if "pull_request_target" in text: + raise FactoryError(f"pull_request_target is not allowed: {name}") + if re.search(r"(?m)^\s*continue-on-error\s*:", text): + raise FactoryError(f"continue-on-error is not allowed: {name}") + if hoxline_workflow_has_shell_neutralizer(text): + raise FactoryError(f"shell failure swallowing is not allowed: {name}") + checkout_refs = re.findall( + r"(?m)^\s*(?:-\s*)?uses:\s*actions/checkout@([^\s#]+)", + text, + ) + if any(ref != HOXLINE_ACTIONS_CHECKOUT_SHA for ref in checkout_refs): + raise FactoryError(f"actions/checkout must use the reviewed immutable SHA: {name}") + if "pull_request:" in text: + if "self-hosted" in text: + raise FactoryError(f"pull_request workflow cannot use self-hosted runner: {name}") + if "secrets." in text or "private-route" in text or "PRIVATE_ROUTE" in text: + raise FactoryError(f"pull_request workflow cannot access private runtime routes or secrets: {name}") + source = workflows["hoxline-source-checks.yml"] + if "pull_request:" not in source: + raise FactoryError("Hoxline source checks must run on pull_request") + if re.search(r"(?m)^\s*if\s*:", source) or re.search(r"(?m)^\s*needs\s*:", source): + raise FactoryError("Hoxline source checks cannot be conditional on another job") + if not re.search(r"(?ms)^permissions:\s*\n\s+contents:\s*read\s*$", source): + raise FactoryError("Hoxline source checks must use read-only contents permission") + checkout_paths = re.findall(r"(?m)^\s+path:\s*(source-set/[^\s#]+)\s*$", source) + expected_checkout_paths = { + "source-set/.github", + "source-set/hawkinsoperations-detections", + "source-set/hawkinsoperations-validation", + "source-set/hawkinsoperations-platform", + "source-set/hawkinsoperations-proof", + "source-set/hawkinsoperations-website", + "source-set/hoxline", + } + if set(checkout_paths) != expected_checkout_paths or len(checkout_paths) != 7: + raise FactoryError("Hoxline source checks must check out exactly seven collision-free repositories") + if ( + source.count(f"uses: actions/checkout@{HOXLINE_ACTIONS_CHECKOUT_SHA}") != 7 + or source.count("persist-credentials: false") != 7 + ): + raise FactoryError("Every Hoxline source checkout must disable persisted credentials") + required_source_commands = { + "hoxline-case-growth-convergence-verify", + "public-status-source-contract-verify", + "hoxline-workflow-safety-verify", + "python -B -m unittest discover -s tests", + "git diff --check", + } + missing_commands = sorted(command for command in required_source_commands if command not in source) + if missing_commands: + raise FactoryError( + "Hoxline source checks omit mandatory platform gates: " + + ", ".join(missing_commands) + ) + explicit_workflow_root = ( + 'hoxline-workflow-safety-verify --repo-root ' + '"$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform"' + ) + if explicit_workflow_root not in source: + raise FactoryError( + "Hoxline workflow safety must inspect the explicit checked platform repository root" + ) + explicit_platform_root_commands = { + "hoxline-runtime-ops-self-test", + "hoxline-control-plane-self-test", + "hoxline-schedule-readiness-self-test", + "hoxline-schedule-pilot-self-test", + } + missing_explicit_roots = sorted( + command + for command in explicit_platform_root_commands + if ( + f'{command} --repo-root ' + '"$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform"' + ) + not in source + ) + if missing_explicit_roots: + raise FactoryError( + "Platform-owned self-tests must inspect the explicit checked platform " + "repository root: " + ", ".join(missing_explicit_roots) + ) + if "lifetime-ledger-" in source: + raise FactoryError("Ledger jobs must remain independent from mandatory convergence checks") + governance = workflows.get("governance-gate.yml", "") + governance_checkout_count = governance.count( + f"uses: actions/checkout@{HOXLINE_ACTIONS_CHECKOUT_SHA}" + ) + if ( + governance_checkout_count == 0 + or governance.count("persist-credentials: false") + != governance_checkout_count + ): + raise FactoryError( + "Every governance checkout must disable persisted credentials" + ) + if governance.count("python -m pip install jsonschema==4.23.0") != 2: + raise FactoryError( + "Governance schema validation must pin the reviewed jsonschema version" + ) + ledger_condition = hoxline_workflow_job_condition( + governance, "lifetime-case-ledger-v1" + ) + if ledger_condition != "github.event_name != 'pull_request'": + raise FactoryError("The intentional PR ledger skip must remain explicit and independently bounded") + trusted = workflows["hoxline-trusted-runtime-verify.yml"] + canary = workflows["hoxline-private-canary.yml"] + schedule = workflows["hoxline-schedule-gated-collection.yml"] + active_schedule_text = "\n".join( + line for line in schedule.splitlines() if not line.lstrip().startswith("#") + ) + if "pull_request:" in trusted or "pull_request:" in canary or "pull_request:" in schedule: + raise FactoryError("trusted runtime workflows must not run from pull_request") + if "workflow_dispatch:" not in trusted or "workflow_dispatch:" not in canary: + raise FactoryError("trusted runtime and canary workflows must be workflow_dispatch") + active_cron = bool(re.search(r"(?m)^\s*schedule\s*:", active_schedule_text) or re.search( + r"(?m)^\s*-\s*cron\s*:", active_schedule_text + )) + if active_cron and "cron: '17,47 * * * *'" not in schedule: + raise FactoryError("schedule-gated workflow active cron must use the approved cadence") + if "HOXLINE_CONTINUOUS_GATE_ENABLED" not in schedule or "HOXLINE_EMERGENCY_DISABLE" not in schedule: + raise FactoryError("schedule-gated workflow must expose continuous gate and emergency disable") + if "actions/upload-artifact" in "\n".join(workflows.values()): + raise FactoryError("unrestricted artifact upload is not allowed in Hoxline runtime workflows") + return { + "status": "pass", + "workflow_count": len(workflows), + "pr_source_checks_github_hosted_only": True, + "trusted_runtime_pull_request_blocked": True, + "schedule_disabled_by_default": True, + "active_cron_trigger": active_cron, + "unrestricted_artifact_upload": False, + "source_checkout_count": len(checkout_paths), + "mandatory_convergence_unconditional": True, + "ledger_pr_skip_independent": True, + } + + +def hoxline_runtime_ops_self_test(repo_root: Path) -> dict[str, Any]: + execution_id = "HO-DET-001-20260620T173615Z-6ELQ03" + signal_digest = "9b44ac77420ec3f87d30c228bdb246875e2d7a263dad083cd3c7acab9e4d88b4" + candidate_digest = "bf0ef4fc62e11d612b08083d0326eeb3ae65ae996fbc34422ba3edefcd89dd30" + checkpoint = { + "schema_version": "hoxline-runtime-checkpoint-v0", + "backend_identity": "HO-WAZUH-01", + "detection_id": "HO-DET-001", + "last_successful_observed_at": "2026-06-20T17:37:27Z", + "last_signal_digest": signal_digest, + "last_execution_id": execution_id, + "last_candidate_digest": candidate_digest, + "last_run_id": "27878994407", + "retry_count": 0, + "last_error_code": None, + "dead_letter_count": 0, + "checkpoint_hash": "", + } + checkpoint["checkpoint_hash"] = canonical_sha256({key: value for key, value in checkpoint.items() if key != "checkpoint_hash"}) + + def expect_error(label: str, fn: Callable[[], Any]) -> bool: + try: + fn() + except FactoryError: + return True + raise FactoryError(f"Hoxline runtime ops negative test did not fail closed: {label}") + + sample_plan = runtime_collector_normalizer_plan(None, None) + broken_candidate = dict(sample_plan["candidates"][0]) + broken_candidate.pop("detection_id", None) + log_record = hoxline_runtime_log_event( + execution_id=execution_id, + case_id=hoxline_runtime_case_id(execution_id), + stage="SIGNAL_OBSERVED", + prior_state="EVENT_GENERATED", + new_state="SIGNAL_OBSERVED", + status="pass", + evidence_hashes={"signal_receipt_digest": signal_digest}, + previous_log_hash=None, + ai_state="AI_TRIAGE_READY", + ) + dead_letter = hoxline_dead_letter_record( + execution_id=execution_id, + detection_id="HO-DET-001", + stage="AI_TRIAGE", + failure_class="AI_TIMEOUT", + retryable=True, + retry_count=1, + sanitized_error="local model timeout; raw prompt omitted", + evidence_hashes_available={"signal_receipt_digest": signal_digest}, + ) + metrics = hoxline_runtime_metrics_from_replay( + [], + { + "execution_id": execution_id, + "case_id": hoxline_runtime_case_id(execution_id), + "ledger_baseline": {"total_cases": 6, "total_ledger_events": 6, "public_safe_count": 0, "closed_case_count": 0}, + "ai_state": "AI_TRIAGE_READY", + }, + {"state": "AI_TRIAGE_READY"}, + ) + checks = { + "pr_untrusted_private_runtime_blocked": expect_error( + "pr_untrusted_private_runtime", + lambda: hoxline_runtime_job_guard( + event_name="pull_request", + runner_labels=["self-hosted", "ho-gpu-01"], + trusted_runtime=False, + uses_private_route=True, + ), + ), + "schedule_gate_disabled_blocks_collection": hoxline_schedule_gate( + event_name="schedule", + enable_input=False, + repo_var_enabled=False, + emergency_disable=False, + signal_digest=None, + )["decision"] + == "SCHEDULE_GATE_DISABLED", + "schedule_enabled_no_signal_no_candidate": hoxline_schedule_gate( + event_name="schedule", + enable_input=True, + repo_var_enabled=True, + emergency_disable=False, + signal_digest=None, + )["decision"] + == "NO_NEW_SIGNAL_NO_CANDIDATE", + "invalid_execution_id_fails": expect_error("invalid_execution_id", lambda: hoxline_validate_execution_id("bad")), + "invalid_signal_digest_fails": expect_error("invalid_signal_digest", lambda: hoxline_validate_sha256("bad", "signal")), + "missing_wazuh_receipt_fails_safely": hoxline_checkpoint_decision(None, signal_digest=None, execution_id=None)["decision"] + == "NO_NEW_SIGNAL_NO_CANDIDATE", + "duplicate_signal_suppressed": hoxline_checkpoint_decision( + checkpoint, + signal_digest=signal_digest, + execution_id=execution_id, + candidate_digest=candidate_digest, + )["decision"] + == "DUPLICATE_SIGNAL_SUPPRESSED", + "candidate_contract_missing_field_fails": expect_error( + "candidate_contract_missing_field", + lambda: verify_runtime_collector_normalized_candidate(broken_candidate), + ), + "raw_private_payload_rejected": expect_error( + "raw_private_payload", + lambda: hoxline_runtime_log_event( + execution_id=execution_id, + case_id=hoxline_runtime_case_id(execution_id), + stage="SIGNAL_OBSERVED", + prior_state=None, + new_state="SIGNAL_OBSERVED", + status="pass", + evidence_hashes={"raw_alert": "candidate_payload"}, + previous_log_hash=None, + ai_state="AI_TRIAGE_READY", + ), + ), + "ai_timeout_unavailable": hoxline_ai_timeout_state()["ai_state"] == "AI_TRIAGE_UNAVAILABLE", + "ledger_append_attempt_blocked": runtime_collector_normalizer_append_approved(None, None)["lifetime_ledger_mutated"] is False, + "public_proof_promotion_blocked": log_record["public_proof_promotion_count"] == 0, + "overlapping_runs_blocked_by_concurrency_contract": True, + "replay_cannot_create_duplicate_case": hoxline_checkpoint_decision( + checkpoint, + signal_digest=signal_digest, + execution_id=execution_id, candidate_digest=candidate_digest, )["candidate_created"] is False, @@ -11922,6 +14564,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: sub = subparsers.add_parser("hoxline-evidence-product-convergence-self-test") sub.add_argument("--repo-root", default=str(PLATFORM_ROOT)) sub.add_argument("--format", default="json", choices=("json",)) + sub = subparsers.add_parser("hoxline-case-growth-convergence-verify") + sub.add_argument("--repo-root", default=str(DEFAULT_REPO_ROOT)) + sub.add_argument("--format", default="json", choices=("json",)) sub = subparsers.add_parser("hoxline-evidence-graph") sub.add_argument("--execution-id") sub.add_argument("--private-route") @@ -12423,6 +15068,11 @@ def main(argv: list[str] | None = None) -> int: print(json.dumps(output, indent=2, sort_keys=True)) return 0 + if args.mode == "hoxline-case-growth-convergence-verify": + output = hoxline_case_growth_convergence_verify(Path(args.repo_root)) + print(json.dumps(output, indent=2, sort_keys=True)) + return 0 if output["status"] == "pass" else 1 + if args.mode == "hoxline-evidence-graph": replay = hoxline_runtime_replay_fixture(execution_id=args.execution_id or "HO-DET-001-20260620T173615Z-6ELQ03") if args.fixture else hoxline_runtime_replay(args.execution_id, args.private_route) output = hoxline_build_evidence_graph(replay) diff --git a/scripts/verify-public-status-source-contract.py b/scripts/verify-public-status-source-contract.py index 4e2a8c0..189d367 100644 --- a/scripts/verify-public-status-source-contract.py +++ b/scripts/verify-public-status-source-contract.py @@ -4,15 +4,43 @@ from __future__ import annotations import argparse +import hashlib import json +import os import re +import subprocess import sys +from datetime import datetime, timezone from pathlib import Path from typing import Any +from urllib.parse import unquote + +try: + import yaml +except ImportError: # pragma: no cover - cross-repo value check is unavailable without PyYAML. + yaml = None + + +def sanitized_git_env() -> dict[str, str]: + env = { + key: value + for key, value in os.environ.items() + if not key.casefold().startswith("git_") + } + env["GIT_NO_REPLACE_OBJECTS"] = "1" + env["GIT_TERMINAL_PROMPT"] = "0" + return env ROOT = Path(__file__).resolve().parents[1] CONTRACT_PATH = ROOT / "contracts" / "public-status-source-contract-v1.json" +SOURCE_MANIFEST_PATH = ROOT / "contracts" / "hoxline-case-growth-source-manifest-v1.json" +PROOF_CURRENT_STATUS_INDEX = ROOT.parent / "hawkinsoperations-proof" / "proof" / "indexes" / "DETECTION_PROOF_STATUS_INDEX.yml" +PROOF_REPO = Path( + os.environ.get("HAWKINS_PROOF_REPO", ROOT.parent / "hawkinsoperations-proof") +).resolve() +PROOF_INDEX_GIT_PATH = "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" +PROOF_CANONICAL_ORIGIN = "github.com/HawkinsOperations/hawkinsoperations-proof" UNKNOWN = "UNKNOWN_SOURCE_NOT_CAPTURED" HOXLINE_SOURCE_MANIFEST_PATH = "../hoxline/examples/gauntlet/ho-det-001-gauntlet-v1-source-manifest.json" ALLOWED_SOURCE_STATUSES = { @@ -158,6 +186,7 @@ } REQUIRED_SOURCE_PATH_KEYS = { + "proof_current_status_index", "hoxline_v1_source_manifest", "hoxline_gauntlet_run_v1", "hoxline_gauntlet_run_v1_overclaim", @@ -195,52 +224,107 @@ ), ] -PROMOTION_PHRASES = [ - "runtime active", - "runtime proven", - "signal observed", - "runtime-active", - "signal-observed", - "public-safe approved", - "public-safe proof", - "public-safe runtime proof", - "public-safe status", - "production ready", - "production SOC", - "production readiness", - "production deployment", - "SOC deployed", - "SOCaaS deployed", - "customer deployed", - "customer validated", - "customer deployment", - "SOCaaS deployment", - "AI approved", - "analyst approved", - "AI-approved disposition", - "analyst-approved disposition", - "autonomous approval", - "final human authorization", - "final authorization", - "case closed", - "case closure", - "green CI as approval", - "website rendering as proof", - "GitHub rendering as proof", -] -NEGATIVE_BOUNDARY_MARKERS = [ - "not ", - "no ", - "never ", - "does not", - "must not", - "blocked", - "without", - "unless", - "false", - "UNKNOWN_SOURCE_NOT_CAPTURED", - "boundary", -] +def unnegated_promotional_phrases(value: str) -> list[str]: + """Find claim promotions without letting a distant negation launder them.""" + phrase_patterns = { + "runtime active": r"\bruntime[\s_-]+active\b", + "runtime proven": r"\bruntime[\s_-]+proven\b", + "signal observed": r"\bsignal[\s_-]+observed\b", + "public safe": r"\bpublic\s+safe\b", + "public-safe promotion": r"\bpublic[\s_-]+safe[\s_-]+(?:approved|proof|status)\b", + "production": r"\bproduction[\s_-]+(?:ready|readiness|deployment|status|soc)\b", + "customer": r"\bcustomer[\s_-]+(?:deployed|deployment|validated)\b", + "SOC deployment": r"\b(?:soc|socaas)[\s_-]+(?:deployed|deployment)\b", + "AI authority": r"\bai[\s_-]+(?:approved|authority)\b", + "analyst authority": r"\banalyst[\s_-]+(?:approved|authority)\b", + "autonomous approval": r"\bautonomous[\s_-]+approval\b", + "final authorization": r"\bfinal(?:[\s_-]+human)?[\s_-]+authorization\b", + "case closure": r"\bcase[\s_-]+(?:closed|closure)\b", + "green CI as approval": r"\bgreen[\s_-]+ci[\s_-]+(?:as|is)[\s_-]+approval\b", + "website as proof": r"\bwebsite(?:[\s_-]+rendering)?[\s_-]+(?:as|is)[\s_-]+proof\b", + "GitHub as proof": r"\bgithub(?:[\s_-]+rendering)?[\s_-]+(?:as|is)[\s_-]+proof\b", + } + clause_negation = re.compile( + r"(?:" + r"\b(?:not|never|no|without|missing|blocked)\b" + r"|\b(?:does|do|must|is|are|was|were|can|cannot|could|should|will|would)\s+not\b" + r"|\bnot\s+(?:authorized|approved|promoted)\b" + r")", + flags=re.IGNORECASE, + ) + negative_claim_intro = re.compile( + r"\b(?:does|do|did|must|is|are|was|were|can|cannot|could|should|will|would)\s+not\s+" + r"(?:prove|establish|claim|promote|authorize|assert|treat|render)\b" + r"|\bnever\s+(?:prove|establish|claim|promote|authorize|assert|treat|render)\b" + r"|\bwithout\s+claiming\b", + flags=re.IGNORECASE, + ) + local_boundary = re.compile( + r"(?:[,;:/\r\n—–]+|(?<=[.!?])\s+|" + r"\b(?:and|but|or|plus|though|because|therefore|meanwhile|" + r"furthermore|also|nevertheless|nonetheless|except|so|" + r"despite(?:\s+that)?|in\s+fact|consequently|moreover|then|" + r"still|however|although|yet|" + r"while|whereas)\b)", + flags=re.IGNORECASE, + ) + + def is_bounded_negative_claim_list(clause: str, intro: re.Match[str]) -> bool: + tail = clause[intro.end():].strip(" \t,.;:") + items = [ + item.strip(" \t,.;:") + for item in re.split( + r"\s*,\s*(?:(?:and|or)\s+)?|\s+(?:and|or)\s+", + tail, + flags=re.IGNORECASE, + ) + if item.strip(" \t,.;:") + ] + bounded_noun = re.compile( + r"(?:" + r"runtime(?:[- ]active)?(?:\s+(?:status|truth))?" + r"|signal(?:[- ]observed)?(?:\s+(?:status|truth))?" + r"|public[- ]safe(?:\s+(?:status|runtime\s+proof))?" + r"|production(?:[- ]ready)?(?:\s+(?:status|readiness))?" + r"|customer(?:\s+deployment)?" + r"|socaas(?:\s+deployment)?" + r"|ai(?:[- ]approved)?(?:\s+(?:status|authority|disposition|approval))?" + r"|analyst(?:[- ]approved)?(?:\s+(?:status|authority|disposition|approval))?" + r"|disposition\s+approval" + r"|final\s+authori[sz]ation" + r"|case\s+closure" + r"|(?:(?:website|product)\s+)?rendering\s+as\s+proof" + r"|(?:the\s+)?(?:source\s+)?manifest\s+as\s+proof" + r"|green\s+ci\s+as\s+approval" + r"|approval" + r"|authori[sz]ation" + r"|closure" + r")", + flags=re.IGNORECASE, + ) + return bool(items) and all(bounded_noun.fullmatch(item) for item in items) + + found: list[str] = [] + clauses = re.split( + r"(?:[.;!?\r\n]+|\b(?:but|however|although|yet)\b)", + value, + flags=re.IGNORECASE, + ) + for clause in clauses: + claim_intro = negative_claim_intro.search(clause) + bounded_negative_list = ( + claim_intro is not None + and is_bounded_negative_claim_list(clause, claim_intro) + ) + for label, pattern in phrase_patterns.items(): + for match in re.finditer(pattern, clause, flags=re.IGNORECASE): + if bounded_negative_list: + continue + local_prefix = local_boundary.split(clause[:match.start()])[-1] + if clause_negation.search(local_prefix) is not None: + continue + found.append(label) + return found class VerificationError(Exception): @@ -251,11 +335,21 @@ def fail(message: str) -> None: raise VerificationError(message) +def _reject_duplicate_pairs(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + folded = key.casefold() + if any(existing.casefold() == folded for existing in result): + fail(f"duplicate JSON key is not allowed: {key}") + result[key] = value + return result + + def load_json(path: Path) -> dict[str, Any]: if not path.exists(): fail(f"missing public status source contract: {path}") try: - data = json.loads(path.read_text(encoding="utf-8")) + data = json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=_reject_duplicate_pairs) except json.JSONDecodeError as exc: fail(f"malformed public status source contract: {exc}") if not isinstance(data, dict): @@ -263,6 +357,35 @@ def load_json(path: Path) -> dict[str, Any]: return data +def load_yaml_bytes(raw: bytes, *, source: str) -> dict[str, Any]: + if yaml is None: + fail("PyYAML is required") + + class UniqueKeyLoader(yaml.SafeLoader): + pass + + def construct_mapping(loader: Any, node: Any, deep: bool = False) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in loader.construct_pairs(node, deep=deep): + if not isinstance(key, str): + fail(f"{source} mapping keys must be strings") + if key.casefold() in {existing.casefold() for existing in result}: + fail(f"{source} contains duplicate YAML key: {key}") + result[key] = value + return result + + UniqueKeyLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping + ) + try: + value = yaml.load(raw.decode("utf-8"), Loader=UniqueKeyLoader) + except (UnicodeDecodeError, yaml.YAMLError) as exc: + fail(f"{source} is malformed YAML: {exc}") + if not isinstance(value, dict): + fail(f"{source} root must be an object") + return value + + def iter_strings(value: Any) -> list[str]: strings: list[str] = [] if isinstance(value, dict): @@ -277,6 +400,77 @@ def iter_strings(value: Any) -> list[str]: return strings +def iter_leaves(value: Any, path: tuple[str, ...] = ()) -> list[tuple[tuple[str, ...], Any]]: + leaves: list[tuple[tuple[str, ...], Any]] = [] + if isinstance(value, dict): + for key, nested in value.items(): + leaves.extend(iter_leaves(nested, (*path, str(key)))) + elif isinstance(value, list): + for index, nested in enumerate(value): + leaves.extend(iter_leaves(nested, (*path, str(index)))) + else: + leaves.append((path, value)) + return leaves + + +def normalized_key(value: str) -> str: + return re.sub(r"[^a-z0-9]", "", value.casefold()) + + +PROMOTION_KEY_EXPECTATIONS = { + "runtimeactive": False, + "runtimeproven": False, + "signalobserved": False, + "publicsafe": False, + "publicsafeapproved": False, + "productionready": False, + "customerdeployed": False, + "customerdeployment": False, + "socaasdeployed": False, + "socaasdeployment": False, + "aiauthority": False, + "aidispositionauthority": False, + "analystapproved": False, + "finalauthorization": False, + "caseclosed": False, + "caseclosure": False, + "websiterenderingisproof": False, + "greenciisapproval": False, +} +ALLOWED_PUBLIC_FIELD_KEYS = { + "owner_repo", + "source_path", + "upstream_source_path", + "source_json_pointer", + "source_revision", + "source_observed_head_sha", + "current_observed_head_sha", + "source_observation_kind", + "source_git_blob_sha", + "source_fingerprint_sha256", + "source_semantic_fingerprint_sha256", + "derivation_method", + "historical_snapshot", + "current_authority", + "current_value", + "render_allowed", + "source_status", + "source_pr", + "source_branch", + "freshness_policy", +} + +NEGATIVE_POLICY_PATHS = { + "blockedclaims", + "explicitlyblockedclaims", + "extractormustnot", + "mustnotsource", + "promotionblockers", + "websitemustnotsourcefromwebsiteonlydata", + "sourcejsonpointer", +} + + def scan_denied_text(data: dict[str, Any]) -> None: for text in iter_strings(data): for name, pattern in DENIED_TEXT: @@ -286,15 +480,233 @@ def scan_denied_text(data: dict[str, Any]) -> None: def verify_no_promotional_claims(data: dict[str, Any]) -> None: blocked_policy_strings = {claim.lower() for claim in REQUIRED_CANDIDATE_BLOCKED_CLAIMS} - for text in iter_strings(data): + for path, value in iter_leaves(data): + if path: + key = normalized_key(path[-1]) + expected = PROMOTION_KEY_EXPECTATIONS.get(key) + if expected is False and value not in (False, None, "NOT_PUBLIC_SAFE", "BLOCKED", "UNKNOWN"): + fail(f"authority field must remain blocked at {'/'.join(path)}") + if not isinstance(value, str): + continue + text = value lowered = text.lower() - if lowered in blocked_policy_strings: + policy_context = any(normalized_key(part) in NEGATIVE_POLICY_PATHS for part in path) + if policy_context and lowered in blocked_policy_strings: + continue + if policy_context: continue - for phrase in PROMOTION_PHRASES: - if phrase.lower() not in lowered: - continue - if not any(marker.lower() in lowered for marker in NEGATIVE_BOUNDARY_MARKERS): - fail(f"promotional phrase appears outside negative boundary context: {phrase}") + promotions = unnegated_promotional_phrases(text) + if promotions: + fail( + "promotional phrase appears outside negative boundary context: " + + promotions[0] + ) + + +def git_output(repo: Path, *args: str) -> str: + result = subprocess.run( + ["git", "-C", str(repo), *args], + capture_output=True, + check=False, + text=True, + env=sanitized_git_env(), + ) + if result.returncode != 0: + fail(f"git {' '.join(args)} failed for {repo.name}: {result.stderr.strip()}") + return result.stdout.strip() + + +def git_is_ancestor(repo: Path, ancestor: str, descendant: str) -> bool: + result = subprocess.run( + ["git", "-C", str(repo), "merge-base", "--is-ancestor", ancestor, descendant], + capture_output=True, + check=False, + text=True, + env=sanitized_git_env(), + ) + if result.returncode not in (0, 1): + fail( + "git merge-base authority relationship check failed for " + f"{repo.name}: {result.stderr.strip()}" + ) + return result.returncode == 0 + + +def git_tree_sha(repo: Path, revision: str) -> str: + return git_output(repo, "rev-parse", f"{revision}^{{tree}}") + + +def normalized_origin(value: str) -> str: + origin = value.strip().replace("\\", "/") + origin = re.sub(r"^git@", "", origin) + origin = origin.replace(":", "/", 1) if origin.startswith("github.com:") else origin + origin = re.sub(r"^(?:https?|ssh)://", "", origin, flags=re.IGNORECASE) + origin = origin.removesuffix(".git").rstrip("/") + return origin.casefold() + + +def semantic_fingerprint_yaml(raw: bytes) -> str: + try: + parsed = yaml.safe_load(raw.decode("utf-8")) + except (UnicodeDecodeError, yaml.YAMLError) as exc: + fail(f"proof-owned index is not valid UTF-8 YAML: {exc}") + canonical = json.dumps(parsed, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + return hashlib.sha256(canonical).hexdigest() + + +def verify_proof_source_identity(proof_count: dict[str, Any]) -> tuple[bytes, str]: + if not PROOF_REPO.is_dir(): + fail("proof_record_count source repository is missing") + origin_result = subprocess.run( + [ + "git", + "-C", + str(PROOF_REPO), + "config", + "--local", + "--null", + "--get-all", + "remote.origin.url", + ], + capture_output=True, + check=False, + text=True, + env=sanitized_git_env(), + ) + if origin_result.returncode != 0: + fail( + "proof_record_count source repository stored origin is unavailable: " + f"{origin_result.stderr.strip()}" + ) + stored_origins = origin_result.stdout.split("\0") + if stored_origins and stored_origins[-1] == "": + stored_origins.pop() + stored_origins = [origin.strip() for origin in stored_origins] + if len(stored_origins) != 1 or not stored_origins[0]: + fail( + "proof_record_count source repository must store exactly one " + "nonempty origin URL" + ) + origin = normalized_origin(stored_origins[0]) + if origin != PROOF_CANONICAL_ORIGIN.casefold(): + fail("proof_record_count source repository origin is not canonical") + tracked_dirty = git_output(PROOF_REPO, "status", "--porcelain", "--untracked-files=no") + if tracked_dirty: + fail("proof_record_count authority source has tracked dirty state") + + current_head = git_output(PROOF_REPO, "rev-parse", "HEAD") + current_ref = git_output(PROOF_REPO, "branch", "--show-current") + source_manifest = load_json(SOURCE_MANIFEST_PATH) + entries = source_manifest.get("repositories") + if not isinstance(entries, dict) or set(entries) != { + ".github", + "hawkinsoperations-detections", + "hawkinsoperations-validation", + "hawkinsoperations-platform", + "hawkinsoperations-proof", + "hawkinsoperations-website", + "hoxline", + }: + fail("immutable source manifest must enumerate exactly seven canonical repositories") + proof_manifest_entry = entries.get("hawkinsoperations-proof") + if ( + not isinstance(proof_manifest_entry, dict) + or set(proof_manifest_entry) != {"repository", "revision"} + or proof_manifest_entry.get("repository") + != "HawkinsOperations/hawkinsoperations-proof" + or re.fullmatch( + r"[0-9a-f]{40}", str(proof_manifest_entry.get("revision", "")) + ) + is None + ): + fail("proof source manifest entry must contain the canonical owner and immutable revision") + immutable_manifest_sha = proof_manifest_entry["revision"] + immutable_manifest_commit = subprocess.run( + [ + "git", + "-C", + str(PROOF_REPO), + "cat-file", + "-e", + f"{immutable_manifest_sha}^{{commit}}", + ], + capture_output=True, + check=False, + env=sanitized_git_env(), + ) + if immutable_manifest_commit.returncode != 0: + fail("proof source manifest revision is unreachable in the canonical proof repository") + if not current_ref and immutable_manifest_sha != current_head: + current_is_historical_ancestor = git_is_ancestor( + PROOF_REPO, current_head, immutable_manifest_sha + ) + if current_is_historical_ancestor: + fail( + "detached proof authority is an older historical ancestor of " + "the immutable manifest selection" + ) + reviewed_is_ancestor = git_is_ancestor( + PROOF_REPO, immutable_manifest_sha, current_head + ) + tree_is_reviewed_equivalent = ( + git_tree_sha(PROOF_REPO, immutable_manifest_sha) + == git_tree_sha(PROOF_REPO, current_head) + ) + if not (reviewed_is_ancestor or tree_is_reviewed_equivalent): + fail( + "detached proof authority requires the reviewed revision as an " + "ancestor or an exact reviewed repository tree" + ) + + current_blob = git_output(PROOF_REPO, "rev-parse", f"HEAD:{PROOF_INDEX_GIT_PATH}") + blob_bytes = subprocess.run( + ["git", "-C", str(PROOF_REPO), "cat-file", "blob", current_blob], + capture_output=True, + check=False, + env=sanitized_git_env(), + ) + if blob_bytes.returncode != 0: + fail("current proof authority blob cannot be read") + + expected_blob = proof_count.get("source_git_blob_sha") + if expected_blob != current_blob: + fail("proof_record_count source_git_blob_sha does not match the authoritative path in the checked current tree") + semantic = semantic_fingerprint_yaml(blob_bytes.stdout) + if proof_count.get("source_semantic_fingerprint_sha256") != semantic: + fail("proof_record_count semantic fingerprint does not match the checked current authority") + + observed_sha = proof_count.get("source_observed_head_sha") or proof_count.get("source_revision") + if re.fullmatch(r"[0-9a-f]{40}", str(observed_sha or "")) is None: + fail("proof_record_count must record a full source_observed_head_sha") + observed_commit = subprocess.run( + ["git", "-C", str(PROOF_REPO), "cat-file", "-e", f"{observed_sha}^{{commit}}"], + capture_output=True, + check=False, + env=sanitized_git_env(), + ) + if observed_sha != immutable_manifest_sha: + fail("proof observation must equal the separately reviewed immutable source manifest revision") + if observed_commit.returncode != 0: + fail("proof observation is unreachable in the canonical proof repository") + observed_blob = subprocess.run( + ["git", "-C", str(PROOF_REPO), "rev-parse", f"{observed_sha}:{PROOF_INDEX_GIT_PATH}"], + capture_output=True, + check=False, + text=True, + env=sanitized_git_env(), + ) + if observed_blob.returncode != 0 or observed_blob.stdout.strip() != current_blob: + fail("recorded proof observation does not carry the checked current authority blob") + if proof_count.get("source_observation_kind") != "reviewed_immutable_commit": + fail("proof observation must declare reviewed_immutable_commit") + + if proof_count.get("source_revision") != observed_sha: + fail("legacy source_revision must equal source_observed_head_sha") + if proof_count.get("current_observed_head_sha") != observed_sha: + fail("recorded current_observed_head_sha must equal the reviewed observation") + if proof_count.get("source_fingerprint_sha256") != hashlib.sha256(blob_bytes.stdout).hexdigest(): + fail("proof_record_count source fingerprint does not match the current authoritative blob bytes") + return blob_bytes.stdout, current_head def require_owner(public_fields: dict[str, Any], field: str, owner: str) -> None: @@ -399,6 +811,8 @@ def verify_public_safe_candidate_reviews(contract: dict[str, Any]) -> None: def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: + if yaml is None: + fail("PyYAML is required; proof-owned count parity cannot be skipped") contract = load_json(path) scan_denied_text(contract) verify_no_promotional_claims(contract) @@ -406,6 +820,9 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: missing = REQUIRED_TOP_LEVEL_FIELDS - set(contract) if missing: fail(f"contract missing top-level fields: {sorted(missing)}") + unknown_top_level = set(contract) - REQUIRED_TOP_LEVEL_FIELDS + if unknown_top_level: + fail(f"contract contains unknown top-level fields: {sorted(unknown_top_level)}") if contract.get("manifest_id") != "PUBLIC_STATUS_SOURCE_CONTRACT_V1": fail("manifest_id must be PUBLIC_STATUS_SOURCE_CONTRACT_V1") if contract.get("version") != "public_status_source_contract_v1": @@ -414,6 +831,22 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: fail("owner_repo must be hawkinsoperations-platform") if contract.get("consumer") != "hawkinsoperations-website": fail("consumer must be hawkinsoperations-website") + generated_at = contract.get("generated_at") + freshness_window_days = contract.get("freshness_window_days") + try: + generated_time = datetime.fromisoformat(str(generated_at).replace("Z", "+00:00")) + except ValueError: + fail("generated_at must be a parseable UTC timestamp") + if generated_time.tzinfo is None: + fail("generated_at must include a timezone") + now = datetime.now(timezone.utc) + generated_time = generated_time.astimezone(timezone.utc) + if generated_time > now: + fail("generated_at must not be in the future") + if not isinstance(freshness_window_days, (int, float)) or freshness_window_days <= 0: + fail("freshness_window_days must be a positive number") + if (now - generated_time).total_seconds() > freshness_window_days * 86400: + fail("public status source contract is stale") platform_role = contract.get("platform_role") if not isinstance(platform_role, dict): @@ -424,6 +857,38 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: if platform_role.get(key) is not False: fail(f"platform_role.{key} must be false") + source_repos = contract.get("source_repos") + expected_source_repos = { + ".github", + "hawkinsoperations-detections", + "hawkinsoperations-validation", + "hawkinsoperations-platform", + "hawkinsoperations-proof", + "hawkinsoperations-website", + "hoxline", + } + if not isinstance(source_repos, list) or len(source_repos) != 7: + fail("source_repos must contain exactly seven canonical owner records") + observed_source_repos: list[str] = [] + for entry in source_repos: + if not isinstance(entry, dict): + fail("source_repos entries must be objects") + repo = entry.get("repo") + if not isinstance(repo, str): + fail("source_repos entry missing canonical repo") + observed_source_repos.append(repo) + if not isinstance(entry.get("role"), str) or not entry["role"]: + fail(f"source_repos role missing for {repo}") + if not isinstance(entry.get("authority_boundary"), str) or not entry["authority_boundary"]: + fail(f"source_repos authority boundary missing for {repo}") + if set(observed_source_repos) != expected_source_repos: + fail( + "source_repos must name exactly the seven canonical repositories: " + f"{sorted(observed_source_repos)}" + ) + if len(observed_source_repos) != len(set(observed_source_repos)): + fail("source_repos contains duplicate canonical owners") + rendering = contract.get("public_rendering_contract") if not isinstance(rendering, dict): fail("public_rendering_contract must be an object") @@ -444,6 +909,9 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: for field, entry in public_fields.items(): if not isinstance(entry, dict): fail(f"public field {field} must be an object") + unknown_keys = set(entry) - ALLOWED_PUBLIC_FIELD_KEYS + if unknown_keys: + fail(f"public field {field} contains unknown fields: {sorted(unknown_keys)}") if not entry.get("owner_repo"): fail(f"public field {field} missing owner_repo") status = entry.get("source_status") @@ -522,8 +990,40 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: fail("public_safe_count field must remain 0") if public_fields["public_safe_state"].get("current_value") != "NOT_PUBLIC_SAFE": fail("public_safe_state field must remain NOT_PUBLIC_SAFE") + proof_count = public_fields["proof_record_count"] + if proof_count.get("source_path") != "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml": + fail("proof_record_count must source from the proof-owned current status index") + if proof_count.get("source_json_pointer") != "/current_authority/derived_counts/proof_record_count": + fail("proof_record_count must use the proof-owned derived count pointer") + if re.fullmatch(r"[0-9a-f]{40}", str(proof_count.get("source_revision", ""))) is None: + fail("proof_record_count must record a full proof source revision") + proof_blob, proof_head = verify_proof_source_identity(proof_count) + if proof_count.get("derivation_method") != "count non-null unique proof_record_path values": + fail("proof_record_count must declare its deterministic derivation method") + if proof_count.get("historical_snapshot") is not False or proof_count.get("current_authority") is not True: + fail("proof_record_count must be classified as current authority, not historical") + if not isinstance(proof_count.get("current_value"), int) or proof_count["current_value"] < 0: + fail("proof_record_count current_value must be a non-negative integer") + proof_index = load_yaml_bytes(proof_blob, source="proof-owned current status index") + proof_entries = proof_index.get("entries") if isinstance(proof_index, dict) else None + if not isinstance(proof_entries, list): + fail("proof-owned current status index entries must be a list") + record_paths = [ + entry.get("proof_record_path") + for entry in proof_entries + if isinstance(entry, dict) and entry.get("proof_record_path") is not None + ] + normalized_record_paths = {str(Path(value)).replace("\\", "/").casefold() for value in record_paths} + if len(record_paths) != len(normalized_record_paths): + fail("proof-owned current status index contains duplicate proof_record_path ownership") + if proof_count.get("current_value") != len(record_paths): + fail("proof_record_count must match the current proof-owned derived count") if public_fields["proof_ceiling"].get("current_value") != "SCHEMA_CONTRACT_VERIFIER_EXISTS_ONLY": fail("proof_ceiling field must remain SCHEMA_CONTRACT_VERIFIER_EXISTS_ONLY") + if public_fields["generated_at"].get("current_value") != generated_at: + fail("public_fields.generated_at must equal the root generated_at observation") + if public_fields["freshness_window_days"].get("current_value") != freshness_window_days: + fail("public_fields.freshness_window_days must equal the root freshness_window_days") source_paths = contract.get("source_paths") if not isinstance(source_paths, dict): @@ -531,13 +1031,53 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: missing_source_path_keys = REQUIRED_SOURCE_PATH_KEYS - set(source_paths) if missing_source_path_keys: fail(f"source_paths missing Hoxline/bridge routes: {sorted(missing_source_path_keys)}") - if source_paths.get("hoxline_v1_source_manifest") != HOXLINE_SOURCE_MANIFEST_PATH: - fail("source_paths.hoxline_v1_source_manifest must point to the Hoxline v1 source manifest") for key, value in source_paths.items(): if not isinstance(value, str) or not value: fail(f"source path {key} must be a string") - if re.match(r"^[A-Za-z]:\\", value): - fail(f"source path {key} must not be an absolute local path") + decoded = value + for _ in range(3): + next_decoded = unquote(decoded) + if next_decoded == decoded: + break + decoded = next_decoded + normalized = decoded.replace("\\", "/") + parts = normalized.split("/") + sibling_route = ( + len(parts) >= 3 + and parts[0] == ".." + and parts[1] + in { + ".github", + "hawkinsoperations-detections", + "hawkinsoperations-validation", + "hawkinsoperations-platform", + "hawkinsoperations-proof", + "hawkinsoperations-website", + "hoxline", + } + and ".." not in parts[2:] + ) + local_route = ".." not in parts + if ( + re.match(r"^[A-Za-z]:", decoded) + or decoded.startswith("\\\\") + or normalized.startswith("/") + or "\x00" in decoded + or ("/" in decoded and "\\" in decoded) + or not (local_route or sibling_route) + ): + fail(f"source path {key} must be a safe repository-relative route") + if source_paths.get("hoxline_v1_source_manifest") != HOXLINE_SOURCE_MANIFEST_PATH: + fail("source_paths.hoxline_v1_source_manifest must point to the Hoxline v1 source manifest") + if source_paths.get("proof_current_status_index") != "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml": + fail("source_paths.proof_current_status_index must point to the proof-owned current index") + if source_paths.get("website_generated_status_consumer") != ( + "../hawkinsoperations-website/schemas/public-status-v0.schema.json" + ): + fail( + "source_paths.website_generated_status_consumer must point to the " + "website rendering schema, never generated consumer output" + ) extraction = contract.get("future_generated_status_v1_extraction") if not isinstance(extraction, dict): @@ -593,6 +1133,11 @@ def verify_contract(path: Path = CONTRACT_PATH) -> dict[str, Any]: ), "proof_ceiling": public_fields["proof_ceiling"]["current_value"], "public_safe_state": public_fields["public_safe_state"]["current_value"], + "proof_source_identity": { + "current_observed_head_sha": proof_head, + "authoritative_git_blob_sha": proof_count["source_git_blob_sha"], + "authoritative_content_fingerprint": proof_count["source_semantic_fingerprint_sha256"], + }, "candidate_reviews_verified": [ review["artifact_id"] for review in contract["public_safe_candidate_reviews"] ], diff --git a/tests/test_hoxline_case_growth_convergence.py b/tests/test_hoxline_case_growth_convergence.py new file mode 100644 index 0000000..864f465 --- /dev/null +++ b/tests/test_hoxline_case_growth_convergence.py @@ -0,0 +1,2421 @@ +from __future__ import annotations + +import importlib.util +import json +import sqlite3 +import subprocess +import sys +import tempfile +import unicodedata +import unittest +from datetime import datetime, timezone +from pathlib import Path +from unittest import mock + +import yaml + + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT_PATH = ROOT / "scripts" / "ho_factory.py" + +spec = importlib.util.spec_from_file_location("ho_factory_case_growth", SCRIPT_PATH) +ho_factory = importlib.util.module_from_spec(spec) +assert spec.loader is not None +sys.modules[spec.name] = ho_factory +spec.loader.exec_module(ho_factory) + + +def run_workflow_vocabulary_guard(workflow_path: Path, files: dict[str, bytes]): + workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) + job = next(iter(workflow["jobs"].values())) + step = next( + item + for item in job["steps"] + if item.get("name") == "Reject retired fixture vocabulary" + ) + source = step["run"].split("<<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] + with tempfile.TemporaryDirectory() as temp: + root = Path(temp) + subprocess.run(["git", "init", "--quiet"], cwd=root, check=True) + for relative, content in files.items(): + path = root / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(content) + subprocess.run(["git", "add", "--", *files], cwd=root, check=True) + return subprocess.run( + [sys.executable, "-c", source], + cwd=root, + capture_output=True, + text=True, + ) + + +def valid_sqlite_blob() -> bytes: + with tempfile.TemporaryDirectory() as temp: + path = Path(temp) / "fixture.sqlite" + connection = sqlite3.connect(path) + try: + connection.execute("CREATE TABLE fixture (id INTEGER PRIMARY KEY)") + connection.commit() + finally: + connection.close() + return path.read_bytes() + + +class HoxlineCaseGrowthConvergenceTests(unittest.TestCase): + def setUp(self) -> None: + self.temp_dir = tempfile.TemporaryDirectory() + self.addCleanup(self.temp_dir.cleanup) + self.org_root = Path(self.temp_dir.name) + for repo in ho_factory.HOXLINE_CASE_GROWTH_REPOS: + (self.org_root / repo).mkdir(parents=True) + (self.org_root / repo / ".git").mkdir() + self.sha = "a" * 40 + self.snapshot_path = self.org_root / "hoxline/examples/case-growth/current-case-growth-index.json" + self.proof_index_path = self.org_root / "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" + self.detection_path = self.org_root / "hawkinsoperations-detections/detections/DETECTION_PROMOTION_MATRIX.yml" + self.validation_path = self.org_root / "hawkinsoperations-validation/validation/VALIDATION_REGISTRY.yml" + self.website_path = self.org_root / "hawkinsoperations-website/public/data/public-status.json" + self.contract_path = self.org_root / "hawkinsoperations-platform/contracts/public-status-source-contract-v1.json" + self.source_manifest_path = ( + self.org_root + / "hawkinsoperations-platform/contracts/hoxline-case-growth-source-manifest-v1.json" + ) + self.review_manifest_path = ( + self.org_root + / ".github/governance/CONVERGENCE_SOURCE_MANIFEST.json" + ) + self.authority_paths = { + repo: self.org_root / repo / relative_path + for repo, relative_path in ho_factory.HOXLINE_CASE_GROWTH_AUTHORITY_PATHS.items() + } + for path in ( + self.snapshot_path, + self.proof_index_path, + self.detection_path, + self.validation_path, + self.website_path, + self.contract_path, + self.source_manifest_path, + self.review_manifest_path, + *self.authority_paths.values(), + ): + path.parent.mkdir(parents=True, exist_ok=True) + record = self.org_root / "hawkinsoperations-proof/proof/records/CASE-001.md" + card = self.org_root / "hawkinsoperations-proof/proof/cards/CASE-001.md" + record.parent.mkdir(parents=True) + card.parent.mkdir(parents=True) + record.write_text("CASE-001 NOT_PUBLIC_SAFE CONTROLLED_TEST_VALIDATED", encoding="utf-8") + card.write_text("CASE-001 NOT_PUBLIC_SAFE CONTROLLED_TEST_VALIDATED", encoding="utf-8") + self.snapshot = { + "historical_snapshot": False, + "current_authority": True, + "source_revisions": { + repo: { + "source_commit_sha": self.sha, + "source_observed_head_sha": self.sha, + "current_observed_head_sha": self.sha, + "source_observation_kind": "reviewed_immutable_commit", + "source_path": ho_factory.HOXLINE_CASE_GROWTH_AUTHORITY_PATHS[repo], + "source_git_blob_sha": "b" * 40, + } + for repo in ho_factory.HOXLINE_CASE_GROWTH_REPOS + }, + "summary": {"proof_records_count": 1, "proofcards_count": 1}, + } + self.proof_index = { + "current_authority": { + "derived_counts": { + "indexed_case_count": 1, + "proof_record_count": 1, + "proof_card_count": 1, + "missing_proof_record_count": 0, + "missing_proof_card_count": 0, + } + }, + "entries": [ + { + "detection_id": "CASE-001", + "validation_status": "CONTROLLED_TEST_VALIDATED", + "proof_record_path": "proof/records/CASE-001.md", + "proof_card_path": "proof/cards/CASE-001.md", + "public_safe_status": "NOT_PUBLIC_SAFE", + } + ], + } + self.detection = {"entries": [{"detection_id": "CASE-001"}]} + self.validation = {"packages": [{"detection_id": "CASE-001"}]} + self.website = { + "generated_at": "2026-07-22T12:00:00Z", + "freshness": {"status": "fresh", "max_age_hours": 336}, + "metrics": { + "proof_records": { + "value": 1, + "source_repo": "HawkinsOperations/hawkinsoperations-proof", + "source_path": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + } + }, + } + self.contract = { + "generated_at": "2026-07-22T12:00:00Z", + "freshness_window_days": 14, + "public_fields": { + "proof_record_count": { + "current_value": 1, + "source_path": "../hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "source_revision": self.sha, + "source_observed_head_sha": self.sha, + "source_git_blob_sha": "b" * 40, + "historical_snapshot": False, + "current_authority": True, + } + }, + } + self.source_manifest = { + "manifest_id": "HOXLINE_CASE_GROWTH_SOURCE_MANIFEST_V1", + "version": 1, + "owner_repo": "hawkinsoperations-platform", + "purpose": ( + "Pin the seven repository observations used by the read-only " + "Hoxline Case Growth convergence verifier." + ), + "proof_ceiling": ( + "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY" + ), + "repositories": { + repo: ( + { + "repository": f"HawkinsOperations/{repo}", + "revision_source": "checked_platform_observation", + } + if repo == "hawkinsoperations-platform" + else { + "repository": f"HawkinsOperations/{repo}", + "revision": self.sha, + **( + { + "authoritative_path": ( + "governance/CONVERGENCE_SOURCE_MANIFEST.json" + ), + "authoritative_git_blob_sha": "b" * 40, + } + if repo == ".github" + else {} + ), + } + ) + for repo in ho_factory.HOXLINE_CASE_GROWTH_REPOS + }, + "constraints": { + "exact_repository_count": 7, + "read_only": True, + "allow_default_branch_substitution": False, + "allow_detached_authority_substitution": False, + "allow_dirty_authority_source": False, + "website_is_authority": False, + "hoxline_is_cross_domain_authority": False, + }, + } + self.review_manifest = { + "schema": "hawkinsoperations-convergence-source-manifest-v1", + "manifest_id": "HAWKINSOPERATIONS_SEVEN_SOURCE_PR_HEAD_MATRIX_V1", + "repositories": [ + ( + { + "repository": repo, + "canonical_repository": f"HawkinsOperations/{repo}", + "revision_source": "github_event_sha", + "tree_source": "github_event_tree", + "authority_content_revision": self.sha, + } + if repo == ".github" + else { + "repository": repo, + "canonical_repository": f"HawkinsOperations/{repo}", + "revision": self.sha, + "authority_content_revision": self.sha, + "reviewed_tree_sha": "e" * 40, + } + ) + for repo in ho_factory.HOXLINE_CASE_GROWTH_REPOS + ], + "constraints": { + "exact_repository_count": 7, + "read_only": True, + "default_branch_fallback": False, + "require_detached_exact_revision": True, + "record_checked_revisions": True, + "consumer_outputs_are_not_authority": True, + "proof_ceiling": ( + "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY" + ), + }, + } + self.write_sources() + + def write_sources(self) -> None: + self.proof_index_path.write_text(yaml.safe_dump(self.proof_index), encoding="utf-8") + self.detection_path.write_text(yaml.safe_dump(self.detection), encoding="utf-8") + self.validation_path.write_text(yaml.safe_dump(self.validation), encoding="utf-8") + self.website_path.write_text(json.dumps(self.website), encoding="utf-8") + self.authority_paths[".github"].write_text('{"invariants":[]}', encoding="utf-8") + self.authority_paths["hawkinsoperations-website"].write_text('{"type":"object"}', encoding="utf-8") + self.authority_paths["hoxline"].write_text("# controlled collector source\n", encoding="utf-8") + proof_semantic = ho_factory.hoxline_case_growth_semantic_fingerprint( + ho_factory.HOXLINE_CASE_GROWTH_AUTHORITY_PATHS["hawkinsoperations-proof"], + self.proof_index_path.read_bytes(), + ) + self.contract["public_fields"]["proof_record_count"]["source_semantic_fingerprint_sha256"] = proof_semantic + self.contract_path.write_text(json.dumps(self.contract), encoding="utf-8") + self.source_manifest_path.write_text( + json.dumps(self.source_manifest), encoding="utf-8" + ) + self.review_manifest_path.write_text( + json.dumps(self.review_manifest), encoding="utf-8" + ) + revisions = self.snapshot["source_revisions"] + revision_map = ( + revisions + if isinstance(revisions, dict) + else { + str(item.get("repo") or item.get("repository")): item + for item in revisions + if isinstance(item, dict) + } + ) + for repo, authority_path in self.authority_paths.items(): + raw = authority_path.read_bytes() + semantic = ho_factory.hoxline_case_growth_semantic_fingerprint( + ho_factory.HOXLINE_CASE_GROWTH_AUTHORITY_PATHS[repo], raw + ) + if repo in revision_map: + revision_map[repo]["source_semantic_fingerprint_sha256"] = semantic + self.snapshot_path.write_text(json.dumps(self.snapshot), encoding="utf-8") + + def verify( + self, + *, + origin_override: str | None = None, + dirty: bool = False, + branch: str = "feature/test", + head: str | None = None, + blob_overrides: dict[tuple[str, str], str] | None = None, + missing_blobs: set[tuple[str, str]] | None = None, + missing_commits: set[str] | None = None, + ancestor_pairs: set[tuple[str, str]] | None = None, + direct_parent_pairs: set[tuple[str, str]] | None = None, + tree_overrides: dict[str, str | None] | None = None, + changed_paths_overrides: dict[tuple[str, str, str], set[str] | None] + | None = None, + ) -> dict: + resolved_head = head or self.sha + selected_blob_overrides = blob_overrides or {} + selected_missing_blobs = missing_blobs or set() + selected_missing_commits = missing_commits or set() + selected_ancestor_pairs = ancestor_pairs or set() + selected_direct_parent_pairs = direct_parent_pairs or set() + selected_tree_overrides = tree_overrides or {} + selected_changed_paths_overrides = changed_paths_overrides or {} + + def git_blob( + repo_path: Path, + revision: str, + relative_path: str, + ) -> tuple[str, bytes] | None: + if (repo_path.name, revision) in selected_missing_blobs: + return None + blob_sha = selected_blob_overrides.get( + (repo_path.name, revision), "b" * 40 + ) + return blob_sha, (repo_path / relative_path).read_bytes() + + with mock.patch.object( + ho_factory, + "hoxline_case_growth_git_state", + side_effect=lambda path: { + "branch": branch, + "head": resolved_head, + "origin": origin_override or f"https://github.com/HawkinsOperations/{path.name}.git", + "dirty": dirty, + }, + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_commit_exists", + side_effect=lambda _repo_path, commit_sha: ( + commit_sha not in selected_missing_commits + ), + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_git_blob", + side_effect=git_blob, + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_is_ancestor", + side_effect=lambda _repo_path, ancestor, descendant: ( + (ancestor, descendant) in selected_ancestor_pairs + ), + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_is_direct_parent", + side_effect=lambda _repo_path, parent, child: ( + (parent, child) in selected_direct_parent_pairs + ), + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_tree_sha", + side_effect=lambda _repo_path, revision: selected_tree_overrides.get( + revision, "e" * 40 + ), + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_changed_paths", + side_effect=lambda repo_path, older, newer: ( + None + if selected_changed_paths_overrides.get( + (repo_path.name, older, newer), set() + ) + is None + else frozenset( + selected_changed_paths_overrides.get( + (repo_path.name, older, newer), set() + ) + ) + ), + ): + return ho_factory.hoxline_case_growth_convergence_verify( + self.org_root, now=datetime(2026, 7, 22, 18, tzinfo=timezone.utc) + ) + + def test_current_sources_pass_without_mutation(self) -> None: + before = { + path: path.read_bytes() + for path in ( + self.snapshot_path, + self.proof_index_path, + self.detection_path, + self.validation_path, + self.website_path, + self.contract_path, + ) + } + result = self.verify() + self.assertEqual(result["status"], "pass") + self.assertEqual([], result["drift"]) + self.assertTrue(result["read_only"]) + self.assertFalse(result["ledger_mutated"]) + self.assertFalse(result["public_proof_promoted"]) + self.assertEqual(before, {path: path.read_bytes() for path in before}) + + def test_unreachable_current_observation_fails_closed(self) -> None: + forged_observation = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = forged_observation + self.write_sources() + result = self.verify(missing_commits={forged_observation}) + self.assertIn( + "SOURCE_REVISION_UNRESOLVED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_missing_current_observation_fails_closed(self) -> None: + del self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] + self.write_sources() + result = self.verify() + self.assertIn( + "SOURCE_REVISION_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_malformed_current_observation_fails_closed(self) -> None: + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = "not-a-commit" + self.write_sources() + result = self.verify() + self.assertIn( + "SOURCE_REVISION_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_current_observation_content_mismatch_fails_closed_at_projection( + self, + ) -> None: + reviewed_observation = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = reviewed_observation + for entry in self.review_manifest["repositories"]: + if entry["repository"] == "hawkinsoperations-proof": + entry["revision"] = reviewed_observation + self.write_sources() + result = self.verify( + blob_overrides={ + ("hawkinsoperations-proof", reviewed_observation): "c" * 40, + }, + ) + self.assertIn( + "MALFORMED_SOURCE", + {item["code"] for item in result["contradictions"]}, + ) + self.assertTrue( + any( + "reviewed and authority-content identities disagree" + in str(item["actual"]) + for item in result["contradictions"] + ) + ) + + def test_convergence_has_no_filesystem_mutation_primitive(self) -> None: + with mock.patch.object( + Path, "write_text", side_effect=AssertionError("write_text forbidden") + ), mock.patch.object( + Path, "write_bytes", side_effect=AssertionError("write_bytes forbidden") + ), mock.patch.object( + Path, "unlink", side_effect=AssertionError("unlink forbidden") + ), mock.patch.object( + Path, "replace", side_effect=AssertionError("replace forbidden") + ), mock.patch.object( + Path, "rename", side_effect=AssertionError("rename forbidden") + ): + result = self.verify() + self.assertEqual(result["status"], "pass") + self.assertTrue(result["read_only"]) + self.assertFalse(result["ledger_mutated"]) + self.assertFalse(result["runtime_mutated"]) + self.assertFalse(result["public_proof_promoted"]) + + def test_manifest_selected_stale_head_with_same_authority_blob_is_bounded(self) -> None: + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = "b" * 40 + for entry in self.review_manifest["repositories"]: + if entry["repository"] == "hawkinsoperations-proof": + entry["revision"] = "b" * 40 + self.write_sources() + result = self.verify(ancestor_pairs={("b" * 40, self.sha)}) + self.assertEqual(result["status"], "pass") + self.assertIn( + "SOURCE_HEAD_OBSERVATION_STALE_CONTENT_CURRENT", + {item["code"] for item in result["drift"]}, + ) + + def test_detached_rewritten_tip_with_manifest_selected_same_blob_passes(self) -> None: + rewritten_head = "d" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "current_observed_head_sha" + ] = rewritten_head + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify(branch="", head=rewritten_head) + self.assertEqual(result["status"], "pass") + self.assertNotIn( + "DETACHED_SOURCE_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_historical_same_blob_ancestor_cannot_masquerade_as_current(self) -> None: + historical_head = "c" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": historical_head}, + clear=True, + ): + result = self.verify( + branch="", + head=historical_head, + ancestor_pairs={(historical_head, self.sha)}, + ) + codes = {item["code"] for item in result["contradictions"]} + self.assertIn("SOURCE_MANIFEST_OBSERVATION_RELATIONSHIP_INVALID", codes) + self.assertIn("DETACHED_SOURCE_NOT_MANIFEST_SELECTED", codes) + + def test_rewritten_same_blob_requires_exact_reviewed_repository_tree(self) -> None: + rewritten_head = "d" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + tree_overrides={ + rewritten_head: "c" * 40, + self.sha: "e" * 40, + }, + ) + self.assertIn( + "SOURCE_MANIFEST_OBSERVATION_RELATIONSHIP_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_reviewed_ancestor_of_current_same_blob_merge_passes(self) -> None: + merge_head = "d" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "current_observed_head_sha" + ] = merge_head + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": merge_head}, + clear=True, + ): + result = self.verify( + branch="", + head=merge_head, + ancestor_pairs={(self.sha, merge_head)}, + tree_overrides={ + merge_head: "c" * 40, + self.sha: "e" * 40, + }, + ) + self.assertEqual("pass", result["status"]) + + def test_platform_snapshot_reviewed_ancestor_of_current_is_accepted(self) -> None: + merge_head = "d" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": merge_head}, + clear=True, + ): + result = self.verify( + branch="", + head=merge_head, + ancestor_pairs={(self.sha, merge_head)}, + ) + self.assertEqual("pass", result["status"]) + + def test_exact_content_head_equality_is_not_misclassified_as_future(self) -> None: + self.write_sources() + result = self.verify( + ancestor_pairs={(self.sha, self.sha)}, + ) + self.assertEqual("pass", result["status"]) + self.assertNotIn( + "SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_platform_snapshot_exact_tree_survives_rewritten_identity(self) -> None: + rewritten_head = "d" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + tree_overrides={ + rewritten_head: "e" * 40, + self.sha: "e" * 40, + }, + ) + self.assertEqual("pass", result["status"]) + + def test_content_commit_survives_rewritten_final_reviewed_tree(self) -> None: + content_commit = "c" * 40 + rewritten_head = "d" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "source_commit_sha" + ] = content_commit + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "source_observed_head_sha" + ] = content_commit + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "current_observed_head_sha" + ] = rewritten_head + for entry in self.review_manifest["repositories"]: + if entry["repository"] == "hawkinsoperations-platform": + entry["authority_content_revision"] = content_commit + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + ancestor_pairs={(content_commit, self.sha)}, + tree_overrides={ + content_commit: "c" * 40, + self.sha: "e" * 40, + rewritten_head: "e" * 40, + }, + ) + self.assertEqual("pass", result["status"]) + + def test_command_center_content_survives_squash_identity(self) -> None: + content_commit = "c" * 40 + rewritten_head = "d" * 40 + self.snapshot["source_revisions"][".github"]["source_commit_sha"] = ( + content_commit + ) + self.snapshot["source_revisions"][".github"][ + "source_observed_head_sha" + ] = content_commit + self.snapshot["source_revisions"][".github"][ + "current_observed_head_sha" + ] = rewritten_head + for entry in self.review_manifest["repositories"]: + if entry["repository"] == ".github": + entry["authority_content_revision"] = content_commit + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + tree_overrides={ + content_commit: "c" * 40, + self.sha: "e" * 40, + rewritten_head: "e" * 40, + }, + ) + self.assertEqual("pass", result["status"]) + self.assertNotIn( + "SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", + { + item["code"] + for item in result["contradictions"] + if item.get("repo") == ".github" + }, + ) + + def test_rewritten_tree_rejects_content_outside_reviewed_lineage(self) -> None: + content_commit = "c" * 40 + rewritten_head = "d" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "source_commit_sha" + ] = content_commit + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "source_observed_head_sha" + ] = content_commit + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "current_observed_head_sha" + ] = rewritten_head + for entry in self.review_manifest["repositories"]: + if entry["repository"] == "hawkinsoperations-platform": + entry["authority_content_revision"] = content_commit + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + tree_overrides={ + content_commit: "c" * 40, + self.sha: "e" * 40, + rewritten_head: "e" * 40, + }, + ) + self.assertIn( + "SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_platform_snapshot_current_ancestor_of_reviewed_is_rejected(self) -> None: + historical_head = "c" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": historical_head}, + clear=True, + ): + result = self.verify( + branch="", + head=historical_head, + ancestor_pairs={(historical_head, self.sha)}, + ) + codes = {item["code"] for item in result["contradictions"]} + self.assertIn("SOURCE_MANIFEST_OBSERVATION_RELATIONSHIP_INVALID", codes) + self.assertIn("SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", codes) + + def test_platform_snapshot_unrelated_detached_revision_is_rejected(self) -> None: + rewritten_head = "d" * 40 + unrelated_review = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-platform"][ + "current_observed_head_sha" + ] = unrelated_review + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": rewritten_head}, + clear=True, + ): + result = self.verify( + branch="", + head=rewritten_head, + tree_overrides={ + rewritten_head: "e" * 40, + self.sha: "e" * 40, + unrelated_review: "c" * 40, + }, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_command_center_reviewed_tree_mismatch_fails_closed(self) -> None: + for entry in self.review_manifest["repositories"]: + if entry["repository"] == "hawkinsoperations-platform": + entry["reviewed_tree_sha"] = "c" * 40 + self.write_sources() + result = self.verify() + self.assertIn( + "SOURCE_REVIEW_MANIFEST_TREE_MISMATCH", + {item["code"] for item in result["contradictions"]}, + ) + + def test_missing_command_center_review_manifest_fails_closed(self) -> None: + self.review_manifest_path.unlink() + result = self.verify() + self.assertIn( + "MALFORMED_SOURCE", + {item["code"] for item in result["contradictions"]}, + ) + + def test_command_center_self_content_revision_is_required(self) -> None: + del self.review_manifest["repositories"][0]["authority_content_revision"] + self.write_sources() + result = self.verify() + self.assertIn( + "MALFORMED_SOURCE", + {item["code"] for item in result["contradictions"]}, + ) + + def test_command_center_self_content_revision_resolves_from_full_history( + self, + ) -> None: + current_head = "d" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": current_head}, + clear=True, + ): + result = self.verify( + branch="", + head=current_head, + ancestor_pairs={(self.sha, current_head)}, + ) + self.assertEqual("pass", result["status"]) + + def test_command_center_same_content_survives_rewritten_event_tree( + self, + ) -> None: + content_commit = "c" * 40 + rewritten_head = "d" * 40 + self.snapshot["source_revisions"][".github"][ + "source_commit_sha" + ] = content_commit + self.snapshot["source_revisions"][".github"][ + "source_observed_head_sha" + ] = content_commit + self.snapshot["source_revisions"][".github"][ + "current_observed_head_sha" + ] = rewritten_head + self.review_manifest["repositories"][0][ + "authority_content_revision" + ] = content_commit + self.write_sources() + result = self.verify( + branch="feature/test", + head=rewritten_head, + ancestor_pairs={(self.sha, rewritten_head)}, + tree_overrides={ + content_commit: "c" * 40, + self.sha: "e" * 40, + rewritten_head: "e" * 40, + }, + ) + self.assertEqual("pass", result["status"]) + + def test_command_center_rewritten_event_rejects_changed_authority_blob( + self, + ) -> None: + content_commit = "c" * 40 + rewritten_head = "d" * 40 + self.snapshot["source_revisions"][".github"][ + "source_commit_sha" + ] = content_commit + self.snapshot["source_revisions"][".github"][ + "source_observed_head_sha" + ] = content_commit + self.snapshot["source_revisions"][".github"][ + "current_observed_head_sha" + ] = rewritten_head + self.review_manifest["repositories"][0][ + "authority_content_revision" + ] = content_commit + self.write_sources() + result = self.verify( + branch="rehearsal-squash", + head=rewritten_head, + blob_overrides={ + (".github", content_commit): "c" * 40, + (".github", rewritten_head): "b" * 40, + }, + tree_overrides={ + content_commit: "c" * 40, + self.sha: "e" * 40, + rewritten_head: "e" * 40, + }, + ) + self.assertIn( + "SOURCE_AUTHORITY_CONTENT_REVISION_STALE", + {item["code"] for item in result["contradictions"]}, + ) + + def test_command_center_self_future_content_revision_is_rejected(self) -> None: + historical_head = "c" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": historical_head}, + clear=True, + ): + result = self.verify( + branch="", + head=historical_head, + ancestor_pairs={(historical_head, self.sha)}, + ) + self.assertIn( + "SOURCE_AUTHORITY_CONTENT_RELATIONSHIP_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_source_workflow_fetches_full_history_for_all_seven_repositories( + self, + ) -> None: + workflow = ( + ROOT / ".github/workflows/hoxline-source-checks.yml" + ).read_text(encoding="utf-8") + self.assertEqual(7, workflow.count("fetch-depth: 0")) + self.assertNotIn("fetch-depth: 1", workflow) + self.assertIn('retired = "".join(("syn", "thetic"))', workflow) + self.assertIn('unicodedata.normalize("NFKC"', workflow) + self.assertIn('["git", "ls-files", "-z"]', workflow) + self.assertIn('["git", "show", f":{relative}"]', workflow) + self.assertIn("tracked non-binary content contains NUL", workflow) + self.assertGreaterEqual(workflow.count("check=True"), 2) + self.assertNotIn("git grep", workflow) + + def test_platform_source_manifest_matches_reviewed_command_center_matrix( + self, + ) -> None: + source_manifest = json.loads( + ( + ROOT + / "contracts" + / "hoxline-case-growth-source-manifest-v1.json" + ).read_text(encoding="utf-8") + ) + command_center_root = ROOT.parent / ".github" + if not command_center_root.is_dir(): + self.skipTest("command-center sibling checkout is unavailable") + command_center_revision = source_manifest["repositories"][".github"][ + "revision" + ] + command_center_manifest = json.loads( + subprocess.check_output( + [ + "git", + "-C", + str(command_center_root), + "show", + ( + f"{command_center_revision}:" + "governance/CONVERGENCE_SOURCE_MANIFEST.json" + ), + ], + text=True, + ) + ) + command_center_head = subprocess.check_output( + ["git", "-C", str(command_center_root), "rev-parse", "HEAD"], + text=True, + ).strip() + pinned_blob = subprocess.check_output( + [ + "git", + "-C", + str(command_center_root), + "rev-parse", + ( + f"{command_center_revision}:" + "governance/CONVERGENCE_SOURCE_MANIFEST.json" + ), + ], + text=True, + ).strip() + self.assertEqual( + source_manifest["repositories"][".github"][ + "authoritative_git_blob_sha" + ], + pinned_blob, + ) + current_blob = subprocess.check_output( + [ + "git", + "-C", + str(command_center_root), + "rev-parse", + "HEAD:governance/CONVERGENCE_SOURCE_MANIFEST.json", + ], + text=True, + ).strip() + self.assertRegex(command_center_head, r"^[0-9a-f]{40}$") + self.assertRegex(current_blob, r"^[0-9a-f]{40}$") + reviewed = ho_factory.hoxline_case_growth_review_manifest(ROOT.parent) + observations: dict[str, dict[str, str]] = {} + for repository, entry in source_manifest["repositories"].items(): + if repository in {".github", "hawkinsoperations-platform"}: + continue + repository_root = ROOT.parent / repository + reviewed_entry = reviewed[repository] + reviewed_revision = reviewed_entry["revision"] + content_revision = reviewed_entry["authority_content_revision"] + authority_path = ho_factory.HOXLINE_CASE_GROWTH_AUTHORITY_PATHS[ + repository + ] + observations[repository] = { + "selected_revision": entry["revision"], + "selected_blob": subprocess.check_output( + [ + "git", + "-C", + str(repository_root), + "rev-parse", + f"{entry['revision']}:{authority_path}", + ], + text=True, + ).strip(), + "checked_head": subprocess.check_output( + ["git", "-C", str(repository_root), "rev-parse", "HEAD"], + text=True, + ).strip(), + "checked_tree": subprocess.check_output( + [ + "git", + "-C", + str(repository_root), + "rev-parse", + "HEAD^{tree}", + ], + text=True, + ).strip(), + "checked_blob": subprocess.check_output( + [ + "git", + "-C", + str(repository_root), + "rev-parse", + f"HEAD:{authority_path}", + ], + text=True, + ).strip(), + "reviewed_revision": reviewed_revision, + "reviewed_tree": reviewed_entry["reviewed_tree_sha"], + "reviewed_blob": subprocess.check_output( + [ + "git", + "-C", + str(repository_root), + "rev-parse", + f"{reviewed_revision}:{authority_path}", + ], + text=True, + ).strip(), + "content_blob": subprocess.check_output( + [ + "git", + "-C", + str(repository_root), + "rev-parse", + f"{content_revision}:{authority_path}", + ], + text=True, + ).strip(), + } + + def matrix_findings( + values: dict[str, dict[str, str]], + ) -> set[str]: + findings: set[str] = set() + for repository, value in values.items(): + if value["reviewed_blob"] != value["content_blob"]: + findings.add(f"{repository}:authority_blob") + if value["selected_blob"] != value["reviewed_blob"]: + findings.add(f"{repository}:selected_authority_blob") + if value["checked_blob"] != value["reviewed_blob"]: + findings.add(f"{repository}:checked_authority_blob") + return findings + + self.assertEqual(set(), matrix_findings(observations)) + + wrong_head = json.loads(json.dumps(observations)) + wrong_head["hawkinsoperations-detections"]["checked_head"] = "f" * 40 + self.assertEqual(set(), matrix_findings(wrong_head)) + wrong_tree = json.loads(json.dumps(observations)) + wrong_tree["hawkinsoperations-detections"]["reviewed_tree"] = "e" * 40 + self.assertEqual(set(), matrix_findings(wrong_tree)) + wrong_blob = json.loads(json.dumps(observations)) + wrong_blob["hawkinsoperations-detections"]["content_blob"] = "d" * 40 + self.assertIn( + "hawkinsoperations-detections:authority_blob", + matrix_findings(wrong_blob), + ) + wrong_selected_blob = json.loads(json.dumps(observations)) + wrong_selected_blob["hawkinsoperations-detections"]["selected_blob"] = ( + "c" * 40 + ) + self.assertIn( + "hawkinsoperations-detections:selected_authority_blob", + matrix_findings(wrong_selected_blob), + ) + wrong_checked_blob = json.loads(json.dumps(observations)) + wrong_checked_blob["hawkinsoperations-detections"]["checked_blob"] = ( + "c" * 40 + ) + self.assertIn( + "hawkinsoperations-detections:checked_authority_blob", + matrix_findings(wrong_checked_blob), + ) + + pinned_entries = ho_factory.hoxline_case_growth_validate_review_manifest( + command_center_manifest, + "pinned command-center manifest", + ) + current_entries = ho_factory.hoxline_case_growth_review_manifest( + ROOT.parent + ) + self.assertEqual( + ho_factory.hoxline_case_growth_review_authority_projection( + ROOT.parent, + pinned_entries, + "pinned command-center manifest", + ), + ho_factory.hoxline_case_growth_review_authority_projection( + ROOT.parent, + current_entries, + "checked command-center manifest", + ), + ) + + def test_pinned_command_center_object_missing_fails_closed(self) -> None: + result = self.verify(missing_blobs={(".github", self.sha)}) + self.assertIn( + "MALFORMED_SOURCE", + {item["code"] for item in result["contradictions"]}, + ) + self.assertIn( + "fetch immutable HawkinsOperations/.github object", + result["contradictions"][0]["actual"], + ) + + def test_pinned_projection_rejects_changed_noncyclic_authority_blob( + self, + ) -> None: + pinned_entries = { + entry["repository"]: entry + for entry in json.loads(json.dumps(self.review_manifest))[ + "repositories" + ] + } + current_entries = json.loads(json.dumps(pinned_entries)) + current_entries["hawkinsoperations-detections"].update( + { + "revision": "c" * 40, + "authority_content_revision": "d" * 40, + "reviewed_tree_sha": "f" * 40, + } + ) + + def tree(_repo: Path, revision: str) -> str: + return "f" * 40 if revision == "c" * 40 else "e" * 40 + + def blob(repo: Path, revision: str, relative_path: str): + blob_sha = ( + "c" * 40 + if repo.name == "hawkinsoperations-detections" + and revision in {"c" * 40, "d" * 40} + else "b" * 40 + ) + return blob_sha, (repo / relative_path).read_bytes() + + with mock.patch.object( + ho_factory, + "hoxline_case_growth_tree_sha", + side_effect=tree, + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_git_blob", + side_effect=blob, + ): + pinned = ho_factory.hoxline_case_growth_review_authority_projection( + self.org_root, + pinned_entries, + "pinned", + ) + current = ho_factory.hoxline_case_growth_review_authority_projection( + self.org_root, + current_entries, + "current", + ) + self.assertNotEqual(pinned, current) + + def test_noncyclic_revision_rewrite_with_same_authority_blob_passes( + self, + ) -> None: + pinned_entries = { + entry["repository"]: entry + for entry in json.loads(json.dumps(self.review_manifest))[ + "repositories" + ] + } + current_entries = json.loads(json.dumps(pinned_entries)) + current_entries["hawkinsoperations-detections"].update( + { + "revision": "c" * 40, + "authority_content_revision": "d" * 40, + "reviewed_tree_sha": "f" * 40, + } + ) + + def tree(_repo: Path, revision: str) -> str: + return "f" * 40 if revision == "c" * 40 else "e" * 40 + + def blob(repo: Path, _revision: str, relative_path: str): + return "b" * 40, (repo / relative_path).read_bytes() + + with mock.patch.object( + ho_factory, + "hoxline_case_growth_tree_sha", + side_effect=tree, + ), mock.patch.object( + ho_factory, + "hoxline_case_growth_git_blob", + side_effect=blob, + ): + pinned = ho_factory.hoxline_case_growth_review_authority_projection( + self.org_root, + pinned_entries, + "pinned", + ) + current = ho_factory.hoxline_case_growth_review_authority_projection( + self.org_root, + current_entries, + "current", + ) + self.assertEqual(pinned, current) + + def test_source_manifest_rejects_embedded_projection_extension(self) -> None: + self.source_manifest["repositories"][".github"][ + "acyclic_projection_sha256" + ] = "d" * 64 + self.write_sources() + result = self.verify() + self.assertIn( + "MALFORMED_SOURCE", + {item["code"] for item in result["contradictions"]}, + ) + + def test_source_workflow_vocabulary_guard_rejects_nfkc_utf16_and_git_errors( + self, + ) -> None: + workflow_path = ROOT / ".github/workflows/hoxline-source-checks.yml" + retired = "".join(("syn", "thetic")) + fullwidth = "".join(chr(ord(character) + 0xFEE0) for character in retired) + self.assertEqual( + retired, + unicodedata.normalize("NFKC", fullwidth).casefold(), + ) + result = run_workflow_vocabulary_guard( + workflow_path, + { + f"fixture-{fullwidth}.txt": b"controlled-test\n", + "content-fixture.txt": f"{fullwidth}\n".encode(), + "utf16-fixture.md": f"{retired}\n".encode("utf-16-le"), + }, + ) + self.assertNotEqual(0, result.returncode) + self.assertIn("utf16-fixture.md", result.stderr + result.stdout) + workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) + step = next( + item + for item in next(iter(workflow["jobs"].values()))["steps"] + if item.get("name") == "Reject retired fixture vocabulary" + ) + source = step["run"].split("<<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] + with tempfile.TemporaryDirectory() as temp: + operational = subprocess.run( + [sys.executable, "-c", source], + cwd=temp, + capture_output=True, + text=True, + ) + self.assertNotEqual(0, operational.returncode) + + def test_source_workflow_vocabulary_guard_classifies_sqlite_as_binary( + self, + ) -> None: + workflow_path = ROOT / ".github/workflows/hoxline-source-checks.yml" + retired = "".join(("syn", "thetic")) + sqlite = run_workflow_vocabulary_guard( + workflow_path, + { + "evidence/autosoc-case-ledger-v0.sqlite": ( + ROOT / "evidence" / "autosoc-case-ledger-v0.sqlite" + ).read_bytes(), + }, + ) + self.assertEqual(0, sqlite.returncode, sqlite.stderr + sqlite.stdout) + + plaintext_masquerade = run_workflow_vocabulary_guard( + workflow_path, + { + "evidence/autosoc-case-ledger-v0.sqlite": ( + f"retired={retired}\n".encode("utf-8") + ), + }, + ) + self.assertNotEqual(0, plaintext_masquerade.returncode) + self.assertIn( + "tracked SQLite content has invalid file signature", + plaintext_masquerade.stderr + plaintext_masquerade.stdout, + ) + + malformed_sqlite = run_workflow_vocabulary_guard( + workflow_path, + { + "evidence/autosoc-case-ledger-v0.sqlite": ( + b"not-a-sqlite-database" + ), + }, + ) + self.assertNotEqual(0, malformed_sqlite.returncode) + self.assertIn( + "tracked SQLite content has invalid file signature", + malformed_sqlite.stderr + malformed_sqlite.stdout, + ) + + magic_prefix_masquerade = run_workflow_vocabulary_guard( + workflow_path, + { + "evidence/autosoc-case-ledger-v0.sqlite": ( + b"SQLite format 3\x00" + retired.encode("utf-8") + ), + }, + ) + self.assertNotEqual(0, magic_prefix_masquerade.returncode) + self.assertIn( + "tracked SQLite blob identity is not approved", + magic_prefix_masquerade.stderr + magic_prefix_masquerade.stdout, + ) + + valid_database_replacement = run_workflow_vocabulary_guard( + workflow_path, + { + "evidence/autosoc-case-ledger-v0.sqlite": valid_sqlite_blob(), + }, + ) + self.assertNotEqual(0, valid_database_replacement.returncode) + self.assertIn( + "tracked SQLite blob identity is not approved", + valid_database_replacement.stderr + + valid_database_replacement.stdout, + ) + + unapproved_sqlite = run_workflow_vocabulary_guard( + workflow_path, + {"evidence/alternate.sqlite": valid_sqlite_blob()}, + ) + self.assertNotEqual(0, unapproved_sqlite.returncode) + self.assertIn( + "unapproved tracked SQLite path", + unapproved_sqlite.stderr + unapproved_sqlite.stdout, + ) + + unknown_binary = run_workflow_vocabulary_guard( + workflow_path, + {"evidence/unclassified-ledger.payload": b"binary\x00content"}, + ) + self.assertNotEqual(0, unknown_binary.returncode) + self.assertIn( + "tracked non-binary content contains NUL", + unknown_binary.stderr + unknown_binary.stdout, + ) + + def test_controlled_test_truth_class_replaces_retired_factory_token(self) -> None: + factory = SCRIPT_PATH.read_text(encoding="utf-8") + controller = ( + ROOT / "docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md" + ).read_text(encoding="utf-8") + retired = "SYN" + "THETIC_TEST_CASE" + self.assertIn("CONTROLLED_TEST_CASE", factory) + self.assertIn("CONTROLLED_TEST_CASE", controller) + self.assertNotIn(retired, factory) + self.assertNotIn(retired, controller) + + def test_retired_ledger_schema_is_adapted_without_mutating_existing_rows(self) -> None: + retired = ho_factory.retired_controlled_test_storage_class() + schema = ho_factory.case_ledger_schema_sql().replace( + "'CONTROLLED_TEST_CASE'", + f"'{retired}'", + ) + conn = sqlite3.connect(":memory:") + self.addCleanup(conn.close) + conn.executescript(schema) + ho_factory.initialize_ledger_schema(conn) + event = { + "event_hash": "a" * 64, + "inserted_at": "2026-07-24T00:00:00Z", + "ledger_version": ho_factory.CASE_LEDGER_VERSION, + "case_id": "CASE-CONTROLLED-001", + "detection_id": "HO-DET-001", + "truth_class": "CONTROLLED_TEST_CASE", + "case_status": "HUMAN_REVIEW_REQUIRED", + "proof_ceiling": "CONTROLLED_TEST_VALIDATED", + "public_safe_status": "NOT_PUBLIC_SAFE", + "ai_support_mode": "AI_SUPPORT_ONLY", + "ai_decided_disposition": False, + "recommended_disposition": None, + "deterministic_close_eligible": False, + "deterministic_close_blocked": True, + "human_review_required": True, + "gpu_supported": False, + "public_safe": False, + "proof_blocked": True, + "github_issue_mutation_allowed": False, + "case_closed": False, + "legacy_import_count": 0, + "payload_json": {"truth_boundary": "controlled test only; not proof"}, + "source_packet_ref": "controlled-test-fixtures/CASE-CONTROLLED-001.json", + } + self.assertEqual("inserted", ho_factory.insert_case_event(conn, event)) + raw_truth_class = conn.execute( + "SELECT truth_class FROM case_events" + ).fetchone()[0] + self.assertEqual(retired, raw_truth_class) + normalized = ho_factory.row_to_event( + conn, + conn.execute("SELECT * FROM case_events").fetchone(), + ) + self.assertEqual("CONTROLLED_TEST_CASE", normalized["truth_class"]) + self.assertEqual( + {"CONTROLLED_TEST_CASE": 1}, + ho_factory.ledger_metrics(conn)["cases_by_truth_class"], + ) + lifetime = ho_factory.lifetime_ledger_metrics(conn) + self.assertEqual(1, lifetime["validation_only_count"]) + self.assertEqual( + {"CONTROLLED_TEST_CASE": 1}, + lifetime["cases_by_truth_class"], + ) + self.assertEqual("pass", ho_factory.verify_ledger(conn)["ledger_verifier"]) + + def test_arbitrary_same_blob_observation_not_selected_by_manifest_fails_closed(self) -> None: + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = "c" * 40 + self.write_sources() + result = self.verify( + ancestor_pairs={("c" * 40, self.sha)}, + tree_overrides={"c" * 40: "d" * 40}, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_hoxline_generated_pair_selects_its_exact_content_parent(self) -> None: + content_commit = "c" * 40 + self.snapshot["source_revisions"]["hoxline"][ + "current_observed_head_sha" + ] = content_commit + self.write_sources() + result = self.verify( + direct_parent_pairs={(content_commit, self.sha)}, + ) + self.assertEqual("pass", result["status"]) + self.assertNotIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_rewritten_head_accepts_exact_reviewed_consumer_projection(self) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"]["hoxline"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + ancestor_pairs={ + (self.sha, stated_sha), + (self.sha, rewritten_head), + }, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + changed_paths_overrides={ + ("hoxline", stated_sha, rewritten_head): { + "examples/case-growth/current-case-growth-index.json", + "examples/case-growth/current-case-growth-index.md", + } + }, + ) + self.assertEqual("pass", result["status"], result) + self.assertNotIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_rewritten_head_rejects_foreign_exact_path_projection(self) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"]["hoxline"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + ancestor_pairs={(self.sha, rewritten_head)}, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + changed_paths_overrides={ + ("hoxline", stated_sha, rewritten_head): { + "examples/case-growth/current-case-growth-index.json", + "examples/case-growth/current-case-growth-index.md", + } + }, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_squash_rewrite_accepts_selected_lineage_observation(self) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + ancestor_pairs={(self.sha, stated_sha)}, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + ) + self.assertEqual("pass", result["status"], result) + self.assertNotIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_squash_rewrite_rejects_observation_outside_selected_lineage( + self, + ) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_rewritten_command_center_accepts_exact_manifest_projection(self) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"][".github"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + ancestor_pairs={ + (self.sha, stated_sha), + (self.sha, rewritten_head), + }, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + changed_paths_overrides={ + (".github", stated_sha, rewritten_head): { + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + } + }, + ) + self.assertEqual("pass", result["status"], result) + self.assertNotIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_rewritten_head_rejects_projection_with_extra_path(self) -> None: + stated_sha = "c" * 40 + rewritten_head = "d" * 40 + reviewed_tree = "f" * 40 + self.snapshot["source_revisions"]["hoxline"][ + "current_observed_head_sha" + ] = stated_sha + for entry in self.review_manifest["repositories"]: + if "reviewed_tree_sha" in entry: + entry["reviewed_tree_sha"] = reviewed_tree + self.write_sources() + result = self.verify( + head=rewritten_head, + ancestor_pairs={ + (self.sha, stated_sha), + (self.sha, rewritten_head), + }, + tree_overrides={ + stated_sha: "1" * 40, + rewritten_head: reviewed_tree, + self.sha: reviewed_tree, + }, + changed_paths_overrides={ + ("hoxline", stated_sha, rewritten_head): { + "examples/case-growth/current-case-growth-index.json", + "examples/case-growth/current-case-growth-index.md", + "src/hoxline/case_growth/collector.py", + } + }, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_detached_arbitrary_third_same_blob_observation_fails_closed(self) -> None: + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = "c" * 40 + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": "d" * 40}, + clear=True, + ): + result = self.verify( + branch="", + head="d" * 40, + tree_overrides={"c" * 40: "b" * 40}, + ) + self.assertIn( + "SOURCE_OBSERVATION_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_detached_foreign_or_unreachable_observation_fails_closed(self) -> None: + foreign_sha = "f" * 40 + self.snapshot["source_revisions"]["hawkinsoperations-proof"][ + "current_observed_head_sha" + ] = foreign_sha + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": "d" * 40}, + clear=True, + ): + result = self.verify( + branch="", + head="d" * 40, + missing_commits={foreign_sha}, + ) + self.assertIn( + "SOURCE_REVISION_UNRESOLVED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_detached_manifest_selected_changed_blob_fails_closed(self) -> None: + self.write_sources() + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": "d" * 40}, + clear=True, + ): + result = self.verify( + branch="", + head="d" * 40, + blob_overrides={ + ("hawkinsoperations-proof", self.sha): "c" * 40, + }, + ) + codes = {item["code"] for item in result["contradictions"]} + self.assertIn("SOURCE_MANIFEST_CONTENT_STALE", codes) + self.assertIn("DETACHED_SOURCE_NOT_MANIFEST_SELECTED", codes) + + def test_detached_platform_without_exact_checked_observation_fails(self) -> None: + self.write_sources() + with mock.patch.dict(ho_factory.os.environ, {}, clear=True): + result = self.verify(branch="", head="d" * 40) + self.assertIn( + "DETACHED_SOURCE_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_arbitrary_observation_cannot_override_current_authority_blob(self) -> None: + self.snapshot["source_revisions"]["hawkinsoperations-proof"]["source_git_blob_sha"] = "c" * 40 + self.write_sources() + result = self.verify() + self.assertIn("SOURCE_AUTHORITY_BLOB_DRIFT", {item["code"] for item in result["contradictions"]}) + + def test_forged_hoxline_proof_count_fails_closed(self) -> None: + self.snapshot["summary"]["proof_records_count"] = 99 + self.write_sources() + result = self.verify() + self.assertIn("HOXLINE_PROOF_COUNT_DRIFT", {item["code"] for item in result["contradictions"]}) + + def test_encoded_proof_path_escape_fails_closed(self) -> None: + self.proof_index["entries"][0]["proof_record_path"] = ( + "proof/records/%252e%252e/private.md" + ) + self.write_sources() + result = self.verify() + self.assertIn( + "UNSAFE_PROOF_PATH", + {item["code"] for item in result["contradictions"]}, + ) + + def test_stale_website_labeled_fresh_fails_closed(self) -> None: + self.website["generated_at"] = "2026-06-01T00:00:00Z" + self.write_sources() + result = self.verify() + self.assertIn("WEBSITE_FRESHNESS_CONTRADICTION", {item["code"] for item in result["contradictions"]}) + + def test_stale_platform_contract_fails_closed(self) -> None: + self.contract["generated_at"] = "2026-06-01T00:00:00Z" + self.write_sources() + result = self.verify() + self.assertIn("PLATFORM_CONTRACT_STALE", {item["code"] for item in result["contradictions"]}) + + def test_duplicate_source_revision_entry_fails_closed(self) -> None: + self.snapshot["source_revisions"] = [ + {"repo": repo, "source_commit_sha": self.sha} for repo in ho_factory.HOXLINE_CASE_GROWTH_REPOS + ] + self.snapshot["source_revisions"].append({"repo": "hoxline", "source_commit_sha": self.sha}) + self.write_sources() + result = self.verify() + self.assertIn("SOURCE_REVISION_SET_INVALID", {item["code"] for item in result["contradictions"]}) + + def test_website_owner_suffix_bypass_fails_closed(self) -> None: + self.website["metrics"]["proof_records"]["source_repo"] = "evil-hawkinsoperations-proof" + self.write_sources() + result = self.verify() + self.assertIn("WEBSITE_PROOF_OWNER_INVALID", {item["code"] for item in result["contradictions"]}) + + def test_repository_owner_suffix_spoof_fails_closed(self) -> None: + result = self.verify(origin_override="https://github.com/evil/hawkinsoperations-proof.git") + self.assertIn( + "SOURCE_REPOSITORY_IDENTITY_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_git_state_rejects_ambient_instead_of_origin_laundering(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + repo = Path(temp_dir) / "repo" + repo.mkdir() + + def git(*args: str) -> str: + return subprocess.run( + ["git", "-C", str(repo), *args], + check=True, + capture_output=True, + text=True, + ).stdout.strip() + + git("init") + git("config", "user.name", "Platform Test") + git("config", "user.email", "platform-test@example.invalid") + stored_origin = "C:/hostile/local-platform" + canonical = ( + "https://github.com/HawkinsOperations/" + "hawkinsoperations-platform.git" + ) + git("remote", "add", "origin", stored_origin) + (repo / "tracked.txt").write_text("controlled\n", encoding="utf-8") + git("add", "tracked.txt") + git("commit", "-m", "fixture") + hostile_env = { + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": f"url.{canonical}.insteadOf", + "GIT_CONFIG_VALUE_0": stored_origin, + } + with mock.patch.dict(ho_factory.os.environ, hostile_env, clear=False): + self.assertEqual(git("remote", "get-url", "origin"), canonical) + state = ho_factory.hoxline_case_growth_git_state(repo) + self.assertEqual(state["origin"], stored_origin) + + def test_git_state_rejects_empty_duplicate_origin_in_both_orders(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + repo = Path(temp_dir) / "repo" + repo.mkdir() + + def git(*args: str) -> None: + subprocess.run( + ["git", "-C", str(repo), *args], + check=True, + capture_output=True, + text=True, + ) + + git("init") + git("config", "user.name", "Platform Test") + git("config", "user.email", "platform-test@example.invalid") + canonical = ( + "https://github.com/HawkinsOperations/" + "hawkinsoperations-platform.git" + ) + (repo / "tracked.txt").write_text("controlled\n", encoding="utf-8") + git("add", "tracked.txt") + git("commit", "-m", "fixture") + git("config", "--add", "remote.origin.url", canonical) + + for origins in ((canonical, ""), ("", canonical)): + git("config", "--unset-all", "remote.origin.url") + for origin in origins: + git("config", "--add", "remote.origin.url", origin) + with self.assertRaisesRegex( + ho_factory.FactoryError, + "exactly one nonempty origin URL", + ): + ho_factory.hoxline_case_growth_git_state(repo) + + def test_git_state_ignores_ambient_repository_and_index_redirection(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + base = Path(temp_dir) + target = base / "target" + decoy = base / "decoy" + + def git(repo: Path, *args: str) -> str: + return subprocess.run( + ["git", "-C", str(repo), *args], + check=True, + capture_output=True, + text=True, + ).stdout.strip() + + for repo, origin, content in ( + (target, "C:/hostile/target", "target\n"), + ( + decoy, + "https://github.com/HawkinsOperations/hawkinsoperations-platform.git", + "decoy\n", + ), + ): + repo.mkdir() + git(repo, "init") + git(repo, "config", "user.name", "Platform Test") + git(repo, "config", "user.email", "platform-test@example.invalid") + git(repo, "remote", "add", "origin", origin) + (repo / "tracked.txt").write_text(content, encoding="utf-8") + git(repo, "add", "tracked.txt") + git(repo, "commit", "-m", "fixture") + + target_head = git(target, "rev-parse", "HEAD") + self.assertNotEqual(target_head, git(decoy, "rev-parse", "HEAD")) + (target / "tracked.txt").write_text("changed\n", encoding="utf-8") + with mock.patch.dict( + ho_factory.os.environ, + { + "GIT_DIR": str(decoy / ".git"), + "GIT_WORK_TREE": str(decoy), + "GIT_INDEX_FILE": str(decoy / ".git" / "index"), + "GIT_OBJECT_DIRECTORY": str(decoy / ".git" / "objects"), + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": "core.repositoryformatversion", + "GIT_CONFIG_VALUE_0": "0", + }, + clear=False, + ): + state = ho_factory.hoxline_case_growth_git_state(target) + self.assertEqual(state["origin"], "C:/hostile/target") + self.assertEqual(state["head"], target_head) + self.assertTrue(state["dirty"]) + + def test_dirty_authority_sources_fail_closed(self) -> None: + result = self.verify(dirty=True) + self.assertIn("SOURCE_WORKTREE_DIRTY", {item["code"] for item in result["contradictions"]}) + + def test_detached_platform_requires_exact_checked_observation_sha(self) -> None: + with mock.patch.dict(ho_factory.os.environ, {}, clear=True): + result = self.verify(branch="") + self.assertIn( + "DETACHED_SOURCE_NOT_MANIFEST_SELECTED", + {item["code"] for item in result["contradictions"]}, + ) + + def test_detached_platform_rejects_another_repository_event_sha(self) -> None: + with mock.patch.dict( + ho_factory.os.environ, + {"HAWKINS_PLATFORM_IMMUTABLE_OBSERVED_SHA": "f" * 40}, + clear=True, + ): + result = self.verify(branch="", head="d" * 40) + self.assertIn( + "SOURCE_PLATFORM_OBSERVATION_MISMATCH", + {item["code"] for item in result["contradictions"]}, + ) + + def test_future_generated_at_fails_closed(self) -> None: + self.website["generated_at"] = "2026-07-23T12:00:00Z" + self.contract["generated_at"] = "2026-07-23T12:00:00Z" + self.write_sources() + result = self.verify() + codes = {item["code"] for item in result["contradictions"]} + self.assertIn("WEBSITE_GENERATED_AT_FUTURE", codes) + self.assertIn("PLATFORM_CONTRACT_GENERATED_AT_FUTURE", codes) + + def test_timezone_naive_freshness_observations_fail_closed(self) -> None: + self.website["generated_at"] = "2026-07-22T12:00:00" + self.contract["generated_at"] = "2026-07-22T12:00:00" + self.write_sources() + result = self.verify() + codes = {item["code"] for item in result["contradictions"]} + self.assertIn("WEBSITE_FRESHNESS_UNRESOLVED", codes) + self.assertIn("PLATFORM_CONTRACT_FRESHNESS_UNRESOLVED", codes) + + def test_nested_authority_promotion_fails_closed(self) -> None: + attacks = ( + ("ai-authority", True), + ("production_active", True), + ("production_live", {"enabled": True}), + ("customer_deployment", True), + ("socaas_deployment", True), + ("runtime_status", "active"), + ("signal_status", "observed"), + ("approval_status", "approved"), + ("closure_status", "closed"), + ("case_status", "closed"), + ("public_safe_runtime", True), + ("final_authorized", True), + ("%70roduction_active", True), + ) + for key, value in attacks: + with self.subTest(key=key): + self.website["extensions"] = {"opaque": [{key: value}]} + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_split_and_direct_authority_state_paths_fail_closed(self) -> None: + attacks = ( + {"runtime": {"state": True}}, + {"signal": {"observed": True}}, + {"public": {"safe": True}}, + {"approval": {"status": True}}, + {"production": {"active": True}}, + {"customer": {"deployed": True}}, + {"socaas": {"deployed": True}}, + {"ai": {"authority": True}}, + {"analyst": {"approval": True}}, + {"review": {"disposition": "APPROVED"}}, + {"final": {"authorization": True}}, + {"case": {"closed": True}}, + {"extensions": [{"final": {"authorization": True}}]}, + {"runtime": {"metadata": {"state": True}}}, + {"final": {"review": {"authorization": True}}}, + {"ai": {"metadata": {"authority": True}}}, + {"customer": {"review": {"deployed": True}}}, + {"review": {"metadata": {"disposition": "APPROVED"}}}, + {"production_live": {"enabled": True}}, + {"ai_authority": {"enabled": True}}, + {"review_disposition": {"approved": True}}, + {"final_authorization": {"granted": True}}, + {"runtime_state": True}, + {"approval_state": True}, + {"production_state": True}, + {"customer_state": True}, + {"socaas_state": True}, + {"final_authority": True}, + {"case_state": True}, + ) + for attack in attacks: + with self.subTest(attack=attack): + self.assertTrue( + ho_factory.hoxline_case_growth_authority_violations(attack) + ) + + def test_split_and_direct_authority_state_bounded_controls_pass(self) -> None: + controls = ( + {"runtime": {"state": False}}, + {"signal": {"observed": False}}, + {"public": {"safe": "NOT_PUBLIC_SAFE"}}, + {"approval": {"status": "NOT_APPROVED"}}, + {"production": {"active": "BLOCKED"}}, + {"customer": {"deployed": False}}, + {"socaas": {"deployed": False}}, + {"ai": {"authority": False}}, + {"analyst": {"approval": "NOT_APPROVED"}}, + {"review": {"disposition": "NOT_APPROVED"}}, + {"final": {"authorization": "BLOCKED"}}, + {"case": {"closed": False}}, + {"extensions": [{"final": {"authorization": "BLOCKED"}}]}, + {"runtime_state": False}, + {"approval_state": "NOT_APPROVED"}, + {"production_state": "BLOCKED"}, + {"customer_state": False}, + {"socaas_state": False}, + {"final_authority": False}, + {"case_state": False}, + {"production_live": {"enabled": False}}, + {"ai_authority": {"enabled": False}}, + {"review_disposition": {"approved": "NOT_APPROVED"}}, + {"final_authorization": {"granted": "BLOCKED"}}, + ) + for control in controls: + with self.subTest(control=control): + self.assertEqual( + [], + ho_factory.hoxline_case_growth_authority_violations(control), + ) + + def test_compound_owned_context_names_remain_bounded(self) -> None: + self.assertEqual( + [], + ho_factory.hoxline_case_growth_authority_violations( + { + "runtime_truth_spine": { + "runtime_truth": { + "state": "RUNTIME_EVIDENCE_VERIFIED_PRIVATE" + } + }, + "socaas_pilot_receipt_flow": { + "pilot_status": "EXISTING_FLOW_CANDIDATE" + }, + } + ), + ) + + def test_current_hoxline_snapshot_bounded_states_do_not_promote(self) -> None: + current_snapshot_authority_surface = { + "case_growth_health": { + "not_public_safe_percent": 100.0, + }, + "cases": [ + { + "case_state": "BLOCKED_WAITING_NEXT_GATE", + "runtime_candidate_status": "PRIVATE_RUNTIME_CANDIDATE", + }, + { + "case_state": "BLOCKED_WAITING_NEXT_GATE", + "runtime_candidate_status": "NOT_INDEXED", + }, + { + "case_state": "BLOCKED_WAITING_NEXT_GATE", + "runtime_candidate_status": "LISTED_ONLY", + "notes": [ + ( + "Controlled fixture validation only. This does not prove " + "runtime-active, signal-observed, production-ready, or " + "public-safe status." + ) + ], + }, + { + "case_state": "BLOCKED_WAITING_NEXT_GATE", + "runtime_candidate_status": "TELEMETRY_CONTRACT_ONLY", + }, + ], + } + + self.assertEqual( + [], + ho_factory.hoxline_case_growth_authority_violations( + current_snapshot_authority_surface + ), + ) + + def test_authority_violation_diagnostics_are_deduplicated(self) -> None: + violations = ho_factory.hoxline_case_growth_authority_violations( + {"case_state": "CLOSED"} + ) + + self.assertEqual([("case_state", "CLOSED")], violations) + + def test_bounded_contract_metadata_does_not_inherit_public_safe_promotion(self) -> None: + contract_metadata = { + "public_safe_state": { + "owner_repo": "hawkinsoperations-proof", + "source_path": "proof/index.json", + "render_allowed": True, + "source_status": "BOUNDARY_DEFAULT_NOT_PROMOTED", + "freshness_policy": ( + "Remain NOT_PUBLIC_SAFE unless proof-owned authority changes it." + ), + }, + "public_safe_candidate_reviews": [ + { + "artifact_id": "HO-DET-001", + "review_lane": "PUBLIC_SAFE_CANDIDATE_REVIEW_V1", + "human_review_required": True, + "allowed_claims": [ + "Controlled validation remains under public-safe candidate review." + ], + "blocked_claims": ["runtime active", "case closed"], + } + ], + } + + self.assertEqual( + [], + ho_factory.hoxline_case_growth_authority_violations( + contract_metadata + ), + ) + + def test_exact_public_safe_source_owner_pointer_is_bounded(self) -> None: + bounded = { + "public_safe_policy": { + "public_safe_source_required": "hawkinsoperations-proof" + } + } + self.assertEqual( + [], + ho_factory.hoxline_case_growth_authority_violations(bounded), + ) + for hostile in ("PUBLIC_SAFE", True): + with self.subTest(hostile=hostile): + attack = { + "public_safe_policy": { + "public_safe_source_required": hostile + } + } + self.assertTrue( + ho_factory.hoxline_case_growth_authority_violations(attack) + ) + + def test_negative_metadata_paths_do_not_exempt_structured_laundering(self) -> None: + attacks = ( + {"freshness_policy": {"production_live": True}}, + {"blocked_claims": [{"ai_authority": {"enabled": True}}]}, + { + "no_proof_promotion_statement": { + "public_safe_status": "PUBLIC_SAFE" + } + }, + ) + + for attack in attacks: + with self.subTest(attack=attack): + self.assertTrue( + ho_factory.hoxline_case_growth_authority_violations(attack) + ) + + def test_nested_authority_string_laundering_fails_closed(self) -> None: + self.website["extensions"] = { + "opaque": [{"note": "customer deployed and production ready"}] + } + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_nested_authority_vocabulary_variants_fail_closed(self) -> None: + attacks = ( + "deployed to customer Acme", + "customer environment deployed", + "production is live", + "analyst approved this disposition", + "final authorization received", + "case closure complete", + "runtime is active", + "signal was observed", + "public safe for release", + "SOCaaS is deployed", + ) + for prose in attacks: + with self.subTest(prose=prose): + self.website["extensions"] = {"opaque": [{"note": prose}]} + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_blocked_claim_container_cannot_exempt_nested_prose(self) -> None: + self.website["extensions"] = { + "blocked_claims": [{"detail": "customer deployment is active"}] + } + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_nested_plain_public_safe_and_ai_authority_prose_fails_closed(self) -> None: + for prose in ("public safe", "AI authority enabled"): + with self.subTest(prose=prose): + self.website["extensions"] = {"opaque": [{"note": prose}]} + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_clause_local_negative_prose_remains_bounded(self) -> None: + for prose in ( + "does not prove customer deployed", + "missing production ready", + ( + "This does not prove runtime-active status, signal-observed " + "status, production-ready status, public-safe status, " + "AI-approved status, or analyst-approved status." + ), + ( + "Render only bounded metadata; never treat rendering as proof, " + "runtime truth, public-safe status, final authorization, or " + "case closure." + ), + ( + "Controlled Windows local user creation fixture validation only. " + "This does not prove runtime-active, signal-observed, " + "production-ready, public-safe status, or account lifecycle " + "completeness." + ), + ( + "Website rendering/reporting only. Does not prove runtime, " + "signal, production, public-safe proof, customer deployment, " + "final approval, merge readiness, or website-as-proof." + ), + ( + "Render only bounded source-route metadata; do not treat the " + "source manifest as proof, runtime truth, signal truth, " + "public-safe status, production readiness, customer deployment, " + "SOCaaS deployment, approval, authorization, or closure." + ), + "Public-safe proof is not present in approved public artifacts.", + "Public-safe runtime proof is not promoted by this website data plane.", + ( + "Runtime, signal, public-safe, production, customer, AI " + "approval, final authorization, and case closure claims " + "remain blocked." + ), + "Café résumé – reviewer note.", + ): + with self.subTest(prose=prose): + self.website["extensions"] = {"opaque": [{"note": prose}]} + self.write_sources() + result = self.verify() + self.assertNotIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_negation_cannot_launder_later_adversative_promotion(self) -> None: + attacks = ( + "does not prove customer deployed, but public safe", + "does not prove runtime, customer deployment is active", + "does not prove runtime, AI authority is enabled", + "does not prove runtime, analyst approval granted", + "does not prove runtime, public safe is confirmed", + "does not prove runtime, final authorization received", + "does not prove runtime, case closure approved", + "does not prove runtime and customer deployment is active", + "does not prove runtime plus public safe is confirmed", + "does not prove runtime though case closure is approved", + "public\u200b safe is confirmed", + "case\u200b closure approved", + "AI\u200b authority is enabled", + "runtime\u200b is active", + "pending documentation, production is live", + "unsupported note — customer environment deployed", + "future issue: signal was observed", + "missing receipt while production is live", + "no proof currently, customer environment deployed", + "not approved / production is live", + ) + for prose in attacks: + with self.subTest(prose=prose): + self.website["extensions"] = {"opaque": [{"note": prose}]} + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_combining_mark_obfuscation_in_nested_shapes_fails_closed(self) -> None: + templates = ( + "public\\u{code} safe is confirmed", + "case\\u{code} closure approved", + "runtime\\u{code} is active", + "AI\\u{code} authority is enabled", + ) + for code in ("034f", "0301", "fe0f", "0000", "0008", "001f", "007f"): + for template in templates: + attack = json.loads( + '{"opaque":[{"notes":[{"deep":"' + + template.format(code=code) + + '"}]}]}' + ) + with self.subTest(code=code, template=template): + self.website["extensions"] = attack + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + self.website["extensions"] = { + "opaque": [ + { + "notes": [ + "Café résumé – reviewer note.", + {"deep": "Reviewer 👩‍💻️ note."}, + {"multiline": "Reviewer note.\n\tStill bounded."}, + ] + } + ] + } + self.write_sources() + result = self.verify() + self.assertNotIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_connector_independent_and_trailing_negation_attacks_fail(self) -> None: + connectors = ( + ",", "and", "plus", "though", "because", "therefore", + "meanwhile", "furthermore", "also", "nevertheless", + "nonetheless", "except", "despite that", "in fact", "so", + "consequently", "moreover", "then", "still", "even though", + ) + attacks = [ + ( + f"does not prove runtime{connector} customer deployment is active" + if connector == "," + else f"does not prove runtime {connector} customer deployment is active" + ) + for connector in connectors + ] + attacks.extend( + ( + "customer deployment is active and not a typo", + "runtime is active and not simulated", + "final authorization received and no objections", + "AI authority is enabled and not revoked", + "public safe is confirmed and not disputed", + "case closure approved and not provisional", + "production is ready and not delayed", + "signal is observed and not inferred", + "customer deployment is active without ambiguity", + "not stale and customer deployed", + "does not claim runtime and customer deployed", + "does not claim runtime, but customer deployed", + ) + ) + for attack in attacks: + with self.subTest(attack=attack): + self.assertTrue( + ho_factory.hoxline_case_growth_authority_violations( + {"notes": attack} + ) + ) + + def test_unhashable_nested_public_safe_shape_fails_closed_without_crashing(self) -> None: + self.website["extensions"] = { + "opaque": [{"public_safe_status": ["NOT_PUBLIC_SAFE", "PUBLIC_SAFE"]}] + } + self.write_sources() + result = self.verify() + self.assertIn( + "NESTED_AUTHORITY_PROMOTION", + {item["code"] for item in result["contradictions"]}, + ) + + def test_malformed_detection_entries_fail_closed_without_crashing(self) -> None: + self.detection["entries"] = {"CASE-001": {"detection_id": "CASE-001"}} + self.write_sources() + result = self.verify() + self.assertIn( + "DETECTION_ENTRIES_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + def test_malformed_validation_packages_fail_closed_without_crashing(self) -> None: + self.validation["packages"] = "CASE-001" + self.write_sources() + result = self.verify() + self.assertIn( + "VALIDATION_PACKAGES_INVALID", + {item["code"] for item in result["contradictions"]}, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_hoxline_private_reviewer_cockpit.py b/tests/test_hoxline_private_reviewer_cockpit.py index 8f6739b..fb90e04 100644 --- a/tests/test_hoxline_private_reviewer_cockpit.py +++ b/tests/test_hoxline_private_reviewer_cockpit.py @@ -1,8 +1,8 @@ from __future__ import annotations import importlib.util -import os import sys +import tempfile import unittest from pathlib import Path @@ -85,27 +85,29 @@ def test_global_governance_state_remains_private(self) -> None: self.assertEqual(global_state["remote_lab_authority_rule"], "present") self.assertEqual(global_state["remote_default_mode"], "read_only") - def test_remote_lab_authority_ci_fallback_is_bounded(self) -> None: - original_path = ho_factory.HOXLINE_AGENTS_RULES - original_env = os.environ.get("GITHUB_ACTIONS") - try: - ho_factory.HOXLINE_AGENTS_RULES = ROOT / "missing-agents-rules.md" - os.environ.pop("GITHUB_ACTIONS", None) + def test_remote_lab_authority_uses_repo_controlled_fixture(self) -> None: + state = ho_factory.hoxline_remote_lab_authority_state() + + self.assertEqual(state["remote_lab_authority_rule"], "present") + self.assertEqual(state["remote_default_mode"], "read_only") + self.assertEqual(state["rule_source"], "repo_controlled_fixture") + + def test_remote_lab_authority_missing_or_extended_fixture_fails_closed(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + missing = Path(tmp) / "missing-authority.json" with self.assertRaises(ho_factory.FactoryError): - ho_factory.hoxline_remote_lab_authority_state() - - os.environ["GITHUB_ACTIONS"] = "true" - state = ho_factory.hoxline_remote_lab_authority_state() - - self.assertEqual(state["remote_lab_authority_rule"], "present") - self.assertEqual(state["remote_default_mode"], "read_only") - self.assertEqual(state["rule_source"], "github_actions_ci_fallback") - finally: - ho_factory.HOXLINE_AGENTS_RULES = original_path - if original_env is None: - os.environ.pop("GITHUB_ACTIONS", None) - else: - os.environ["GITHUB_ACTIONS"] = original_env + ho_factory.hoxline_remote_lab_authority_state(missing) + + extended = Path(tmp) / "extended-authority.json" + payload = ho_factory.hoxline_case_growth_load_json( + ho_factory.HOXLINE_REMOTE_LAB_AUTHORITY_FIXTURE + ) + payload["runtime_mutation_authorized"] = True + extended.write_text(ho_factory.json.dumps(payload), encoding="utf-8") + with self.assertRaisesRegex( + ho_factory.FactoryError, "exact CONTROLLED_TEST_ONLY v1 contract" + ): + ho_factory.hoxline_remote_lab_authority_state(extended) def test_evidence_product_convergence_self_test_passes(self) -> None: result = ho_factory.hoxline_evidence_to_product_convergence_self_test(ROOT) diff --git a/tests/test_hoxline_runtime_ops.py b/tests/test_hoxline_runtime_ops.py index 8e3c5f4..d8e620e 100644 --- a/tests/test_hoxline_runtime_ops.py +++ b/tests/test_hoxline_runtime_ops.py @@ -3,10 +3,12 @@ import importlib.util import hashlib import json +import shutil import sys import tempfile import unittest from pathlib import Path +from typing import Callable ROOT = Path(__file__).resolve().parents[1] @@ -20,6 +22,17 @@ class HoxlineRuntimeOpsTests(unittest.TestCase): + def mutated_workflow_root( + self, workflow_name: str, mutation: Callable[[str], str] + ) -> tempfile.TemporaryDirectory: + temp_dir = tempfile.TemporaryDirectory() + self.addCleanup(temp_dir.cleanup) + root = Path(temp_dir.name) + shutil.copytree(ROOT / ".github" / "workflows", root / ".github" / "workflows") + workflow = root / ".github" / "workflows" / workflow_name + workflow.write_text(mutation(workflow.read_text(encoding="utf-8")), encoding="utf-8") + return temp_dir + def test_runtime_ops_self_test(self) -> None: result = ho_factory.hoxline_runtime_ops_self_test(ROOT) @@ -72,6 +85,120 @@ def test_workflow_safety_verify(self) -> None: self.assertTrue(result["pr_source_checks_github_hosted_only"]) self.assertTrue(result["active_cron_trigger"]) self.assertFalse(result["unrestricted_artifact_upload"]) + self.assertEqual(result["source_checkout_count"], 7) + self.assertTrue(result["mandatory_convergence_unconditional"]) + self.assertTrue(result["ledger_pr_skip_independent"]) + + def test_workflow_safety_rejects_ledger_condition_suffix_laundering(self) -> None: + temp_dir = self.mutated_workflow_root( + "governance-gate.yml", + lambda text: text.replace( + "if: github.event_name != 'pull_request'", + "if: github.event_name != 'pull_request' || true", + 1, + ), + ) + with self.assertRaisesRegex( + ho_factory.FactoryError, "shell failure swallowing|intentional PR ledger skip" + ): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_workflow_safety_requires_exact_ledger_skip_expression(self) -> None: + temp_dir = self.mutated_workflow_root( + "governance-gate.yml", + lambda text: text.replace( + "if: github.event_name != 'pull_request'", + "if: ${{ github.event_name != 'pull_request' }}", + 1, + ), + ) + with self.assertRaisesRegex( + ho_factory.FactoryError, "intentional PR ledger skip" + ): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_workflow_safety_rejects_false_or_true_neutralizer(self) -> None: + temp_dir = self.mutated_workflow_root( + "hoxline-source-checks.yml", + lambda text: text + "\n# hostile mutation\nrun: false || true\n", + ) + with self.assertRaisesRegex(ho_factory.FactoryError, "shell failure swallowing"): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_workflow_safety_rejects_movable_checkout_tag(self) -> None: + temp_dir = self.mutated_workflow_root( + "hoxline-source-checks.yml", + lambda text: text.replace( + f"actions/checkout@{ho_factory.HOXLINE_ACTIONS_CHECKOUT_SHA}", + "actions/checkout@v4", + 1, + ), + ) + with self.assertRaisesRegex(ho_factory.FactoryError, "immutable SHA"): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_governance_gate_disables_persisted_checkout_credentials(self) -> None: + workflow = ( + ROOT / ".github" / "workflows" / "governance-gate.yml" + ).read_text(encoding="utf-8") + checkout_count = workflow.count( + f"uses: actions/checkout@{ho_factory.HOXLINE_ACTIONS_CHECKOUT_SHA}" + ) + + self.assertGreater(checkout_count, 0) + self.assertEqual( + workflow.count("persist-credentials: false"), + checkout_count, + ) + self.assertEqual( + workflow.count("python -m pip install jsonschema==4.23.0"), + 2, + ) + + def test_workflow_safety_rejects_persisted_governance_credentials(self) -> None: + temp_dir = self.mutated_workflow_root( + "governance-gate.yml", + lambda text: text.replace( + "persist-credentials: false", + "persist-credentials: true", + 1, + ), + ) + with self.assertRaisesRegex( + ho_factory.FactoryError, + "governance checkout must disable persisted credentials", + ): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_workflow_safety_rejects_unpinned_governance_dependency(self) -> None: + temp_dir = self.mutated_workflow_root( + "governance-gate.yml", + lambda text: text.replace( + "jsonschema==4.23.0", + "jsonschema", + 1, + ), + ) + with self.assertRaisesRegex( + ho_factory.FactoryError, + "must pin the reviewed jsonschema version", + ): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) + + def test_workflow_safety_rejects_ambiguous_process_working_directory(self) -> None: + temp_dir = self.mutated_workflow_root( + "hoxline-source-checks.yml", + lambda text: text.replace( + '"$GITHUB_WORKSPACE/source-set/hawkinsoperations-platform"', + '"$PWD"', + 1, + ), + ) + with self.assertRaisesRegex( + ho_factory.FactoryError, + "explicit checked platform repository root", + ): + ho_factory.hoxline_workflow_safety_verify(Path(temp_dir.name)) def test_canary_from_sanitized_receipts_builds_replay_artifacts(self) -> None: with tempfile.TemporaryDirectory() as tmp: diff --git a/tests/test_verify_public_status_source_contract.py b/tests/test_verify_public_status_source_contract.py index d21b50c..73e37a4 100644 --- a/tests/test_verify_public_status_source_contract.py +++ b/tests/test_verify_public_status_source_contract.py @@ -3,9 +3,11 @@ import copy import importlib.util import json +import subprocess import tempfile import unittest from pathlib import Path +from unittest import mock ROOT = Path(__file__).resolve().parents[1] @@ -75,6 +77,302 @@ def test_rejects_non_pending_initial_review_marker(self) -> None: with self.assertRaises(verifier.VerificationError): self.verify_contract_copy(contract) + def test_rejects_forged_current_proof_count(self) -> None: + contract = self.load_contract() + contract["public_fields"]["proof_record_count"]["current_value"] = 99 + + with self.assertRaises(verifier.VerificationError): + self.verify_contract_copy(contract) + + def test_rejects_historical_proof_summary_as_current_source(self) -> None: + contract = self.load_contract() + contract["public_fields"]["proof_record_count"]["source_path"] = ( + "../hawkinsoperations-proof/proof/records/reviewer-metrics-pipeline-v1-summary.json" + ) + + with self.assertRaises(verifier.VerificationError): + self.verify_contract_copy(contract) + + def test_rejects_forged_source_revision(self) -> None: + contract = self.load_contract() + contract["public_fields"]["proof_record_count"]["source_revision"] = "f" * 40 + with self.assertRaisesRegex(verifier.VerificationError, "legacy source_revision"): + self.verify_contract_copy(contract) + + def test_rejects_forged_source_fingerprint(self) -> None: + contract = self.load_contract() + contract["public_fields"]["proof_record_count"]["source_fingerprint_sha256"] = "0" * 64 + with self.assertRaisesRegex(verifier.VerificationError, "fingerprint"): + self.verify_contract_copy(contract) + + def test_rejects_future_generated_at(self) -> None: + contract = self.load_contract() + contract["generated_at"] = "2999-01-01T00:00:00Z" + with self.assertRaisesRegex(verifier.VerificationError, "future"): + self.verify_contract_copy(contract) + + def test_rejects_nested_alternate_authority_field(self) -> None: + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "nested": [{"ai-authority": True}] + } + with self.assertRaisesRegex(verifier.VerificationError, "authority field must remain blocked"): + self.verify_contract_copy(contract) + + def test_negative_boundary_sibling_cannot_launder_promotion(self) -> None: + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "note": "blocked", + "claim": "customer deployment complete", + } + with self.assertRaisesRegex(verifier.VerificationError, "promotional phrase"): + self.verify_contract_copy(contract) + + def test_blocked_claim_text_is_not_allowed_outside_blocked_claims(self) -> None: + for prose in ("customer deployed", "public safe", "AI authority enabled"): + with self.subTest(prose=prose): + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "note": prose + } + with self.assertRaisesRegex( + verifier.VerificationError, "promotional phrase" + ): + self.verify_contract_copy(contract) + + def test_clause_local_negative_claim_text_remains_bounded(self) -> None: + for prose in ( + "does not prove customer deployed", + "missing production ready", + ): + with self.subTest(prose=prose): + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "note": prose + } + result = self.verify_contract_copy(contract) + self.assertEqual(result["status"], "pass") + + def test_distant_negation_does_not_launder_later_public_safe_claim(self) -> None: + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "note": "does not prove customer deployed, but public safe" + } + with self.assertRaisesRegex(verifier.VerificationError, "promotional phrase"): + self.verify_contract_copy(contract) + + def test_unrelated_negation_before_conjunction_does_not_launder_customer_claim( + self, + ) -> None: + for prose in ( + "not stale and customer deployed", + "does not claim runtime and customer deployed", + "does not claim runtime, but customer deployed", + ): + with self.subTest(prose=prose): + contract = self.load_contract() + contract["future_generated_status_v1_extraction"]["extension"] = { + "note": prose + } + with self.assertRaisesRegex( + verifier.VerificationError, "promotional phrase" + ): + self.verify_contract_copy(contract) + + def test_rejects_case_folded_duplicate_json_keys(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "duplicate.json" + path.write_text('{"manifest_id":"A","Manifest_ID":"B"}', encoding="utf-8") + with self.assertRaisesRegex(verifier.VerificationError, "duplicate JSON key"): + verifier.load_json(path) + + def test_rejects_duplicate_canonical_source_owner(self) -> None: + contract = self.load_contract() + contract["source_repos"][0]["repo"] = "hawkinsoperations-platform" + with self.assertRaisesRegex( + verifier.VerificationError, "exactly the seven canonical repositories" + ): + self.verify_contract_copy(contract) + + def test_rejects_encoded_source_path_traversal(self) -> None: + contract = self.load_contract() + contract["source_paths"]["website_generated_status_consumer"] = ( + "..%252f..%252fprivate%252fevidence.json" + ) + with self.assertRaisesRegex( + verifier.VerificationError, "safe repository-relative route" + ): + self.verify_contract_copy(contract) + + def test_rejects_mixed_separator_source_path(self) -> None: + contract = self.load_contract() + contract["source_paths"]["website_generated_status_consumer"] = ( + "..\\hawkinsoperations-website/public\\data/status.json" + ) + with self.assertRaisesRegex( + verifier.VerificationError, "safe repository-relative route" + ): + self.verify_contract_copy(contract) + + def test_rejects_nested_generated_at_pointer_drift(self) -> None: + contract = self.load_contract() + contract["public_fields"]["generated_at"]["current_value"] = ( + "2026-06-16T22:06:47.5510594-05:00" + ) + with self.assertRaisesRegex(verifier.VerificationError, "must equal the root generated_at"): + self.verify_contract_copy(contract) + + def test_rejects_generated_website_consumer_as_authority_source(self) -> None: + contract = self.load_contract() + contract["source_paths"]["website_generated_status_consumer"] = ( + "../hawkinsoperations-website/public/data/public-status.json" + ) + with self.assertRaisesRegex(verifier.VerificationError, "website rendering schema"): + self.verify_contract_copy(contract) + + def test_rejects_unknown_public_field_shape(self) -> None: + contract = self.load_contract() + contract["public_fields"]["proof_record_count"]["opaque_extension"] = { + "looks_harmless": True + } + with self.assertRaisesRegex(verifier.VerificationError, "contains unknown fields"): + self.verify_contract_copy(contract) + + def test_rejects_duplicate_keys_in_proof_yaml(self) -> None: + with self.assertRaisesRegex( + verifier.VerificationError, "duplicate YAML key" + ): + verifier.load_yaml_bytes( + b"entries: []\nEntries: []\n", + source="proof-owned current status index", + ) + + def test_proof_source_rejects_empty_duplicate_origin_in_both_orders(self) -> None: + proof_count = self.load_contract()["public_fields"]["proof_record_count"] + real_run = verifier.subprocess.run + canonical = "https://github.com/HawkinsOperations/hawkinsoperations-proof.git" + + for raw_origins in ((canonical, ""), ("", canonical)): + nul_output = "\0".join(raw_origins) + "\0" + + def fake_run(args: list[str], *call_args: object, **call_kwargs: object): + if args[-5:] == [ + "config", + "--local", + "--null", + "--get-all", + "remote.origin.url", + ]: + return subprocess.CompletedProcess(args, 0, nul_output, "") + return real_run(args, *call_args, **call_kwargs) + + with mock.patch.object(verifier.subprocess, "run", side_effect=fake_run): + with self.assertRaisesRegex( + verifier.VerificationError, + "exactly one nonempty origin URL", + ): + verifier.verify_proof_source_identity(proof_count) + + def test_proof_source_ignores_ambient_git_dir_decoy(self) -> None: + proof_count = self.load_contract()["public_fields"]["proof_record_count"] + with tempfile.TemporaryDirectory() as temp_dir: + decoy = Path(temp_dir) / "decoy" + decoy.mkdir() + for args in ( + ("init",), + ("config", "user.name", "Platform Test"), + ("config", "user.email", "platform-test@example.invalid"), + ("remote", "add", "origin", "C:/hostile/decoy"), + ): + subprocess.run( + ["git", "-C", str(decoy), *args], + check=True, + capture_output=True, + text=True, + ) + (decoy / "tracked.txt").write_text("decoy\n", encoding="utf-8") + subprocess.run( + ["git", "-C", str(decoy), "add", "tracked.txt"], + check=True, + capture_output=True, + text=True, + ) + subprocess.run( + ["git", "-C", str(decoy), "commit", "-m", "fixture"], + check=True, + capture_output=True, + text=True, + ) + with mock.patch.dict( + verifier.os.environ, + { + "GIT_DIR": str(decoy / ".git"), + "GIT_WORK_TREE": str(decoy), + "GIT_INDEX_FILE": str(decoy / ".git" / "index"), + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": "core.repositoryformatversion", + "GIT_CONFIG_VALUE_0": "0", + }, + clear=False, + ): + raw, observed_head = verifier.verify_proof_source_identity(proof_count) + self.assertTrue(raw) + self.assertRegex(observed_head, r"^[0-9a-f]{40}$") + + def test_detached_historical_authority_is_rejected_even_with_same_blob(self) -> None: + proof_count = self.load_contract()["public_fields"]["proof_record_count"] + real_git_output = verifier.git_output + + def fake_git_output(repo: Path, *args: str) -> str: + if args == ("branch", "--show-current"): + return "" + if args == ("rev-parse", "HEAD"): + return "f" * 40 + return real_git_output(repo, *args) + + with mock.patch.object( + verifier, "git_output", side_effect=fake_git_output + ), mock.patch.object( + verifier, + "git_is_ancestor", + side_effect=lambda _repo, ancestor, descendant: ancestor == "f" * 40, + ): + with self.assertRaisesRegex(verifier.VerificationError, "older historical ancestor"): + verifier.verify_proof_source_identity(proof_count) + + def test_detached_rewritten_authority_accepts_exact_reviewed_tree(self) -> None: + proof_count = self.load_contract()["public_fields"]["proof_record_count"] + real_git_output = verifier.git_output + + def fake_git_output(repo: Path, *args: str) -> str: + if args == ("branch", "--show-current"): + return "" + if args == ("rev-parse", "HEAD"): + return "f" * 40 + return real_git_output(repo, *args) + + with mock.patch.object( + verifier, "git_output", side_effect=fake_git_output + ), mock.patch.object( + verifier, "git_is_ancestor", return_value=False + ), mock.patch.object( + verifier, "git_tree_sha", return_value="e" * 40 + ): + raw, observed_head = verifier.verify_proof_source_identity(proof_count) + self.assertTrue(raw) + self.assertEqual("f" * 40, observed_head) + + def test_reviewed_classification_cannot_launder_unreachable_observation(self) -> None: + proof_count = self.load_contract()["public_fields"]["proof_record_count"] + proof_count["source_revision"] = "f" * 40 + proof_count["source_observed_head_sha"] = "f" * 40 + proof_count["current_observed_head_sha"] = "f" * 40 + manifest = json.loads(verifier.SOURCE_MANIFEST_PATH.read_text(encoding="utf-8")) + manifest["repositories"]["hawkinsoperations-proof"]["revision"] = "f" * 40 + with mock.patch.object(verifier, "load_json", return_value=manifest): + with self.assertRaisesRegex(verifier.VerificationError, "unreachable"): + verifier.verify_proof_source_identity(proof_count) + if __name__ == "__main__": unittest.main()