From 07514a9efd767f655639b4833ced2e923cd3fe06 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:42:21 -0500 Subject: [PATCH 01/73] feat: converge case growth and expand fixture review --- .../case-growth/sample-case-growth-index.json | 233 +++++++- .../aws-det-001-artifact-manifest-v1.json | 78 +++ .../fixtures/aws-det-001-safe-fixture.json | 20 + .../aws-det-001-safe-negative-fixture.json | 20 + .../fixtures/ho-det-013-safe-fixture.json | 20 + .../ho-det-013-safe-negative-fixture.json | 20 + .../fixtures/id-det-001-safe-fixture.json | 20 + .../id-det-001-safe-negative-fixture.json | 20 + .../fixtures/id-det-002-safe-fixture.json | 20 + .../id-det-002-safe-negative-fixture.json | 20 + .../fixtures/id-det-003-safe-fixture.json | 20 + .../id-det-003-safe-negative-fixture.json | 20 + .../fixtures/id-det-004-safe-fixture.json | 20 + .../id-det-004-safe-negative-fixture.json | 20 + .../ho-det-013-artifact-manifest-v1.json | 78 +++ .../ho-ndr-001-artifact-manifest-v1.json | 64 +++ .../id-det-001-artifact-manifest-v1.json | 78 +++ .../id-det-002-artifact-manifest-v1.json | 78 +++ .../id-det-003-artifact-manifest-v1.json | 78 +++ .../id-det-004-artifact-manifest-v1.json | 78 +++ .../multi-artifact-review-index-v1.json | 42 +- schemas/artifact-manifest-v1.schema.json | 2 + schemas/case-growth-index-v0.schema.json | 55 +- src/hoxline/case_growth/__init__.py | 9 +- src/hoxline/case_growth/collector.py | 534 +++++++++++++++++- src/hoxline/case_growth/discovery.py | 56 ++ src/hoxline/case_growth/render.py | 32 +- src/hoxline/case_growth/report.py | 10 +- src/hoxline/cli.py | 63 ++- src/hoxline/review_engine.py | 138 ++++- tests/test_case_growth_index_v0.py | 201 ++++++- ...i_artifact_review_manifest_expansion_v1.py | 144 ++++- 32 files changed, 2212 insertions(+), 79 deletions(-) create mode 100644 examples/review/aws-det-001-artifact-manifest-v1.json create mode 100644 examples/review/fixtures/aws-det-001-safe-fixture.json create mode 100644 examples/review/fixtures/aws-det-001-safe-negative-fixture.json create mode 100644 examples/review/fixtures/ho-det-013-safe-fixture.json create mode 100644 examples/review/fixtures/ho-det-013-safe-negative-fixture.json create mode 100644 examples/review/fixtures/id-det-001-safe-fixture.json create mode 100644 examples/review/fixtures/id-det-001-safe-negative-fixture.json create mode 100644 examples/review/fixtures/id-det-002-safe-fixture.json create mode 100644 examples/review/fixtures/id-det-002-safe-negative-fixture.json create mode 100644 examples/review/fixtures/id-det-003-safe-fixture.json create mode 100644 examples/review/fixtures/id-det-003-safe-negative-fixture.json create mode 100644 examples/review/fixtures/id-det-004-safe-fixture.json create mode 100644 examples/review/fixtures/id-det-004-safe-negative-fixture.json create mode 100644 examples/review/ho-det-013-artifact-manifest-v1.json create mode 100644 examples/review/ho-ndr-001-artifact-manifest-v1.json create mode 100644 examples/review/id-det-001-artifact-manifest-v1.json create mode 100644 examples/review/id-det-002-artifact-manifest-v1.json create mode 100644 examples/review/id-det-003-artifact-manifest-v1.json create mode 100644 examples/review/id-det-004-artifact-manifest-v1.json diff --git a/examples/case-growth/sample-case-growth-index.json b/examples/case-growth/sample-case-growth-index.json index 976e6a4..f74b1ee 100644 --- a/examples/case-growth/sample-case-growth-index.json +++ b/examples/case-growth/sample-case-growth-index.json @@ -1,68 +1,258 @@ { - "schema_version": "case-growth-index-v0", - "generated_at": "2026-06-27T10:02:21Z", - "repo_root": "tests\\fixtures\\case_growth\\org", + "schema_version": "case-growth-index-v1", + "generated_at": "2026-07-22T23:09:52Z", + "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", + "historical_snapshot": false, + "current_authority": true, + "snapshot_state": { + "freshness": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "self_source_revision_semantics": "worktree-head-or-snapshot-commit-parent" + }, + "source_revisions": [ + { + "repository": ".github", + "authority_role": "org command-center routing", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "scripts/verify-command-center-invariants.py", + "source_file_sha256": null, + "source_freshness_state": "MISSING_AUTHORITY_SOURCE", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": false, + "missing_source_state": true, + "dangling_reference_state": true, + "contradictions": [], + "drift": [], + "next_legal_action": "restore .github/scripts/verify-command-center-invariants.py from its owning repository" + }, + { + "repository": "hawkinsoperations-detections", + "authority_role": "detection source truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "detections/DETECTION_PROMOTION_MATRIX.yml", + "source_file_sha256": "07d582de56b33c6e78d9ad8965cdfc635d87c1004ef1db75b9a7f1aa9fecbd61", + "source_freshness_state": "WORKTREE_MODIFIED", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-validation", + "authority_role": "controlled validation truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "validation/VALIDATION_REGISTRY.yml", + "source_file_sha256": "2c12c3e8348ce058d3e4dd8ef7c437c3c761cf73b540233f49d4c7f1dea045be", + "source_freshness_state": "WORKTREE_MODIFIED", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-platform", + "authority_role": "platform contract truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "contracts/public-status-source-contract-v1.json", + "source_file_sha256": null, + "source_freshness_state": "MISSING_AUTHORITY_SOURCE", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": false, + "missing_source_state": true, + "dangling_reference_state": true, + "contradictions": [], + "drift": [], + "next_legal_action": "restore hawkinsoperations-platform/contracts/public-status-source-contract-v1.json from its owning repository" + }, + { + "repository": "hawkinsoperations-proof", + "authority_role": "proof and claim-boundary truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "source_file_sha256": "1ff1ec190570218bae05656abae8c619edc451b27700de3cf6da9ca8de364d08", + "source_freshness_state": "WORKTREE_MODIFIED", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-website", + "authority_role": "rendering-only public status", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "public/data/public-status.json", + "source_file_sha256": null, + "source_freshness_state": "MISSING_AUTHORITY_SOURCE", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": false, + "missing_source_state": true, + "dangling_reference_state": true, + "contradictions": [], + "drift": [], + "next_legal_action": "restore hawkinsoperations-website/public/data/public-status.json from its owning repository" + }, + { + "repository": "hoxline", + "authority_role": "case-growth and fixture-review product truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "bed34b8704f476075d65ec50095bb37121cb635b", + "source_parent_sha": "0be75c48ea4269fd6f7726620bfd157c42c43e95", + "self_referential": true, + "revision_scope": "authoritative_sources_excluding_snapshot", + "source_path": "src/hoxline/case_growth/collector.py", + "source_file_sha256": null, + "source_freshness_state": "MISSING_AUTHORITY_SOURCE", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": false, + "missing_source_state": true, + "dangling_reference_state": true, + "contradictions": [], + "drift": [], + "next_legal_action": "restore hoxline/src/hoxline/case_growth/collector.py from its owning repository" + } + ], + "contradictions": [ + { + "code": "MISSING_AUTHORITY_SOURCE", + "source_owner": ".github", + "source_path": "scripts/verify-command-center-invariants.py", + "expected": "existing authoritative source", + "actual": "missing", + "classification": "ACTIONABLE_DRIFT", + "next_legal_action": "restore .github/scripts/verify-command-center-invariants.py from its owning repository" + }, + { + "code": "MISSING_AUTHORITY_SOURCE", + "source_owner": "hawkinsoperations-platform", + "source_path": "contracts/public-status-source-contract-v1.json", + "expected": "existing authoritative source", + "actual": "missing", + "classification": "ACTIONABLE_DRIFT", + "next_legal_action": "restore hawkinsoperations-platform/contracts/public-status-source-contract-v1.json from its owning repository" + }, + { + "code": "MISSING_AUTHORITY_SOURCE", + "source_owner": "hawkinsoperations-website", + "source_path": "public/data/public-status.json", + "expected": "existing authoritative source", + "actual": "missing", + "classification": "ACTIONABLE_DRIFT", + "next_legal_action": "restore hawkinsoperations-website/public/data/public-status.json from its owning repository" + }, + { + "code": "MISSING_AUTHORITY_SOURCE", + "source_owner": "hoxline", + "source_path": "src/hoxline/case_growth/collector.py", + "expected": "existing authoritative source", + "actual": "missing", + "classification": "ACTIONABLE_DRIFT", + "next_legal_action": "restore hoxline/src/hoxline/case_growth/collector.py from its owning repository" + } + ], + "drift": [], + "next_legal_action": "restore .github/scripts/verify-command-center-invariants.py from its owning repository", "repos_scanned": [ { "repo": ".github", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\.github", + "path": ".github", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "org metadata and reviewer routing only; not proof authority", "files_scanned": 1 }, { "repo": "hawkinsoperations-detections", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hawkinsoperations-detections", + "path": "hawkinsoperations-detections", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "source package and source status authority only", "files_scanned": 5 }, { "repo": "hawkinsoperations-validation", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hawkinsoperations-validation", + "path": "hawkinsoperations-validation", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "controlled validation authority only", "files_scanned": 7 }, { "repo": "hawkinsoperations-platform", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hawkinsoperations-platform", + "path": "hawkinsoperations-platform", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "platform runtime-candidate, collector, receipt, and ledger contract authority only", "files_scanned": 4 }, { "repo": "hawkinsoperations-proof", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hawkinsoperations-proof", + "path": "hawkinsoperations-proof", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "proof ceiling, proof record, ProofCard, public-safe, blocked-claim, and next-gate authority", "files_scanned": 4 }, { "repo": "hawkinsoperations-website", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hawkinsoperations-website", + "path": "hawkinsoperations-website", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "route/rendering surface only; not proof authority", "files_scanned": 1 }, { "repo": "hoxline", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline\\tests\\fixtures\\case_growth\\org\\hoxline", + "path": "hoxline", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", "files_scanned": 2 @@ -175,7 +365,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-27T04:32:03-05:00", + "last_updated": "2026-06-27T05:31:19-05:00", "next_gate": "human review before public wording", "evidence_confidence": "HIGH", "notes": [ @@ -216,7 +406,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-27T04:32:03-05:00", + "last_updated": "2026-06-27T05:31:19-05:00", "next_gate": "create controlled validation package", "evidence_confidence": "MEDIUM", "notes": [ @@ -286,7 +476,7 @@ "metrics_refs": [ "hoxline/examples/gauntlet/sample-work-impact-metrics.json" ], - "last_updated": "2026-06-27T04:32:03-05:00", + "last_updated": "2026-06-27T05:31:19-05:00", "next_gate": "human review before runtime, signal, customer, production, public wording, or final human gate promotion", "evidence_confidence": "MEDIUM", "notes": [ @@ -309,5 +499,6 @@ "final_authorization_claimed": false, "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false - } + }, + "reproducibility_sha256": "a70e4f765aa56a3fe8a5ac43eaac3032c7becf76004ab3b901f63150b5b329d1" } diff --git a/examples/review/aws-det-001-artifact-manifest-v1.json b/examples/review/aws-det-001-artifact-manifest-v1.json new file mode 100644 index 0000000..8c52815 --- /dev/null +++ b/examples/review/aws-det-001-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "AWS-DET-001", + "artifact_name": "AWS IAM denied action fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "aws-det-001", + "endpoint_mutation": false, + "expected_event_ids": [], + "expected_event_keys": [ + "CreateUser" + ], + "expected_rule_ids": [], + "field_mapping": { + "event_key": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/aws-det-001-safe-negative-fixture.json", + "positive": "examples/review/fixtures/aws-det-001-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "AWS-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [ + "CreateUser" + ], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "AWS CloudTrail-style controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the AWS-DET-001 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/fixtures/aws-det-001-safe-fixture.json b/examples/review/fixtures/aws-det-001-safe-fixture.json new file mode 100644 index 0000000..f69c031 --- /dev/null +++ b/examples/review/fixtures/aws-det-001-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "AWS-DET-001", + "endpoint_mutation": false, + "events": [ + { + "event_key": "CreateUser", + "channel": "AWS CloudTrail fixture", + "action": "iam_action", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "aws-det-001-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/aws-det-001-safe-negative-fixture.json b/examples/review/fixtures/aws-det-001-safe-negative-fixture.json new file mode 100644 index 0000000..f4f9517 --- /dev/null +++ b/examples/review/fixtures/aws-det-001-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "AWS-DET-001", + "endpoint_mutation": false, + "events": [ + { + "event_key": "ListUsers", + "channel": "AWS CloudTrail fixture", + "action": "iam_action", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "aws-det-001-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/ho-det-013-safe-fixture.json b/examples/review/fixtures/ho-det-013-safe-fixture.json new file mode 100644 index 0000000..6a1eedf --- /dev/null +++ b/examples/review/fixtures/ho-det-013-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "HO-DET-013", + "endpoint_mutation": false, + "events": [ + { + "event_id": 1102, + "channel": "Windows Security", + "action": "security_control_tamper", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "ho-det-013-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/ho-det-013-safe-negative-fixture.json b/examples/review/fixtures/ho-det-013-safe-negative-fixture.json new file mode 100644 index 0000000..c975f4d --- /dev/null +++ b/examples/review/fixtures/ho-det-013-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "HO-DET-013", + "endpoint_mutation": false, + "events": [ + { + "event_id": 7036, + "channel": "Windows Security", + "action": "security_control_tamper", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "ho-det-013-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-001-safe-fixture.json b/examples/review/fixtures/id-det-001-safe-fixture.json new file mode 100644 index 0000000..371c33b --- /dev/null +++ b/examples/review/fixtures/id-det-001-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-001", + "endpoint_mutation": false, + "events": [ + { + "event_key": "impossible_travel", + "channel": "Identity fixture", + "action": "identity_session_context", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "id-det-001-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-001-safe-negative-fixture.json b/examples/review/fixtures/id-det-001-safe-negative-fixture.json new file mode 100644 index 0000000..39cbef6 --- /dev/null +++ b/examples/review/fixtures/id-det-001-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-001", + "endpoint_mutation": false, + "events": [ + { + "event_key": "known_device_login", + "channel": "Identity fixture", + "action": "identity_session_context", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "id-det-001-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-002-safe-fixture.json b/examples/review/fixtures/id-det-002-safe-fixture.json new file mode 100644 index 0000000..ea52cb3 --- /dev/null +++ b/examples/review/fixtures/id-det-002-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-002", + "endpoint_mutation": false, + "events": [ + { + "event_key": "risky_authentication_change", + "channel": "Identity fixture", + "action": "authentication_control_change", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "id-det-002-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-002-safe-negative-fixture.json b/examples/review/fixtures/id-det-002-safe-negative-fixture.json new file mode 100644 index 0000000..f584eab --- /dev/null +++ b/examples/review/fixtures/id-det-002-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-002", + "endpoint_mutation": false, + "events": [ + { + "event_key": "approved_authentication_change", + "channel": "Identity fixture", + "action": "authentication_control_change", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "id-det-002-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-003-safe-fixture.json b/examples/review/fixtures/id-det-003-safe-fixture.json new file mode 100644 index 0000000..bb79978 --- /dev/null +++ b/examples/review/fixtures/id-det-003-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-003", + "endpoint_mutation": false, + "events": [ + { + "event_key": "privileged_role_assignment", + "channel": "Identity fixture", + "action": "privilege_change", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "id-det-003-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-003-safe-negative-fixture.json b/examples/review/fixtures/id-det-003-safe-negative-fixture.json new file mode 100644 index 0000000..e8104f5 --- /dev/null +++ b/examples/review/fixtures/id-det-003-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-003", + "endpoint_mutation": false, + "events": [ + { + "event_key": "approved_role_assignment", + "channel": "Identity fixture", + "action": "privilege_change", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "id-det-003-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-004-safe-fixture.json b/examples/review/fixtures/id-det-004-safe-fixture.json new file mode 100644 index 0000000..5d0e4cb --- /dev/null +++ b/examples/review/fixtures/id-det-004-safe-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-004", + "endpoint_mutation": false, + "events": [ + { + "event_key": "impossible_travel", + "channel": "Identity fixture", + "action": "travel_session_context", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": true, + "fixture_id": "id-det-004-positive-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/fixtures/id-det-004-safe-negative-fixture.json b/examples/review/fixtures/id-det-004-safe-negative-fixture.json new file mode 100644 index 0000000..fa7480a --- /dev/null +++ b/examples/review/fixtures/id-det-004-safe-negative-fixture.json @@ -0,0 +1,20 @@ +{ + "artifact_id": "ID-DET-004", + "endpoint_mutation": false, + "events": [ + { + "event_key": "approved_travel", + "channel": "Identity fixture", + "action": "travel_session_context", + "actor": "synthetic-review-actor" + } + ], + "expected_detection": false, + "fixture_id": "id-det-004-negative-fixture-v1", + "fixture_kind": "synthetic-demo-only", + "host": "synthetic-fixture-host", + "network_required": false, + "runtime_required": false, + "safe_fixture": true, + "schema_version": "hoxline-demo-fixture-v0" +} diff --git a/examples/review/ho-det-013-artifact-manifest-v1.json b/examples/review/ho-det-013-artifact-manifest-v1.json new file mode 100644 index 0000000..3ca38a3 --- /dev/null +++ b/examples/review/ho-det-013-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "HO-DET-013", + "artifact_name": "Security control tamper fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "ho-det-013", + "endpoint_mutation": false, + "expected_event_ids": [ + 1102 + ], + "expected_event_keys": [], + "expected_rule_ids": [], + "field_mapping": { + "event_id": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/ho-det-013-safe-negative-fixture.json", + "positive": "examples/review/fixtures/ho-det-013-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "HO-DET-013 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [ + 1102 + ], + "event_keys": [], + "event_key_field": "event_id", + "required_fields": [ + "event_id", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "Windows controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the HO-DET-013 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/ho-ndr-001-artifact-manifest-v1.json b/examples/review/ho-ndr-001-artifact-manifest-v1.json new file mode 100644 index 0000000..ede758c --- /dev/null +++ b/examples/review/ho-ndr-001-artifact-manifest-v1.json @@ -0,0 +1,64 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "boundary contract review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "HO-NDR-001", + "artifact_name": "Security Onion NDR boundary contract", + "artifact_type": "boundary-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "endpoint_mutation": false, + "expected_block_reason": "HO-NDR-001 is boundary-contract scoped and has no owned positive and negative fixture contract; runtime, telemetry, and live Security Onion claims remain blocked.", + "expected_event_ids": [], + "expected_event_keys": [], + "expected_review_outcome": "BLOCKED", + "expected_rule_ids": [], + "fixture_paths": { + "negative": "examples/review/fixtures/ho-ndr-001-boundary-negative-unavailable.json", + "positive": "examples/review/fixtures/ho-ndr-001-boundary-positive-unavailable.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "separate owned fixture contract and human review", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Boundary contract only; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "HO-NDR-001 remains blocked in local fixture review until an owned fixture contract exists." + ], + "runtime_boundary": "No runtime execution, Security Onion access, endpoint mutation, SIEM action, or live signal action.", + "runtime_proof": false, + "signal_boundary": "No synthetic or live signal is claimed.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "boundary contract only", + "source": "Security Onion boundary metadata only", + "wazuh_rule_ids": [] + }, + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/id-det-001-artifact-manifest-v1.json b/examples/review/id-det-001-artifact-manifest-v1.json new file mode 100644 index 0000000..4a06150 --- /dev/null +++ b/examples/review/id-det-001-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "ID-DET-001", + "artifact_name": "Identity session context fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "id-det-001", + "endpoint_mutation": false, + "expected_event_ids": [], + "expected_event_keys": [ + "impossible_travel" + ], + "expected_rule_ids": [], + "field_mapping": { + "event_key": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/id-det-001-safe-negative-fixture.json", + "positive": "examples/review/fixtures/id-det-001-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "ID-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [ + "impossible_travel" + ], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "Identity controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the ID-DET-001 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/id-det-002-artifact-manifest-v1.json b/examples/review/id-det-002-artifact-manifest-v1.json new file mode 100644 index 0000000..97bc977 --- /dev/null +++ b/examples/review/id-det-002-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "ID-DET-002", + "artifact_name": "Identity authentication control fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "id-det-002", + "endpoint_mutation": false, + "expected_event_ids": [], + "expected_event_keys": [ + "risky_authentication_change" + ], + "expected_rule_ids": [], + "field_mapping": { + "event_key": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/id-det-002-safe-negative-fixture.json", + "positive": "examples/review/fixtures/id-det-002-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "ID-DET-002 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [ + "risky_authentication_change" + ], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "Identity controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the ID-DET-002 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/id-det-003-artifact-manifest-v1.json b/examples/review/id-det-003-artifact-manifest-v1.json new file mode 100644 index 0000000..9b7c6e2 --- /dev/null +++ b/examples/review/id-det-003-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "ID-DET-003", + "artifact_name": "Identity privilege change fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "id-det-003", + "endpoint_mutation": false, + "expected_event_ids": [], + "expected_event_keys": [ + "privileged_role_assignment" + ], + "expected_rule_ids": [], + "field_mapping": { + "event_key": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/id-det-003-safe-negative-fixture.json", + "positive": "examples/review/fixtures/id-det-003-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "ID-DET-003 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [ + "privileged_role_assignment" + ], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "Identity controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the ID-DET-003 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/id-det-004-artifact-manifest-v1.json b/examples/review/id-det-004-artifact-manifest-v1.json new file mode 100644 index 0000000..e145e5a --- /dev/null +++ b/examples/review/id-det-004-artifact-manifest-v1.json @@ -0,0 +1,78 @@ +{ + "ai_disposition_authority": false, + "allowed_claim_class": "controlled local fixture review only", + "artifact_family": "synthetic-review-only", + "artifact_id": "ID-DET-004", + "artifact_name": "Identity travel context fixture review", + "artifact_type": "detection-review-artifact", + "blocked_claim_classes": [ + "production ready", + "public-safe runtime proof", + "SOCaaS deployed", + "customer deployed", + "autonomous SOC", + "AI-approved disposition", + "analyst-approved disposition", + "final authorization", + "case closure", + "website rendering as proof", + "green CI as approval" + ], + "confidence": "bounded-fixture-only", + "detection_family": "id-det-004", + "endpoint_mutation": false, + "expected_event_ids": [], + "expected_event_keys": [ + "impossible_travel" + ], + "expected_rule_ids": [], + "field_mapping": { + "event_key": "fixture selector", + "channel": "fixture source label", + "action": "bounded fixture action", + "actor": "synthetic actor label" + }, + "fixture_paths": { + "negative": "examples/review/fixtures/id-det-004-safe-negative-fixture.json", + "positive": "examples/review/fixtures/id-det-004-safe-fixture.json" + }, + "human_review_required": true, + "lifetime_ledger_changed": false, + "manifest_version": "artifact-manifest-v1", + "next_gate": "human_review_gate", + "platform_owner": "hawkinsoperations-platform", + "product_owner": "hoxline", + "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", + "proof_owner": "hawkinsoperations-proof", + "public_proof_promoted": false, + "public_safe_status": "NOT_PUBLIC_SAFE", + "requested_claims": [ + "ID-DET-004 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + ], + "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", + "runtime_proof": false, + "severity": "medium", + "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "source_owner": "hawkinsoperations-detections", + "telemetry_contract": { + "event_ids": [], + "event_keys": [ + "impossible_travel" + ], + "event_key_field": "event_key", + "required_fields": [ + "event_key", + "channel", + "action", + "actor" + ], + "scope": "fixture-only metadata contract", + "source": "Identity controlled fixture metadata", + "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", + "wazuh_rule_ids": [] + }, + "triage_what_happened": "A synthetic fixture represented the ID-DET-004 controlled review pattern.", + "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", + "validation_owner": "hawkinsoperations-validation", + "wazuh_mutation": false +} diff --git a/examples/review/multi-artifact-review-index-v1.json b/examples/review/multi-artifact-review-index-v1.json index 9d66cc9..a4e503d 100644 --- a/examples/review/multi-artifact-review-index-v1.json +++ b/examples/review/multi-artifact-review-index-v1.json @@ -16,16 +16,52 @@ { "artifact_id": "HO-DET-012", "manifest_path": "examples/review/ho-det-012-artifact-manifest-v1.json" + }, + { + "artifact_id": "HO-DET-013", + "manifest_path": "examples/review/ho-det-013-artifact-manifest-v1.json" + }, + { + "artifact_id": "AWS-DET-001", + "manifest_path": "examples/review/aws-det-001-artifact-manifest-v1.json" + }, + { + "artifact_id": "ID-DET-001", + "manifest_path": "examples/review/id-det-001-artifact-manifest-v1.json" + }, + { + "artifact_id": "ID-DET-002", + "manifest_path": "examples/review/id-det-002-artifact-manifest-v1.json" + }, + { + "artifact_id": "ID-DET-003", + "manifest_path": "examples/review/id-det-003-artifact-manifest-v1.json" + }, + { + "artifact_id": "ID-DET-004", + "manifest_path": "examples/review/id-det-004-artifact-manifest-v1.json" + }, + { + "artifact_id": "HO-NDR-001", + "manifest_path": "examples/review/ho-ndr-001-artifact-manifest-v1.json" } ], "batch_claim_boundary": "Local fixture review set only; not runtime proof and not proof authority.", - "description": "Governed local fixture review set for HO-DET-009, HO-DET-010, HO-DET-011, and HO-DET-012.", - "expected_blocked_artifacts": [], + "description": "Governed local cross-domain fixture review set with an explicit boundary-contract BLOCKED outcome for HO-NDR-001.", + "expected_blocked_artifacts": [ + "HO-NDR-001" + ], "expected_pass_artifacts": [ "HO-DET-009", "HO-DET-010", "HO-DET-011", - "HO-DET-012" + "HO-DET-012", + "HO-DET-013", + "AWS-DET-001", + "ID-DET-001", + "ID-DET-002", + "ID-DET-003", + "ID-DET-004" ], "generated_outputs": [ "batch-machine-state.json", diff --git a/schemas/artifact-manifest-v1.schema.json b/schemas/artifact-manifest-v1.schema.json index 7c0ca40..e9c050d 100644 --- a/schemas/artifact-manifest-v1.schema.json +++ b/schemas/artifact-manifest-v1.schema.json @@ -16,6 +16,8 @@ "properties": { "source": { "type": "string" }, "event_ids": { "type": "array", "items": { "type": "integer" } }, + "event_keys": { "type": "array", "items": { "type": "string" } }, + "event_key_field": { "type": "string" }, "wazuh_rule_ids": { "type": "array", "items": { "type": "integer" } } } }, diff --git a/schemas/case-growth-index-v0.schema.json b/schemas/case-growth-index-v0.schema.json index ca9154e..29f8c74 100644 --- a/schemas/case-growth-index-v0.schema.json +++ b/schemas/case-growth-index-v0.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://hawkinsoperations.example/schemas/case-growth-index-v0.schema.json", - "title": "Hoxline Case Growth Index v0", + "title": "Hoxline Case Growth Index v1", "type": "object", "additionalProperties": false, "required": [ @@ -9,6 +9,14 @@ "generated_at", "repo_root", "proof_ceiling", + "historical_snapshot", + "current_authority", + "snapshot_state", + "source_revisions", + "contradictions", + "drift", + "next_legal_action", + "reproducibility_sha256", "repos_scanned", "repo_slot_accuracy", "source_files_scanned_count", @@ -20,10 +28,53 @@ "boundary" ], "properties": { - "schema_version": { "const": "case-growth-index-v0" }, + "schema_version": { "const": "case-growth-index-v1" }, "generated_at": { "type": "string", "minLength": 1 }, "repo_root": { "type": "string", "minLength": 1 }, "proof_ceiling": { "const": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY" }, + "historical_snapshot": { "const": false }, + "current_authority": { "const": true }, + "snapshot_state": { "type": "object" }, + "source_revisions": { + "type": "array", + "minItems": 7, + "maxItems": 7, + "items": { + "type": "object", + "required": [ + "repository", + "authority_role", + "resolved_ref", + "source_commit_sha", + "source_path", + "source_file_sha256", + "source_freshness_state", + "snapshot_freshness_state", + "historical_snapshot", + "current_authority", + "missing_source_state", + "dangling_reference_state", + "contradictions", + "drift", + "next_legal_action" + ], + "properties": { + "repository": { "type": "string" }, + "source_commit_sha": { "type": "string" }, + "source_path": { "type": "string" }, + "source_file_sha256": { "type": ["string", "null"] }, + "historical_snapshot": { "type": "boolean" }, + "current_authority": { "type": "boolean" }, + "missing_source_state": { "type": "boolean" }, + "dangling_reference_state": { "type": "boolean" } + }, + "additionalProperties": true + } + }, + "contradictions": { "type": "array", "items": { "type": "object" } }, + "drift": { "type": "array", "items": { "type": "object" } }, + "next_legal_action": { "type": "string", "minLength": 1 }, + "reproducibility_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "repos_scanned": { "type": "array", "items": { "type": "object" } diff --git a/src/hoxline/case_growth/__init__.py b/src/hoxline/case_growth/__init__.py index 037d5c4..ba80ebb 100644 --- a/src/hoxline/case_growth/__init__.py +++ b/src/hoxline/case_growth/__init__.py @@ -1,6 +1,11 @@ from __future__ import annotations -from .collector import build_case_growth_index +from .collector import build_case_growth_index, diff_case_growth_snapshot, verify_case_growth_snapshot from .render import render_case_growth_markdown -__all__ = ["build_case_growth_index", "render_case_growth_markdown"] +__all__ = [ + "build_case_growth_index", + "diff_case_growth_snapshot", + "render_case_growth_markdown", + "verify_case_growth_snapshot", +] diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 9b39a5e..c273867 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -2,6 +2,8 @@ from copy import deepcopy from datetime import datetime, timezone +import hashlib +import json from pathlib import Path import re from typing import Any @@ -10,10 +12,16 @@ REPO_NAMES, case_growth_files, discover_case_ids, + file_sha256, + git_blob_sha256, + git_commit_exists, last_git_update, load_structured, repo_branch, repo_dirty, + repo_dirty_paths, + repo_head_sha, + repo_parent_sha, repo_relative, resolve_repo_paths, ) @@ -21,6 +29,16 @@ PROOF_CEILING = "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY" +AUTHORITY_SOURCES = { + ".github": ("org command-center routing", "scripts/verify-command-center-invariants.py"), + "hawkinsoperations-detections": ("detection source truth", "detections/DETECTION_PROMOTION_MATRIX.yml"), + "hawkinsoperations-validation": ("controlled validation truth", "validation/VALIDATION_REGISTRY.yml"), + "hawkinsoperations-platform": ("platform contract truth", "contracts/public-status-source-contract-v1.json"), + "hawkinsoperations-proof": ("proof and claim-boundary truth", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml"), + "hawkinsoperations-website": ("rendering-only public status", "public/data/public-status.json"), + "hoxline": ("case-growth and fixture-review product truth", "src/hoxline/case_growth/collector.py"), +} + BOUNDARY = { "runtime_public_proof_claimed": False, "signal_public_proof_claimed": False, @@ -85,7 +103,7 @@ def build_case_growth_index(repo_root: Path, generated_at: str | None = None) -> repos_scanned.append( { "repo": name, - "path": str(path) if path is not None else "NOT_FOUND", + "path": name if path is not None else "NOT_FOUND", "exists": path is not None, "branch": repo_branch(path) if path is not None else "NOT_FOUND", "dirty": repo_dirty(path) if path is not None else None, @@ -116,11 +134,25 @@ def build_case_growth_index(repo_root: Path, generated_at: str | None = None) -> case_growth_health = _build_case_growth_health(summary) _add_cross_repo_quality_notes(ordered_rows, data_quality_notes) - return { - "schema_version": "case-growth-index-v0", + source_revisions = _build_source_revisions(repo_paths) + contradictions, drift = _source_convergence_findings(repo_paths, source_revisions, summary, ordered_rows) + result = { + "schema_version": "case-growth-index-v1", "generated_at": generated, - "repo_root": str(repo_root), + "repo_root": "HawkinsOperations", "proof_ceiling": PROOF_CEILING, + "historical_snapshot": False, + "current_authority": True, + "snapshot_state": { + "freshness": "CURRENT", + "historical_snapshot": False, + "current_authority": True, + "self_source_revision_semantics": "worktree-head-or-snapshot-commit-parent", + }, + "source_revisions": source_revisions, + "contradictions": contradictions, + "drift": drift, + "next_legal_action": _next_legal_action(source_revisions, contradictions, drift), "repos_scanned": repos_scanned, "repo_slot_accuracy": repo_slot_accuracy, "source_files_scanned_count": scanned_count, @@ -131,6 +163,8 @@ def build_case_growth_index(repo_root: Path, generated_at: str | None = None) -> "data_quality_notes": data_quality_notes, "boundary": deepcopy(BOUNDARY), } + result["reproducibility_sha256"] = _reproducibility_hash(result) + return result def _repo_boundary(repo_name: str) -> str: @@ -145,6 +179,275 @@ def _repo_boundary(repo_name: str) -> str: }[repo_name] +def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str, Any]]: + revisions: list[dict[str, Any]] = [] + for repository in REPO_NAMES: + repo = repo_paths.get(repository) + authority_role, relative_path = AUTHORITY_SOURCES[repository] + source = repo / relative_path if repo is not None else None + source_exists = source is not None and source.is_file() + sha = repo_head_sha(repo) if repo is not None else "UNKNOWN" + branch = repo_branch(repo) if repo is not None else "NOT_FOUND" + dirty = repo_dirty(repo) if repo is not None else False + dirty_paths = repo_dirty_paths(repo) if repo is not None else [] + if repo is None: + freshness = "MISSING_REPOSITORY" + elif not source_exists: + freshness = "MISSING_AUTHORITY_SOURCE" + elif sha == "UNKNOWN": + freshness = "UNVERSIONED_SOURCE" + elif repository == "hoxline" and dirty and all(path.startswith("examples/case-growth/") for path in dirty_paths): + freshness = "CURRENT_SELF_REFERENTIAL" + elif dirty: + freshness = "WORKTREE_MODIFIED" + else: + freshness = "CURRENT" + committed_fingerprint = git_blob_sha256(repo, sha, relative_path) if repo is not None else None + revisions.append( + { + "repository": repository, + "authority_role": authority_role, + "resolved_ref": branch, + "source_commit_sha": sha, + "source_parent_sha": repo_parent_sha(repo) if repo is not None and repository == "hoxline" else None, + "self_referential": repository == "hoxline", + "revision_scope": ( + "authoritative_sources_excluding_snapshot" if repository == "hoxline" else "authoritative_source_at_commit" + ), + "source_path": relative_path, + "source_file_sha256": ( + committed_fingerprint + if committed_fingerprint is not None + else file_sha256(source) if source_exists and source is not None else None + ), + "source_freshness_state": freshness, + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": False, + "current_authority": source_exists and sha != "UNKNOWN", + "missing_source_state": not source_exists, + "dangling_reference_state": repo is not None and not source_exists, + "contradictions": [], + "drift": [], + "next_legal_action": ( + "none; preserve source ownership" + if freshness == "CURRENT" + else "review and commit scoped authority-source changes before regenerating" + if freshness == "WORKTREE_MODIFIED" + else f"restore {repository}/{relative_path} from its owning repository" + ), + } + ) + return revisions + + +def _finding( + code: str, + owner: str, + path: str, + expected: Any, + actual: Any, + remediation: str, + classification: str = "ACTIONABLE_DRIFT", +) -> dict[str, Any]: + return { + "code": code, + "source_owner": owner, + "source_path": path, + "expected": expected, + "actual": actual, + "classification": classification, + "next_legal_action": remediation, + } + + +def _duplicates(values: list[str]) -> list[str]: + return sorted({value for value in values if values.count(value) > 1}) + + +def _source_convergence_findings( + repo_paths: dict[str, Path | None], + source_revisions: list[dict[str, Any]], + summary: dict[str, int], + rows: list[dict[str, Any]], +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + contradictions: list[dict[str, Any]] = [] + drift: list[dict[str, Any]] = [] + for revision in source_revisions: + if revision["missing_source_state"]: + contradictions.append( + _finding( + "MISSING_AUTHORITY_SOURCE", + revision["repository"], + revision["source_path"], + "existing authoritative source", + "missing", + revision["next_legal_action"], + ) + ) + + structured_sources = ( + ("hawkinsoperations-detections", "detections/DETECTION_PROMOTION_MATRIX.yml", "entries"), + ("hawkinsoperations-validation", "validation/VALIDATION_REGISTRY.yml", "packages"), + ("hawkinsoperations-proof", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "entries"), + ) + for owner, relative_path, collection in structured_sources: + repo = repo_paths.get(owner) + if repo is None or not (repo / relative_path).is_file(): + continue + data = load_structured(repo / relative_path) or {} + ids = [str(item.get("detection_id")) for item in data.get(collection, []) if isinstance(item, dict) and item.get("detection_id")] + for duplicate in _duplicates(ids): + contradictions.append( + _finding( + "DUPLICATE_CASE_ID", + owner, + relative_path, + "one entry per case ID", + duplicate, + f"remove or reconcile the duplicate {duplicate} entry in the owning source", + ) + ) + + proof_repo = repo_paths.get("hawkinsoperations-proof") + if proof_repo is not None: + proof_path = proof_repo / AUTHORITY_SOURCES["hawkinsoperations-proof"][1] + if proof_path.is_file(): + proof_data = load_structured(proof_path) or {} + for entry in proof_data.get("entries", []): + if not isinstance(entry, dict): + continue + case_id = str(entry.get("detection_id") or "UNKNOWN") + for field in ("proof_record_path", "proof_card_path"): + ref = entry.get(field) + if ref and not (proof_repo / str(ref)).is_file(): + contradictions.append( + _finding( + "DANGLING_PROOF_PATH", + "hawkinsoperations-proof", + str(ref), + "existing source-controlled file", + f"missing reference for {case_id}", + f"repair or explicitly clear {field} for {case_id} in the proof index", + ) + ) + + detection_repo = repo_paths.get("hawkinsoperations-detections") + if detection_repo is not None and proof_repo is not None: + matrix_path = detection_repo / AUTHORITY_SOURCES["hawkinsoperations-detections"][1] + proof_path = proof_repo / AUTHORITY_SOURCES["hawkinsoperations-proof"][1] + if matrix_path.is_file() and proof_path.is_file(): + matrix_entries = { + str(item.get("detection_id")): item + for item in (load_structured(matrix_path) or {}).get("entries", []) + if isinstance(item, dict) and item.get("detection_id") + } + proof_entries = { + str(item.get("detection_id")): item + for item in (load_structured(proof_path) or {}).get("entries", []) + if isinstance(item, dict) and item.get("detection_id") + } + for case_id, proof_entry in proof_entries.items(): + matrix_entry = matrix_entries.get(case_id, {}) + notes = str(matrix_entry.get("notes") or "") + if proof_entry.get("proof_record_path") and re.search(r"(?i)\bno\b.*\bproof record\b", notes): + contradictions.append( + _finding( + "DETECTION_PROOF_RECORD_CONTRADICTION", + "hawkinsoperations-detections", + "detections/DETECTION_PROMOTION_MATRIX.yml", + f"proof record exists at {proof_entry['proof_record_path']}", + notes, + f"update the detection matrix note for {case_id} from the proof-owned current index", + ) + ) + + website_repo = repo_paths.get("hawkinsoperations-website") + if website_repo is not None: + website_path = website_repo / AUTHORITY_SOURCES["hawkinsoperations-website"][1] + if website_path.is_file(): + website = load_structured(website_path) or {} + rendered = ((website.get("metrics") or {}).get("proof_records") or {}).get("value") + current = summary["proof_records_count"] + if rendered is not None and rendered != current: + drift.append( + _finding( + "WEBSITE_PROOF_COUNT_DRIFT", + "hawkinsoperations-proof", + "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + current, + rendered, + "regenerate website public status from the proof-owned current index; website remains rendering-only", + ) + ) + + current_shas = {item["repository"]: item["source_commit_sha"] for item in source_revisions} + for surface_owner, relative_path in ( + ("hawkinsoperations-platform", AUTHORITY_SOURCES["hawkinsoperations-platform"][1]), + ("hawkinsoperations-website", AUTHORITY_SOURCES["hawkinsoperations-website"][1]), + ): + surface_repo = repo_paths.get(surface_owner) + if surface_repo is None or not (surface_repo / relative_path).is_file(): + continue + surface = load_structured(surface_repo / relative_path) or {} + refs = surface.get("source_commit_refs") if isinstance(surface.get("source_commit_refs"), dict) else {} + revision_items = surface.get("source_revisions") + if isinstance(revision_items, list): + for item in revision_items: + if isinstance(item, dict): + name = item.get("repository") or item.get("repo") or item.get("name") + sha = item.get("source_commit_sha") or item.get("commit_sha") or item.get("source_revision") + if name and sha: + refs[str(name).removeprefix("HawkinsOperations/")] = sha + for owner, stated_sha in refs.items(): + normalized_owner = str(owner).removeprefix("HawkinsOperations/") + current_sha = current_shas.get(normalized_owner) + if current_sha and stated_sha != current_sha: + drift.append( + _finding( + "SOURCE_REVISION_DRIFT", + surface_owner, + relative_path, + current_sha, + stated_sha, + f"regenerate {relative_path} from {normalized_owner} at its current source revision", + ) + ) + + case_ids = [str(row.get("case_id")) for row in rows] + for duplicate in _duplicates(case_ids): + contradictions.append( + _finding( + "DUPLICATE_GENERATED_CASE_ID", + "hoxline", + "generated cases", + "unique case IDs", + duplicate, + "reconcile duplicate source entries before regenerating the snapshot", + ) + ) + return contradictions, drift + + +def _next_legal_action( + source_revisions: list[dict[str, Any]], contradictions: list[dict[str, Any]], drift: list[dict[str, Any]] +) -> str: + if contradictions: + return str(contradictions[0]["next_legal_action"]) + if drift: + return str(drift[0]["next_legal_action"]) + if any(item["source_freshness_state"] == "WORKTREE_MODIFIED" for item in source_revisions): + return "commit only the validated scoped changes, then regenerate the current snapshot from clean source revisions" + return "none; current source-controlled inputs converge" + + +def _reproducibility_hash(index: dict[str, Any]) -> str: + stable = deepcopy(index) + stable.pop("generated_at", None) + stable.pop("reproducibility_sha256", None) + encoded = json.dumps(stable, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") + return hashlib.sha256(encoded).hexdigest() + + def _base_row(case_id: str) -> dict[str, Any]: detection_id = None if case_id.startswith("HOX-GAUNTLET-") else case_id return { @@ -675,3 +978,226 @@ def _add_cross_repo_quality_notes(rows: list[dict[str, Any]], notes: list[str]) notes.append(f"{row['case_id']} has controlled validation but no proof record") if row["proof_record_status"] == "PROOF_RECORD_EXISTS" and row["proofcard_status"] != "PROOFCARD_EXISTS": notes.append(f"{row['case_id']} has proof record but no ProofCard") + + +def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tuple[list[str], dict[str, Any]]: + errors: list[str] = [] + current = build_case_growth_index(repo_root, generated_at=str(snapshot.get("generated_at") or "1970-01-01T00:00:00Z")) + historical = snapshot.get("historical_snapshot") is True + current_authority = snapshot.get("current_authority") is True + if historical and current_authority: + errors.append("snapshot cannot be both historical_snapshot=true and current_authority=true") + if not historical and not current_authority: + errors.append("non-historical snapshot must declare current_authority=true") + if snapshot.get("schema_version") != "case-growth-index-v1": + errors.append("snapshot schema_version must be case-growth-index-v1") + + if _contains_absolute_local_path(snapshot): + errors.append("snapshot contains an absolute local path") + + cases = snapshot.get("cases") if isinstance(snapshot.get("cases"), list) else [] + case_ids = [str(item.get("case_id")) for item in cases if isinstance(item, dict) and item.get("case_id")] + for duplicate in _duplicates(case_ids): + errors.append(f"duplicate case ID in snapshot: {duplicate}") + + boundary = snapshot.get("boundary") if isinstance(snapshot.get("boundary"), dict) else {} + for key, value in boundary.items(): + if value is not False: + errors.append(f"unauthorized boundary promotion: {key}={value!r}") + for row in cases: + if not isinstance(row, dict): + continue + case_id = row.get("case_id", "UNKNOWN") + if row.get("public_safe_status") not in {None, "NOT_PUBLIC_SAFE"}: + errors.append(f"{case_id}: unauthorized public-safe status {row.get('public_safe_status')!r}") + if row.get("case_state") == "CLOSED": + errors.append(f"{case_id}: unauthorized case closure") + if row.get("signal_status") not in {None, "NOT_PROVEN"}: + errors.append(f"{case_id}: signal status exceeds checked-source authority") + errors.extend(f"{case_id}: {error}" for error in _case_claim_violations(row)) + + stated_revisions = snapshot.get("source_revisions") if isinstance(snapshot.get("source_revisions"), list) else [] + if len(stated_revisions) != len(REPO_NAMES): + errors.append(f"source_revisions must contain exactly {len(REPO_NAMES)} repositories") + stated_names = [str(item.get("repository") or "") for item in stated_revisions if isinstance(item, dict)] + if len(stated_names) != len(set(stated_names)): + errors.append("source_revisions repository names must be unique") + if set(stated_names) != set(REPO_NAMES): + missing = sorted(set(REPO_NAMES) - set(stated_names)) + extra = sorted(set(stated_names) - set(REPO_NAMES)) + errors.append(f"source_revisions must match exact seven-repository set; missing={missing}, extra={extra}") + current_by_repo = {item["repository"]: item for item in current["source_revisions"]} + repo_paths = resolve_repo_paths(Path(repo_root)) + for stated in stated_revisions: + if not isinstance(stated, dict): + errors.append("source_revisions entries must be objects") + continue + repository = str(stated.get("repository") or "") + if repository not in current_by_repo: + errors.append(f"unknown source repository in snapshot: {repository or 'MISSING'}") + continue + current_revision = current_by_repo[repository] + stated_sha = stated.get("source_commit_sha") + accepted_shas = {current_revision["source_commit_sha"]} + if repository == "hoxline": + accepted_shas.add(current_revision.get("source_parent_sha")) + if stated.get("self_referential") is not True: + errors.append("hoxline: self_referential must be true") + if stated.get("revision_scope") != "authoritative_sources_excluding_snapshot": + errors.append("hoxline: revision_scope must be authoritative_sources_excluding_snapshot") + if current_authority and stated_sha != current_revision.get("source_parent_sha"): + errors.append("hoxline: current checked snapshot must cite the immediate parent engine commit") + if not re.fullmatch(r"[0-9a-f]{40}", str(stated_sha or "")): + errors.append(f"{repository}: source_commit_sha must be a 40-character Git SHA") + elif repo_paths.get(repository) is None or not git_commit_exists(repo_paths[repository], str(stated_sha)): + errors.append(f"{repository}: source_commit_sha is not a reachable commit in the stated repository") + else: + if stated_sha not in accepted_shas and not historical: + errors.append( + f"{repository}: stale source revision {stated_sha}; current is {current_revision['source_commit_sha']}; " + "regenerate or explicitly label the snapshot historical" + ) + source_path = str(stated.get("source_path") or "") + source_prefix = f"{repository}/" + commit_path = source_path[len(source_prefix) :] if source_path.startswith(source_prefix) else source_path + commit_fingerprint = git_blob_sha256(repo_paths[repository], str(stated_sha), commit_path) + if commit_fingerprint is None: + errors.append(f"{repository}: authoritative source path is missing from the stated commit") + elif stated.get("source_file_sha256") != commit_fingerprint: + errors.append(f"{repository}: source_file_sha256 does not match the authoritative blob at the stated commit") + if stated.get("source_path") != current_revision["source_path"]: + errors.append(f"{repository}: authoritative source path disagrees with current owner path") + if stated.get("source_file_sha256") != current_revision.get("source_file_sha256") and not historical: + errors.append( + f"{repository}: authoritative source fingerprint drifted; regenerate from {current_revision['source_path']}" + ) + if stated.get("missing_source_state") is True or stated.get("dangling_reference_state") is True: + errors.append(f"{repository}: snapshot records missing or dangling authority source") + allowed_freshness = {"CURRENT", "CURRENT_SELF_REFERENTIAL"} if repository == "hoxline" else {"CURRENT"} + if current_authority and stated.get("source_freshness_state") not in allowed_freshness: + errors.append( + f"{repository}: current snapshot source_freshness_state must be one of {sorted(allowed_freshness)}, " + f"got {stated.get('source_freshness_state')!r}" + ) + if current_authority and current_revision.get("source_freshness_state") not in allowed_freshness: + errors.append( + f"{repository}: current repository source is not clean/current: " + f"{current_revision.get('source_freshness_state')!r}" + ) + + stated_hash = snapshot.get("reproducibility_sha256") + if stated_hash != _reproducibility_hash(snapshot): + errors.append("snapshot reproducibility_sha256 does not reproduce from its normalized content") + + if current_authority: + if snapshot.get("summary") != current.get("summary"): + errors.append("current snapshot summary counts disagree with current authoritative repository state") + if snapshot.get("case_ids_discovered_count") != current.get("case_ids_discovered_count"): + errors.append("current snapshot case count disagrees with current authoritative repository state") + for field in ("cases", "case_growth_health", "repo_slot_accuracy", "boundary"): + if snapshot.get(field) != current.get(field): + errors.append(f"current snapshot {field} disagrees with normalized current authoritative content") + for finding in current.get("contradictions", []): + errors.append(f"current contradiction {finding['code']}: {finding['actual']} ({finding['next_legal_action']})") + for finding in current.get("drift", []): + errors.append(f"current drift {finding['code']}: expected {finding['expected']}, actual {finding['actual']} ({finding['next_legal_action']})") + return errors, current + + +ABSOLUTE_LOCAL_PATH = re.compile( + r"(?i)(?:[A-Z]:[\\/]|(?]+)" +) + + +def _contains_absolute_local_path(value: Any) -> bool: + if isinstance(value, dict): + return any(_contains_absolute_local_path(key) or _contains_absolute_local_path(item) for key, item in value.items()) + if isinstance(value, list): + return any(_contains_absolute_local_path(item) for item in value) + if isinstance(value, str): + return ABSOLUTE_LOCAL_PATH.search(value) is not None + return False + + +def _case_claim_violations(row: dict[str, Any]) -> list[str]: + violations: list[str] = [] + runtime = str(row.get("runtime_candidate_status") or "") + authority = str(row.get("claim_authority_status") or "") + if re.search(r"(?i)RUNTIME[_ -]?ACTIVE|PRODUCTION[_ -]?READY", runtime): + violations.append(f"unauthorized runtime_candidate_status {runtime!r}") + if re.search(r"(?i)(?:AI|ANALYST)[_ -]?APPROVED|FINAL[_ -]?AUTHORIZATION|CASE[_ -]?CLOSED", authority): + violations.append(f"unauthorized claim_authority_status {authority!r}") + safe_row = {key: value for key, value in row.items() if key != "blocked_claims"} + text = json.dumps(safe_row, sort_keys=True) + for label, pattern in ( + ("AI-approved disposition", r"(?i)AI[-_ ]approved disposition"), + ("analyst-approved disposition", r"(?i)analyst[-_ ]approved disposition"), + ("final authorization", r"(?i)final[-_ ]authorization"), + ("case closure", r"(?i)case[-_ ](?:closure|closed)"), + ): + if re.search(pattern, text): + violations.append(f"unauthorized {label} wording outside blocked_claims") + return violations + + +def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict[str, Any]: + current = build_case_growth_index(repo_root, generated_at=str(snapshot.get("generated_at") or "1970-01-01T00:00:00Z")) + historical = snapshot.get("historical_snapshot") is True + before_revisions = { + item.get("repository"): item + for item in snapshot.get("source_revisions", []) + if isinstance(item, dict) and item.get("repository") + } + changes: list[dict[str, Any]] = [] + for current_revision in current["source_revisions"]: + repository = current_revision["repository"] + before = before_revisions.get(repository, {}) + for field in ("source_commit_sha", "source_file_sha256", "source_path"): + if before.get(field) != current_revision[field]: + changes.append( + { + "field": field, + "source_owner": repository, + "source_path": current_revision["source_path"], + "before": before.get(field), + "after": current_revision[field], + "old_source_revision": before.get("source_commit_sha"), + "current_source_revision": current_revision["source_commit_sha"], + "classification": "EXPECTED_HISTORICAL_CONTEXT" if historical else "ACTIONABLE_DRIFT", + "next_remediation": "retain as historical context" if historical else "regenerate the current snapshot from the owning source", + } + ) + before_summary = snapshot.get("summary") if isinstance(snapshot.get("summary"), dict) else {} + for field, after in current["summary"].items(): + before = before_summary.get(field) + if before != after: + changes.append( + { + "field": f"summary.{field}", + "source_owner": "hoxline", + "source_path": "derived from seven source-owned inputs", + "before": before, + "after": after, + "old_source_revision": None, + "current_source_revision": current_by_repo_sha(current, "hoxline"), + "classification": "EXPECTED_HISTORICAL_CONTEXT" if historical else "ACTIONABLE_DRIFT", + "next_remediation": "retain as historical context" if historical else "regenerate the current snapshot", + } + ) + return { + "schema_version": "case-growth-diff-v1", + "historical_snapshot": historical, + "current_authority": snapshot.get("current_authority") is True, + "changes": changes, + "contradictions": current["contradictions"], + "drift": current["drift"], + "next_legal_action": current["next_legal_action"] if changes or current["drift"] else "none; snapshot converges", + } + + +def current_by_repo_sha(index: dict[str, Any], repository: str) -> str | None: + for item in index.get("source_revisions", []): + if item.get("repository") == repository: + return item.get("source_commit_sha") + return None diff --git a/src/hoxline/case_growth/discovery.py b/src/hoxline/case_growth/discovery.py index 081c1a3..84547cc 100644 --- a/src/hoxline/case_growth/discovery.py +++ b/src/hoxline/case_growth/discovery.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import hashlib import re import subprocess from pathlib import Path @@ -65,6 +66,61 @@ def repo_dirty(repo_path: Path) -> bool: return bool(git_lines(repo_path, ["status", "--short"])) +def repo_dirty_paths(repo_path: Path) -> list[str]: + return [line[3:].strip().replace("\\", "/") for line in git_lines(repo_path, ["status", "--short"]) if len(line) > 3] + + +def repo_head_sha(repo_path: Path) -> str: + lines = git_lines(repo_path, ["rev-parse", "HEAD"]) + return lines[0] if lines and re.fullmatch(r"[0-9a-f]{40}", lines[0]) else "UNKNOWN" + + +def repo_parent_sha(repo_path: Path) -> str: + lines = git_lines(repo_path, ["rev-parse", "HEAD^"]) + return lines[0] if lines and re.fullmatch(r"[0-9a-f]{40}", lines[0]) else "UNKNOWN" + + +def git_commit_exists(repo_path: Path, sha: str) -> bool: + if not re.fullmatch(r"[0-9a-f]{40}", sha): + return False + try: + result = subprocess.run( + ["git", "-C", str(repo_path), "cat-file", "-e", f"{sha}^{{commit}}"], + check=False, + capture_output=True, + text=True, + ) + except OSError: + return False + return result.returncode == 0 + + +def git_blob_sha256(repo_path: Path, sha: str, repo_relative_path: str) -> str | None: + if not re.fullmatch(r"[0-9a-f]{40}", sha): + return None + normalized_path = repo_relative_path.replace("\\", "/").lstrip("/") + if not normalized_path or ".." in Path(normalized_path).parts: + return None + try: + result = subprocess.run( + ["git", "-C", str(repo_path), "show", f"{sha}:{normalized_path}"], + check=False, + capture_output=True, + ) + except OSError: + return None + if result.returncode != 0: + return None + return hashlib.sha256(result.stdout).hexdigest() + + +def file_sha256(path: Path) -> str | None: + try: + return hashlib.sha256(path.read_bytes()).hexdigest() + except OSError: + return None + + def tracked_files(repo_path: Path) -> list[Path]: files = git_lines(repo_path, ["ls-files"]) if files: diff --git a/src/hoxline/case_growth/render.py b/src/hoxline/case_growth/render.py index 7a6a02b..0f8217d 100644 --- a/src/hoxline/case_growth/render.py +++ b/src/hoxline/case_growth/render.py @@ -8,11 +8,14 @@ def render_case_growth_markdown(index: dict[str, Any]) -> str: health = index["case_growth_health"] repo_slots = index.get("repo_slot_accuracy", {}) lines = [ - "# Hoxline Case Growth Index v0", + "# Hoxline Case Growth Index v1", "", f"Generated: `{index['generated_at']}`", f"Proof ceiling: `{index['proof_ceiling']}`", f"Repo-slot accuracy: `{repo_slots.get('wording', 'UNKNOWN_WITH_REASON')}`", + f"Historical snapshot: `{str(index.get('historical_snapshot')).lower()}`", + f"Current authority: `{str(index.get('current_authority')).lower()}`", + f"Reproducibility SHA-256: `{index.get('reproducibility_sha256')}`", "", "## Summary", "", @@ -41,6 +44,33 @@ def render_case_growth_markdown(index: dict[str, Any]) -> str: ): lines.append(f"| `{key}` | {summary[key]} |") + lines.extend( + [ + "", + "## Source Revisions", + "", + "| Repository | Authority role | Source path | Source revision | Source freshness | Snapshot freshness |", + "| --- | --- | --- | --- | --- | --- |", + ] + ) + for source in index.get("source_revisions", []): + lines.append( + f"| {_cell(source['repository'])} | {_cell(source['authority_role'])} | {_cell(source['source_path'])} | " + f"`{source['source_commit_sha']}` | `{source['source_freshness_state']}` | `{source['snapshot_freshness_state']}` |" + ) + + lines.extend(["", "## Convergence Findings", ""]) + findings = list(index.get("contradictions", [])) + list(index.get("drift", [])) + if findings: + for finding in findings: + lines.append( + f"- `{finding['code']}` owner `{finding['source_owner']}` path `{finding['source_path']}`: " + f"expected `{finding['expected']}`, actual `{finding['actual']}`; next: {finding['next_legal_action']}" + ) + else: + lines.append("- No missing, dangling, contradictory, or stale source-owned state detected.") + lines.extend(["", f"Next legal action: {index.get('next_legal_action')}"]) + lines.extend( [ "", diff --git a/src/hoxline/case_growth/report.py b/src/hoxline/case_growth/report.py index 12cf597..7b584f9 100644 --- a/src/hoxline/case_growth/report.py +++ b/src/hoxline/case_growth/report.py @@ -1,6 +1,12 @@ from __future__ import annotations -from .collector import ROW_FIELDS, build_case_growth_index +from .collector import ROW_FIELDS, build_case_growth_index, diff_case_growth_snapshot, verify_case_growth_snapshot from .render import render_case_growth_markdown -__all__ = ["ROW_FIELDS", "build_case_growth_index", "render_case_growth_markdown"] +__all__ = [ + "ROW_FIELDS", + "build_case_growth_index", + "diff_case_growth_snapshot", + "render_case_growth_markdown", + "verify_case_growth_snapshot", +] diff --git a/src/hoxline/cli.py b/src/hoxline/cli.py index da7337b..1f33cd1 100644 --- a/src/hoxline/cli.py +++ b/src/hoxline/cli.py @@ -5,7 +5,12 @@ from pathlib import Path import sys -from .case_growth import build_case_growth_index, render_case_growth_markdown +from .case_growth import ( + build_case_growth_index, + diff_case_growth_snapshot, + render_case_growth_markdown, + verify_case_growth_snapshot, +) from .gauntlet import GauntletError, build_full_loop_run, render_markdown, verify_full_loop_run_file from .gauntlet import decide_claim_authority_v1, render_proofcard_v1, summarize_gauntlet_run_v1 from .demo import DemoError, build_demo_run, default_output_dir, render_quickstart_console, verify_demo_run_dir, write_demo_run @@ -57,6 +62,10 @@ def main(argv: list[str] | None = None) -> int: return _verify_review_batch(args) if args.command == "case-growth" and args.case_growth_command == "index": return _run_case_growth_index(args) + if args.command == "case-growth" and args.case_growth_command == "verify": + return _run_case_growth_verify(args) + if args.command == "case-growth" and args.case_growth_command == "diff": + return _run_case_growth_diff(args) parser.print_help() return 2 @@ -149,6 +158,14 @@ def _build_parser() -> argparse.ArgumentParser: case_growth_index_parser.add_argument("--repo-root", required=True, help="HawkinsOperations local org repo root") case_growth_index_parser.add_argument("--format", choices=("json", "markdown"), default="json", help="output format") case_growth_index_parser.add_argument("--output", help="optional output path") + case_growth_verify_parser = case_growth_subparsers.add_parser("verify", help="fail closed when a snapshot drifts from current authority") + case_growth_verify_parser.add_argument("--repo-root", required=True, help="HawkinsOperations local org repo root") + case_growth_verify_parser.add_argument("--snapshot", required=True, help="checked-in Case Growth snapshot JSON") + case_growth_verify_parser.add_argument("--format", choices=("json", "text"), default="json", help="output format") + case_growth_diff_parser = case_growth_subparsers.add_parser("diff", help="show source-owned snapshot drift") + case_growth_diff_parser.add_argument("--repo-root", required=True, help="HawkinsOperations local org repo root") + case_growth_diff_parser.add_argument("--snapshot", required=True, help="checked-in Case Growth snapshot JSON") + case_growth_diff_parser.add_argument("--format", choices=("json", "markdown"), default="json", help="output format") return parser def _add_demo_run_args(parser: argparse.ArgumentParser) -> None: @@ -239,6 +256,50 @@ def _run_case_growth_index(args: argparse.Namespace) -> int: return 0 +def _run_case_growth_verify(args: argparse.Namespace) -> int: + try: + snapshot = _load_json(Path(args.snapshot)) + errors, current = verify_case_growth_snapshot(Path(args.repo_root), snapshot) + except (OSError, ValueError) as exc: + print(f"Hoxline Case Growth verify: error: {exc}", file=sys.stderr) + return 2 + payload = { + "status": "FAIL" if errors else "PASS", + "error_count": len(errors), + "errors": errors, + "current_reproducibility_sha256": current["reproducibility_sha256"], + "next_legal_action": errors[0] if errors else "none; snapshot converges with current authority", + } + if args.format == "json": + print(json.dumps(payload, indent=2, sort_keys=True)) + else: + print(f"Hoxline Case Growth verify: {payload['status']}") + for error in errors: + print(f"- {error}") + return 1 if errors else 0 + + +def _run_case_growth_diff(args: argparse.Namespace) -> int: + try: + snapshot = _load_json(Path(args.snapshot)) + report = diff_case_growth_snapshot(Path(args.repo_root), snapshot) + except (OSError, ValueError) as exc: + print(f"Hoxline Case Growth diff: error: {exc}", file=sys.stderr) + return 2 + if args.format == "json": + print(json.dumps(report, indent=2, sort_keys=True)) + else: + lines = ["# Hoxline Case Growth Diff v1", "", f"Historical snapshot: `{str(report['historical_snapshot']).lower()}`", ""] + lines.extend( + f"- `{item['field']}`: `{item['before']}` -> `{item['after']}`; owner `{item['source_owner']}`; " + f"path `{item['source_path']}`; `{item['classification']}`; next: {item['next_remediation']}" + for item in report["changes"] + ) + lines.extend(["", f"Next legal action: {report['next_legal_action']}"]) + print("\n".join(lines)) + return 0 + + def _verify_gauntlet(args: argparse.Namespace) -> int: try: errors = verify_full_loop_run_file(Path(args.input), Path(args.schema)) diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 4ef2a27..f5cdcbe 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -2,6 +2,7 @@ from copy import deepcopy from datetime import datetime, timezone +import hashlib import json from pathlib import Path import re @@ -24,6 +25,7 @@ BATCH_INDEX_VERSION = "multi-artifact-review-index-v1" BATCH_MACHINE_STATE_VERSION = "batch-machine-state-v1" ARTIFACT_ID = "HO-DET-010" +ARTIFACT_ID_PATTERN = re.compile(r"^(?:HO-DET|HO-NDR|ID-DET|AWS-DET)-\d{3}$") EXPECTED_PASS_OUTPUTS = [ "artifact-manifest.json", "intake.json", @@ -97,6 +99,9 @@ "analyst-approved disposition": re.compile(r"\banalyst[- ]approved\b", re.IGNORECASE), "final authorization": re.compile(r"\bfinal authorization\b", re.IGNORECASE), "case closure": re.compile(r"\bcase closure\b|\bcase[- ]closed\b", re.IGNORECASE), + "live cloud claim": re.compile(r"\blive (?:AWS|cloud)(?: runtime| proof| signal)?\b", re.IGNORECASE), + "live identity runtime claim": re.compile(r"\blive (?:IdP|identity)(?: runtime| proof| signal)?\b", re.IGNORECASE), + "live Security Onion proof": re.compile(r"\blive Security Onion(?: proof| signal| runtime)?\b", re.IGNORECASE), } PRIVATE_FIELD_PATTERNS = [ re.compile(pattern, re.IGNORECASE) @@ -126,6 +131,10 @@ r"\bprivate execution ID\b", ) ] +ABSOLUTE_LOCAL_PATH = re.compile( + r"(?i)(?:[A-Z]:[\\/]|(?]+)" +) class ReviewEngineError(ValueError): @@ -282,18 +291,21 @@ def run_batch_review(index_path: Path, output_dir: Path | None = None, force: bo for artifact_entry in index["artifacts"]: artifact_id = artifact_entry["artifact_id"] manifest_path = _resolve_path(Path(artifact_entry["manifest_path"]), root) - artifact_out = artifacts_root / artifact_id + artifact_out = (artifacts_root / artifact_id).resolve() + if not _is_relative_to(artifact_out, artifacts_root.resolve()): + raise ReviewBlocked(f"artifact output path escapes batch root: {artifact_id}") run = run_review(manifest_path, artifact_out, force=True, repo_root=root) state = run["machine_state"] artifact_runs.append( { "artifact_id": artifact_id, - "manifest_path": str(manifest_path), - "output_dir": str(artifact_out.resolve()), - "machine_state": str((artifact_out / "machine-state.json").resolve()), - "reviewer_pack": str((artifact_out / "reviewer-pack.md").resolve()) if state["final_status"] == "PASS" else None, - "blocked_review": str((artifact_out / "blocked-review.md").resolve()) if state["final_status"] == "BLOCKED" else None, - "run_summary": str((artifact_out / "run-summary.json").resolve()), + "manifest_path": _display_path(manifest_path), + "output_dir": f"artifacts/{artifact_id}", + "machine_state": f"artifacts/{artifact_id}/machine-state.json", + "machine_state_sha256": _sha256_file(artifact_out / "machine-state.json"), + "reviewer_pack": f"artifacts/{artifact_id}/reviewer-pack.md" if state["final_status"] == "PASS" else None, + "blocked_review": f"artifacts/{artifact_id}/blocked-review.md" if state["final_status"] == "BLOCKED" else None, + "run_summary": f"artifacts/{artifact_id}/run-summary.json", "final_status": state["final_status"], "block_reason": state.get("block_reason"), "public_safe_status": state["public_safe_status"], @@ -366,8 +378,13 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: if not state_path.is_file(): errors.append(f"missing artifact machine-state for {artifact_id}") continue + if artifact.get("machine_state_sha256") != _sha256_file(state_path): + errors.append(f"{artifact_id}: machine-state hash mismatch") artifact_errors = verify_review_run(state_path) errors.extend(f"{artifact_id}: {error}" for error in artifact_errors) + child_state = _load_json(state_path) + if child_state.get("artifact_id") != artifact_id: + errors.append(f"{artifact_id}: aggregate artifact_id does not match child machine-state artifact_id") if artifact.get("public_safe_status") != PUBLIC_SAFE_STATUS: errors.append(f"{artifact_id}: public_safe_status must remain NOT_PUBLIC_SAFE") for false_field in ("endpoint_mutation", "wazuh_mutation", "runtime_proof", "public_proof_promoted", "lifetime_ledger_changed", "private_evidence_committed"): @@ -446,6 +463,8 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa artifact_id = item.get("artifact_id") if not artifact_id: raise ReviewBlocked("batch index artifact entry missing artifact_id") + if not ARTIFACT_ID_PATTERN.fullmatch(str(artifact_id)): + raise ReviewBlocked(f"batch index artifact_id has invalid format: {artifact_id}") if artifact_id in seen: raise ReviewBlocked(f"duplicate artifact_id in batch index: {artifact_id}") seen.add(str(artifact_id)) @@ -458,6 +477,11 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa allowed_root = (repo_root / "examples" / "review").resolve() if not _is_relative_to(resolved.resolve(), allowed_root): raise ReviewBlocked(f"batch index manifest path outside examples/review for {artifact_id}") + manifest = _load_json(resolved) + if manifest.get("artifact_id") != artifact_id: + raise ReviewBlocked( + f"batch index artifact_id {artifact_id} does not match manifest artifact_id {manifest.get('artifact_id')}" + ) def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Path, artifacts: list[dict[str, Any]]) -> dict[str, Any]: @@ -468,7 +492,7 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa "engine_version": BATCH_ENGINE_VERSION, "batch_id": output_dir.name, "index_id": index.get("index_id"), - "index_path": str(index_path), + "index_path": _display_path(index_path), "artifacts": artifacts, "expected_pass_artifacts": list(index.get("expected_pass_artifacts", [])), "expected_blocked_artifacts": list(index.get("expected_blocked_artifacts", [])), @@ -640,7 +664,7 @@ def _batch_run_summary(state: dict[str, Any]) -> dict[str, Any]: def _safe_blocked_index(index: dict[str, Any], index_path: Path, block_reason: str) -> dict[str, Any]: return { "schema_version": "blocked-batch-index-v1", - "index_path": str(index_path), + "index_path": _display_path(index_path), "index_id": index.get("index_id", "UNKNOWN") if index else "UNKNOWN", "final_status": "BLOCKED", "block_reason": block_reason, @@ -724,7 +748,7 @@ def _build_blocked_run(manifest: dict[str, Any], manifest_path: Path, output_dir def _safe_blocked_manifest(manifest: dict[str, Any], manifest_path: Path, block_reason: str) -> dict[str, Any]: return { "schema_version": "blocked-artifact-manifest-v1", - "manifest_path": str(manifest_path), + "manifest_path": _display_path(manifest_path), "artifact_id": manifest.get("artifact_id", "UNKNOWN") if manifest else "UNKNOWN", "final_status": "BLOCKED", "block_reason": block_reason, @@ -826,15 +850,19 @@ def _artifact_intake(manifest: dict[str, Any]) -> dict[str, Any]: def _telemetry_contract_check(manifest: dict[str, Any], fixture: dict[str, Any]) -> dict[str, Any]: contract = manifest["telemetry_contract"] - observed_event_ids = sorted({int(event["event_id"]) for event in fixture["events"]}) + observed_event_ids = sorted({int(event["event_id"]) for event in fixture["events"] if "event_id" in event}) + event_key_field = str(contract.get("event_key_field") or "event_key") + observed_event_keys = sorted({str(event[event_key_field]) for event in fixture["events"] if event_key_field in event}) return { "schema_version": "telemetry-contract-check-v0", "artifact_id": manifest["artifact_id"], "required_source": contract["source"], "event_ids": list(manifest["expected_event_ids"]), "fixture_event_ids": observed_event_ids, + "event_keys": list(manifest.get("expected_event_keys", [])), + "fixture_event_keys": observed_event_keys, "wazuh_rule_family": list(manifest["expected_rule_ids"]), - "required_fields": list(contract.get("required_fields", ["event_id", "channel", "action", "actor"])), + "required_fields": list(contract.get("required_fields", [_manifest_event_selector(manifest), "channel", "action", "actor"])), "missing_required_fields": [], "result": "pass", "scope": "pass for fixture only", @@ -885,7 +913,8 @@ def _synthetic_signal(manifest: dict[str, Any], fixture: dict[str, Any], validat def _enrichment(manifest: dict[str, Any], fixture: dict[str, Any]) -> dict[str, Any]: - event_mapping = {str(event_id): f"fixture event metadata for {manifest['artifact_id']}" for event_id in manifest["expected_event_ids"]} + selectors = [str(event_id) for event_id in manifest["expected_event_ids"]] + [str(key) for key in manifest.get("expected_event_keys", [])] + event_mapping = {selector: f"fixture event metadata for {manifest['artifact_id']}" for selector in selectors} return { "schema_version": "enrichment-v0", "artifact_id": manifest["artifact_id"], @@ -1089,7 +1118,10 @@ def _validate_review_fixture(fixture: dict[str, Any], manifest: dict[str, Any], for event in events: if not isinstance(event, dict): raise ReviewBlocked("fixture events must be objects") - for field in ("event_id", "channel", "action", "actor"): + required_fields = manifest.get("telemetry_contract", {}).get( + "required_fields", [_manifest_event_selector(manifest), "channel", "action", "actor"] + ) + for field in required_fields: if field not in event: raise ReviewBlocked(f"fixture event missing field: {field}") if expected_detection and not _fixture_matches_manifest(fixture, manifest): @@ -1102,14 +1134,24 @@ def _fixture_matches_manifest(fixture: dict[str, Any], manifest: dict[str, Any]) if fixture.get("expected_detection") is not True: return False expected_ids = {int(event_id) for event_id in manifest.get("expected_event_ids", [])} + expected_keys = {str(value) for value in manifest.get("expected_event_keys", [])} + event_key_field = _manifest_event_selector(manifest) for event in fixture.get("events", []): if not isinstance(event, dict): continue - if int(event.get("event_id", 0)) in expected_ids and event.get("channel") in {"Windows Security", "Windows System", "TaskScheduler Operational"}: + event_id = event.get("event_id") + if event_id is not None and expected_ids and int(event_id) in expected_ids: + return True + if expected_keys and str(event.get(event_key_field, "")) in expected_keys: return True return False +def _manifest_event_selector(manifest: dict[str, Any]) -> str: + contract = manifest.get("telemetry_contract") if isinstance(manifest.get("telemetry_contract"), dict) else {} + return "event_id" if manifest.get("expected_event_ids") else str(contract.get("event_key_field") or "event_key") + + def _allowed_claims_for(manifest: dict[str, Any]) -> list[str]: requested = manifest.get("requested_claims", []) if requested: @@ -1134,9 +1176,12 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: if manifest.get("ai_disposition_authority") is not False: raise ReviewBlocked("ai_disposition_authority must be false") _validate_manifest_flags(manifest) - _validate_telemetry_contract(manifest) _validate_claims(manifest) _validate_no_private_markers(manifest, "manifest") + if manifest.get("expected_review_outcome") == "BLOCKED": + reason = str(manifest.get("expected_block_reason") or "source metadata supports a boundary contract only") + raise ReviewBlocked(reason) + _validate_telemetry_contract(manifest) paths = _fixture_paths(manifest, repo_root) for label, path in paths.items(): if not path.is_file(): @@ -1163,18 +1208,24 @@ def _validate_telemetry_contract(manifest: dict[str, Any]) -> None: contract = manifest.get("telemetry_contract") if not isinstance(contract, dict): raise ReviewBlocked("telemetry_contract must be an object") - if contract.get("source") != "Windows Security EventChannel": - raise ReviewBlocked("telemetry_contract.source must be Windows Security EventChannel") + if not isinstance(contract.get("source"), str) or not contract.get("source"): + raise ReviewBlocked("telemetry_contract.source must be a non-empty fixture metadata source") event_ids = contract.get("event_ids") expected_event_ids = manifest.get("expected_event_ids") - if not isinstance(event_ids, list) or not event_ids: - raise ReviewBlocked("telemetry_contract.event_ids must be a non-empty list") + event_keys = contract.get("event_keys", []) + expected_event_keys = manifest.get("expected_event_keys", []) + if not isinstance(event_ids, list): + raise ReviewBlocked("telemetry_contract.event_ids must be a list") if sorted(event_ids) != sorted(expected_event_ids or []): raise ReviewBlocked("telemetry_contract.event_ids must match expected_event_ids") + if not isinstance(event_keys, list) or sorted(event_keys) != sorted(expected_event_keys or []): + raise ReviewBlocked("telemetry_contract.event_keys must match expected_event_keys") + if not event_ids and not event_keys: + raise ReviewBlocked("telemetry contract must declare event_ids or event_keys") rule_ids = contract.get("wazuh_rule_ids") expected_rule_ids = manifest.get("expected_rule_ids") - if not isinstance(rule_ids, list) or not rule_ids: - raise ReviewBlocked("telemetry_contract.wazuh_rule_ids must be a non-empty list") + if not isinstance(rule_ids, list): + raise ReviewBlocked("telemetry_contract.wazuh_rule_ids must be a list") if sorted(rule_ids) != sorted(expected_rule_ids or []): raise ReviewBlocked("telemetry_contract.wazuh_rule_ids must match expected_rule_ids") @@ -1183,7 +1234,22 @@ def _validate_claims(manifest: dict[str, Any]) -> None: requested_claims = manifest.get("requested_claims") if not isinstance(requested_claims, list): raise ReviewBlocked("requested_claims must be a list") - requested_text = "\n".join(str(item) for item in requested_claims) + expected_owners = { + "source_owner": "hawkinsoperations-detections", + "validation_owner": "hawkinsoperations-validation", + "platform_owner": "hawkinsoperations-platform", + "proof_owner": "hawkinsoperations-proof", + "product_owner": "hoxline", + } + for field, expected in expected_owners.items(): + if field in manifest and manifest.get(field) != expected: + raise ReviewBlocked(f"{field} must be the exact source-owned repository {expected}") + scan_manifest = { + key: value + for key, value in manifest.items() + if key != "blocked_claim_classes" and not (key == "expected_block_reason" and manifest.get("expected_review_outcome") == "BLOCKED") + } + requested_text = json.dumps(scan_manifest, sort_keys=True) for label, pattern in PROHIBITED_CLAIM_PATTERNS.items(): if pattern.search(requested_text): raise ReviewBlocked(f"requested claim is unsupported and blocked: {label}") @@ -1225,6 +1291,8 @@ def _validate_no_private_markers(value: Any, label: str, path: str = "") -> None for index, item in enumerate(value): _validate_no_private_markers(item, label, f"{path}[{index}]") elif isinstance(value, str): + if ABSOLUTE_LOCAL_PATH.search(value): + raise ReviewBlocked(f"{label} contains an absolute local path") for pattern in PRIVATE_VALUE_PATTERNS: if pattern.search(value): raise ReviewBlocked(f"{label} contains prohibited private/raw value marker") @@ -1245,7 +1313,7 @@ def _machine_state( "engine_version": ENGINE_VERSION, "run_id": run_id, "artifact_id": manifest.get("artifact_id", "UNKNOWN") if manifest else "UNKNOWN", - "manifest_path": str(manifest_path), + "manifest_path": _display_path(manifest_path), "stages": stages, "outputs": outputs, "final_status": final_status, @@ -1288,7 +1356,7 @@ def _pass_stages(outputs: dict[str, str]) -> list[dict[str, Any]]: summaries = { "artifact_intake": "manifest accepted and intake created", "evidence_graph": "evidence graph linked local fixture review nodes", - "telemetry_contract_check": "Windows Security EventChannel event and rule metadata checked", + "telemetry_contract_check": "declared fixture selector and source metadata checked", "controlled_validation": "positive and negative bundled fixtures validated", "synthetic_signal": "safe fixture signal simulated without endpoint mutation", "enrichment": "ATT&CK, event, source, and field mapping attached", @@ -1430,10 +1498,12 @@ def _verify_pass_outputs(run_dir: Path, state: dict[str, Any], errors: list[str] proofcard = _load_json(run_dir / "proofcard.json") claim_authority = _load_json(run_dir / "claim-authority.json") reviewer_pack = (run_dir / "reviewer-pack.md").read_text(encoding="utf-8") - if telemetry.get("required_source") != "Windows Security EventChannel": - errors.append("telemetry contract must use Windows Security EventChannel") + if telemetry.get("required_source") != manifest.get("telemetry_contract", {}).get("source"): + errors.append("telemetry contract source must match artifact manifest") if sorted(telemetry.get("event_ids", [])) != sorted(manifest.get("expected_event_ids", [])): errors.append("telemetry contract event IDs must match artifact manifest") + if sorted(telemetry.get("event_keys", [])) != sorted(manifest.get("expected_event_keys", [])): + errors.append("telemetry contract event keys must match artifact manifest") if sorted(telemetry.get("wazuh_rule_family", [])) != sorted(manifest.get("expected_rule_ids", [])): errors.append("telemetry contract Wazuh rule metadata must match artifact manifest") if validation.get("result") != "pass" or validation.get("endpoint_mutation") is not False: @@ -1482,6 +1552,10 @@ def _prepare_output_dir(output_dir: Path, force: bool) -> None: output_dir.mkdir(parents=True, exist_ok=False) +def _sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + def _write_file_map(output_dir: Path, file_map: dict[str, Any]) -> None: for name, value in file_map.items(): path = output_dir / name @@ -1497,12 +1571,22 @@ def _private_output_hits(run_dir: Path) -> list[str]: if not path.is_file() or path.suffix not in {".json", ".md"}: continue text = path.read_text(encoding="utf-8") + if ABSOLUTE_LOCAL_PATH.search(text): + hits.append(f"{path.name}:absolute-local-path") for pattern in PRIVATE_VALUE_PATTERNS: if pattern.search(text): hits.append(f"{path.name}:{pattern.pattern}") return hits +def _display_path(path: Path) -> str: + resolved = path.resolve() + repo_root = Path(__file__).resolve().parents[2] + if _is_relative_to(resolved, repo_root): + return resolved.relative_to(repo_root).as_posix() + return resolved.name + + def _resolve_path(path: Path, base: Path) -> Path: if path.is_absolute(): return path diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index 872c1e1..d2d7bee 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -7,6 +7,7 @@ import sys import tempfile import unittest +from unittest import mock from pathlib import Path try: @@ -14,7 +15,15 @@ except ImportError: # pragma: no cover - exercised only when optional test dep is absent jsonschema = None -from hoxline.case_growth.collector import BOUNDARY, ROW_FIELDS, build_case_growth_index +from hoxline.case_growth.collector import ( + BOUNDARY, + ROW_FIELDS, + _reproducibility_hash, + build_case_growth_index, + diff_case_growth_snapshot, + verify_case_growth_snapshot, +) +from hoxline.case_growth.discovery import REPO_NAMES from hoxline.case_growth.render import render_case_growth_markdown from hoxline.cli import main @@ -99,13 +108,14 @@ def derived_health(summary: dict[str, int]) -> dict[str, float]: class CaseGrowthIndexV0Tests(unittest.TestCase): - def setUp(self) -> None: - self.index = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") - self.rows = self.index["cases"] - assert isinstance(self.rows, list) + @classmethod + def setUpClass(cls) -> None: + cls.index = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") + cls.rows = cls.index["cases"] + assert isinstance(cls.rows, list) def test_fixture_repo_root_loads(self) -> None: - self.assertEqual(self.index["schema_version"], "case-growth-index-v0") + self.assertEqual(self.index["schema_version"], "case-growth-index-v1") self.assertGreaterEqual(self.index["summary"]["cases_total"], 1) def test_cli_prints_json(self) -> None: @@ -114,7 +124,7 @@ def test_cli_prints_json(self) -> None: status = main(["case-growth", "index", "--repo-root", str(FIXTURE_ROOT), "--format", "json"]) self.assertEqual(status, 0) payload = json.loads(stdout.getvalue()) - self.assertEqual(payload["schema_version"], "case-growth-index-v0") + self.assertEqual(payload["schema_version"], "case-growth-index-v1") def test_cli_prints_markdown(self) -> None: stdout = io.StringIO() @@ -251,6 +261,8 @@ def test_output_contains_at_least_one_case_row(self) -> None: def test_generated_markdown_includes_table_headers(self) -> None: markdown = render_case_growth_markdown(self.index) self.assertIn("| Metric | Count |", markdown) + self.assertIn("## Source Revisions", markdown) + self.assertIn("## Convergence Findings", markdown) self.assertIn("## Case Growth Health", markdown) self.assertIn("| Health metric | Value |", markdown) self.assertIn("| Top bottleneck |", markdown) @@ -264,6 +276,181 @@ def test_current_sample_json_has_summary_cases_boundary(self) -> None: self.assertIn("cases", sample) self.assertIn("boundary", sample) + def test_v1_source_revisions_are_exactly_seven_and_sanitized(self) -> None: + revisions = self.index["source_revisions"] + self.assertEqual(len(revisions), 7) + self.assertEqual({item["repository"] for item in revisions}, set(REPO_NAMES)) + serialized = json.dumps(self.index) + self.assertNotIn("C:\\\\Raylee\\\\", serialized) + self.assertNotIn("C:/Raylee/", serialized) + for item in revisions: + self.assertIn("authority_role", item) + self.assertIn("source_commit_sha", item) + self.assertIn("source_file_sha256", item) + self.assertIn("source_freshness_state", item) + self.assertIn("next_legal_action", item) + + def test_reproducibility_hash_ignores_only_generated_at(self) -> None: + first = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") + second = build_case_growth_index(FIXTURE_ROOT, generated_at="2030-01-01T00:00:00Z") + self.assertEqual(first["reproducibility_sha256"], second["reproducibility_sha256"]) + + def test_verify_rejects_absolute_path_duplicate_and_promotion(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["repo_root"] = r"C:\Raylee\Repo\HawkinsOperations" + hostile["cases"].append(json.loads(json.dumps(hostile["cases"][0]))) + hostile["cases"][0]["public_safe_status"] = "PUBLIC_SAFE" + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("absolute local path" in error for error in errors)) + self.assertTrue(any("duplicate case ID" in error for error in errors)) + self.assertTrue(any("unauthorized public-safe status" in error for error in errors)) + + def test_verify_rejects_forged_counts_and_historical_current_conflict(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["summary"]["proof_records_count"] += 99 + hostile["historical_snapshot"] = True + hostile["current_authority"] = True + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertIn("snapshot cannot be both historical_snapshot=true and current_authority=true", errors) + self.assertTrue(any("summary counts disagree" in error for error in errors)) + + def test_diff_classifies_explicit_historical_changes_as_context(self) -> None: + historical = json.loads(json.dumps(self.index)) + historical["historical_snapshot"] = True + historical["current_authority"] = False + historical["summary"]["proof_records_count"] = -1 + report = diff_case_growth_snapshot(FIXTURE_ROOT, historical) + change = next(item for item in report["changes"] if item["field"] == "summary.proof_records_count") + self.assertEqual(change["classification"], "EXPECTED_HISTORICAL_CONTEXT") + + def test_cli_verify_fails_closed_on_hostile_snapshot(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["repo_root"] = r"C:\Users\operator\snapshot.json" + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + with tempfile.TemporaryDirectory() as temp_dir: + snapshot = Path(temp_dir) / "hostile.json" + snapshot.write_text(json.dumps(hostile), encoding="utf-8") + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + status = main( + [ + "case-growth", + "verify", + "--repo-root", + str(FIXTURE_ROOT), + "--snapshot", + str(snapshot), + "--format", + "json", + ] + ) + self.assertEqual(status, 1) + payload = json.loads(stdout.getvalue()) + self.assertEqual(payload["status"], "FAIL") + self.assertTrue(any("absolute local path" in error for error in payload["errors"])) + + def test_cli_diff_emits_reviewer_readable_json(self) -> None: + historical = json.loads(json.dumps(self.index)) + historical["historical_snapshot"] = True + historical["current_authority"] = False + historical["summary"]["proof_records_count"] = -1 + with tempfile.TemporaryDirectory() as temp_dir: + snapshot = Path(temp_dir) / "historical.json" + snapshot.write_text(json.dumps(historical), encoding="utf-8") + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + status = main( + [ + "case-growth", + "diff", + "--repo-root", + str(FIXTURE_ROOT), + "--snapshot", + str(snapshot), + "--format", + "json", + ] + ) + self.assertEqual(status, 0) + payload = json.loads(stdout.getvalue()) + self.assertEqual(payload["schema_version"], "case-growth-diff-v1") + self.assertTrue(payload["changes"]) + + def test_verify_requires_exact_unique_seven_repository_set(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["source_revisions"][-1] = json.loads(json.dumps(hostile["source_revisions"][0])) + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("repository names must be unique" in error for error in errors)) + self.assertTrue(any("exact seven-repository set" in error for error in errors)) + + def test_verify_rejects_forged_runtime_and_claim_authority_fields(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["cases"][0]["runtime_candidate_status"] = "RUNTIME_ACTIVE" + hostile["cases"][0]["claim_authority_status"] = "ANALYST_APPROVED" + hostile["cases"][0]["next_gate"] = "final authorization and case closure" + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("unauthorized runtime_candidate_status" in error for error in errors)) + self.assertTrue(any("unauthorized claim_authority_status" in error for error in errors)) + self.assertTrue(any("final authorization wording" in error for error in errors)) + self.assertTrue(any("case closure wording" in error for error in errors)) + + def test_historical_snapshot_sha_must_resolve_in_stated_repository(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["historical_snapshot"] = True + hostile["current_authority"] = False + hostile["source_revisions"][0]["source_commit_sha"] = "f" * 40 + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("not a reachable commit" in error for error in errors)) + + def test_historical_snapshot_fingerprint_must_match_stated_commit_blob(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["historical_snapshot"] = True + hostile["current_authority"] = False + hostile["source_revisions"][0]["source_commit_sha"] = "a" * 40 + hostile["source_revisions"][0]["source_file_sha256"] = "0" * 64 + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + with ( + mock.patch("hoxline.case_growth.collector.git_commit_exists", return_value=True), + mock.patch("hoxline.case_growth.collector.git_blob_sha256", return_value="b" * 64), + ): + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("does not match the authoritative blob at the stated commit" in error for error in errors)) + + def test_current_snapshot_rejects_worktree_modified_freshness(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hostile["source_revisions"][0]["source_freshness_state"] = "WORKTREE_MODIFIED" + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("source_freshness_state must be one of" in error for error in errors)) + + def test_current_hoxline_snapshot_requires_self_reference_and_immediate_parent(self) -> None: + hostile = json.loads(json.dumps(self.index)) + hoxline_revision = next( + revision for revision in hostile["source_revisions"] if revision["repository"] == "hoxline" + ) + hoxline_revision["self_referential"] = False + hoxline_revision["revision_scope"] = "authoritative_source_at_commit" + hoxline_revision["source_commit_sha"] = "a" * 40 + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("self_referential must be true" in error for error in errors)) + self.assertTrue(any("revision_scope must be authoritative_sources_excluding_snapshot" in error for error in errors)) + self.assertTrue(any("must cite the immediate parent engine commit" in error for error in errors)) + + def test_verify_rejects_drive_unc_and_posix_absolute_paths(self) -> None: + for leaked_path in (r"D:\private\evidence.json", r"\\private-host\share\evidence.json", "/home/reviewer/evidence.json"): + with self.subTest(leaked_path=leaked_path): + hostile = json.loads(json.dumps(self.index)) + hostile["cases"][0]["source_evidence_refs"] = [leaked_path] + hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("absolute local path" in error for error in errors)) + def test_anti_vague_output_has_numeric_counts_and_evidence_refs(self) -> None: self.assertGreater(self.index["summary"]["cases_total"], 0) self.assertTrue(any(row["source_evidence_refs"] or row["validation_evidence_refs"] for row in self.rows)) diff --git a/tests/test_multi_artifact_review_manifest_expansion_v1.py b/tests/test_multi_artifact_review_manifest_expansion_v1.py index cd5a4e3..87aec85 100644 --- a/tests/test_multi_artifact_review_manifest_expansion_v1.py +++ b/tests/test_multi_artifact_review_manifest_expansion_v1.py @@ -17,7 +17,14 @@ "HO-DET-010": ROOT / "examples" / "review" / "ho-det-010-artifact-manifest-v1.json", "HO-DET-011": ROOT / "examples" / "review" / "ho-det-011-artifact-manifest-v1.json", "HO-DET-012": ROOT / "examples" / "review" / "ho-det-012-artifact-manifest-v1.json", + "HO-DET-013": ROOT / "examples" / "review" / "ho-det-013-artifact-manifest-v1.json", + "AWS-DET-001": ROOT / "examples" / "review" / "aws-det-001-artifact-manifest-v1.json", + "ID-DET-001": ROOT / "examples" / "review" / "id-det-001-artifact-manifest-v1.json", + "ID-DET-002": ROOT / "examples" / "review" / "id-det-002-artifact-manifest-v1.json", + "ID-DET-003": ROOT / "examples" / "review" / "id-det-003-artifact-manifest-v1.json", + "ID-DET-004": ROOT / "examples" / "review" / "id-det-004-artifact-manifest-v1.json", } +BLOCKED_MANIFEST = ROOT / "examples" / "review" / "ho-ndr-001-artifact-manifest-v1.json" def _json(path: Path) -> dict[str, object]: @@ -63,10 +70,10 @@ def test_batch_machine_state_contains_all_artifacts_and_boundaries(tmp_path) -> state = _json(output_dir / "batch-machine-state.json") artifacts = {item["artifact_id"]: item for item in state["artifacts"]} - assert set(artifacts) == {"HO-DET-009", "HO-DET-010", "HO-DET-011", "HO-DET-012"} - assert state["final_status"] == "PASS" - assert state["expected_pass_artifacts"] == ["HO-DET-009", "HO-DET-010", "HO-DET-011", "HO-DET-012"] - assert state["expected_blocked_artifacts"] == [] + assert set(artifacts) == {*MANIFESTS, "HO-NDR-001"} + assert state["final_status"] == "MIXED" + assert set(state["expected_pass_artifacts"]) == set(MANIFESTS) + assert state["expected_blocked_artifacts"] == ["HO-NDR-001"] assert state["public_safe_status"] == "NOT_PUBLIC_SAFE" assert state["human_review_required"] is True assert state["ai_disposition_authority"] is False @@ -76,9 +83,128 @@ def test_batch_machine_state_contains_all_artifacts_and_boundaries(tmp_path) -> assert state["public_proof_promoted"] is False assert state["lifetime_ledger_changed"] is False for artifact_id, artifact in artifacts.items(): - assert Path(artifact["machine_state"]).is_file(), artifact_id - assert Path(artifact["reviewer_pack"]).is_file(), artifact_id - assert artifact["final_status"] == "PASS" + assert (output_dir / artifact["machine_state"]).is_file(), artifact_id + assert len(artifact["machine_state_sha256"]) == 64 + if artifact_id == "HO-NDR-001": + assert (output_dir / artifact["blocked_review"]).is_file(), artifact_id + assert artifact["reviewer_pack"] is None + assert artifact["final_status"] == "BLOCKED" + else: + assert (output_dir / artifact["reviewer_pack"]).is_file(), artifact_id + assert artifact["final_status"] == "PASS" + + +def test_batch_replay_rejects_tampered_artifact_hash(tmp_path) -> None: + output_dir = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(INDEX), "--output", str(output_dir), "--force"]) == 0 + state_path = output_dir / "batch-machine-state.json" + state = _json(state_path) + state["artifacts"][0]["machine_state_sha256"] = "0" * 64 + state_path.write_text(json.dumps(state, indent=2), encoding="utf-8") + errors = verify_batch_run(state_path) + assert any("machine-state hash mismatch" in error for error in errors) + + +def test_batch_rejects_path_traversal_without_deleting_outside_file(tmp_path) -> None: + sentinel = tmp_path / "sentinel.txt" + sentinel.write_text("preserve", encoding="utf-8") + index = _json(INDEX) + index["artifacts"][0]["artifact_id"] = "../sentinel" + hostile = tmp_path / "traversal.json" + hostile.write_text(json.dumps(index), encoding="utf-8") + output_dir = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(hostile), "--output", str(output_dir), "--force"]) == 1 + assert sentinel.read_text(encoding="utf-8") == "preserve" + state = _json(output_dir / "batch-machine-state.json") + assert "invalid format" in state["block_reason"] + + +def test_batch_rejects_index_manifest_artifact_id_mismatch(tmp_path) -> None: + index = _json(INDEX) + index["artifacts"][0]["artifact_id"] = "HO-DET-008" + hostile = tmp_path / "mismatch.json" + hostile.write_text(json.dumps(index), encoding="utf-8") + output_dir = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(hostile), "--output", str(output_dir), "--force"]) == 1 + state = _json(output_dir / "batch-machine-state.json") + assert "does not match manifest artifact_id" in state["block_reason"] + + +def test_boundary_contract_artifact_is_honestly_blocked(tmp_path) -> None: + output_dir = tmp_path / "ho-ndr-001" + assert main(["review", "run", "--artifact", str(BLOCKED_MANIFEST), "--output", str(output_dir), "--force"]) == 1 + state = _json(output_dir / "machine-state.json") + assert state["final_status"] == "BLOCKED" + assert "boundary-contract scoped" in state["block_reason"] + assert state["public_safe_status"] == "NOT_PUBLIC_SAFE" + assert state["human_review_required"] is True + assert state["ai_disposition_authority"] is False + + +def test_named_cross_domain_fixture_selector_is_replayable(tmp_path) -> None: + output_dir = tmp_path / "aws" + assert main(["review", "run", "--artifact", str(MANIFESTS["AWS-DET-001"]), "--output", str(output_dir), "--force"]) == 0 + telemetry = _json(output_dir / "telemetry-contract-check.json") + assert telemetry["event_ids"] == [] + assert telemetry["event_keys"] == ["CreateUser"] + assert telemetry["fixture_event_keys"] == ["CreateUser"] + + +def test_absolute_path_and_cross_domain_live_claims_fail_closed(tmp_path) -> None: + base = _json(MANIFESTS["AWS-DET-001"]) + attacks = { + "absolute-path": r"C:\Raylee\private\fixture.json", + "drive-absolute-path": r"D:\private\evidence.json", + "unc-absolute-path": r"\\private-host\share\evidence.json", + "posix-absolute-path": "/home/reviewer/private/evidence.json", + "live-cloud": "live AWS proof", + "live-identity": "live identity runtime", + "live-security-onion": "live Security Onion proof", + "unsafe-next-gate": "analyst-approved disposition", + } + for name, claim in attacks.items(): + hostile = json.loads(json.dumps(base)) + if name == "unsafe-next-gate": + hostile["next_gate"] = claim + else: + hostile["requested_claims"] = [claim] + manifest = tmp_path / f"{name}.json" + manifest.write_text(json.dumps(hostile), encoding="utf-8") + output_dir = tmp_path / f"run-{name}" + assert main(["review", "run", "--artifact", str(manifest), "--output", str(output_dir), "--force"]) == 1 + assert _json(output_dir / "machine-state.json")["final_status"] == "BLOCKED" + + +def test_prohibited_claims_in_owner_and_nested_rendered_fields_fail_closed(tmp_path) -> None: + base = _json(MANIFESTS["AWS-DET-001"]) + hostile_values = { + "source-owner": ("source_owner", "ANALYST_APPROVED final authorization"), + "detection-family": ("detection_family", "case closure"), + "telemetry-source": ("telemetry_contract.source", "live AWS proof"), + "nested-field-mapping": ("field_mapping.actor", "AI-approved disposition"), + } + for name, (field_path, claim) in hostile_values.items(): + hostile = json.loads(json.dumps(base)) + target = hostile + parts = field_path.split(".") + for part in parts[:-1]: + target = target[part] + target[parts[-1]] = claim + manifest = tmp_path / f"hostile-{name}.json" + manifest.write_text(json.dumps(hostile), encoding="utf-8") + output_dir = tmp_path / f"run-hostile-{name}" + assert main(["review", "run", "--artifact", str(manifest), "--output", str(output_dir), "--force"]) == 1 + assert _json(output_dir / "machine-state.json")["final_status"] == "BLOCKED" + + +def test_generated_batch_artifacts_do_not_leak_absolute_workstation_paths(tmp_path) -> None: + output_dir = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(INDEX), "--output", str(output_dir), "--force"]) == 0 + for path in output_dir.rglob("*"): + if path.is_file() and path.suffix in {".json", ".md"}: + text = path.read_text(encoding="utf-8") + assert "C:\\Raylee\\" not in text + assert "C:\\Users\\" not in text def test_aggregate_reviewer_pack_explains_pass_and_block_sections(tmp_path) -> None: @@ -113,8 +239,8 @@ def test_hostile_batch_indexes_fail_closed(tmp_path) -> None: def test_expected_pass_artifact_blocking_causes_nonzero(tmp_path) -> None: index = _json(INDEX) - hostile_manifest = ROOT / "examples" / "review" / "hostile" / "missing-telemetry-contract.json" - index["artifacts"] = [{"artifact_id": "HO-DET-010", "manifest_path": str(hostile_manifest)}] + hostile_manifest = "examples/review/hostile/missing-telemetry-contract.json" + index["artifacts"] = [{"artifact_id": "HO-DET-010", "manifest_path": hostile_manifest}] index["expected_pass_artifacts"] = ["HO-DET-010"] index["expected_blocked_artifacts"] = [] temp_index = tmp_path / "expected-pass-blocks.json" From cff5924ce734ab4abd79307bd4d2a555c2bfc35d Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:46:24 -0500 Subject: [PATCH 02/73] fix: normalize volatile state and bounded metrics --- src/hoxline/case_growth/collector.py | 13 ++++++++++++- src/hoxline/case_growth/discovery.py | 14 ++++++++++++-- tests/test_case_growth_index_v0.py | 7 +++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index c273867..5e9fa44 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -395,7 +395,13 @@ def _source_convergence_findings( for item in revision_items: if isinstance(item, dict): name = item.get("repository") or item.get("repo") or item.get("name") - sha = item.get("source_commit_sha") or item.get("commit_sha") or item.get("source_revision") + sha = ( + item.get("repository_revision") + or item.get("source_repository_revision") + or item.get("source_commit_sha") + or item.get("commit_sha") + or item.get("source_revision") + ) if name and sha: refs[str(name).removeprefix("HawkinsOperations/")] = sha for owner, stated_sha in refs.items(): @@ -1130,6 +1136,11 @@ def _case_claim_violations(row: dict[str, Any]) -> list[str]: violations.append(f"unauthorized claim_authority_status {authority!r}") safe_row = {key: value for key, value in row.items() if key != "blocked_claims"} text = json.dumps(safe_row, sort_keys=True) + text = re.sub( + r"(?i)\b(?:missing|blocked|not)(?:_[a-z0-9]+)*_(?:final_authorization|case_closure|ai_approved_disposition|analyst_approved_disposition)\b", + "", + text, + ) for label, pattern in ( ("AI-approved disposition", r"(?i)AI[-_ ]approved disposition"), ("analyst-approved disposition", r"(?i)analyst[-_ ]approved disposition"), diff --git a/src/hoxline/case_growth/discovery.py b/src/hoxline/case_growth/discovery.py index 84547cc..489e963 100644 --- a/src/hoxline/case_growth/discovery.py +++ b/src/hoxline/case_growth/discovery.py @@ -62,12 +62,22 @@ def repo_branch(repo_path: Path) -> str: return lines[0] if lines else "UNKNOWN_WITH_REASON: no git branch available" +def _is_volatile_generated_path(path: str) -> bool: + normalized = path.replace("\\", "/").lstrip("./") + return ( + "/__pycache__/" in f"/{normalized}" + or normalized.endswith(".pyc") + or normalized.startswith(".hoxline/") + ) + + def repo_dirty(repo_path: Path) -> bool: - return bool(git_lines(repo_path, ["status", "--short"])) + return bool(repo_dirty_paths(repo_path)) def repo_dirty_paths(repo_path: Path) -> list[str]: - return [line[3:].strip().replace("\\", "/") for line in git_lines(repo_path, ["status", "--short"]) if len(line) > 3] + paths = [line[3:].strip().replace("\\", "/") for line in git_lines(repo_path, ["status", "--short"]) if len(line) > 3] + return [path for path in paths if not _is_volatile_generated_path(path)] def repo_head_sha(repo_path: Path) -> str: diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index d2d7bee..b6176c2 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -398,6 +398,13 @@ def test_verify_rejects_forged_runtime_and_claim_authority_fields(self) -> None: self.assertTrue(any("final authorization wording" in error for error in errors)) self.assertTrue(any("case closure wording" in error for error in errors)) + def test_missing_authorization_metric_token_is_bounded_context(self) -> None: + bounded = json.loads(json.dumps(self.index)) + bounded["cases"][0]["notes"] = ["missing_human_final_authorization"] + bounded["reproducibility_sha256"] = _reproducibility_hash(bounded) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, bounded) + self.assertFalse(any("final authorization wording" in error for error in errors)) + def test_historical_snapshot_sha_must_resolve_in_stated_repository(self) -> None: hostile = json.loads(json.dumps(self.index)) hostile["historical_snapshot"] = True From 46cd4380435fcd40722bab1d32adaca7ae64d1ec Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:47:35 -0500 Subject: [PATCH 03/73] fix: compare generated surface repository revisions --- src/hoxline/case_growth/collector.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 5e9fa44..6f22993 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -389,7 +389,14 @@ def _source_convergence_findings( if surface_repo is None or not (surface_repo / relative_path).is_file(): continue surface = load_structured(surface_repo / relative_path) or {} - refs = surface.get("source_commit_refs") if isinstance(surface.get("source_commit_refs"), dict) else {} + path_refs = surface.get("source_commit_refs") if isinstance(surface.get("source_commit_refs"), dict) else {} + repository_refs = ( + surface.get("source_repository_commit_refs") + if isinstance(surface.get("source_repository_commit_refs"), dict) + else {} + ) + refs = dict(path_refs) + refs.update(repository_refs) revision_items = surface.get("source_revisions") if isinstance(revision_items, list): for item in revision_items: @@ -407,7 +414,13 @@ def _source_convergence_findings( for owner, stated_sha in refs.items(): normalized_owner = str(owner).removeprefix("HawkinsOperations/") current_sha = current_shas.get(normalized_owner) - if current_sha and stated_sha != current_sha: + direct_parent_cycle = ( + surface_owner == "hawkinsoperations-website" + and normalized_owner in {"hawkinsoperations-website", "hoxline"} + and repo_paths.get(normalized_owner) is not None + and stated_sha == repo_parent_sha(repo_paths[normalized_owner]) + ) + if current_sha and stated_sha != current_sha and not direct_parent_cycle: drift.append( _finding( "SOURCE_REVISION_DRIFT", From 680d7130c058f7a96856a247504ea6712e1b5043 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:50:00 -0500 Subject: [PATCH 04/73] chore: record converged seven-repository snapshot --- .../current-case-growth-index.json | 484 +++++++++++++----- .../case-growth/current-case-growth-index.md | 69 ++- 2 files changed, 387 insertions(+), 166 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index a21c93e..8d28a33 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,71 +1,224 @@ { - "schema_version": "case-growth-index-v0", - "generated_at": "2026-06-27T10:05:45Z", - "repo_root": "C:\\Raylee\\Repo\\HawkinsOperations", + "schema_version": "case-growth-index-v1", + "generated_at": "2026-07-22T23:48:12Z", + "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", + "historical_snapshot": false, + "current_authority": true, + "snapshot_state": { + "freshness": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "self_source_revision_semantics": "worktree-head-or-snapshot-commit-parent" + }, + "source_revisions": [ + { + "repository": ".github", + "authority_role": "org command-center routing", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "b051aa3981e52a56b45a3078fcbf32ac34a99f92", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "scripts/verify-command-center-invariants.py", + "source_file_sha256": "e1ba441b72cfae9ca16f3b22f02003e77d3718c349acde736f7e8e17a7012bbc", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-detections", + "authority_role": "detection source truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "detections/DETECTION_PROMOTION_MATRIX.yml", + "source_file_sha256": "b73825c8655d7df62cac7737b08b3c95ec72b12c42bb6e3d265f7e74d0068e67", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-validation", + "authority_role": "controlled validation truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "a3a2f6938412f3012bea1bc447bb084d82ed1936", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "validation/VALIDATION_REGISTRY.yml", + "source_file_sha256": "f907b8c46da583a851ece20b0cb6f10bdd8077d59e98491b80975cbbd4ede4aa", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-platform", + "authority_role": "platform contract truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "7ed8f08c9c3e4bc07797961ba84292ec96978da6", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "contracts/public-status-source-contract-v1.json", + "source_file_sha256": "7f25adcdef7f89f86bb27377de4ea86b57d7ad7f7653e56a0573e76e6cf6cd23", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-proof", + "authority_role": "proof and claim-boundary truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "042a918ad4a8473cd5abcfd575072fc094639682", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "source_file_sha256": "a8c24bd78bd20f318d4dfb5a930ea44bc108305bcc18b23db6ff2fc11e81a52d", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hawkinsoperations-website", + "authority_role": "rendering-only public status", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "950691ff0f3ad224964e713b6ec6beec7f6528b4", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "authoritative_source_at_commit", + "source_path": "public/data/public-status.json", + "source_file_sha256": "90c95bad6cf05382b714aa2426c1d808e722d986dfc550e7a034fbcea53b05a3", + "source_freshness_state": "CURRENT", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "none; preserve source ownership" + }, + { + "repository": "hoxline", + "authority_role": "case-growth and fixture-review product truth", + "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "source_commit_sha": "46cd4380435fcd40722bab1d32adaca7ae64d1ec", + "source_parent_sha": "cff5924ce734ab4abd79307bd4d2a555c2bfc35d", + "self_referential": true, + "revision_scope": "authoritative_sources_excluding_snapshot", + "source_path": "src/hoxline/case_growth/collector.py", + "source_file_sha256": "06e72b91bdd256277a13e83391e97c6577e68bcc9498bbda5e9d84c061025b3a", + "source_freshness_state": "CURRENT_SELF_REFERENTIAL", + "snapshot_freshness_state": "CURRENT", + "historical_snapshot": false, + "current_authority": true, + "missing_source_state": false, + "dangling_reference_state": false, + "contradictions": [], + "drift": [], + "next_legal_action": "restore hoxline/src/hoxline/case_growth/collector.py from its owning repository" + } + ], + "contradictions": [], + "drift": [], + "next_legal_action": "none; current source-controlled inputs converge", "repos_scanned": [ { "repo": ".github", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\.github", + "path": ".github", "exists": true, - "branch": "main", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "org metadata and reviewer routing only; not proof authority", "files_scanned": 34 }, { "repo": "hawkinsoperations-detections", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hawkinsoperations-detections", + "path": "hawkinsoperations-detections", "exists": true, - "branch": "main", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "source package and source status authority only", "files_scanned": 92 }, { "repo": "hawkinsoperations-validation", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hawkinsoperations-validation", + "path": "hawkinsoperations-validation", "exists": true, - "branch": "main", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "controlled validation authority only", "files_scanned": 198 }, { "repo": "hawkinsoperations-platform", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hawkinsoperations-platform", + "path": "hawkinsoperations-platform", "exists": true, - "branch": "main", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "platform runtime-candidate, collector, receipt, and ledger contract authority only", - "files_scanned": 118 + "files_scanned": 119 }, { "repo": "hawkinsoperations-proof", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hawkinsoperations-proof", + "path": "hawkinsoperations-proof", "exists": true, - "branch": "main", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "proof ceiling, proof record, ProofCard, public-safe, blocked-claim, and next-gate authority", - "files_scanned": 103 + "files_scanned": 126 }, { "repo": "hawkinsoperations-website", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hawkinsoperations-website", + "path": "hawkinsoperations-website", "exists": true, - "branch": "feature/homepage-hoxline-interaction-v2", - "dirty": true, + "branch": "feature/hoxline-case-growth-convergence-v1", + "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", - "files_scanned": 261 + "files_scanned": 270 }, { "repo": "hoxline", - "path": "C:\\Raylee\\Repo\\HawkinsOperations\\hoxline", + "path": "hoxline", "exists": true, - "branch": "feature/hoxline-case-growth-index-v0", + "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": true, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", - "files_scanned": 162 + "files_scanned": 181 } ], "repo_slot_accuracy": { @@ -76,7 +229,7 @@ "hawkinsoperations_github_sibling_exists": false, "wording": "seven expected repo slots evaluated; seven present local repos scanned" }, - "source_files_scanned_count": 968, + "source_files_scanned_count": 1020, "case_ids_discovered_count": 27, "summary": { "cases_total": 27, @@ -85,32 +238,32 @@ "runtime_candidate_lanes_count": 5, "private_runtime_evidence_captured_count": 1, "scheduled_collector_lanes_count": 4, - "proof_records_count": 4, - "proofcards_count": 4, + "proof_records_count": 11, + "proofcards_count": 12, "claim_authority_cases_count": 26, "metrics_available_count": 1, "public_safe_cases_count": 0, "closed_cases_count": 0, - "blocked_claims_count": 243, + "blocked_claims_count": 295, "cases_with_next_gate_count": 26, - "cases_missing_proof_record_count": 23, - "cases_missing_proofcard_count": 23, + "cases_missing_proof_record_count": 16, + "cases_missing_proofcard_count": 15, "cases_not_public_safe_count": 27, "unknown_state_count": 1 }, "case_growth_health": { "validation_coverage_percent": 85.71, - "proof_record_coverage_percent": 14.81, - "proofcard_coverage_percent": 14.81, + "proof_record_coverage_percent": 40.74, + "proofcard_coverage_percent": 44.44, "scheduled_collector_coverage_percent": 14.81, "runtime_candidate_coverage_percent": 18.52, "metrics_coverage_percent": 3.7, "public_safe_percent": 0.0, "closed_case_percent": 0.0, - "blocked_claim_density": 9.0, + "blocked_claim_density": 10.93, "next_gate_coverage_percent": 96.3, - "missing_proof_record_percent": 85.19, - "missing_proofcard_percent": 85.19, + "missing_proof_record_percent": 59.26, + "missing_proofcard_percent": 55.56, "not_public_safe_percent": 100.0, "overall_health_status": "PUBLIC_SAFE_BLOCKED", "strongest_lane": "controlled_validation", @@ -179,7 +332,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "separate proof scope before any live-cloud or public route approval", "evidence_confidence": "HIGH", "notes": [ @@ -260,7 +413,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -290,7 +443,9 @@ "website route/rendering mention observed at hawkinsoperations-website/components/ReviewerRunPath.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/components/TruthSurfaceSeparation.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/components/claim-firewall/ClaimFirewallSimulator.tsx; not treated as proof", + "website route/rendering mention observed at hawkinsoperations-website/components/command-center/HomeAutomationCockpitV2.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/components/command-center/VerifyTerminalDrawer.tsx; not treated as proof", + "website route/rendering mention observed at hawkinsoperations-website/components/visual-intelligence/OrbitInteractionWorkbench.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/components/visual-intelligence/index.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/design/HOXLINE_WEBSITE_VISUAL_SYSTEM_V0.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/design/WEBSITE_PR68_ORG_SYSTEM_REVIEW.md; not treated as proof", @@ -358,7 +513,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -401,7 +556,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -444,7 +599,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -487,7 +642,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -530,7 +685,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -573,7 +728,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -616,7 +771,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -650,6 +805,7 @@ ], "runtime_candidate_status": "PRIVATE_RUNTIME_CANDIDATE", "runtime_evidence_refs": [ + "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "hawkinsoperations-platform/.github/workflows/hoxline-schedule-gated-collection.yml" ], "scheduled_collector_status": "SCHEDULED_COLLECTOR_LANE_PRESENT_GATED", @@ -657,13 +813,15 @@ "hawkinsoperations-platform/.github/workflows/hoxline-schedule-gated-collection.yml" ], "signal_status": "NOT_PROVEN", - "signal_evidence_refs": [], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "signal_evidence_refs": [ + "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" + ], + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/HO-DET-009.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/HO-DET-009.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 10, + "blocked_claim_count": 19, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -674,18 +832,28 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "live SIEM proof", + "live Cribl proof", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T16:42:32-05:00", - "next_gate": "platform fixture support and separately approved runtime receipt only after cleanup gates pass", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording", + "evidence_confidence": "HIGH", "notes": [ "detections: Matrix records source package enforcement only; controlled-test validation belongs to validation and no runtime, signal, or public-safe claim is made here.", "validation: Controlled Windows local user creation fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or account lifecycle completeness.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform scheduled-collector context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof" ] }, @@ -716,6 +884,7 @@ ], "runtime_candidate_status": "PRIVATE_RUNTIME_CANDIDATE", "runtime_evidence_refs": [ + "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "hawkinsoperations-platform/.github/workflows/hoxline-schedule-gated-collection.yml" ], "scheduled_collector_status": "SCHEDULED_COLLECTOR_LANE_PRESENT_GATED", @@ -723,13 +892,15 @@ "hawkinsoperations-platform/.github/workflows/hoxline-schedule-gated-collection.yml" ], "signal_status": "NOT_PROVEN", - "signal_evidence_refs": [], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "signal_evidence_refs": [ + "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" + ], + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/HO-DET-010.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/HO-DET-010.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 12, + "blocked_claim_count": 20, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -742,18 +913,27 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "live SIEM proof", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T16:42:32-05:00", - "next_gate": "reviewer validates source and controlled-test validation before any private runtime gate", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", + "evidence_confidence": "HIGH", "notes": [ "detections: Source package exists for Windows local Administrators group membership changes. Runtime, signal, public-safe, production, and disposition claims remain blocked.", "validation: Controlled Windows local Administrators group membership fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform private-runtime candidate context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof" ] }, @@ -800,8 +980,8 @@ ], "proof_record_status": "PROOF_RECORD_EXISTS", "proof_record_path": "proof/records/HO-DET-011.md", - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/HO-DET-011.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", "blocked_claim_count": 13, "blocked_claims": [ @@ -823,13 +1003,13 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "proof-card or public reviewer route only after separate human-approved proof scope", "evidence_confidence": "HIGH", "notes": [ "detections: Source package remains NOT_PUBLIC_SAFE; private runtime material is not promoted by this source matrix.", "validation: Controlled Windows service-creation fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", - "proof: Existing proof record supports private runtime evidence capture only. It does not promote public runtime, signal, or public-safe status.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Private runtime evidence capture remains non-public and this card does not create public runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/detections/page.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/app/proof/page.tsx; not treated as proof", @@ -913,7 +1093,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion", "evidence_confidence": "HIGH", "notes": [ @@ -974,12 +1154,12 @@ "signal_evidence_refs": [ "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" ], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/HO-DET-013.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/HO-DET-013.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 12, + "blocked_claim_count": 19, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -992,19 +1172,26 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", - "next_gate": "separate proof record or runtime/signal review before any public proof promotion", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", + "evidence_confidence": "HIGH", "notes": [ - "detections: HO-DET-013 landed as source truth only; this matrix does not expand it.", + "detections: Controlled-test validation is external validation truth; this source matrix does not promote runtime, signal, proof, or public-safe claims.", "validation: Controlled telemetry/security-control tamper fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or control coverage completeness.", - "proof: Source and controlled validation exist in their owning repos. This index does not create a proof record, runtime proof, signal proof, public-safe status, or promotion authority.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform private-runtime candidate context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/docs/design/WEBSITE_PR68_ORG_SYSTEM_REVIEW.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_CLAUDE_DESIGN_BRIEF.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_MISSING_WORK_MAP.md; not treated as proof", @@ -1048,7 +1235,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1091,7 +1278,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1134,7 +1321,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1221,7 +1408,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "captured cross-source corroboration evidence under separate proof scope", "evidence_confidence": "MEDIUM", "notes": [ @@ -1280,7 +1467,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T10:58:36-05:00", + "last_updated": "2026-07-22T18:33:32-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1343,7 +1530,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T11:11:58-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval", "evidence_confidence": "MEDIUM", "notes": [ @@ -1397,7 +1584,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T11:11:58-05:00", + "last_updated": "2026-07-22T18:33:34-05:00", "next_gate": "preserve hero baseline while successor HO-DET-001 remains the current reviewed source package", "evidence_confidence": "MEDIUM", "notes": [ @@ -1478,12 +1665,12 @@ "signal_evidence_refs": [ "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" ], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/ID-DET-001.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/ID-DET-001.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 11, + "blocked_claim_count": 18, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -1495,19 +1682,26 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", - "next_gate": "proof record creation under separate proof scope", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", + "evidence_confidence": "HIGH", "notes": [ "detections: Source package records source truth; validation and identity-provider evidence remain external gates.", "validation: Controlled identity-event fixture validation only. This does not prove live IdP, live SIEM, complete identity coverage, signal observation, or public-safe status.", - "proof: Validation status is external validation truth. Proof repo has no ID-DET-001 proof record in this phase.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform identity-lane context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/components/FeaturedWork.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/design/WEBSITE_PR68_ORG_SYSTEM_REVIEW.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_MISSING_WORK_MAP.md; not treated as proof", @@ -1555,12 +1749,12 @@ "signal_evidence_refs": [ "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" ], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/ID-DET-002.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/ID-DET-002.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 11, + "blocked_claim_count": 18, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -1572,19 +1766,26 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", - "next_gate": "proof record creation under separate proof scope", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", + "evidence_confidence": "HIGH", "notes": [ - "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. Proof repo has no ID-DET-002 proof record in this phase.", + "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. The proof-owned current index links an ID-DET-002 record; that linkage does not raise detection authority.", "validation: Controlled identity-event fixture validation only. This does not prove live IdP, live SIEM, complete identity coverage, signal observation, or public-safe status.", - "proof: Validation status is external validation truth. Proof repo has no ID-DET-002 proof record in this phase.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform identity-lane context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_MISSING_WORK_MAP.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofPackManifest.ts; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofRecords.ts; not treated as proof", @@ -1627,12 +1828,12 @@ "signal_evidence_refs": [ "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" ], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/ID-DET-003.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/ID-DET-003.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 11, + "blocked_claim_count": 18, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -1644,19 +1845,26 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", - "next_gate": "proof record creation under separate proof scope", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", + "evidence_confidence": "HIGH", "notes": [ - "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. Proof repo has no ID-DET-003 proof record in this phase.", + "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. The proof-owned current index links an ID-DET-003 record; that linkage does not raise detection authority.", "validation: Controlled identity-administration fixture validation only. This does not prove live IdP, live SIEM, complete identity coverage, signal observation, or public-safe status.", - "proof: Validation status is external validation truth. Proof repo has no ID-DET-003 proof record in this phase.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform identity-lane context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/src/data/governanceSaves.ts; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofPackManifest.ts; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofRecords.ts; not treated as proof", @@ -1699,12 +1907,12 @@ "signal_evidence_refs": [ "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml" ], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, + "proof_record_status": "PROOF_RECORD_EXISTS", + "proof_record_path": "proof/records/ID-DET-004.md", + "proofcard_status": "PROOFCARD_EXISTS", + "proofcard_path": "proof/cards/ID-DET-004.md", "claim_authority_status": "BLOCKED_CLAIMS_INDEXED", - "blocked_claim_count": 11, + "blocked_claim_count": 18, "blocked_claims": [ "runtime-active public proof", "signal-observed public proof", @@ -1716,19 +1924,26 @@ "fleet-wide", "autonomous SOC", "AI-approved disposition", - "analyst-approved disposition" + "analyst-approved disposition", + "customer deployment", + "SOCaaS deployment", + "final authorization", + "case closure", + "website rendering as proof", + "GitHub rendering as proof", + "green CI as approval" ], "public_safe_status": "NOT_PUBLIC_SAFE", "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-06-24T18:36:09-05:00", - "next_gate": "proof record creation under separate proof scope", - "evidence_confidence": "MEDIUM", + "last_updated": "2026-07-22T18:33:34-05:00", + "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", + "evidence_confidence": "HIGH", "notes": [ - "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. Proof repo has no ID-DET-004 proof record in this phase.", + "detections: Source artifacts exist and validation truth is CONTROLLED_TEST_VALIDATED in hawkinsoperations-validation. The proof-owned current index links an ID-DET-004 record; that linkage does not raise detection authority.", "validation: Controlled identity-event fixture validation only. This does not prove live IdP, live SIEM, complete identity coverage, signal observation, or public-safe status.", - "proof: Validation status is external validation truth. Proof repo has no ID-DET-004 proof record in this phase.", + "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform identity-lane context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_MISSING_WORK_MAP.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofPackManifest.ts; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/proofRecords.ts; not treated as proof", @@ -1739,15 +1954,7 @@ ], "data_quality_notes": [ "platform lifetime ledger manifest reports closed_case_count=0", - "HO-DET-009 has controlled validation but no proof record", - "HO-DET-010 has controlled validation but no proof record", - "HO-DET-011 has proof record but no ProofCard", - "HO-DET-013 has controlled validation but no proof record", - "HOD-001 has controlled validation but no proof record", - "ID-DET-001 has controlled validation but no proof record", - "ID-DET-002 has controlled validation but no proof record", - "ID-DET-003 has controlled validation but no proof record", - "ID-DET-004 has controlled validation but no proof record" + "HOD-001 has controlled validation but no proof record" ], "boundary": { "runtime_public_proof_claimed": false, @@ -1760,5 +1967,6 @@ "final_authorization_claimed": false, "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false - } + }, + "reproducibility_sha256": "1fb0f02d22e3b2f6c99f437338953a44ddda212d8e1376598a7a52b726a6f371" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index c264b5b..28640c0 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,8 +1,11 @@ -# Hoxline Case Growth Index v0 +# Hoxline Case Growth Index v1 -Generated: `2026-06-27T10:05:59Z` +Generated: `2026-07-22T23:48:42Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` +Historical snapshot: `false` +Current authority: `true` +Reproducibility SHA-256: `1fb0f02d22e3b2f6c99f437338953a44ddda212d8e1376598a7a52b726a6f371` ## Summary @@ -14,35 +17,53 @@ Repo-slot accuracy: `seven expected repo slots evaluated; seven present local re | `runtime_candidate_lanes_count` | 5 | | `private_runtime_evidence_captured_count` | 1 | | `scheduled_collector_lanes_count` | 4 | -| `proof_records_count` | 4 | -| `proofcards_count` | 4 | +| `proof_records_count` | 11 | +| `proofcards_count` | 12 | | `claim_authority_cases_count` | 26 | | `metrics_available_count` | 1 | | `public_safe_cases_count` | 0 | | `closed_cases_count` | 0 | -| `blocked_claims_count` | 243 | +| `blocked_claims_count` | 295 | | `cases_with_next_gate_count` | 26 | -| `cases_missing_proof_record_count` | 23 | -| `cases_missing_proofcard_count` | 23 | +| `cases_missing_proof_record_count` | 16 | +| `cases_missing_proofcard_count` | 15 | | `cases_not_public_safe_count` | 27 | | `unknown_state_count` | 1 | +## Source Revisions + +| Repository | Authority role | Source path | Source revision | Source freshness | Snapshot freshness | +| --- | --- | --- | --- | --- | --- | +| `.github` | `org command-center routing` | `scripts/verify-command-center-invariants.py` | `b051aa3981e52a56b45a3078fcbf32ac34a99f92` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `a3a2f6938412f3012bea1bc447bb084d82ed1936` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `7ed8f08c9c3e4bc07797961ba84292ec96978da6` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `950691ff0f3ad224964e713b6ec6beec7f6528b4` | `CURRENT` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `46cd4380435fcd40722bab1d32adaca7ae64d1ec` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | + +## Convergence Findings + +- No missing, dangling, contradictory, or stale source-owned state detected. + +Next legal action: none; current source-controlled inputs converge + ## Case Growth Health | Health metric | Value | | --- | ---: | | `validation_coverage_percent` | 85.71 | -| `proof_record_coverage_percent` | 14.81 | -| `proofcard_coverage_percent` | 14.81 | +| `proof_record_coverage_percent` | 40.74 | +| `proofcard_coverage_percent` | 44.44 | | `scheduled_collector_coverage_percent` | 14.81 | | `runtime_candidate_coverage_percent` | 18.52 | | `metrics_coverage_percent` | 3.7 | | `public_safe_percent` | 0.0 | | `closed_case_percent` | 0.0 | -| `blocked_claim_density` | 9.0 | +| `blocked_claim_density` | 10.93 | | `next_gate_coverage_percent` | 96.3 | -| `missing_proof_record_percent` | 85.19 | -| `missing_proofcard_percent` | 85.19 | +| `missing_proof_record_percent` | 59.26 | +| `missing_proofcard_percent` | 55.56 | | `not_public_safe_percent` | 100.0 | | Assessment | Value | @@ -74,11 +95,11 @@ The health section is derived from numeric index counts only. It does not promot | `HO-DET-006` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-007` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-008` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | -| `HO-DET-009` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_CANDIDATE` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `platform fixture support and separately approved runtime receipt only after cleanup gates pass` | -| `HO-DET-010` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_CANDIDATE` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any private runtime gate` | -| `HO-DET-011` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_EVIDENCE_CAPTURED` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `PROOF_RECORD_EXISTS` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof-card or public reviewer route only after separate human-approved proof scope` | +| `HO-DET-009` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_CANDIDATE` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording` | +| `HO-DET-010` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_CANDIDATE` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved private runtime gate` | +| `HO-DET-011` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_EVIDENCE_CAPTURED` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof-card or public reviewer route only after separate human-approved proof scope` | | `HO-DET-012` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `PRIVATE_RUNTIME_CANDIDATE` | `SCHEDULED_COLLECTOR_LANE_PRESENT_GATED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion` | -| `HO-DET-013` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `separate proof record or runtime/signal review before any public proof promotion` | +| `HO-DET-013` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved private runtime gate` | | `HO-DET-014` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-015` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-016` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | @@ -88,10 +109,10 @@ The health section is derived from numeric index counts only. It does not promot | `HO-PIPE-001` | `SOURCE_EXISTS` | `VALIDATION_CONTRACT_ENFORCED` | `TELEMETRY_CONTRACT_ONLY` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval` | | `HOD-001` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `preserve hero baseline while successor HO-DET-001 remains the current reviewed source package` | | `HOX-GAUNTLET-001` | `SOURCE_EXISTS` | `CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | true | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `human review before runtime, signal, customer, production, public wording, or final human gate promotion` | -| `ID-DET-001` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof record creation under separate proof scope` | -| `ID-DET-002` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof record creation under separate proof scope` | -| `ID-DET-003` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof record creation under separate proof scope` | -| `ID-DET-004` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof record creation under separate proof scope` | +| `ID-DET-001` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved identity runtime gate` | +| `ID-DET-002` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved identity runtime gate` | +| `ID-DET-003` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved identity runtime gate` | +| `ID-DET-004` | `SOURCE_EXISTS` | `CONTROLLED_TEST_VALIDATED` | `NOT_PROVEN` | `NOT_INDEXED` | `PROOF_RECORD_EXISTS` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `reviewer validates source and controlled-test validation before any separately approved identity runtime gate` | ## Boundary @@ -111,12 +132,4 @@ The health section is derived from numeric index counts only. It does not promot ## Data Quality Notes - platform lifetime ledger manifest reports closed_case_count=0 -- HO-DET-009 has controlled validation but no proof record -- HO-DET-010 has controlled validation but no proof record -- HO-DET-011 has proof record but no ProofCard -- HO-DET-013 has controlled validation but no proof record - HOD-001 has controlled validation but no proof record -- ID-DET-001 has controlled validation but no proof record -- ID-DET-002 has controlled validation but no proof record -- ID-DET-003 has controlled validation but no proof record -- ID-DET-004 has controlled validation but no proof record From 769622bee18921f7e75d0c0db2532db0c3054579 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:53:54 -0500 Subject: [PATCH 05/73] fix: classify generated-surface revision cycles --- src/hoxline/case_growth/collector.py | 40 +++++++++++++++++++++++++--- tests/test_case_growth_index_v0.py | 13 +++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 6f22993..91ba83c 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -1058,6 +1058,14 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu current_revision = current_by_repo[repository] stated_sha = stated.get("source_commit_sha") accepted_shas = {current_revision["source_commit_sha"]} + if repository == "hawkinsoperations-website": + website_repo = repo_paths.get(repository) + website_parent = repo_parent_sha(website_repo) if website_repo is not None else None + if ( + stated_sha == website_parent + and stated.get("source_file_sha256") == current_revision.get("source_file_sha256") + ): + accepted_shas.add(website_parent) if repository == "hoxline": accepted_shas.add(current_revision.get("source_parent_sha")) if stated.get("self_referential") is not True: @@ -1179,6 +1187,13 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict before = before_revisions.get(repository, {}) for field in ("source_commit_sha", "source_file_sha256", "source_path"): if before.get(field) != current_revision[field]: + expected_self_reference = ( + repository == "hoxline" + and field == "source_commit_sha" + and before.get("self_referential") is True + and before.get("revision_scope") == "authoritative_sources_excluding_snapshot" + and before.get(field) == current_revision.get("source_parent_sha") + ) changes.append( { "field": field, @@ -1188,8 +1203,20 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict "after": current_revision[field], "old_source_revision": before.get("source_commit_sha"), "current_source_revision": current_revision["source_commit_sha"], - "classification": "EXPECTED_HISTORICAL_CONTEXT" if historical else "ACTIONABLE_DRIFT", - "next_remediation": "retain as historical context" if historical else "regenerate the current snapshot from the owning source", + "classification": ( + "EXPECTED_HISTORICAL_CONTEXT" + if historical + else "EXPECTED_SELF_REFERENTIAL_CONTEXT" + if expected_self_reference + else "ACTIONABLE_DRIFT" + ), + "next_remediation": ( + "retain as historical context" + if historical + else "none; the snapshot commit intentionally follows its cited engine commit" + if expected_self_reference + else "regenerate the current snapshot from the owning source" + ), } ) before_summary = snapshot.get("summary") if isinstance(snapshot.get("summary"), dict) else {} @@ -1209,6 +1236,7 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict "next_remediation": "retain as historical context" if historical else "regenerate the current snapshot", } ) + actionable_changes = [item for item in changes if item["classification"] == "ACTIONABLE_DRIFT"] return { "schema_version": "case-growth-diff-v1", "historical_snapshot": historical, @@ -1216,7 +1244,13 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict "changes": changes, "contradictions": current["contradictions"], "drift": current["drift"], - "next_legal_action": current["next_legal_action"] if changes or current["drift"] else "none; snapshot converges", + "next_legal_action": ( + current["next_legal_action"] + if actionable_changes or current["drift"] + else "none; snapshot converges with expected self-reference" + if changes + else "none; snapshot converges" + ), } diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index b6176c2..2f65dc5 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -325,6 +325,19 @@ def test_diff_classifies_explicit_historical_changes_as_context(self) -> None: change = next(item for item in report["changes"] if item["field"] == "summary.proof_records_count") self.assertEqual(change["classification"], "EXPECTED_HISTORICAL_CONTEXT") + def test_diff_classifies_snapshot_commit_self_reference_as_expected(self) -> None: + snapshot = json.loads(json.dumps(self.index)) + hoxline_revision = next(item for item in snapshot["source_revisions"] if item["repository"] == "hoxline") + hoxline_revision["source_commit_sha"] = hoxline_revision["source_parent_sha"] + report = diff_case_growth_snapshot(FIXTURE_ROOT, snapshot) + change = next( + item + for item in report["changes"] + if item["source_owner"] == "hoxline" and item["field"] == "source_commit_sha" + ) + self.assertEqual(change["classification"], "EXPECTED_SELF_REFERENTIAL_CONTEXT") + self.assertTrue(report["next_legal_action"].startswith("none;")) + def test_cli_verify_fails_closed_on_hostile_snapshot(self) -> None: hostile = json.loads(json.dumps(self.index)) hostile["repo_root"] = r"C:\Users\operator\snapshot.json" From 6721b497fbd5821b8b4bcac1381a0250dc358f44 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 18:56:29 -0500 Subject: [PATCH 06/73] chore: refresh converged snapshot after cycle hardening --- .../current-case-growth-index.json | 20 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 8d28a33..284c313 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-22T23:48:12Z", + "generated_at": "2026-07-22T23:55:08Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -116,12 +116,12 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "950691ff0f3ad224964e713b6ec6beec7f6528b4", + "source_commit_sha": "05da628443f2be88b9ce9fe5fc2dc9caccab09a7", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", "source_path": "public/data/public-status.json", - "source_file_sha256": "90c95bad6cf05382b714aa2426c1d808e722d986dfc550e7a034fbcea53b05a3", + "source_file_sha256": "10598dfa4c6a4943692895463f2da313fad2076200ff764947e679d658bbccdd", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -136,13 +136,13 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "46cd4380435fcd40722bab1d32adaca7ae64d1ec", - "source_parent_sha": "cff5924ce734ab4abd79307bd4d2a555c2bfc35d", + "source_commit_sha": "769622bee18921f7e75d0c0db2532db0c3054579", + "source_parent_sha": "680d7130c058f7a96856a247504ea6712e1b5043", "self_referential": true, "revision_scope": "authoritative_sources_excluding_snapshot", "source_path": "src/hoxline/case_growth/collector.py", - "source_file_sha256": "06e72b91bdd256277a13e83391e97c6577e68bcc9498bbda5e9d84c061025b3a", - "source_freshness_state": "CURRENT_SELF_REFERENTIAL", + "source_file_sha256": "035b88b3b78706de32fa3fc2dfb4abc70198271c792791930f06d9e61ee35773", + "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, "current_authority": true, @@ -150,7 +150,7 @@ "dangling_reference_state": false, "contradictions": [], "drift": [], - "next_legal_action": "restore hoxline/src/hoxline/case_growth/collector.py from its owning repository" + "next_legal_action": "none; preserve source ownership" } ], "contradictions": [], @@ -216,7 +216,7 @@ "path": "hoxline", "exists": true, "branch": "feature/hoxline-case-growth-convergence-v1", - "dirty": true, + "dirty": false, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", "files_scanned": 181 } @@ -1968,5 +1968,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "1fb0f02d22e3b2f6c99f437338953a44ddda212d8e1376598a7a52b726a6f371" + "reproducibility_sha256": "b9c189817cb94bc534a3e2ef6c3ba276ae10ce39726d82ba831bf974a0ae31b2" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 28640c0..4244d51 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,11 +1,11 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-22T23:48:42Z` +Generated: `2026-07-22T23:55:40Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Reproducibility SHA-256: `1fb0f02d22e3b2f6c99f437338953a44ddda212d8e1376598a7a52b726a6f371` +Reproducibility SHA-256: `0f95327e9278c67c8348b98483547be01020af23ea8afc5f6661ac470ad3cf05` ## Summary @@ -39,8 +39,8 @@ Reproducibility SHA-256: `1fb0f02d22e3b2f6c99f437338953a44ddda212d8e1376598a7a52 | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `a3a2f6938412f3012bea1bc447bb084d82ed1936` | `CURRENT` | `CURRENT` | | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `7ed8f08c9c3e4bc07797961ba84292ec96978da6` | `CURRENT` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `950691ff0f3ad224964e713b6ec6beec7f6528b4` | `CURRENT` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `46cd4380435fcd40722bab1d32adaca7ae64d1ec` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `05da628443f2be88b9ce9fe5fc2dc9caccab09a7` | `CURRENT` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `769622bee18921f7e75d0c0db2532db0c3054579` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | ## Convergence Findings From e0c18eee12ff1c07ec5e0b3225c83cc0a620d83f Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:00:09 -0500 Subject: [PATCH 07/73] fix: verify generated and checked self revisions --- src/hoxline/case_growth/collector.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 91ba83c..216faca 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -1072,8 +1072,13 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu errors.append("hoxline: self_referential must be true") if stated.get("revision_scope") != "authoritative_sources_excluding_snapshot": errors.append("hoxline: revision_scope must be authoritative_sources_excluding_snapshot") - if current_authority and stated_sha != current_revision.get("source_parent_sha"): - errors.append("hoxline: current checked snapshot must cite the immediate parent engine commit") + if current_authority and stated_sha not in { + current_revision.get("source_commit_sha"), + current_revision.get("source_parent_sha"), + }: + errors.append( + "hoxline: current snapshot must cite the current engine commit or the immediate parent of its checked snapshot commit" + ) if not re.fullmatch(r"[0-9a-f]{40}", str(stated_sha or "")): errors.append(f"{repository}: source_commit_sha must be a 40-character Git SHA") elif repo_paths.get(repository) is None or not git_commit_exists(repo_paths[repository], str(stated_sha)): From 9ca329d0d347bebe3bfd785d467c158a1f4d6aa4 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:02:55 -0500 Subject: [PATCH 08/73] fix: resolve review roots from supplied indexes --- src/hoxline/review_engine.py | 14 ++++++++++++-- ..._multi_artifact_review_manifest_expansion_v1.py | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index f5cdcbe..21f1a0f 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -157,10 +157,20 @@ def default_batch_dir(repo_root: Path | None = None) -> Path: return root / ".hoxline" / "batch-runs" / stamp +def _review_repo_root(input_path: Path, explicit_root: Path | None = None) -> Path: + if explicit_root is not None: + return explicit_root.resolve() + resolved = input_path.resolve() + for candidate in (resolved.parent, *resolved.parents): + if (candidate / "pyproject.toml").is_file() and (candidate / "examples" / "review").is_dir(): + return candidate + return Path.cwd().resolve() + + def run_review(artifact_path: Path, output_dir: Path | None = None, force: bool = False, repo_root: Path | None = None) -> dict[str, Any]: - root = repo_root or Path(__file__).resolve().parents[2] out_dir = output_dir or default_run_dir(Path.cwd()) manifest_path = _resolve_path(artifact_path, Path.cwd()) + root = _review_repo_root(manifest_path, repo_root) manifest: dict[str, Any] = {} try: manifest = _load_json(manifest_path) @@ -275,9 +285,9 @@ def render_run_console(run: dict[str, Any]) -> str: def run_batch_review(index_path: Path, output_dir: Path | None = None, force: bool = False, repo_root: Path | None = None) -> dict[str, Any]: - root = repo_root or Path(__file__).resolve().parents[2] out_dir = output_dir or default_batch_dir(Path.cwd()) resolved_index = _resolve_path(index_path, Path.cwd()) + root = _review_repo_root(resolved_index, repo_root) index: dict[str, Any] = {} try: index = _load_json(resolved_index) diff --git a/tests/test_multi_artifact_review_manifest_expansion_v1.py b/tests/test_multi_artifact_review_manifest_expansion_v1.py index 87aec85..c449c1d 100644 --- a/tests/test_multi_artifact_review_manifest_expansion_v1.py +++ b/tests/test_multi_artifact_review_manifest_expansion_v1.py @@ -6,7 +6,7 @@ import sys from hoxline.cli import main -from hoxline.review_engine import verify_batch_run +from hoxline.review_engine import _review_repo_root, verify_batch_run ROOT = Path(__file__).resolve().parents[1] @@ -32,6 +32,10 @@ def _json(path: Path) -> dict[str, object]: return json.load(handle) +def test_review_repo_root_is_derived_from_index_not_installed_module() -> None: + assert _review_repo_root(INDEX) == ROOT + + def test_single_artifact_manifests_pass(tmp_path) -> None: for artifact_id, manifest in MANIFESTS.items(): output_dir = tmp_path / artifact_id From 2f22d64226e88640aa762275b434604049a34c66 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:04:29 -0500 Subject: [PATCH 09/73] chore: refresh snapshot after clean-room hardening --- .../case-growth/current-case-growth-index.json | 14 +++++++------- examples/case-growth/current-case-growth-index.md | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 284c313..a532bc0 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-22T23:55:08Z", + "generated_at": "2026-07-23T00:03:27Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -116,12 +116,12 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "05da628443f2be88b9ce9fe5fc2dc9caccab09a7", + "source_commit_sha": "b08d44dfae587a2e5608a8bfe239118ea78ca58e", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", "source_path": "public/data/public-status.json", - "source_file_sha256": "10598dfa4c6a4943692895463f2da313fad2076200ff764947e679d658bbccdd", + "source_file_sha256": "01c5af1a958e72acd33c34a2b96363e020ecd39f3c3031044b5d7f633a1bcc63", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -136,12 +136,12 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "769622bee18921f7e75d0c0db2532db0c3054579", - "source_parent_sha": "680d7130c058f7a96856a247504ea6712e1b5043", + "source_commit_sha": "9ca329d0d347bebe3bfd785d467c158a1f4d6aa4", + "source_parent_sha": "e0c18eee12ff1c07ec5e0b3225c83cc0a620d83f", "self_referential": true, "revision_scope": "authoritative_sources_excluding_snapshot", "source_path": "src/hoxline/case_growth/collector.py", - "source_file_sha256": "035b88b3b78706de32fa3fc2dfb4abc70198271c792791930f06d9e61ee35773", + "source_file_sha256": "78ea9f9ba940dbc658560f481aa8952a42986f6c59cf7b8595d3fde381fa8e4e", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -1968,5 +1968,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "b9c189817cb94bc534a3e2ef6c3ba276ae10ce39726d82ba831bf974a0ae31b2" + "reproducibility_sha256": "8965d983dc0d1a5bb3a12ee60a956a33654e54ad1863d04535c59aa997865b55" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 4244d51..1c0fe4a 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,11 +1,11 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-22T23:55:40Z` +Generated: `2026-07-23T00:03:58Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Reproducibility SHA-256: `0f95327e9278c67c8348b98483547be01020af23ea8afc5f6661ac470ad3cf05` +Reproducibility SHA-256: `8e4aca644228accca4fd3dcbfd3cfe9c0c53315ab1886c8b61d8f014ea46c668` ## Summary @@ -39,8 +39,8 @@ Reproducibility SHA-256: `0f95327e9278c67c8348b98483547be01020af23ea8afc5f6661ac | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `a3a2f6938412f3012bea1bc447bb084d82ed1936` | `CURRENT` | `CURRENT` | | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `7ed8f08c9c3e4bc07797961ba84292ec96978da6` | `CURRENT` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `05da628443f2be88b9ce9fe5fc2dc9caccab09a7` | `CURRENT` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `769622bee18921f7e75d0c0db2532db0c3054579` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `b08d44dfae587a2e5608a8bfe239118ea78ca58e` | `CURRENT` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `9ca329d0d347bebe3bfd785d467c158a1f4d6aa4` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | ## Convergence Findings From 4c96f52f28a3f0af767a94529cff165fc2042ca2 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:10:43 -0500 Subject: [PATCH 10/73] fix: sanitize blocked diagnostics in installed runs --- src/hoxline/case_growth/collector.py | 2 +- src/hoxline/demo.py | 7 ++++++- src/hoxline/review_engine.py | 7 ++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 216faca..2c541cd 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -1077,7 +1077,7 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu current_revision.get("source_parent_sha"), }: errors.append( - "hoxline: current snapshot must cite the current engine commit or the immediate parent of its checked snapshot commit" + "hoxline: current snapshot must cite the immediate parent engine commit when checked in, or the current engine commit when newly generated" ) if not re.fullmatch(r"[0-9a-f]{40}", str(stated_sha or "")): errors.append(f"{repository}: source_commit_sha must be a 40-character Git SHA") diff --git a/src/hoxline/demo.py b/src/hoxline/demo.py index f16a20a..e6fc172 100644 --- a/src/hoxline/demo.py +++ b/src/hoxline/demo.py @@ -102,7 +102,12 @@ def build_demo_run( fixture_path: Path | None = None, negative_fixture_path: Path | None = None, ) -> dict[str, Any]: - root = repo_root or Path(__file__).resolve().parents[2] + if repo_root is not None: + root = repo_root.resolve() + elif (Path.cwd() / "examples" / "demo").is_dir(): + root = Path.cwd().resolve() + else: + root = Path(__file__).resolve().parents[2] fixture = _load_json(fixture_path or root / "examples" / "demo" / "ho-det-010-safe-fixture.json") negative_fixture = _load_json( negative_fixture_path or root / "examples" / "demo" / "ho-det-010-safe-negative-fixture.json" diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 21f1a0f..14c1b12 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -483,7 +483,7 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa raise ReviewBlocked(f"batch index artifact {artifact_id} missing manifest_path") resolved = _resolve_path(Path(str(manifest_path)), repo_root) if not resolved.is_file(): - raise ReviewBlocked(f"batch index manifest path missing for {artifact_id}: {resolved}") + raise ReviewBlocked(f"batch index manifest path missing for {artifact_id}: {manifest_path}") allowed_root = (repo_root / "examples" / "review").resolve() if not _is_relative_to(resolved.resolve(), allowed_root): raise ReviewBlocked(f"batch index manifest path outside examples/review for {artifact_id}") @@ -1195,7 +1195,8 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: paths = _fixture_paths(manifest, repo_root) for label, path in paths.items(): if not path.is_file(): - raise ReviewBlocked(f"{label} fixture path missing: {path}") + declared = (manifest.get("fixture_paths") or {}).get(label, "missing") + raise ReviewBlocked(f"{label} fixture path missing: {declared}") _validate_fixture_path(path, repo_root) fixture = _load_json(path) _validate_no_private_markers(fixture, f"{label} fixture") @@ -1286,7 +1287,7 @@ def _validate_fixture_path(path: Path, repo_root: Path) -> None: resolved = path.resolve() allowed_roots = [(repo_root / "examples" / "demo").resolve(), (repo_root / "examples" / "review").resolve()] if not any(_is_relative_to(resolved, allowed) for allowed in allowed_roots): - raise ReviewBlocked(f"fixture path outside allowed example roots: {path}") + raise ReviewBlocked("fixture path outside allowed example roots") def _validate_no_private_markers(value: Any, label: str, path: str = "") -> None: From ab42606514011c3fe0ede9772d4a03b41e03e4e4 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:12:13 -0500 Subject: [PATCH 11/73] chore: refresh final clean-room snapshot --- .../case-growth/current-case-growth-index.json | 14 +++++++------- examples/case-growth/current-case-growth-index.md | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index a532bc0..18aa156 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T00:03:27Z", + "generated_at": "2026-07-23T00:11:11Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -116,12 +116,12 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "b08d44dfae587a2e5608a8bfe239118ea78ca58e", + "source_commit_sha": "771c90515d687eb0ce86fd438d664a9234912f67", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", "source_path": "public/data/public-status.json", - "source_file_sha256": "01c5af1a958e72acd33c34a2b96363e020ecd39f3c3031044b5d7f633a1bcc63", + "source_file_sha256": "06675ad7edcbb201b41904acd7c7614c7787edd3919244f47f1520bcde246f6f", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -136,12 +136,12 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "9ca329d0d347bebe3bfd785d467c158a1f4d6aa4", - "source_parent_sha": "e0c18eee12ff1c07ec5e0b3225c83cc0a620d83f", + "source_commit_sha": "4c96f52f28a3f0af767a94529cff165fc2042ca2", + "source_parent_sha": "2f22d64226e88640aa762275b434604049a34c66", "self_referential": true, "revision_scope": "authoritative_sources_excluding_snapshot", "source_path": "src/hoxline/case_growth/collector.py", - "source_file_sha256": "78ea9f9ba940dbc658560f481aa8952a42986f6c59cf7b8595d3fde381fa8e4e", + "source_file_sha256": "da1f3bcdeb763f988dd5d80011bec1c3b965a7fe9e9424265dc31191e29ef8dc", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -1968,5 +1968,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "8965d983dc0d1a5bb3a12ee60a956a33654e54ad1863d04535c59aa997865b55" + "reproducibility_sha256": "8f5da0db18eccb38f58e979ce50b5ac460527d388358fbefc4d353da437ef87b" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 1c0fe4a..ba220a5 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,11 +1,11 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T00:03:58Z` +Generated: `2026-07-23T00:11:42Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Reproducibility SHA-256: `8e4aca644228accca4fd3dcbfd3cfe9c0c53315ab1886c8b61d8f014ea46c668` +Reproducibility SHA-256: `85b30834147614510b1a1a42f7c5652821adeae05e487811e5d2c52ea41d990b` ## Summary @@ -39,8 +39,8 @@ Reproducibility SHA-256: `8e4aca644228accca4fd3dcbfd3cfe9c0c53315ab1886c8b61d8f0 | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `a3a2f6938412f3012bea1bc447bb084d82ed1936` | `CURRENT` | `CURRENT` | | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `7ed8f08c9c3e4bc07797961ba84292ec96978da6` | `CURRENT` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `b08d44dfae587a2e5608a8bfe239118ea78ca58e` | `CURRENT` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `9ca329d0d347bebe3bfd785d467c158a1f4d6aa4` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `771c90515d687eb0ce86fd438d664a9234912f67` | `CURRENT` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `4c96f52f28a3f0af767a94529cff165fc2042ca2` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | ## Convergence Findings From f2ebea13f5b19fcca348b5359bc9372a7b893962 Mon Sep 17 00:00:00 2001 From: Raylee Hawkins Date: Wed, 22 Jul 2026 19:33:47 -0500 Subject: [PATCH 12/73] chore: refresh post-CI convergence snapshot --- .../case-growth/current-case-growth-index.json | 16 ++++++++-------- .../case-growth/current-case-growth-index.md | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 18aa156..b761007 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T00:11:11Z", + "generated_at": "2026-07-23T00:32:08Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -56,7 +56,7 @@ "repository": "hawkinsoperations-validation", "authority_role": "controlled validation truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "a3a2f6938412f3012bea1bc447bb084d82ed1936", + "source_commit_sha": "d150cf172a4cb6065fd54c19c59beb9b581ba629", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", @@ -76,7 +76,7 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "7ed8f08c9c3e4bc07797961ba84292ec96978da6", + "source_commit_sha": "d1ea495f2b010fbaefc2dccf22986526958ba424", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", @@ -116,12 +116,12 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "771c90515d687eb0ce86fd438d664a9234912f67", + "source_commit_sha": "ef7ad97279c06634e153be6e8ecee1959b9f7d78", "source_parent_sha": null, "self_referential": false, "revision_scope": "authoritative_source_at_commit", "source_path": "public/data/public-status.json", - "source_file_sha256": "06675ad7edcbb201b41904acd7c7614c7787edd3919244f47f1520bcde246f6f", + "source_file_sha256": "f3f31e71520e42ce3bbbae1164c19496b20fe59e781f76bf597a1c602d2f1063", "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -136,8 +136,8 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "4c96f52f28a3f0af767a94529cff165fc2042ca2", - "source_parent_sha": "2f22d64226e88640aa762275b434604049a34c66", + "source_commit_sha": "ab42606514011c3fe0ede9772d4a03b41e03e4e4", + "source_parent_sha": "4c96f52f28a3f0af767a94529cff165fc2042ca2", "self_referential": true, "revision_scope": "authoritative_sources_excluding_snapshot", "source_path": "src/hoxline/case_growth/collector.py", @@ -1968,5 +1968,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "8f5da0db18eccb38f58e979ce50b5ac460527d388358fbefc4d353da437ef87b" + "reproducibility_sha256": "2f21fa37b84454e2dc47cf2dddac630e62d85cd48a164ef206c4ef11cbb2c7ed" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index ba220a5..3fbd38e 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,11 +1,11 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T00:11:42Z` +Generated: `2026-07-23T00:32:37Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Reproducibility SHA-256: `85b30834147614510b1a1a42f7c5652821adeae05e487811e5d2c52ea41d990b` +Reproducibility SHA-256: `d2b500c8db243e40366512e14393ce0e0f2cf4ff3ee9304b3c0b2f5938d5cdac` ## Summary @@ -36,11 +36,11 @@ Reproducibility SHA-256: `85b30834147614510b1a1a42f7c5652821adeae05e487811e5d2c5 | --- | --- | --- | --- | --- | --- | | `.github` | `org command-center routing` | `scripts/verify-command-center-invariants.py` | `b051aa3981e52a56b45a3078fcbf32ac34a99f92` | `CURRENT` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `a3a2f6938412f3012bea1bc447bb084d82ed1936` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `7ed8f08c9c3e4bc07797961ba84292ec96978da6` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `d150cf172a4cb6065fd54c19c59beb9b581ba629` | `CURRENT` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `d1ea495f2b010fbaefc2dccf22986526958ba424` | `CURRENT` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `771c90515d687eb0ce86fd438d664a9234912f67` | `CURRENT` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `4c96f52f28a3f0af767a94529cff165fc2042ca2` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `ef7ad97279c06634e153be6e8ecee1959b9f7d78` | `CURRENT` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `ab42606514011c3fe0ede9772d4a03b41e03e4e4` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | ## Convergence Findings From ac7374624da1b61b42b1f11286d1499f5ffc2cbc Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 00:29:16 -0500 Subject: [PATCH 13/73] fix(hoxline): bind replay and content authority --- .github/workflows/ci.yml | 230 +++- .../fixtures/id-det-002-safe-fixture.json | 2 +- .../ho-det-010-artifact-manifest-v1.json | 12 +- .../id-det-002-artifact-manifest-v1.json | 4 +- schemas/artifact-manifest-v1.schema.json | 138 ++- schemas/case-growth-index-v0.schema.json | 14 + ...multi-artifact-review-index-v1.schema.json | 103 +- schemas/review-machine-state-v1.schema.json | 76 +- src/hoxline/case_growth/collector.py | 283 ++--- src/hoxline/case_growth/discovery.py | 120 ++- src/hoxline/case_growth/render.py | 11 +- src/hoxline/cli.py | 55 +- src/hoxline/review_engine.py | 987 ++++++++++++++++-- tests/test_action_contract.py | 23 + tests/test_case_growth_index_v0.py | 94 +- ...i_artifact_review_manifest_expansion_v1.py | 84 +- tests/test_review_engine_v1.py | 108 +- 17 files changed, 1977 insertions(+), 367 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83c788b..5867642 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,63 +4,231 @@ on: pull_request: push: +permissions: + contents: read + jobs: - test: + hoxline-trust-boundaries: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: python-version: ["3.11", "3.12"] steps: - - uses: actions/checkout@v4 + - name: Check out Hoxline PR revision + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + path: org/hoxline + persist-credentials: false + + - name: Check out command center + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/.github + ref: fee75e7a88c0b623590786e02f69d4297a0e10b4 + path: org/.github + persist-credentials: false + + - name: Check out detection authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-detections + ref: 16d4432eb46d457939f1cfe573c0c5d712650efb + path: org/hawkinsoperations-detections + persist-credentials: false + + - name: Check out validation authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-validation + ref: 34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc + path: org/hawkinsoperations-validation + persist-credentials: false + + - name: Check out platform authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-platform + ref: 775feb781d9ee7665c2ce623824de040e3341735 + path: org/hawkinsoperations-platform + persist-credentials: false + + - name: Check out proof authority + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-proof + ref: 8ff2c1a2f1762e7635fe24194617f110275c2f0c + path: org/hawkinsoperations-proof + persist-credentials: false + + - name: Check out website rendering contract + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: HawkinsOperations/hawkinsoperations-website + ref: 3c8202b730836074e20c4fa1e3601741a02ee616 + path: org/hawkinsoperations-website + persist-credentials: false + + - name: Verify and record exact seven-repository checkout + working-directory: org/hoxline + shell: bash + run: | + python - <<'PY' + from pathlib import Path + import subprocess + + root = Path("..").resolve() + expected = [ + ".github", + "hawkinsoperations-detections", + "hawkinsoperations-validation", + "hawkinsoperations-platform", + "hawkinsoperations-proof", + "hawkinsoperations-website", + "hoxline", + ] + immutable = { + ".github": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", + "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", + "hawkinsoperations-platform": "775feb781d9ee7665c2ce623824de040e3341735", + "hawkinsoperations-proof": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", + "hawkinsoperations-website": "3c8202b730836074e20c4fa1e3601741a02ee616", + } + actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) + if actual != sorted(expected): + raise SystemExit(f"exact seven-repository checkout required; expected={sorted(expected)}, actual={actual}") + for name in expected: + sha = subprocess.check_output( + ["git", "-C", str(root / name), "rev-parse", "HEAD"], + text=True, + ).strip() + print(f"{name}={sha}") + if name in immutable and sha != immutable[name]: + raise SystemExit( + f"{name} checkout mismatch: expected {immutable[name]}, got {sha}" + ) + PY - - uses: actions/setup-python@v5 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - - name: Install package and test dependencies + - name: Install package and declared test dependencies + working-directory: org/hoxline run: | python -m pip install --upgrade pip python -m pip install -e ".[test]" - - name: Run tests - run: python -m pytest + - name: Compile + working-directory: org/hoxline + run: python -B -m compileall src tests + + - name: Run unittest suite + working-directory: org/hoxline + run: python -B -m unittest discover -s tests + + - name: Run full pytest suite including hostile replay cases + working-directory: org/hoxline + run: python -B -m pytest + + - name: Generate Case Growth pair from exact seven checked sources + working-directory: org/hoxline + run: >- + python -B -m hoxline.cli case-growth index + --repo-root .. + --format json + --paired-output-base "${{ runner.temp }}/current-case-growth-index" + + - name: Verify checked Case Growth pair + working-directory: org/hoxline + run: >- + python -B -m hoxline.cli case-growth verify + --repo-root .. + --snapshot examples/case-growth/current-case-growth-index.json - - name: Passing example succeeds + - name: Diff checked Case Growth pair + working-directory: org/hoxline + run: >- + python -B -m hoxline.cli case-growth diff + --repo-root .. + --snapshot examples/case-growth/current-case-growth-index.json + --format json + + - name: Run expanded batch and verify complete replay + working-directory: org/hoxline + run: | + python -B -m hoxline review batch run \ + --index examples/review/multi-artifact-review-index-v1.json \ + --output "${{ runner.temp }}/hoxline-batch" \ + --force + python -B -m hoxline review batch verify \ + --run "${{ runner.temp }}/hoxline-batch/batch-machine-state.json" + + - name: Run one-command reviewer demo + working-directory: org/hoxline + run: >- + python -B -m hoxline demo quickstart + --output "${{ runner.temp }}/hoxline-demo" + --force + + - name: Passing Claim Firewall example succeeds + working-directory: org/hoxline run: python -m claimfirewall scan examples/pass.md --policy policy/blocked_claims.yml - - name: Failing example fails + - name: Failing Claim Firewall example is rejected + working-directory: org/hoxline run: | - if python -m claimfirewall scan examples/fail.md --policy policy/blocked_claims.yml; then - echo "Expected failing example to report blocked claims" - exit 1 - fi + python - <<'PY' + import subprocess + import sys + + result = subprocess.run( + [ + sys.executable, + "-m", + "claimfirewall", + "scan", + "examples/fail.md", + "--policy", + "policy/blocked_claims.yml", + ], + check=False, + ) + if result.returncode != 1: + raise SystemExit(f"expected blocked example exit 1, got {result.returncode}") + PY - name: Safe context docs succeed + working-directory: org/hoxline run: python -m claimfirewall scan README.md CLAIM_BOUNDARY.md --policy policy/blocked_claims.yml - - name: JSON output is valid and contains findings + - name: Reject legacy product naming + working-directory: org/hoxline run: | - set +e - python -m claimfirewall scan examples/fail.md --policy policy/blocked_claims.yml --format json > findings.json - status=$? - set -e - if [ "$status" -ne 1 ]; then - echo "Expected JSON scan to exit 1" - exit 1 - fi - python -m json.tool findings.json > /dev/null python - <<'PY' - import json from pathlib import Path - data = json.loads(Path("findings.json").read_text()) - if not data.get("findings"): - raise SystemExit("Expected JSON findings") + import re + + pattern = re.compile(r"c[l]aimlint", re.IGNORECASE) + hits = [] + for path in Path(".").rglob("*"): + if not path.is_file() or ".git" in path.parts: + continue + try: + text = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + continue + if pattern.search(text): + hits.append(path.as_posix()) + if hits: + raise SystemExit(f"legacy product naming found: {hits}") PY - - name: Old name search + - name: Verify no tracked drift + working-directory: org/hoxline run: | - if rg -n "c[l]aimlint|C[l]aimlint|C[L]AIMLINT" .; then - echo "Old naming found" - exit 1 - fi + git diff --check + test -z "$(git status --porcelain --untracked-files=no)" diff --git a/examples/review/fixtures/id-det-002-safe-fixture.json b/examples/review/fixtures/id-det-002-safe-fixture.json index ea52cb3..fc0c48f 100644 --- a/examples/review/fixtures/id-det-002-safe-fixture.json +++ b/examples/review/fixtures/id-det-002-safe-fixture.json @@ -3,7 +3,7 @@ "endpoint_mutation": false, "events": [ { - "event_key": "risky_authentication_change", + "event_key": "mfa_push_fatigue_volume", "channel": "Identity fixture", "action": "authentication_control_change", "actor": "synthetic-review-actor" diff --git a/examples/review/ho-det-010-artifact-manifest-v1.json b/examples/review/ho-det-010-artifact-manifest-v1.json index 9e938d0..15b6052 100644 --- a/examples/review/ho-det-010-artifact-manifest-v1.json +++ b/examples/review/ho-det-010-artifact-manifest-v1.json @@ -12,12 +12,8 @@ "telemetry_contract": { "source": "Windows Security EventChannel", "event_ids": [ - 4720, - 4725, - 4726, 4732, - 4733, - 4738 + 4733 ], "wazuh_rule_ids": [ 910101, @@ -39,12 +35,8 @@ "negative": "examples/demo/ho-det-010-safe-negative-fixture.json" }, "expected_event_ids": [ - 4720, - 4725, - 4726, 4732, - 4733, - 4738 + 4733 ], "expected_rule_ids": [ 910101, diff --git a/examples/review/id-det-002-artifact-manifest-v1.json b/examples/review/id-det-002-artifact-manifest-v1.json index 97bc977..771c9e8 100644 --- a/examples/review/id-det-002-artifact-manifest-v1.json +++ b/examples/review/id-det-002-artifact-manifest-v1.json @@ -23,7 +23,7 @@ "endpoint_mutation": false, "expected_event_ids": [], "expected_event_keys": [ - "risky_authentication_change" + "mfa_push_fatigue_volume" ], "expected_rule_ids": [], "field_mapping": { @@ -57,7 +57,7 @@ "telemetry_contract": { "event_ids": [], "event_keys": [ - "risky_authentication_change" + "mfa_push_fatigue_volume" ], "event_key_field": "event_key", "required_fields": [ diff --git a/schemas/artifact-manifest-v1.schema.json b/schemas/artifact-manifest-v1.schema.json index e9c050d..718852f 100644 --- a/schemas/artifact-manifest-v1.schema.json +++ b/schemas/artifact-manifest-v1.schema.json @@ -3,35 +3,141 @@ "$id": "https://hawkinsoperations.example/schemas/artifact-manifest-v1.schema.json", "title": "Hoxline Artifact Manifest v1", "type": "object", - "required": ["manifest_version", "artifact_id", "artifact_name", "artifact_type", "artifact_family", "source_owner", "validation_owner", "platform_owner", "proof_owner", "product_owner", "telemetry_contract", "fixture_paths", "expected_event_ids", "expected_rule_ids", "allowed_claim_class", "requested_claims", "blocked_claim_classes", "public_safe_status", "human_review_required", "ai_disposition_authority", "proof_boundary", "runtime_boundary", "signal_boundary", "next_gate"], + "additionalProperties": false, + "required": [ + "manifest_version", + "artifact_id", + "artifact_name", + "artifact_type", + "artifact_family", + "source_owner", + "validation_owner", + "platform_owner", + "proof_owner", + "product_owner", + "telemetry_contract", + "fixture_paths", + "expected_event_ids", + "expected_rule_ids", + "allowed_claim_class", + "requested_claims", + "blocked_claim_classes", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "proof_boundary", + "runtime_boundary", + "signal_boundary", + "next_gate" + ], "properties": { "manifest_version": { "const": "artifact-manifest-v1" }, - "artifact_id": { "type": "string" }, - "artifact_name": { "type": "string" }, - "artifact_type": { "type": "string" }, - "artifact_family": { "type": "string" }, + "artifact_id": { + "type": "string", + "pattern": "^(?:HO-DET|HO-NDR|ID-DET|AWS-DET)-[0-9]{3}$" + }, + "artifact_name": { "type": "string", "minLength": 1 }, + "artifact_type": { "const": "detection-review-artifact" }, + "artifact_family": { "const": "synthetic-review-only" }, + "source_owner": { "const": "hawkinsoperations-detections" }, + "validation_owner": { "const": "hawkinsoperations-validation" }, + "platform_owner": { "const": "hawkinsoperations-platform" }, + "proof_owner": { "const": "hawkinsoperations-proof" }, + "product_owner": { "const": "hoxline" }, "telemetry_contract": { "type": "object", - "required": ["source", "event_ids", "wazuh_rule_ids"], + "additionalProperties": false, + "required": ["source", "event_ids", "wazuh_rule_ids", "required_fields", "scope"], "properties": { - "source": { "type": "string" }, - "event_ids": { "type": "array", "items": { "type": "integer" } }, - "event_keys": { "type": "array", "items": { "type": "string" } }, - "event_key_field": { "type": "string" }, - "wazuh_rule_ids": { "type": "array", "items": { "type": "integer" } } + "source": { "type": "string", "minLength": 1 }, + "event_ids": { + "type": "array", + "uniqueItems": true, + "items": { "type": "integer", "minimum": 0 } + }, + "event_keys": { + "type": "array", + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "event_key_field": { "type": "string", "minLength": 1 }, + "wazuh_rule_ids": { + "type": "array", + "uniqueItems": true, + "items": { "type": "integer", "minimum": 0 } + }, + "required_fields": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "scope": { "type": "string", "minLength": 1 }, + "source_control_note": { "type": "string", "minLength": 1 } } }, "fixture_paths": { "type": "object", + "additionalProperties": false, "required": ["positive", "negative"], "properties": { - "positive": { "type": "string" }, - "negative": { "type": "string" } + "positive": { "type": "string", "pattern": "^examples/" }, + "negative": { "type": "string", "pattern": "^examples/" } } }, + "expected_event_ids": { + "type": "array", + "uniqueItems": true, + "items": { "type": "integer", "minimum": 0 } + }, + "expected_event_keys": { + "type": "array", + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "expected_rule_ids": { + "type": "array", + "uniqueItems": true, + "items": { "type": "integer", "minimum": 0 } + }, + "allowed_claim_class": { "type": "string", "minLength": 1 }, + "requested_claims": { + "type": "array", + "items": { "type": "string", "minLength": 1 } + }, + "blocked_claim_classes": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, "public_safe_status": { "const": "NOT_PUBLIC_SAFE" }, "human_review_required": { "const": true }, - "ai_disposition_authority": { "const": false } - }, - "additionalProperties": true + "ai_disposition_authority": { "const": false }, + "endpoint_mutation": { "const": false }, + "wazuh_mutation": { "const": false }, + "runtime_proof": { "const": false }, + "public_proof_promoted": { "const": false }, + "lifetime_ledger_changed": { "const": false }, + "proof_boundary": { "type": "string", "minLength": 1 }, + "runtime_boundary": { "type": "string", "minLength": 1 }, + "signal_boundary": { "type": "string", "minLength": 1 }, + "next_gate": { "type": "string", "minLength": 1 }, + "expected_review_outcome": { "const": "BLOCKED" }, + "expected_block_reason": { "type": "string", "minLength": 1 }, + "additional_telemetry_sources": { + "type": "array", + "items": { "type": "string", "minLength": 1 } + }, + "attack_mapping": { "type": "object" }, + "confidence": { "type": "string" }, + "detection_family": { "type": "string" }, + "field_mapping": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "severity": { "type": "string" }, + "triage_what_happened": { "type": "string" }, + "triage_why_it_matters": { "type": "string" } + } } diff --git a/schemas/case-growth-index-v0.schema.json b/schemas/case-growth-index-v0.schema.json index 29f8c74..bd0c5cf 100644 --- a/schemas/case-growth-index-v0.schema.json +++ b/schemas/case-growth-index-v0.schema.json @@ -16,6 +16,7 @@ "contradictions", "drift", "next_legal_action", + "source_manifest_digest", "reproducibility_sha256", "repos_scanned", "repo_slot_accuracy", @@ -46,7 +47,13 @@ "authority_role", "resolved_ref", "source_commit_sha", + "source_observed_head_sha", + "current_observed_head_sha", + "source_observation_kind", "source_path", + "authoritative_path", + "authoritative_git_blob_sha", + "authoritative_content_fingerprint", "source_file_sha256", "source_freshness_state", "snapshot_freshness_state", @@ -61,7 +68,13 @@ "properties": { "repository": { "type": "string" }, "source_commit_sha": { "type": "string" }, + "source_observed_head_sha": { "type": "string" }, + "current_observed_head_sha": { "type": "string" }, + "source_observation_kind": { "const": "reviewed_immutable_commit" }, "source_path": { "type": "string" }, + "authoritative_path": { "type": "string" }, + "authoritative_git_blob_sha": { "type": ["string", "null"] }, + "authoritative_content_fingerprint": { "type": ["string", "null"] }, "source_file_sha256": { "type": ["string", "null"] }, "historical_snapshot": { "type": "boolean" }, "current_authority": { "type": "boolean" }, @@ -74,6 +87,7 @@ "contradictions": { "type": "array", "items": { "type": "object" } }, "drift": { "type": "array", "items": { "type": "object" } }, "next_legal_action": { "type": "string", "minLength": 1 }, + "source_manifest_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "reproducibility_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, "repos_scanned": { "type": "array", diff --git a/schemas/multi-artifact-review-index-v1.schema.json b/schemas/multi-artifact-review-index-v1.schema.json index df5ceb4..79ede0c 100644 --- a/schemas/multi-artifact-review-index-v1.schema.json +++ b/schemas/multi-artifact-review-index-v1.schema.json @@ -1,54 +1,9 @@ { "$id": "https://hawkinsoperations.example/schemas/multi-artifact-review-index-v1.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "additionalProperties": true, - "properties": { - "ai_disposition_authority": { - "const": false - }, - "artifacts": { - "items": { - "properties": { - "artifact_id": { - "type": "string" - }, - "manifest_path": { - "type": "string" - } - }, - "required": [ - "artifact_id", - "manifest_path" - ], - "type": "object" - }, - "type": "array" - }, - "expected_blocked_artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "expected_pass_artifacts": { - "items": { - "type": "string" - }, - "type": "array" - }, - "human_review_required": { - "const": true - }, - "index_id": { - "type": "string" - }, - "index_version": { - "const": "multi-artifact-review-index-v1" - }, - "public_safe_status": { - "const": "NOT_PUBLIC_SAFE" - } - }, + "title": "Hoxline Multi-Artifact Review Index v1", + "type": "object", + "additionalProperties": false, "required": [ "index_version", "index_id", @@ -66,6 +21,54 @@ "generated_outputs", "next_gate" ], - "title": "Hoxline Multi-Artifact Review Index v1", - "type": "object" + "properties": { + "index_version": { "const": "multi-artifact-review-index-v1" }, + "index_id": { "type": "string", "minLength": 1 }, + "description": { "type": "string", "minLength": 1 }, + "artifacts": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["artifact_id", "manifest_path"], + "properties": { + "artifact_id": { + "type": "string", + "pattern": "^(?:HO-DET|HO-NDR|ID-DET|AWS-DET)-[0-9]{3}$" + }, + "manifest_path": { + "type": "string", + "pattern": "^examples/review/[a-z0-9-]+-artifact-manifest-v1[.]json$" + } + } + } + }, + "expected_pass_artifacts": { + "type": "array", + "uniqueItems": true, + "items": { "type": "string" } + }, + "expected_blocked_artifacts": { + "type": "array", + "uniqueItems": true, + "items": { "type": "string" } + }, + "batch_claim_boundary": { "type": "string", "minLength": 1 }, + "public_safe_status": { "const": "NOT_PUBLIC_SAFE" }, + "human_review_required": { "const": true }, + "ai_disposition_authority": { "const": false }, + "runtime_boundary": { "type": "string", "minLength": 1 }, + "signal_boundary": { "type": "string", "minLength": 1 }, + "proof_boundary": { "type": "string", "minLength": 1 }, + "generated_outputs": { + "const": [ + "batch-machine-state.json", + "batch-summary.md", + "batch-reviewer-pack.md", + "batch-run-summary.json" + ] + }, + "next_gate": { "type": "string", "minLength": 1 } + } } diff --git a/schemas/review-machine-state-v1.schema.json b/schemas/review-machine-state-v1.schema.json index e59a848..4c82f35 100644 --- a/schemas/review-machine-state-v1.schema.json +++ b/schemas/review-machine-state-v1.schema.json @@ -3,18 +3,88 @@ "$id": "https://hawkinsoperations.example/schemas/review-machine-state-v1.schema.json", "title": "Hoxline Review Machine State v1", "type": "object", - "required": ["schema_version", "engine_version", "run_id", "artifact_id", "manifest_path", "stages", "outputs", "final_status", "requested_claims", "blocked_claims", "proof_boundary", "runtime_boundary", "signal_boundary", "public_safe_status", "human_review_required", "ai_disposition_authority", "endpoint_mutation", "wazuh_mutation", "private_evidence_committed", "public_proof_promoted", "lifetime_ledger_changed", "next_gate", "created_at"], + "additionalProperties": false, + "required": [ + "schema_version", + "engine_version", + "run_id", + "artifact_id", + "manifest_path", + "stages", + "outputs", + "output_digests", + "state_integrity_digest", + "final_status", + "block_reason", + "allowed_claim", + "requested_claims", + "blocked_claims", + "proof_boundary", + "runtime_boundary", + "signal_boundary", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "endpoint_mutation", + "wazuh_mutation", + "runtime_proof", + "private_evidence_committed", + "public_proof_promoted", + "lifetime_ledger_changed", + "next_gate", + "created_at", + "product" + ], "properties": { "schema_version": { "const": "review-machine-state-v1" }, "engine_version": { "const": "hoxline-review-engine-v1" }, + "run_id": { "type": "string" }, + "artifact_id": { "type": "string" }, + "manifest_path": { "type": "string" }, + "stages": { "type": "array", "minItems": 11, "maxItems": 11 }, + "outputs": { + "type": "object", + "additionalProperties": { "type": "string" } + }, + "output_digests": { + "type": "object", + "additionalProperties": { "type": "string", "pattern": "^[0-9a-f]{64}$" } + }, + "state_integrity_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, + "input_digests": { + "type": "object", + "additionalProperties": { "type": "string", "pattern": "^[0-9a-f]{64}$" } + }, + "source_manifest_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, + "authority_binding": { "type": "object" }, "final_status": { "enum": ["PASS", "BLOCKED"] }, + "block_reason": { "type": ["string", "null"] }, + "allowed_claim": { "type": ["string", "null"] }, + "requested_claims": { "type": "array" }, + "blocked_claims": { "type": "array" }, + "proof_boundary": { "type": "string" }, + "runtime_boundary": { "type": "string" }, + "signal_boundary": { "type": "string" }, "public_safe_status": { "const": "NOT_PUBLIC_SAFE" }, "human_review_required": { "const": true }, "ai_disposition_authority": { "const": false }, "endpoint_mutation": { "const": false }, "wazuh_mutation": { "const": false }, + "runtime_proof": { "const": false }, + "private_evidence_committed": { "const": false }, "public_proof_promoted": { "const": false }, - "lifetime_ledger_changed": { "const": false } + "lifetime_ledger_changed": { "const": false }, + "next_gate": { "type": "string" }, + "created_at": { "type": "string" }, + "product": { "type": "string" } }, - "additionalProperties": true + "allOf": [ + { + "if": { "properties": { "final_status": { "const": "PASS" } } }, + "then": { + "required": ["input_digests", "source_manifest_digest", "authority_binding"], + "properties": { "block_reason": { "type": "null" } } + } + } + ] } diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 2c541cd..c302284 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -13,7 +13,7 @@ case_growth_files, discover_case_ids, file_sha256, - git_blob_sha256, + git_blob_identity, git_commit_exists, last_git_update, load_structured, @@ -21,24 +21,30 @@ repo_dirty, repo_dirty_paths, repo_head_sha, - repo_parent_sha, + repo_origin, repo_relative, resolve_repo_paths, + semantic_fingerprint, ) PROOF_CEILING = "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY" AUTHORITY_SOURCES = { - ".github": ("org command-center routing", "scripts/verify-command-center-invariants.py"), + ".github": ("org command-center routing", "governance/COMMAND_CENTER_INVARIANTS.json"), "hawkinsoperations-detections": ("detection source truth", "detections/DETECTION_PROMOTION_MATRIX.yml"), "hawkinsoperations-validation": ("controlled validation truth", "validation/VALIDATION_REGISTRY.yml"), "hawkinsoperations-platform": ("platform contract truth", "contracts/public-status-source-contract-v1.json"), "hawkinsoperations-proof": ("proof and claim-boundary truth", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml"), - "hawkinsoperations-website": ("rendering-only public status", "public/data/public-status.json"), + "hawkinsoperations-website": ("rendering-only public status contract", "schemas/public-status-v0.schema.json"), "hoxline": ("case-growth and fixture-review product truth", "src/hoxline/case_growth/collector.py"), } +CANONICAL_ORIGINS = { + repository: f"github.com/HawkinsOperations/{repository}".casefold() + for repository in REPO_NAMES +} + BOUNDARY = { "runtime_public_proof_claimed": False, "signal_public_proof_claimed": False, @@ -136,20 +142,27 @@ def build_case_growth_index(repo_root: Path, generated_at: str | None = None) -> source_revisions = _build_source_revisions(repo_paths) contradictions, drift = _source_convergence_findings(repo_paths, source_revisions, summary, ordered_rows) + global_current_authority = ( + not contradictions + and not drift + and all(item.get("current_authority") is True for item in source_revisions) + ) result = { "schema_version": "case-growth-index-v1", "generated_at": generated, "repo_root": "HawkinsOperations", "proof_ceiling": PROOF_CEILING, "historical_snapshot": False, - "current_authority": True, + "current_authority": global_current_authority, "snapshot_state": { - "freshness": "CURRENT", + "freshness": "CURRENT" if global_current_authority else "BLOCKED", "historical_snapshot": False, - "current_authority": True, - "self_source_revision_semantics": "worktree-head-or-snapshot-commit-parent", + "current_authority": global_current_authority, + "identity_model": "repo_path_git_blob_and_semantic_fingerprint_with_separate_head_observation", + "generated_consumers_are_authority": False, }, "source_revisions": source_revisions, + "source_manifest_digest": _source_manifest_digest(source_revisions), "contradictions": contradictions, "drift": drift, "next_legal_action": _next_legal_action(source_revisions, contradictions, drift), @@ -179,6 +192,15 @@ def _repo_boundary(repo_name: str) -> str: }[repo_name] +def _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 _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str, Any]]: revisions: list[dict[str, Any]] = [] for repository in REPO_NAMES: @@ -190,40 +212,57 @@ def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str branch = repo_branch(repo) if repo is not None else "NOT_FOUND" dirty = repo_dirty(repo) if repo is not None else False dirty_paths = repo_dirty_paths(repo) if repo is not None else [] + authority_dirty = relative_path.replace("\\", "/").casefold() in { + path.replace("\\", "/").casefold() for path in dirty_paths + } + origin = repo_origin(repo) if repo is not None else "UNKNOWN" + canonical_origin = _normalized_origin(origin) == CANONICAL_ORIGINS[repository] if repo is None: freshness = "MISSING_REPOSITORY" elif not source_exists: freshness = "MISSING_AUTHORITY_SOURCE" elif sha == "UNKNOWN": freshness = "UNVERSIONED_SOURCE" - elif repository == "hoxline" and dirty and all(path.startswith("examples/case-growth/") for path in dirty_paths): - freshness = "CURRENT_SELF_REFERENTIAL" - elif dirty: + elif authority_dirty: freshness = "WORKTREE_MODIFIED" + elif not canonical_origin: + freshness = "REPOSITORY_IDENTITY_INVALID" else: freshness = "CURRENT" - committed_fingerprint = git_blob_sha256(repo, sha, relative_path) if repo is not None else None + blob_identity = git_blob_identity(repo, sha, relative_path) if repo is not None else None + committed_fingerprint = hashlib.sha256(blob_identity[1]).hexdigest() if blob_identity is not None else None + semantic = semantic_fingerprint(relative_path, blob_identity[1]) if blob_identity is not None else None revisions.append( { "repository": repository, "authority_role": authority_role, "resolved_ref": branch, "source_commit_sha": sha, - "source_parent_sha": repo_parent_sha(repo) if repo is not None and repository == "hoxline" else None, - "self_referential": repository == "hoxline", - "revision_scope": ( - "authoritative_sources_excluding_snapshot" if repository == "hoxline" else "authoritative_source_at_commit" - ), + "source_observed_head_sha": sha, + "current_observed_head_sha": sha, + "source_observation_kind": "reviewed_immutable_commit", + "source_parent_sha": None, + "self_referential": False, + "revision_scope": "content_addressed_authority", "source_path": relative_path, + "authoritative_path": relative_path, + "authoritative_git_blob_sha": blob_identity[0] if blob_identity is not None else None, + "source_git_blob_sha": blob_identity[0] if blob_identity is not None else None, "source_file_sha256": ( committed_fingerprint if committed_fingerprint is not None else file_sha256(source) if source_exists and source is not None else None ), + "authoritative_content_fingerprint": semantic, + "source_semantic_fingerprint_sha256": semantic, + "canonical_origin": CANONICAL_ORIGINS[repository], + "observed_origin": _normalized_origin(origin), + "repository_dirty_observed": dirty, + "authority_source_dirty": authority_dirty, "source_freshness_state": freshness, "snapshot_freshness_state": "CURRENT", "historical_snapshot": False, - "current_authority": source_exists and sha != "UNKNOWN", + "current_authority": source_exists and sha != "UNKNOWN" and canonical_origin and not authority_dirty, "missing_source_state": not source_exists, "dangling_reference_state": repo is not None and not source_exists, "contradictions": [], @@ -363,7 +402,7 @@ def _source_convergence_findings( website_repo = repo_paths.get("hawkinsoperations-website") if website_repo is not None: - website_path = website_repo / AUTHORITY_SOURCES["hawkinsoperations-website"][1] + website_path = website_repo / "public" / "data" / "public-status.json" if website_path.is_file(): website = load_structured(website_path) or {} rendered = ((website.get("metrics") or {}).get("proof_records") or {}).get("value") @@ -380,58 +419,6 @@ def _source_convergence_findings( ) ) - current_shas = {item["repository"]: item["source_commit_sha"] for item in source_revisions} - for surface_owner, relative_path in ( - ("hawkinsoperations-platform", AUTHORITY_SOURCES["hawkinsoperations-platform"][1]), - ("hawkinsoperations-website", AUTHORITY_SOURCES["hawkinsoperations-website"][1]), - ): - surface_repo = repo_paths.get(surface_owner) - if surface_repo is None or not (surface_repo / relative_path).is_file(): - continue - surface = load_structured(surface_repo / relative_path) or {} - path_refs = surface.get("source_commit_refs") if isinstance(surface.get("source_commit_refs"), dict) else {} - repository_refs = ( - surface.get("source_repository_commit_refs") - if isinstance(surface.get("source_repository_commit_refs"), dict) - else {} - ) - refs = dict(path_refs) - refs.update(repository_refs) - revision_items = surface.get("source_revisions") - if isinstance(revision_items, list): - for item in revision_items: - if isinstance(item, dict): - name = item.get("repository") or item.get("repo") or item.get("name") - sha = ( - item.get("repository_revision") - or item.get("source_repository_revision") - or item.get("source_commit_sha") - or item.get("commit_sha") - or item.get("source_revision") - ) - if name and sha: - refs[str(name).removeprefix("HawkinsOperations/")] = sha - for owner, stated_sha in refs.items(): - normalized_owner = str(owner).removeprefix("HawkinsOperations/") - current_sha = current_shas.get(normalized_owner) - direct_parent_cycle = ( - surface_owner == "hawkinsoperations-website" - and normalized_owner in {"hawkinsoperations-website", "hoxline"} - and repo_paths.get(normalized_owner) is not None - and stated_sha == repo_parent_sha(repo_paths[normalized_owner]) - ) - if current_sha and stated_sha != current_sha and not direct_parent_cycle: - drift.append( - _finding( - "SOURCE_REVISION_DRIFT", - surface_owner, - relative_path, - current_sha, - stated_sha, - f"regenerate {relative_path} from {normalized_owner} at its current source revision", - ) - ) - case_ids = [str(row.get("case_id")) for row in rows] for duplicate in _duplicates(case_ids): contradictions.append( @@ -467,6 +454,21 @@ def _reproducibility_hash(index: dict[str, Any]) -> str: return hashlib.sha256(encoded).hexdigest() +def _source_manifest_digest(source_revisions: list[dict[str, Any]]) -> str: + manifest = [ + { + "repository": item.get("repository"), + "authority_role": item.get("authority_role"), + "authoritative_path": item.get("authoritative_path") or item.get("source_path"), + "authoritative_git_blob_sha": item.get("authoritative_git_blob_sha"), + "authoritative_content_fingerprint": item.get("authoritative_content_fingerprint"), + } + for item in source_revisions + ] + encoded = json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode("utf-8") + return hashlib.sha256(encoded).hexdigest() + + def _base_row(case_id: str) -> dict[str, Any]: detection_id = None if case_id.startswith("HOX-GAUNTLET-") else case_id return { @@ -1057,55 +1059,68 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu continue current_revision = current_by_repo[repository] stated_sha = stated.get("source_commit_sha") - accepted_shas = {current_revision["source_commit_sha"]} - if repository == "hawkinsoperations-website": - website_repo = repo_paths.get(repository) - website_parent = repo_parent_sha(website_repo) if website_repo is not None else None - if ( - stated_sha == website_parent - and stated.get("source_file_sha256") == current_revision.get("source_file_sha256") - ): - accepted_shas.add(website_parent) - if repository == "hoxline": - accepted_shas.add(current_revision.get("source_parent_sha")) - if stated.get("self_referential") is not True: - errors.append("hoxline: self_referential must be true") - if stated.get("revision_scope") != "authoritative_sources_excluding_snapshot": - errors.append("hoxline: revision_scope must be authoritative_sources_excluding_snapshot") - if current_authority and stated_sha not in { - current_revision.get("source_commit_sha"), - current_revision.get("source_parent_sha"), - }: - errors.append( - "hoxline: current snapshot must cite the immediate parent engine commit when checked in, or the current engine commit when newly generated" - ) + observed_head = stated.get("source_observed_head_sha") + current_observed = stated.get("current_observed_head_sha") + if stated.get("source_observation_kind") != "reviewed_immutable_commit": + errors.append(f"{repository}: source_observation_kind must be reviewed_immutable_commit") + if observed_head != stated_sha or current_observed != stated_sha: + errors.append( + f"{repository}: recorded observed-head fields must identify the same reviewed source commit" + ) + resolved_ref = stated.get("resolved_ref") + if not isinstance(resolved_ref, str) or not re.fullmatch(r"[A-Za-z0-9._/-]+", resolved_ref): + errors.append(f"{repository}: resolved_ref is malformed") + source_path = str(stated.get("authoritative_path") or stated.get("source_path") or "") + if source_path != current_revision["source_path"]: + errors.append(f"{repository}: authoritative source path disagrees with current owner path") + stated_blob = stated.get("authoritative_git_blob_sha") or stated.get("source_git_blob_sha") + current_blob = current_revision.get("authoritative_git_blob_sha") + if stated_blob != current_blob: + errors.append( + f"{repository}: authoritative Git blob disagrees with the file at the checked current tree" + ) + stated_semantic = ( + stated.get("authoritative_content_fingerprint") + or stated.get("source_semantic_fingerprint_sha256") + ) + current_semantic = current_revision.get("authoritative_content_fingerprint") + if stated_semantic != current_semantic: + errors.append( + f"{repository}: authoritative semantic fingerprint disagrees with checked current content" + ) + if stated.get("revision_scope") != "content_addressed_authority": + errors.append(f"{repository}: revision_scope must be content_addressed_authority") + if stated.get("self_referential") is not False: + errors.append(f"{repository}: generated consumers must not be self-referential authority") + if stated.get("canonical_origin") != current_revision.get("canonical_origin"): + errors.append(f"{repository}: canonical repository origin disagrees with owner") + if stated.get("observed_origin") != current_revision.get("observed_origin"): + errors.append(f"{repository}: observed repository origin is not canonical") if not re.fullmatch(r"[0-9a-f]{40}", str(stated_sha or "")): errors.append(f"{repository}: source_commit_sha must be a 40-character Git SHA") - elif repo_paths.get(repository) is None or not git_commit_exists(repo_paths[repository], str(stated_sha)): - errors.append(f"{repository}: source_commit_sha is not a reachable commit in the stated repository") - else: - if stated_sha not in accepted_shas and not historical: + elif repo_paths.get(repository) is None: + errors.append(f"{repository}: source repository is missing") + elif git_commit_exists(repo_paths[repository], str(stated_sha)): + observed_identity = git_blob_identity(repo_paths[repository], str(stated_sha), source_path) + if observed_identity is None or observed_identity[0] != current_blob: errors.append( - f"{repository}: stale source revision {stated_sha}; current is {current_revision['source_commit_sha']}; " - "regenerate or explicitly label the snapshot historical" + f"{repository}: observed commit does not carry the checked current authoritative blob" ) - source_path = str(stated.get("source_path") or "") - source_prefix = f"{repository}/" - commit_path = source_path[len(source_prefix) :] if source_path.startswith(source_prefix) else source_path - commit_fingerprint = git_blob_sha256(repo_paths[repository], str(stated_sha), commit_path) - if commit_fingerprint is None: - errors.append(f"{repository}: authoritative source path is missing from the stated commit") - elif stated.get("source_file_sha256") != commit_fingerprint: - errors.append(f"{repository}: source_file_sha256 does not match the authoritative blob at the stated commit") - if stated.get("source_path") != current_revision["source_path"]: - errors.append(f"{repository}: authoritative source path disagrees with current owner path") + else: + # The observed branch tip is freshness metadata, not the authority + # identity. A squash/rebase may make that commit unavailable while + # the checked current path/blob/semantic identity remains exact. + # `source_observation_kind` and the three equal observation fields + # above keep this explicitly bounded rather than silently treating + # an arbitrary ancestor as current authority. + pass if stated.get("source_file_sha256") != current_revision.get("source_file_sha256") and not historical: errors.append( f"{repository}: authoritative source fingerprint drifted; regenerate from {current_revision['source_path']}" ) if stated.get("missing_source_state") is True or stated.get("dangling_reference_state") is True: errors.append(f"{repository}: snapshot records missing or dangling authority source") - allowed_freshness = {"CURRENT", "CURRENT_SELF_REFERENTIAL"} if repository == "hoxline" else {"CURRENT"} + allowed_freshness = {"CURRENT"} if current_authority and stated.get("source_freshness_state") not in allowed_freshness: errors.append( f"{repository}: current snapshot source_freshness_state must be one of {sorted(allowed_freshness)}, " @@ -1120,6 +1135,22 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu stated_hash = snapshot.get("reproducibility_sha256") if stated_hash != _reproducibility_hash(snapshot): errors.append("snapshot reproducibility_sha256 does not reproduce from its normalized content") + stated_source_manifest = snapshot.get("source_manifest_digest") + if stated_source_manifest != _source_manifest_digest(stated_revisions): + errors.append("snapshot source_manifest_digest does not reproduce from its authority identities") + if current_authority and stated_source_manifest != current.get("source_manifest_digest"): + errors.append("current snapshot source_manifest_digest disagrees with checked authority content") + snapshot_state = snapshot.get("snapshot_state") if isinstance(snapshot.get("snapshot_state"), dict) else {} + if snapshot_state.get("identity_model") != ( + "repo_path_git_blob_and_semantic_fingerprint_with_separate_head_observation" + ): + errors.append("snapshot identity model is missing or unsupported") + if snapshot_state.get("generated_consumers_are_authority") is not False: + errors.append("generated consumers must not be classified as authority") + if snapshot_state.get("historical_snapshot") is not historical: + errors.append("snapshot_state historical classification disagrees with snapshot") + if snapshot_state.get("current_authority") is not current_authority: + errors.append("snapshot_state current authority classification disagrees with snapshot") if current_authority: if snapshot.get("summary") != current.get("summary"): @@ -1190,14 +1221,22 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict for current_revision in current["source_revisions"]: repository = current_revision["repository"] before = before_revisions.get(repository, {}) - for field in ("source_commit_sha", "source_file_sha256", "source_path"): + for field in ( + "source_commit_sha", + "source_observed_head_sha", + "current_observed_head_sha", + "source_file_sha256", + "authoritative_git_blob_sha", + "authoritative_content_fingerprint", + "source_path", + ): if before.get(field) != current_revision[field]: - expected_self_reference = ( - repository == "hoxline" - and field == "source_commit_sha" - and before.get("self_referential") is True - and before.get("revision_scope") == "authoritative_sources_excluding_snapshot" - and before.get(field) == current_revision.get("source_parent_sha") + observation_only_content_current = ( + field in {"source_commit_sha", "source_observed_head_sha", "current_observed_head_sha"} + and before.get("authoritative_git_blob_sha") + == current_revision.get("authoritative_git_blob_sha") + and before.get("authoritative_content_fingerprint") + == current_revision.get("authoritative_content_fingerprint") ) changes.append( { @@ -1211,15 +1250,15 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict "classification": ( "EXPECTED_HISTORICAL_CONTEXT" if historical - else "EXPECTED_SELF_REFERENTIAL_CONTEXT" - if expected_self_reference + else "OBSERVATION_ONLY_CONTENT_CURRENT" + if observation_only_content_current else "ACTIONABLE_DRIFT" ), "next_remediation": ( "retain as historical context" if historical - else "none; the snapshot commit intentionally follows its cited engine commit" - if expected_self_reference + else "refresh observed-head metadata when producing the next reviewer snapshot; no authority-content regeneration required" + if observation_only_content_current else "regenerate the current snapshot from the owning source" ), } @@ -1252,7 +1291,7 @@ def diff_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> dict "next_legal_action": ( current["next_legal_action"] if actionable_changes or current["drift"] - else "none; snapshot converges with expected self-reference" + else "none; authority content converges and only observed-head metadata changed" if changes else "none; snapshot converges" ), diff --git a/src/hoxline/case_growth/discovery.py b/src/hoxline/case_growth/discovery.py index 489e963..d691a6e 100644 --- a/src/hoxline/case_growth/discovery.py +++ b/src/hoxline/case_growth/discovery.py @@ -24,16 +24,52 @@ "hoxline", ) +CASE_AUTHORITY_COLLECTIONS = ( + ("hawkinsoperations-detections", "detections/DETECTION_PROMOTION_MATRIX.yml", "entries"), + ("hawkinsoperations-validation", "validation/VALIDATION_REGISTRY.yml", "packages"), + ("hawkinsoperations-proof", "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", "entries"), +) + + +class _UniqueKeyLoader(yaml.SafeLoader): + pass + + +def _construct_unique_mapping(loader: _UniqueKeyLoader, node: yaml.MappingNode, deep: bool = False) -> dict[Any, Any]: + pairs = loader.construct_pairs(node, deep=deep) + result: dict[Any, Any] = {} + seen: set[str] = set() + for key, value in pairs: + normalized = str(key).casefold() + if normalized in seen: + raise ValueError("structured authority source contains a duplicate key") + seen.add(normalized) + result[key] = value + return result + + +_UniqueKeyLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + _construct_unique_mapping, +) + + +def _unique_json_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + seen: set[str] = set() + for key, value in pairs: + normalized = key.casefold() + if normalized in seen: + raise ValueError("structured authority source contains a duplicate key") + seen.add(normalized) + result[key] = value + return result + def resolve_repo_paths(repo_root: Path) -> dict[str, Path | None]: root = repo_root.resolve() paths: dict[str, Path | None] = {} - github_candidates = ( - root / ".github", - root / "HawkinsOperations.github", - root.parent / "HawkinsOperations.github", - ) - paths[".github"] = next((path for path in github_candidates if path.exists()), None) + paths[".github"] = root / ".github" if (root / ".github").is_dir() else None for name in REPO_NAMES: if name == ".github": continue @@ -124,6 +160,51 @@ def git_blob_sha256(repo_path: Path, sha: str, repo_relative_path: str) -> str | return hashlib.sha256(result.stdout).hexdigest() +def git_blob_identity(repo_path: Path, sha: str, repo_relative_path: str) -> tuple[str, bytes] | None: + if not re.fullmatch(r"[0-9a-f]{40}", sha): + return None + normalized_path = repo_relative_path.replace("\\", "/").lstrip("/") + if ( + not normalized_path + or normalized_path.startswith("/") + or "\\" in normalized_path + or any(part in {"", ".", ".."} for part in normalized_path.split("/")) + ): + return None + blob = git_lines(repo_path, ["rev-parse", f"{sha}:{normalized_path}"]) + if len(blob) != 1 or not re.fullmatch(r"[0-9a-f]{40}", blob[0]): + return None + try: + result = subprocess.run( + ["git", "-C", str(repo_path), "cat-file", "blob", blob[0]], + check=False, + capture_output=True, + ) + except OSError: + return None + if result.returncode != 0: + return None + return blob[0], result.stdout + + +def semantic_fingerprint(repo_relative_path: str, raw: bytes) -> str: + suffix = Path(repo_relative_path).suffix.casefold() + if suffix == ".json": + value = json.loads(raw.decode("utf-8"), object_pairs_hook=_unique_json_object) + canonical = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + elif suffix in {".yml", ".yaml"}: + value = yaml.load(raw.decode("utf-8"), Loader=_UniqueKeyLoader) + 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") + return hashlib.sha256(canonical).hexdigest() + + +def repo_origin(repo_path: Path) -> str: + lines = git_lines(repo_path, ["remote", "get-url", "origin"]) + return lines[0] if lines else "UNKNOWN" + + def file_sha256(path: Path) -> str | None: try: return hashlib.sha256(path.read_bytes()).hexdigest() @@ -174,27 +255,32 @@ def repo_relative(repo_name: str, repo_path: Path, path: Path) -> str: def load_structured(path: Path) -> Any: text = path.read_text(encoding="utf-8") if path.suffix.lower() == ".json": - return json.loads(text) + return json.loads(text, object_pairs_hook=_unique_json_object) if path.suffix.lower() in {".yml", ".yaml"}: - return yaml.safe_load(text) + return yaml.load(text, Loader=_UniqueKeyLoader) raise ValueError(f"unsupported structured file: {path}") def discover_case_ids(repo_paths: dict[str, Path | None]) -> tuple[set[str], int]: ids: set[str] = set() scanned = 0 - for repo_name, repo_path in repo_paths.items(): + for repo_name, relative_path, collection_name in CASE_AUTHORITY_COLLECTIONS: + repo_path = repo_paths.get(repo_name) if repo_path is None: continue - for path in case_growth_files(repo_name, repo_path): - scanned += 1 - if path.suffix.lower() in {".png", ".jpg", ".jpeg", ".gif", ".ico", ".webp", ".avif", ".zip", ".sqlite"}: - continue - try: - text = path.read_text(encoding="utf-8", errors="ignore") - except OSError: + path = repo_path / relative_path + if not path.is_file(): + continue + scanned += 1 + data = load_structured(path) + if not isinstance(data, dict) or not isinstance(data.get(collection_name), list): + continue + for entry in data[collection_name]: + if not isinstance(entry, dict): continue - ids.update(CASE_ID_PATTERN.findall(text)) + case_id = entry.get("detection_id") + if isinstance(case_id, str) and CASE_ID_PATTERN.fullmatch(case_id): + ids.add(case_id) return ids, scanned diff --git a/src/hoxline/case_growth/render.py b/src/hoxline/case_growth/render.py index 0f8217d..56cb3f4 100644 --- a/src/hoxline/case_growth/render.py +++ b/src/hoxline/case_growth/render.py @@ -15,6 +15,7 @@ def render_case_growth_markdown(index: dict[str, Any]) -> str: f"Repo-slot accuracy: `{repo_slots.get('wording', 'UNKNOWN_WITH_REASON')}`", f"Historical snapshot: `{str(index.get('historical_snapshot')).lower()}`", f"Current authority: `{str(index.get('current_authority')).lower()}`", + f"Source manifest digest: `{index.get('source_manifest_digest')}`", f"Reproducibility SHA-256: `{index.get('reproducibility_sha256')}`", "", "## Summary", @@ -49,14 +50,16 @@ def render_case_growth_markdown(index: dict[str, Any]) -> str: "", "## Source Revisions", "", - "| Repository | Authority role | Source path | Source revision | Source freshness | Snapshot freshness |", - "| --- | --- | --- | --- | --- | --- |", + "| Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness |", + "| --- | --- | --- | --- | --- | --- | --- |", ] ) for source in index.get("source_revisions", []): lines.append( - f"| {_cell(source['repository'])} | {_cell(source['authority_role'])} | {_cell(source['source_path'])} | " - f"`{source['source_commit_sha']}` | `{source['source_freshness_state']}` | `{source['snapshot_freshness_state']}` |" + f"| {_cell(source['repository'])} | {_cell(source['authority_role'])} | " + f"{_cell(source.get('authoritative_path') or source['source_path'])} | " + f"`{source['source_commit_sha']}` | `{source.get('authoritative_git_blob_sha')}` | " + f"`{source.get('authoritative_content_fingerprint')}` | `{source['source_freshness_state']}` |" ) lines.extend(["", "## Convergence Findings", ""]) diff --git a/src/hoxline/cli.py b/src/hoxline/cli.py index 1f33cd1..0b1c0a0 100644 --- a/src/hoxline/cli.py +++ b/src/hoxline/cli.py @@ -2,8 +2,10 @@ import argparse import json +import os from pathlib import Path import sys +import tempfile from .case_growth import ( build_case_growth_index, @@ -158,6 +160,10 @@ def _build_parser() -> argparse.ArgumentParser: case_growth_index_parser.add_argument("--repo-root", required=True, help="HawkinsOperations local org repo root") case_growth_index_parser.add_argument("--format", choices=("json", "markdown"), default="json", help="output format") case_growth_index_parser.add_argument("--output", help="optional output path") + case_growth_index_parser.add_argument( + "--paired-output-base", + help="write a content-bound JSON/Markdown pair using this path without a suffix", + ) case_growth_verify_parser = case_growth_subparsers.add_parser("verify", help="fail closed when a snapshot drifts from current authority") case_growth_verify_parser.add_argument("--repo-root", required=True, help="HawkinsOperations local org repo root") case_growth_verify_parser.add_argument("--snapshot", required=True, help="checked-in Case Growth snapshot JSON") @@ -242,10 +248,16 @@ def _run_case_growth_index(args: argparse.Namespace) -> int: print(f"Hoxline Case Growth Index: error: {exc}", file=sys.stderr) return 2 - if args.format == "json": - output = json.dumps(index, indent=2) + "\n" - else: - output = render_case_growth_markdown(index) + json_output = json.dumps(index, indent=2) + "\n" + markdown_output = render_case_growth_markdown(index) + output = json_output if args.format == "json" else markdown_output + + if args.paired_output_base: + try: + _write_case_growth_pair(Path(args.paired_output_base), json_output, markdown_output) + except OSError as exc: + print(f"Hoxline Case Growth Index: paired output error: {exc}", file=sys.stderr) + return 2 if args.output: output_path = Path(args.output) @@ -256,10 +268,43 @@ def _run_case_growth_index(args: argparse.Namespace) -> int: return 0 +def _write_case_growth_pair(base_path: Path, json_output: str, markdown_output: str) -> None: + if base_path.suffix: + raise OSError("paired output base must not include a suffix") + base_path.parent.mkdir(parents=True, exist_ok=True) + targets = ((base_path.with_suffix(".json"), json_output), (base_path.with_suffix(".md"), markdown_output)) + temporary: list[tuple[Path, Path]] = [] + try: + for target, content in targets: + with tempfile.NamedTemporaryFile( + mode="w", + encoding="utf-8", + newline="\n", + dir=target.parent, + prefix=f".{target.name}.", + suffix=".tmp", + delete=False, + ) as handle: + handle.write(content) + temporary.append((Path(handle.name), target)) + for temp_path, target in temporary: + os.replace(temp_path, target) + finally: + for temp_path, _ in temporary: + if temp_path.exists(): + temp_path.unlink() + + def _run_case_growth_verify(args: argparse.Namespace) -> int: try: - snapshot = _load_json(Path(args.snapshot)) + snapshot_path = Path(args.snapshot) + snapshot = _load_json(snapshot_path) errors, current = verify_case_growth_snapshot(Path(args.repo_root), snapshot) + markdown_path = snapshot_path.with_suffix(".md") + if not markdown_path.is_file(): + errors.append(f"paired Case Growth Markdown is missing: {markdown_path.name}") + elif markdown_path.read_text(encoding="utf-8") != render_case_growth_markdown(snapshot): + errors.append("paired Case Growth Markdown is not the exact render of the checked JSON") except (OSError, ValueError) as exc: print(f"Hoxline Case Growth verify: error: {exc}", file=sys.stderr) return 2 diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 14c1b12..231c687 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -2,12 +2,18 @@ from copy import deepcopy from datetime import datetime, timezone +import base64 +import binascii import hashlib import json -from pathlib import Path +from pathlib import Path, PurePosixPath, PureWindowsPath import re import shutil +import subprocess from typing import Any +from urllib.parse import unquote, urlsplit + +import yaml from .demo import ( BLOCKED_CLAIM_FAMILIES, @@ -91,14 +97,20 @@ ] PROHIBITED_CLAIM_PATTERNS = { "public-safe runtime proof": re.compile(r"public[- ]safe runtime proof", re.IGNORECASE), + "public-safe promotion": re.compile( + r"\bpublic[-_ ]safe(?:[-_ ](?:approved|promotion|promoted|proof|status|true))\b", + re.IGNORECASE, + ), + "runtime promotion": re.compile(r"\bruntime[-_ ]active\b", re.IGNORECASE), + "signal promotion": re.compile(r"\bsignal[-_ ]observed\b|\bsignal[-_ ]proof\b", re.IGNORECASE), "production": re.compile(r"\bproduction(?:[- ]ready| readiness)?\b", re.IGNORECASE), "customer deployment": re.compile(r"\bcustomer(?:[- ]deployed| deployment)?\b", re.IGNORECASE), "SOCaaS deployment": re.compile(r"\bSOCaaS(?:[- ]ready| deployed| deployment)?\b", re.IGNORECASE), "autonomous SOC": re.compile(r"\bautonomous SOC\b", re.IGNORECASE), - "AI-approved disposition": re.compile(r"\bAI[- ]approved\b", re.IGNORECASE), - "analyst-approved disposition": re.compile(r"\banalyst[- ]approved\b", re.IGNORECASE), - "final authorization": re.compile(r"\bfinal authorization\b", re.IGNORECASE), - "case closure": re.compile(r"\bcase closure\b|\bcase[- ]closed\b", re.IGNORECASE), + "AI-approved disposition": re.compile(r"\bAI[-_ ]approved\b", re.IGNORECASE), + "analyst-approved disposition": re.compile(r"\banalyst[-_ ]approved\b", re.IGNORECASE), + "final authorization": re.compile(r"\bfinal[-_ ]authorization\b", re.IGNORECASE), + "case closure": re.compile(r"\bcase[-_ ]closure\b|\bcase[-_ ]closed\b", re.IGNORECASE), "live cloud claim": re.compile(r"\blive (?:AWS|cloud)(?: runtime| proof| signal)?\b", re.IGNORECASE), "live identity runtime claim": re.compile(r"\blive (?:IdP|identity)(?: runtime| proof| signal)?\b", re.IGNORECASE), "live Security Onion proof": re.compile(r"\blive Security Onion(?: proof| signal| runtime)?\b", re.IGNORECASE), @@ -135,6 +147,112 @@ r"(?i)(?:[A-Z]:[\\/]|(?]+)" ) +CANONICAL_ORIGINS = { + "hawkinsoperations-detections": "https://github.com/HawkinsOperations/hawkinsoperations-detections", + "hawkinsoperations-validation": "https://github.com/HawkinsOperations/hawkinsoperations-validation", +} +MANIFEST_ALLOWED_FIELDS = set(REQUIRED_MANIFEST_FIELDS) | { + "additional_telemetry_sources", + "attack_mapping", + "confidence", + "detection_family", + "endpoint_mutation", + "expected_block_reason", + "expected_event_keys", + "expected_review_outcome", + "field_mapping", + "lifetime_ledger_changed", + "public_proof_promoted", + "runtime_proof", + "severity", + "triage_what_happened", + "triage_why_it_matters", + "wazuh_mutation", +} +TELEMETRY_CONTRACT_ALLOWED_FIELDS = { + "event_ids", + "event_key_field", + "event_keys", + "required_fields", + "scope", + "source", + "source_control_note", + "wazuh_rule_ids", +} +BATCH_INDEX_ALLOWED_FIELDS = { + "index_version", + "index_id", + "description", + "artifacts", + "expected_pass_artifacts", + "expected_blocked_artifacts", + "batch_claim_boundary", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "runtime_boundary", + "signal_boundary", + "proof_boundary", + "generated_outputs", + "next_gate", +} +FIXTURE_ALLOWED_FIELDS = { + "artifact_id", + "endpoint_mutation", + "description", + "events", + "expected_detection", + "fixture_id", + "fixture_kind", + "host", + "network_required", + "runtime_required", + "safe_fixture", + "schema_version", +} +SECURITY_FALSE_FIELDS = { + "ai_disposition_authority", + "analyst_disposition_authority", + "analyst_approval", + "case_closed", + "case_closure", + "endpoint_mutation", + "final_authorization", + "lifetime_ledger_changed", + "private_evidence_committed", + "public_proof_promoted", + "public_safe", + "runtime_active", + "runtime_proof", + "signal_observed", + "wazuh_mutation", +} +SECURITY_FALSE_KEY_TOKENS = { + re.sub(r"[^a-z0-9]", "", value.casefold()) for value in SECURITY_FALSE_FIELDS +} | { + "aiapproved", + "aiapproval", + "analystapproved", + "approvedbyanalyst", + "caseclosureapproved", + "finalapproved", + "publicsafeapproved", +} +SECURITY_FIXED_FIELDS: dict[str, Any] = { + "public_safe_status": PUBLIC_SAFE_STATUS, + "human_review_required": True, + "ai_disposition_authority": False, +} +REVIEW_OUTPUT_SECURITY_FIELDS: dict[str, Any] = { + **SECURITY_FIXED_FIELDS, + "endpoint_mutation": False, + "wazuh_mutation": False, + "runtime_proof": False, + "public_proof_promoted": False, + "lifetime_ledger_changed": False, + "private_evidence_committed": False, +} +_AUTHORITY_BINDING_CACHE: dict[tuple[str, ...], dict[str, Any]] = {} class ReviewEngineError(ValueError): @@ -145,6 +263,246 @@ class ReviewBlocked(ReviewEngineError): """Raised for governed BLOCKED review outcomes.""" +class _UniqueKeyLoader(yaml.SafeLoader): + """Safe YAML loader that rejects exact and case-folded duplicate keys.""" + + +def _construct_unique_mapping(loader: _UniqueKeyLoader, node: yaml.MappingNode, deep: bool = False) -> dict[Any, Any]: + pairs = loader.construct_pairs(node, deep=deep) + result: dict[Any, Any] = {} + seen: set[str] = set() + for key, value in pairs: + normalized = str(key).casefold() + if normalized in seen: + raise ReviewBlocked("structured input contains a duplicate key") + seen.add(normalized) + result[key] = value + return result + + +_UniqueKeyLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + _construct_unique_mapping, +) + + +def _unique_json_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + seen: set[str] = set() + for key, value in pairs: + normalized = key.casefold() + if normalized in seen: + raise ReviewBlocked("structured input contains a duplicate key") + seen.add(normalized) + result[key] = value + return result + + +def _require_exact_keys(value: dict[str, Any], allowed: set[str], label: str) -> None: + unknown = sorted(set(value) - allowed) + if unknown: + raise ReviewBlocked(f"{label} contains unsupported fields") + + +def _normalize_security_key(value: Any) -> str: + decoded = _decoded_text_variants(str(value))[-1] + return re.sub(r"[^a-z0-9]", "", decoded.casefold()) + + +def _decoded_text_variants(value: str) -> list[str]: + variants = [value] + current = value + for _ in range(3): + decoded = unquote(current) + if decoded == current: + break + variants.append(decoded) + current = decoded + compact = value.strip() + if len(compact) >= 8 and len(compact) % 4 == 0 and re.fullmatch(r"[A-Za-z0-9+/=_-]+", compact): + try: + padded = compact + "=" * ((4 - len(compact) % 4) % 4) + decoded_bytes = base64.urlsafe_b64decode(padded.encode("ascii")) + decoded = decoded_bytes.decode("utf-8") + if decoded and decoded not in variants and all(char.isprintable() or char.isspace() for char in decoded): + variants.append(decoded) + except (binascii.Error, UnicodeDecodeError, ValueError): + pass + return variants + + +def _string_has_unsafe_claim(value: str) -> bool: + for candidate in _decoded_text_variants(value): + if any(pattern.search(candidate) for pattern in PROHIBITED_CLAIM_PATTERNS.values()): + return True + stripped = candidate.strip() + if stripped.startswith(("{", "[")): + try: + nested = json.loads(stripped, object_pairs_hook=_unique_json_object) + except (json.JSONDecodeError, ReviewEngineError): + continue + try: + _validate_recursive_boundaries(nested, "encoded structured value") + except ReviewBlocked: + return True + return False + + +def _validate_recursive_boundaries(value: Any, label: str, path: str = "") -> None: + if isinstance(value, dict): + for key, item in value.items(): + key_path = f"{path}.{key}" if path else str(key) + normalized_key = _normalize_security_key(key) + if normalized_key in SECURITY_FALSE_KEY_TOKENS and item not in (False, None, 0, "", [], {}): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") + for canonical, expected in SECURITY_FIXED_FIELDS.items(): + if normalized_key == _normalize_security_key(canonical) and item != expected: + raise ReviewBlocked(f"{label} violates a fixed authority boundary") + _validate_recursive_boundaries(item, label, key_path) + return + if isinstance(value, list): + for index, item in enumerate(value): + _validate_recursive_boundaries(item, label, f"{path}[{index}]") + return + if isinstance(value, str): + _validate_declared_string(value, label) + if _string_has_unsafe_claim(value): + raise ReviewBlocked(f"{label} contains an unsupported claim") + + +def _validate_declared_string(value: str, label: str) -> None: + for candidate in _decoded_text_variants(value): + if "\x00" in candidate or any(ord(char) < 32 and char not in "\t\r\n" for char in candidate): + raise ReviewBlocked(f"{label} contains an unsafe encoded value") + if _looks_like_local_or_escaping_path(candidate): + raise ReviewBlocked(f"{label} contains a prohibited local or escaping path") + + +def _looks_like_local_or_escaping_path(value: str) -> bool: + candidate = value.strip() + if not candidate: + return False + if re.match(r"(?i)^file:", candidate): + return True + parsed = urlsplit(candidate) + if parsed.scheme and len(parsed.scheme) > 1 and parsed.scheme.casefold() != "https": + return True + if re.match(r"(?i)^[a-z]:", candidate): + return True + if candidate.startswith(("\\\\", "//", "/", "\\")): + return True + normalized = candidate.replace("\\", "/") + if "\\" in candidate and "/" in candidate: + return True + segments = normalized.split("/") + if any(segment in {".", ".."} for segment in segments): + return True + return False + + +def _normalize_repo_relative_path(raw: Any, label: str) -> str: + if not isinstance(raw, str) or not raw.strip(): + raise ReviewBlocked(f"{label} must be a non-empty repository-relative path") + variants = _decoded_text_variants(raw) + if len(variants) > 1: + raise ReviewBlocked(f"{label} must not use encoded path characters") + value = variants[0] + if _looks_like_local_or_escaping_path(value) or "\\" in value: + raise ReviewBlocked(f"{label} must be a contained POSIX repository-relative path") + pure = PurePosixPath(value) + normalized = pure.as_posix() + if normalized != value or pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts): + raise ReviewBlocked(f"{label} is not canonical") + if PureWindowsPath(value).is_absolute() or PureWindowsPath(value).drive: + raise ReviewBlocked(f"{label} must not be a Windows path") + return normalized + + +def _contained_file(base: Path, raw: Any, label: str, allowed_root: Path | None = None) -> Path: + normalized = _normalize_repo_relative_path(raw, label) + candidate = (base / normalized).resolve() + root = (allowed_root or base).resolve() + if not _is_relative_to(candidate, root): + raise ReviewBlocked(f"{label} escapes its allowed root") + if candidate.is_symlink(): + raise ReviewBlocked(f"{label} must not be a symbolic link") + return candidate + + +def _git(repo: Path, *args: str) -> str: + result = subprocess.run( + ["git", "-C", str(repo), *args], + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise ReviewBlocked("authority repository Git identity could not be verified") + return result.stdout.strip() + + +def _canonical_origin(value: str) -> str: + normalized = value.strip().removesuffix(".git").replace("git@github.com:", "https://github.com/") + return normalized.casefold() + + +def _authority_file_identity(repo: Path, relative_path: str) -> dict[str, str]: + path = _contained_file(repo, relative_path, "authority path") + if not path.is_file(): + raise ReviewBlocked("authority path is missing") + tracked = _git(repo, "ls-files", "--error-unmatch", "--", relative_path) + if tracked.replace("\\", "/") != relative_path: + raise ReviewBlocked("authority path is not tracked at its canonical name") + blob = _git(repo, "rev-parse", f"HEAD:{relative_path}") + current_blob = _git(repo, "hash-object", "--", relative_path) + if blob != current_blob: + raise ReviewBlocked("authority source is dirty") + return { + "repository": repo.name, + "path": relative_path, + "git_blob_sha": blob, + "sha256": _sha256_file(path), + } + + +def _semantic_digest(value: Any) -> str: + encoded = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + return hashlib.sha256(encoded).hexdigest() + + +def _state_integrity_digest(state: dict[str, Any], field: str) -> str: + payload = {key: value for key, value in state.items() if key != field} + return _semantic_digest(payload) + + +def _sanitize_block_reason(reason: str) -> str: + lowered = reason.casefold() + if "private" in lowered or "raw" in lowered: + return "input rejected by private-data boundary" + if "path" in lowered or "absolute" in lowered or "escape" in lowered or "symbolic" in lowered: + return "input rejected by path-containment boundary" + if any( + marker in lowered + for marker in ( + "claim", + "authority", + "approval", + "authorization", + "closure", + "customer", + "production", + "public_safe", + "public-safe", + "runtime", + "signal", + ) + ): + return "input rejected by claim-authority boundary" + if "duplicate" in lowered or "unsupported field" in lowered or "structured" in lowered: + return "input rejected by strict-structure boundary" + return re.sub(r"(?i)(?:[A-Z]:[\\/]|\\\\|/home/|/users/)\S*", "[redacted]", reason)[:240] + + def default_run_dir(repo_root: Path | None = None) -> Path: root = repo_root or Path.cwd() stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") @@ -167,6 +525,214 @@ def _review_repo_root(input_path: Path, explicit_root: Path | None = None) -> Pa return Path.cwd().resolve() +def _load_yaml_object(path: Path) -> dict[str, Any]: + try: + value = yaml.load(path.read_text(encoding="utf-8"), Loader=_UniqueKeyLoader) + except (OSError, yaml.YAMLError, ReviewEngineError) as exc: + raise ReviewBlocked("authority YAML could not be parsed strictly") from exc + if not isinstance(value, dict): + raise ReviewBlocked("authority YAML must contain an object") + return value + + +def _single_entry(items: Any, key: str, expected: str, label: str) -> dict[str, Any]: + if not isinstance(items, list): + raise ReviewBlocked(f"{label} inventory must be a list") + matches = [item for item in items if isinstance(item, dict) and item.get(key) == expected] + if len(matches) != 1: + raise ReviewBlocked(f"{label} must contain exactly one matching identity") + return matches[0] + + +def _case_lists(value: dict[str, Any]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + cases = value.get("cases") + if isinstance(cases, dict): + positive = cases.get("positive") + negative = cases.get("negative") + else: + positive = value.get("positive") + negative = value.get("negative") + if not isinstance(positive, list) or not isinstance(negative, list) or not positive or not negative: + raise ReviewBlocked("validation-owned fixture contract must include positive and negative cases") + if not all(isinstance(item, dict) and isinstance(item.get("id"), str) for item in [*positive, *negative]): + raise ReviewBlocked("validation-owned fixture cases require explicit identifiers") + ids = [str(item["id"]).casefold() for item in [*positive, *negative]] + if len(ids) != len(set(ids)): + raise ReviewBlocked("validation-owned fixture case identifiers must be unique") + return positive, negative + + +def _owned_authority_binding(manifest: dict[str, Any], repo_root: Path) -> dict[str, Any]: + artifact_id = str(manifest["artifact_id"]) + if artifact_id.startswith("HO-NDR-"): + raise ReviewBlocked("boundary-contract artifact has no owned controlled-validation PASS authority") + org_root = repo_root.parent.resolve() + detection_repo = (org_root / "hawkinsoperations-detections").resolve() + validation_repo = (org_root / "hawkinsoperations-validation").resolve() + for repo_name, repo in ( + ("hawkinsoperations-detections", detection_repo), + ("hawkinsoperations-validation", validation_repo), + ): + if repo.parent != org_root or not (repo / ".git").exists(): + raise ReviewBlocked("required authority repository is missing") + origin = _canonical_origin(_git(repo, "remote", "get-url", "origin")) + if origin != _canonical_origin(CANONICAL_ORIGINS[repo_name]): + raise ReviewBlocked("authority repository origin is not canonical") + if _git(repo, "rev-parse", "--abbrev-ref", "HEAD") == "HEAD": + raise ReviewBlocked("detached authority repository is not accepted") + cache_key = ( + artifact_id, + str(detection_repo), + _git(detection_repo, "rev-parse", "HEAD"), + _git(detection_repo, "status", "--porcelain", "--untracked-files=no"), + str(validation_repo), + _git(validation_repo, "rev-parse", "HEAD"), + _git(validation_repo, "status", "--porcelain", "--untracked-files=no"), + _semantic_digest( + { + "expected_event_ids": manifest.get("expected_event_ids", []), + "expected_event_keys": manifest.get("expected_event_keys", []), + "expected_rule_ids": manifest.get("expected_rule_ids", []), + } + ), + ) + if cache_key in _AUTHORITY_BINDING_CACHE: + return deepcopy(_AUTHORITY_BINDING_CACHE[cache_key]) + + matrix_path = "detections/DETECTION_PROMOTION_MATRIX.yml" + registry_path = "validation/VALIDATION_REGISTRY.yml" + matrix = _load_yaml_object(detection_repo / matrix_path) + registry = _load_yaml_object(validation_repo / registry_path) + source_entry = _single_entry(matrix.get("entries"), "detection_id", artifact_id, "detection matrix") + validation_entry = _single_entry(registry.get("packages"), "detection_id", artifact_id, "validation registry") + + if source_entry.get("source_status") != "SOURCE_EXISTS": + raise ReviewBlocked("source-owned package is not SOURCE_EXISTS") + if source_entry.get("validation_expected_owner") != "hawkinsoperations-validation": + raise ReviewBlocked("source-owned validation handoff owner is invalid") + if source_entry.get("runtime_active") is not False or source_entry.get("signal_observed") is not False: + raise ReviewBlocked("source-owned entry exceeds the allowed runtime or signal boundary") + if source_entry.get("public_safe_status") != PUBLIC_SAFE_STATUS: + raise ReviewBlocked("source-owned entry exceeds the public-safe boundary") + + package_path = _normalize_repo_relative_path(source_entry.get("package_path"), "source package path") + package = (detection_repo / package_path).resolve() + if not _is_relative_to(package, detection_repo) or not package.is_dir(): + raise ReviewBlocked("source-owned package path is missing or outside its repository") + required_files = source_entry.get("required_files") + if not isinstance(required_files, list) or not required_files or not all(isinstance(item, str) for item in required_files): + raise ReviewBlocked("source-owned entry must declare required package files") + normalized_required: list[str] = [] + for item in required_files: + relative = _normalize_repo_relative_path(f"{package_path}/{item}", "source required file") + if relative.casefold() in {path.casefold() for path in normalized_required}: + raise ReviewBlocked("source required files contain a normalized duplicate") + normalized_required.append(relative) + if not (detection_repo / relative).is_file(): + raise ReviewBlocked("source required file is missing") + if f"{package_path}/rule.yml" not in normalized_required or f"{package_path}/status.yml" not in normalized_required: + raise ReviewBlocked("source package must include rule.yml and status.yml") + source_rule = _load_yaml_object(detection_repo / package_path / "rule.yml") + source_status = _load_yaml_object(detection_repo / package_path / "status.yml") + if source_rule.get("detection_id") != artifact_id or source_status.get("detection_id") != artifact_id: + raise ReviewBlocked("source package identity disagrees with the artifact identity") + + exact_validation = { + "validation_owner": "hawkinsoperations-validation", + "source_owner": "hawkinsoperations-detections", + "expected_result": "PASS", + "actual_result": "PASS", + "human_review_required": True, + "ai_disposition_authority": False, + "validation_kind": "controlled_validation", + "public_safe_status": PUBLIC_SAFE_STATUS, + "runtime_status": False, + "signal_status": False, + "source_dependency_required": True, + "ci_source_dependency_mode": "required", + } + for key, expected in exact_validation.items(): + if validation_entry.get(key) != expected: + raise ReviewBlocked("validation-owned registry entry is not eligible for fixture PASS") + expected_source_reference = f"hawkinsoperations-detections/{package_path}" + if validation_entry.get("source_reference") != expected_source_reference: + raise ReviewBlocked("validation-owned source handoff disagrees with the source package") + + validation_paths: list[str] = [] + for key in ( + "fixture_file", + "report_json", + "report_markdown", + "validator_script", + "parity_script", + "claim_boundary_script", + ): + relative = _normalize_repo_relative_path(validation_entry.get(key), f"validation {key}") + if relative.casefold() in {path.casefold() for path in validation_paths}: + raise ReviewBlocked("validation registry paths contain a normalized duplicate") + validation_paths.append(relative) + if not (validation_repo / relative).is_file(): + raise ReviewBlocked("validation-owned required file is missing") + + validation_fixture = _load_json(validation_repo / str(validation_entry["fixture_file"])) + validation_report = _load_json(validation_repo / str(validation_entry["report_json"])) + for value, label in ((validation_fixture, "validation fixture"), (validation_report, "validation report")): + if value.get("detection_id") != artifact_id: + raise ReviewBlocked(f"{label} identity disagrees with the artifact identity") + positive, negative = _case_lists(validation_fixture) + report_status = str(validation_report.get("status") or validation_report.get("result") or "").casefold() + if report_status != "pass": + raise ReviewBlocked("validation-owned report does not record PASS") + expected_positive = validation_entry.get("expected_positive_count") + expected_negative = validation_entry.get("expected_negative_count") + if expected_positive != len(positive) or expected_negative != len(negative): + raise ReviewBlocked("validation-owned registry case counts disagree with its fixture") + + searchable_source = "\n".join((detection_repo / path).read_text(encoding="utf-8") for path in normalized_required) + searchable_validation = json.dumps(validation_fixture, sort_keys=True) + for event_id in manifest.get("expected_event_ids", []): + if not re.search(rf"(? dict[str, Any]: out_dir = output_dir or default_run_dir(Path.cwd()) manifest_path = _resolve_path(artifact_path, Path.cwd()) @@ -176,12 +742,13 @@ def run_review(artifact_path: Path, output_dir: Path | None = None, force: bool manifest = _load_json(manifest_path) _validate_manifest(manifest, manifest_path, root) fixture_paths = _fixture_paths(manifest, root) - review_outputs = _build_review_outputs(manifest, fixture_paths) - run = _build_pass_run(manifest, manifest_path, out_dir, review_outputs) + authority_binding = _owned_authority_binding(manifest, root) + review_outputs = _build_review_outputs(manifest, fixture_paths, authority_binding) + run = _build_pass_run(manifest, manifest_path, out_dir, review_outputs, fixture_paths, authority_binding) _write_pass_outputs(out_dir, run, force) return run except ReviewBlocked as exc: - run = _build_blocked_run(manifest, manifest_path, out_dir, str(exc)) + run = _build_blocked_run(manifest, manifest_path, out_dir, _sanitize_block_reason(str(exc))) _write_blocked_outputs(out_dir, run, force) return run @@ -193,6 +760,8 @@ def verify_review_run(machine_state_path: Path) -> list[str]: except (OSError, ReviewEngineError) as exc: return [str(exc)] run_dir = machine_state_path.parent + if machine_state_path.is_symlink() or not _is_relative_to(machine_state_path.resolve(), run_dir.resolve()): + return ["machine-state path escapes its run root"] final_status = state.get("final_status") if final_status not in {"PASS", "BLOCKED"}: errors.append("machine-state final_status must be PASS or BLOCKED") @@ -203,9 +772,32 @@ def verify_review_run(machine_state_path: Path) -> list[str]: if [stage.get("stage_name") for stage in state.get("stages", [])] != STAGE_REGISTRY: errors.append("machine-state stages must match review engine stage registry") expected_outputs = EXPECTED_PASS_OUTPUTS if final_status == "PASS" else EXPECTED_BLOCKED_OUTPUTS + output_refs = state.get("outputs") + if not isinstance(output_refs, dict) or set(output_refs.values()) != set(expected_outputs) - {"proofcard.md"}: + errors.append("machine-state output references must exactly match the engine contract") + declared_output_names = set(state.get("output_digests", {})) + expected_digest_names = set(expected_outputs) - {"machine-state.json"} + if declared_output_names != expected_digest_names: + errors.append("machine-state output digest inventory must exactly match generated outputs") for name in expected_outputs: - if not (run_dir / name).is_file(): + path = (run_dir / name).resolve() + if not _is_relative_to(path, run_dir.resolve()) or path.is_symlink(): + errors.append(f"output path is not contained: {name}") + elif not path.is_file(): errors.append(f"missing output file: {name}") + for name, expected_digest in state.get("output_digests", {}).items(): + try: + normalized = _normalize_repo_relative_path(name, "machine-state output path") + except ReviewBlocked as exc: + errors.append(str(exc)) + continue + path = (run_dir / normalized).resolve() + if not _is_relative_to(path, run_dir.resolve()) or path.is_symlink() or not path.is_file(): + errors.append(f"bound output is missing or escapes the run root: {name}") + elif expected_digest != _sha256_file(path): + errors.append(f"output digest mismatch: {name}") + if state.get("state_integrity_digest") != _state_integrity_digest(state, "state_integrity_digest"): + errors.append("machine-state integrity digest mismatch") for field, expected in { "public_safe_status": PUBLIC_SAFE_STATUS, "human_review_required": True, @@ -219,11 +811,40 @@ def verify_review_run(machine_state_path: Path) -> list[str]: }.items(): if state.get(field) != expected: errors.append(f"machine-state field {field} must be {expected!r}") + try: + scan_state = { + key: value + for key, value in state.items() + if key not in {"blocked_claims", "stages", "proof_boundary", "runtime_boundary", "signal_boundary"} + } + _validate_recursive_boundaries(scan_state, "machine-state") + _validate_no_private_markers(state, "machine-state") + except ReviewBlocked as exc: + errors.append(_sanitize_block_reason(str(exc))) if final_status == "PASS": non_pass = [stage["stage_name"] for stage in state["stages"] if stage.get("status") != "PASS"] if non_pass: errors.append(f"PASS run has non-PASS stages: {', '.join(non_pass)}") _verify_pass_outputs(run_dir, state, errors) + try: + manifest = _load_json(run_dir / "artifact-manifest.json") + _validate_manifest(manifest, run_dir / "artifact-manifest.json", Path(__file__).resolve().parents[2]) + fixture_paths = _fixture_paths(manifest, Path(__file__).resolve().parents[2]) + authority = _owned_authority_binding(manifest, Path(__file__).resolve().parents[2]) + expected_inputs = { + "manifest": _semantic_digest(manifest), + "positive_fixture": _sha256_file(fixture_paths["positive"]), + "negative_fixture": _sha256_file(fixture_paths["negative"]), + "source_manifest": authority["source_manifest_digest"], + } + if state.get("input_digests") != expected_inputs: + errors.append("machine-state input digest contract does not match current owned inputs") + if state.get("source_manifest_digest") != authority["source_manifest_digest"]: + errors.append("machine-state source manifest digest mismatch") + if state.get("authority_binding") != authority: + errors.append("machine-state authority binding does not match current owned sources") + except (OSError, ReviewEngineError) as exc: + errors.append(f"owned input replay failed closed: {_sanitize_block_reason(str(exc))}") if final_status == "BLOCKED" and not state.get("block_reason"): errors.append("BLOCKED run must include block_reason") private_hits = _private_output_hits(run_dir) @@ -313,6 +934,10 @@ def run_batch_review(index_path: Path, output_dir: Path | None = None, force: bo "output_dir": f"artifacts/{artifact_id}", "machine_state": f"artifacts/{artifact_id}/machine-state.json", "machine_state_sha256": _sha256_file(artifact_out / "machine-state.json"), + "manifest_sha256": state.get("input_digests", {}).get("manifest"), + "source_manifest_digest": state.get("source_manifest_digest"), + "state_integrity_digest": state.get("state_integrity_digest"), + "output_digests": state.get("output_digests", {}), "reviewer_pack": f"artifacts/{artifact_id}/reviewer-pack.md" if state["final_status"] == "PASS" else None, "blocked_review": f"artifacts/{artifact_id}/blocked-review.md" if state["final_status"] == "BLOCKED" else None, "run_summary": f"artifacts/{artifact_id}/run-summary.json", @@ -327,6 +952,7 @@ def run_batch_review(index_path: Path, output_dir: Path | None = None, force: bo "private_evidence_committed": state["private_evidence_committed"], "public_proof_promoted": state["public_proof_promoted"], "lifetime_ledger_changed": state["lifetime_ledger_changed"], + "next_gate": state["next_gate"], } ) @@ -339,8 +965,9 @@ def run_batch_review(index_path: Path, output_dir: Path | None = None, force: bo return {"output_dir": str(out_dir), "index": index, "batch_machine_state": batch_state} except ReviewBlocked as exc: _prepare_output_dir(out_dir, force) - safe_index = _safe_blocked_index(index, resolved_index, str(exc)) - batch_state = _blocked_batch_machine_state(safe_index, resolved_index, out_dir, str(exc)) + safe_reason = _sanitize_block_reason(str(exc)) + safe_index = _safe_blocked_index(index, resolved_index, safe_reason) + batch_state = _blocked_batch_machine_state(safe_index, resolved_index, out_dir, safe_reason) _write_batch_outputs(out_dir, safe_index, batch_state) return {"output_dir": str(out_dir), "index": safe_index, "batch_machine_state": batch_state} @@ -352,6 +979,8 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: except (OSError, ReviewEngineError) as exc: return [str(exc)] run_dir = batch_machine_state_path.parent + if batch_machine_state_path.is_symlink() or not _is_relative_to(batch_machine_state_path.resolve(), run_dir.resolve()): + return ["batch-machine-state path escapes its run root"] if state.get("schema_version") != BATCH_MACHINE_STATE_VERSION: errors.append(f"batch-machine-state schema_version must be {BATCH_MACHINE_STATE_VERSION}") if state.get("engine_version") != BATCH_ENGINE_VERSION: @@ -359,8 +988,27 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: if state.get("final_status") not in {"PASS", "MIXED", "BLOCKED"}: errors.append("batch-machine-state final_status must be PASS, MIXED, or BLOCKED") for name in BATCH_EXPECTED_OUTPUTS: - if not (run_dir / name).is_file(): + path = (run_dir / name).resolve() + if not _is_relative_to(path, run_dir.resolve()) or path.is_symlink(): + errors.append(f"batch output path is not contained: {name}") + elif not path.is_file(): errors.append(f"missing batch output file: {name}") + expected_digest_names = set(BATCH_EXPECTED_OUTPUTS) - {"batch-machine-state.json"} + if set(state.get("output_digests", {})) != expected_digest_names: + errors.append("batch output digest inventory must exactly match generated outputs") + for name, expected_digest in state.get("output_digests", {}).items(): + try: + normalized = _normalize_repo_relative_path(name, "batch output path") + except ReviewBlocked as exc: + errors.append(str(exc)) + continue + path = (run_dir / normalized).resolve() + if not _is_relative_to(path, run_dir.resolve()) or path.is_symlink() or not path.is_file(): + errors.append(f"bound batch output is missing or escapes the run root: {name}") + elif expected_digest != _sha256_file(path): + errors.append(f"batch output digest mismatch: {name}") + if state.get("batch_state_integrity_digest") != _state_integrity_digest(state, "batch_state_integrity_digest"): + errors.append("batch-machine-state integrity digest mismatch") for field, expected in { "public_safe_status": PUBLIC_SAFE_STATUS, "human_review_required": True, @@ -380,11 +1028,22 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: artifacts = state.get("artifacts", []) if state.get("final_status") != "BLOCKED" and not artifacts: errors.append("non-BLOCKED batch run must include artifact states") + artifact_ids: set[str] = set() + source_digests: list[str] = [] for artifact in artifacts: artifact_id = artifact.get("artifact_id", "UNKNOWN") - state_path = Path(str(artifact.get("machine_state", ""))) - if not state_path.is_absolute(): - state_path = run_dir / state_path + if artifact_id in artifact_ids: + errors.append(f"duplicate aggregate artifact identity: {artifact_id}") + artifact_ids.add(str(artifact_id)) + try: + state_relative = _normalize_repo_relative_path(artifact.get("machine_state"), "artifact machine-state path") + except ReviewBlocked as exc: + errors.append(f"{artifact_id}: {exc}") + continue + state_path = (run_dir / state_relative).resolve() + if not _is_relative_to(state_path, (run_dir / "artifacts").resolve()) or state_path.is_symlink(): + errors.append(f"{artifact_id}: child machine-state escapes artifacts root") + continue if not state_path.is_file(): errors.append(f"missing artifact machine-state for {artifact_id}") continue @@ -395,12 +1054,53 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: child_state = _load_json(state_path) if child_state.get("artifact_id") != artifact_id: errors.append(f"{artifact_id}: aggregate artifact_id does not match child machine-state artifact_id") + for key in ( + "final_status", + "block_reason", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "endpoint_mutation", + "wazuh_mutation", + "runtime_proof", + "private_evidence_committed", + "public_proof_promoted", + "lifetime_ledger_changed", + "next_gate", + "source_manifest_digest", + "state_integrity_digest", + "output_digests", + ): + if artifact.get(key) != child_state.get(key): + errors.append(f"{artifact_id}: aggregate {key} does not match child machine-state") + if artifact.get("manifest_sha256") != child_state.get("input_digests", {}).get("manifest"): + errors.append(f"{artifact_id}: aggregate manifest digest does not match child machine-state") + if child_state.get("source_manifest_digest"): + source_digests.append(str(child_state["source_manifest_digest"])) if artifact.get("public_safe_status") != PUBLIC_SAFE_STATUS: errors.append(f"{artifact_id}: public_safe_status must remain NOT_PUBLIC_SAFE") for false_field in ("endpoint_mutation", "wazuh_mutation", "runtime_proof", "public_proof_promoted", "lifetime_ledger_changed", "private_evidence_committed"): if artifact.get(false_field) is not False: errors.append(f"{artifact_id}: {false_field} must be false") errors.extend(_batch_expectation_errors(state)) + expected_aggregate = _semantic_digest(sorted(source_digests)) if source_digests else None + if state.get("source_manifest_digest") != expected_aggregate: + errors.append("batch source manifest digest does not match child authority bindings") + input_index = run_dir / "input-index.json" + if input_index.is_file() and state.get("input_index_sha256") != _sha256_file(input_index): + errors.append("batch input-index digest mismatch") + if state.get("final_status") != "BLOCKED": + try: + index = _load_json(input_index) + _validate_batch_index(index, input_index, Path(__file__).resolve().parents[2]) + if state.get("index_id") != index.get("index_id"): + errors.append("batch index identity mismatch") + if state.get("expected_pass_artifacts") != index.get("expected_pass_artifacts"): + errors.append("batch expected PASS list does not match input index") + if state.get("expected_blocked_artifacts") != index.get("expected_blocked_artifacts"): + errors.append("batch expected BLOCKED list does not match input index") + except (OSError, ReviewEngineError) as exc: + errors.append(f"batch input-index replay failed closed: {_sanitize_block_reason(str(exc))}") private_hits = _private_output_hits(run_dir) if private_hits: errors.append(f"private/raw markers found in batch outputs: {', '.join(private_hits)}") @@ -453,6 +1153,7 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa for field in required: if field not in index: raise ReviewBlocked(f"batch index missing required field: {field}") + _require_exact_keys(index, BATCH_INDEX_ALLOWED_FIELDS, "batch index") if index["index_version"] != BATCH_INDEX_VERSION: raise ReviewBlocked(f"index_version must be {BATCH_INDEX_VERSION}") if index.get("public_safe_status") != PUBLIC_SAFE_STATUS: @@ -462,14 +1163,17 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa if index.get("ai_disposition_authority") is not False: raise ReviewBlocked("batch ai_disposition_authority must be false") _validate_claims({"requested_claims": [index.get("batch_claim_boundary", "")], "blocked_claim_classes": BLOCKED_CLAIM_FAMILIES}) + _validate_recursive_boundaries(index, "batch index") _validate_no_private_markers(index, "batch index") artifacts = index.get("artifacts") if not isinstance(artifacts, list) or not artifacts: raise ReviewBlocked("batch index artifacts must be a non-empty list") seen: set[str] = set() + normalized_manifest_paths: set[str] = set() for item in artifacts: if not isinstance(item, dict): raise ReviewBlocked("batch index artifact entries must be objects") + _require_exact_keys(item, {"artifact_id", "manifest_path"}, "batch artifact entry") artifact_id = item.get("artifact_id") if not artifact_id: raise ReviewBlocked("batch index artifact entry missing artifact_id") @@ -481,9 +1185,18 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa manifest_path = item.get("manifest_path") if not manifest_path: raise ReviewBlocked(f"batch index artifact {artifact_id} missing manifest_path") - resolved = _resolve_path(Path(str(manifest_path)), repo_root) + canonical_manifest_path = _normalize_repo_relative_path(manifest_path, "batch manifest path") + if canonical_manifest_path.casefold() in normalized_manifest_paths: + raise ReviewBlocked("batch index contains a duplicate normalized manifest path") + normalized_manifest_paths.add(canonical_manifest_path.casefold()) + resolved = _contained_file( + repo_root, + canonical_manifest_path, + "batch manifest path", + repo_root / "examples" / "review", + ) if not resolved.is_file(): - raise ReviewBlocked(f"batch index manifest path missing for {artifact_id}: {manifest_path}") + raise ReviewBlocked("batch index manifest path is missing") allowed_root = (repo_root / "examples" / "review").resolve() if not _is_relative_to(resolved.resolve(), allowed_root): raise ReviewBlocked(f"batch index manifest path outside examples/review for {artifact_id}") @@ -492,6 +1205,23 @@ def _validate_batch_index(index: dict[str, Any], index_path: Path, repo_root: Pa raise ReviewBlocked( f"batch index artifact_id {artifact_id} does not match manifest artifact_id {manifest.get('artifact_id')}" ) + expected_pass = index.get("expected_pass_artifacts") + expected_blocked = index.get("expected_blocked_artifacts") + if not isinstance(expected_pass, list) or not isinstance(expected_blocked, list): + raise ReviewBlocked("batch expectations must be arrays") + if not all(isinstance(item, str) and ARTIFACT_ID_PATTERN.fullmatch(item) for item in [*expected_pass, *expected_blocked]): + raise ReviewBlocked("batch expectation identities are malformed") + if len({item.casefold() for item in expected_pass}) != len(expected_pass) or len( + {item.casefold() for item in expected_blocked} + ) != len(expected_blocked): + raise ReviewBlocked("batch expectations contain duplicate normalized identities") + if set(expected_pass) & set(expected_blocked): + raise ReviewBlocked("batch PASS and BLOCKED expectations must be disjoint") + if set(expected_pass) | set(expected_blocked) != seen: + raise ReviewBlocked("batch expectations must classify every declared artifact exactly once") + expected_generated = [name for name in BATCH_EXPECTED_OUTPUTS if name != "input-index.json"] + if index.get("generated_outputs") != expected_generated: + raise ReviewBlocked("batch generated_outputs must match the engine output contract") def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Path, artifacts: list[dict[str, Any]]) -> dict[str, Any]: @@ -507,6 +1237,12 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa "expected_pass_artifacts": list(index.get("expected_pass_artifacts", [])), "expected_blocked_artifacts": list(index.get("expected_blocked_artifacts", [])), "final_status": final_status, + "actual_pass_artifacts": sorted( + artifact["artifact_id"] for artifact in artifacts if artifact.get("final_status") == "PASS" + ), + "actual_blocked_artifacts": sorted( + artifact["artifact_id"] for artifact in artifacts if artifact.get("final_status") == "BLOCKED" + ), "block_reason": None, "batch_claim_boundary": index.get("batch_claim_boundary"), "proof_boundary": index.get("proof_boundary"), @@ -526,11 +1262,21 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa "next_gate": index.get("next_gate"), "created_at": datetime.now(timezone.utc).isoformat(), "product": PRODUCT, + "source_manifest_digest": _semantic_digest( + sorted( + str(artifact["source_manifest_digest"]) + for artifact in artifacts + if artifact.get("source_manifest_digest") + ) + ) + if artifacts + else None, } def _blocked_batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Path, block_reason: str) -> dict[str, Any]: state = _batch_machine_state(index, index_path, output_dir, []) + state["batch_id"] = "blocked-batch-run" state["final_status"] = "BLOCKED" state["block_reason"] = block_reason return state @@ -547,20 +1293,28 @@ def _batch_expectation_errors(state: dict[str, Any]) -> list[str]: errors.append(f"expected PASS artifacts {sorted(expected_pass)} but saw {sorted(actual_pass)}") if expected_blocked != actual_blocked: errors.append(f"expected BLOCKED artifacts {sorted(expected_blocked)} but saw {sorted(actual_blocked)}") + if state.get("actual_pass_artifacts") != sorted(actual_pass): + errors.append("recorded actual PASS artifacts do not match child states") + if state.get("actual_blocked_artifacts") != sorted(actual_blocked): + errors.append("recorded actual BLOCKED artifacts do not match child states") + expected_status = "PASS" if actual_pass and not actual_blocked else "MIXED" if actual_pass and actual_blocked else "BLOCKED" + if not errors and state.get("final_status") != expected_status: + errors.append(f"aggregate final status must be {expected_status}") return errors def _write_batch_outputs(output_dir: Path, index: dict[str, Any], state: dict[str, Any]) -> None: - _write_file_map( - output_dir, - { - "input-index.json": index, - "batch-machine-state.json": state, - "batch-summary.md": _batch_summary_markdown(state), - "batch-reviewer-pack.md": _batch_reviewer_pack(state), - "batch-run-summary.json": _batch_run_summary(state), - }, - ) + file_map = { + "input-index.json": index, + "batch-summary.md": _batch_summary_markdown(state), + "batch-reviewer-pack.md": _batch_reviewer_pack(state), + "batch-run-summary.json": _batch_run_summary(state), + } + _write_file_map(output_dir, file_map) + state["input_index_sha256"] = _sha256_file(output_dir / "input-index.json") + state["output_digests"] = {name: _sha256_file(output_dir / name) for name in sorted(file_map)} + state["batch_state_integrity_digest"] = _state_integrity_digest(state, "batch_state_integrity_digest") + _write_file_map(output_dir, {"batch-machine-state.json": state}) def _batch_summary_markdown(state: dict[str, Any]) -> str: @@ -672,10 +1426,12 @@ def _batch_run_summary(state: dict[str, Any]) -> dict[str, Any]: def _safe_blocked_index(index: dict[str, Any], index_path: Path, block_reason: str) -> dict[str, Any]: + index_id = index.get("index_id") if index else None + safe_index_id = index_id if isinstance(index_id, str) and re.fullmatch(r"[A-Za-z0-9._-]{1,96}", index_id) else "UNKNOWN" return { "schema_version": "blocked-batch-index-v1", - "index_path": _display_path(index_path), - "index_id": index.get("index_id", "UNKNOWN") if index else "UNKNOWN", + "index_path": "blocked-input-index.json", + "index_id": safe_index_id, "final_status": "BLOCKED", "block_reason": block_reason, "redaction": "Original hostile batch index content is not copied into blocked outputs.", @@ -691,7 +1447,14 @@ def _safe_blocked_index(index: dict[str, Any], index_path: Path, block_reason: s } -def _build_pass_run(manifest: dict[str, Any], manifest_path: Path, output_dir: Path, review_outputs: dict[str, Any]) -> dict[str, Any]: +def _build_pass_run( + manifest: dict[str, Any], + manifest_path: Path, + output_dir: Path, + review_outputs: dict[str, Any], + fixture_paths: dict[str, Path], + authority_binding: dict[str, Any], +) -> dict[str, Any]: run_id = output_dir.name output_refs = { "artifact_manifest": "artifact-manifest.json", @@ -716,6 +1479,14 @@ def _build_pass_run(manifest: dict[str, Any], manifest_path: Path, output_dir: P stages=_pass_stages(output_refs), outputs=output_refs, ) + state["source_manifest_digest"] = authority_binding["source_manifest_digest"] + state["authority_binding"] = authority_binding + state["input_digests"] = { + "manifest": _semantic_digest(manifest), + "positive_fixture": _sha256_file(fixture_paths["positive"]), + "negative_fixture": _sha256_file(fixture_paths["negative"]), + "source_manifest": authority_binding["source_manifest_digest"], + } reviewer_pack = _reviewer_pack(manifest, state) summary = _run_summary(manifest, state, EXPECTED_PASS_OUTPUTS) return { @@ -729,16 +1500,17 @@ def _build_pass_run(manifest: dict[str, Any], manifest_path: Path, output_dir: P def _build_blocked_run(manifest: dict[str, Any], manifest_path: Path, output_dir: Path, block_reason: str) -> dict[str, Any]: - run_id = output_dir.name + run_id = "blocked-review-run" output_refs = { "artifact_manifest": "artifact-manifest.json", "machine_state": "machine-state.json", "blocked_review": "blocked-review.md", "run_summary": "run-summary.json", } + safe_manifest = _safe_blocked_manifest(manifest, manifest_path, block_reason) state = _machine_state( - manifest=manifest, - manifest_path=manifest_path, + manifest=safe_manifest, + manifest_path=Path("blocked-input.json"), run_id=run_id, final_status="BLOCKED", stages=_blocked_stages(block_reason, output_refs), @@ -747,7 +1519,7 @@ def _build_blocked_run(manifest: dict[str, Any], manifest_path: Path, output_dir ) return { "output_dir": str(output_dir), - "manifest": _safe_blocked_manifest(manifest, manifest_path, block_reason), + "manifest": safe_manifest, "blocked_review": _blocked_review(state), "machine_state": state, "run_summary": _run_summary(manifest, state, EXPECTED_BLOCKED_OUTPUTS), @@ -756,10 +1528,12 @@ def _build_blocked_run(manifest: dict[str, Any], manifest_path: Path, output_dir def _safe_blocked_manifest(manifest: dict[str, Any], manifest_path: Path, block_reason: str) -> dict[str, Any]: + artifact_id = manifest.get("artifact_id") if manifest else None + safe_artifact_id = artifact_id if isinstance(artifact_id, str) and ARTIFACT_ID_PATTERN.fullmatch(artifact_id) else "UNKNOWN" return { "schema_version": "blocked-artifact-manifest-v1", - "manifest_path": _display_path(manifest_path), - "artifact_id": manifest.get("artifact_id", "UNKNOWN") if manifest else "UNKNOWN", + "manifest_path": "blocked-input.json", + "artifact_id": safe_artifact_id, "final_status": "BLOCKED", "block_reason": block_reason, "redaction": "Original hostile manifest content is not copied into blocked outputs.", @@ -785,30 +1559,47 @@ def _write_pass_outputs(output_dir: Path, run: dict[str, Any], force: bool) -> N "proofcard.md": outputs["proofcard_markdown"], "claim-authority.json": outputs["claim_authority"], "reviewer-pack.md": run["reviewer_pack"], - "machine-state.json": run["machine_state"], - "run-summary.json": run["run_summary"], } _write_file_map(output_dir, file_map) + state = run["machine_state"] + state["output_digests"] = { + name: _sha256_file(output_dir / name) + for name in sorted(file_map) + } + summary = _run_summary(run["manifest"], state, EXPECTED_PASS_OUTPUTS) + _write_file_map(output_dir, {"run-summary.json": summary}) + state["output_digests"]["run-summary.json"] = _sha256_file(output_dir / "run-summary.json") + state["state_integrity_digest"] = _state_integrity_digest(state, "state_integrity_digest") + _write_file_map(output_dir, {"machine-state.json": state}) + run["run_summary"] = summary def _write_blocked_outputs(output_dir: Path, run: dict[str, Any], force: bool) -> None: _prepare_output_dir(output_dir, force) - _write_file_map( - output_dir, - { - "artifact-manifest.json": run["manifest"], - "machine-state.json": run["machine_state"], - "blocked-review.md": run["blocked_review"], - "run-summary.json": run["run_summary"], - }, - ) + file_map = { + "artifact-manifest.json": run["manifest"], + "blocked-review.md": run["blocked_review"], + } + _write_file_map(output_dir, file_map) + state = run["machine_state"] + state["output_digests"] = {name: _sha256_file(output_dir / name) for name in sorted(file_map)} + summary = _run_summary(run["manifest"], state, EXPECTED_BLOCKED_OUTPUTS) + _write_file_map(output_dir, {"run-summary.json": summary}) + state["output_digests"]["run-summary.json"] = _sha256_file(output_dir / "run-summary.json") + state["state_integrity_digest"] = _state_integrity_digest(state, "state_integrity_digest") + _write_file_map(output_dir, {"machine-state.json": state}) + run["run_summary"] = summary def _node(node_id: str, node_type: str, owner: str, status: str) -> dict[str, str]: return {"id": node_id, "type": node_type, "owner": owner, "status": status} -def _build_review_outputs(manifest: dict[str, Any], fixture_paths: dict[str, Path]) -> dict[str, Any]: +def _build_review_outputs( + manifest: dict[str, Any], + fixture_paths: dict[str, Path], + authority_binding: dict[str, Any], +) -> dict[str, Any]: positive_fixture = _load_json(fixture_paths["positive"]) negative_fixture = _load_json(fixture_paths["negative"]) _validate_review_fixture(positive_fixture, manifest, True) @@ -816,7 +1607,7 @@ def _build_review_outputs(manifest: dict[str, Any], fixture_paths: dict[str, Pat intake = _artifact_intake(manifest) telemetry = _telemetry_contract_check(manifest, positive_fixture) - validation = _controlled_validation(manifest, positive_fixture, negative_fixture, telemetry) + validation = _controlled_validation(manifest, positive_fixture, negative_fixture, telemetry, authority_binding) signal = _synthetic_signal(manifest, positive_fixture, validation) enrichment = _enrichment(manifest, positive_fixture) triage = _triage(manifest, signal, enrichment, validation) @@ -886,6 +1677,7 @@ def _controlled_validation( fixture: dict[str, Any], negative_fixture: dict[str, Any], telemetry: dict[str, Any], + authority_binding: dict[str, Any], ) -> dict[str, Any]: positive_match = _fixture_matches_manifest(fixture, manifest) negative_match = _fixture_matches_manifest(negative_fixture, manifest) @@ -900,6 +1692,8 @@ def _controlled_validation( "matched_positive_cases": 1 if positive_match else 0, "false_positive_negative_cases": 1 if negative_match else 0, "result": result, + "owned_authority_binding": authority_binding, + "source_manifest_digest": authority_binding["source_manifest_digest"], "endpoint_mutation": False, "runtime_rerun": False, "wazuh_mutation": False, @@ -1175,6 +1969,7 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: for field in REQUIRED_MANIFEST_FIELDS: if field not in manifest: raise ReviewBlocked(f"manifest missing required field: {field}") + _require_exact_keys(manifest, MANIFEST_ALLOWED_FIELDS, "artifact manifest") if manifest["manifest_version"] != MANIFEST_VERSION: raise ReviewBlocked(f"manifest_version must be {MANIFEST_VERSION}") if manifest["artifact_id"] != ARTIFACT_ID and manifest.get("artifact_family") != "synthetic-review-only": @@ -1185,12 +1980,20 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: raise ReviewBlocked("human_review_required must be true") if manifest.get("ai_disposition_authority") is not False: raise ReviewBlocked("ai_disposition_authority must be false") + if manifest.get("expected_review_outcome") not in {None, "BLOCKED"}: + raise ReviewBlocked("expected_review_outcome may only declare BLOCKED") _validate_manifest_flags(manifest) _validate_claims(manifest) + scan_manifest = { + key: value + for key, value in manifest.items() + if key != "blocked_claim_classes" + and not (key == "expected_block_reason" and manifest.get("expected_review_outcome") == "BLOCKED") + } + _validate_recursive_boundaries(scan_manifest, "artifact manifest") _validate_no_private_markers(manifest, "manifest") if manifest.get("expected_review_outcome") == "BLOCKED": - reason = str(manifest.get("expected_block_reason") or "source metadata supports a boundary contract only") - raise ReviewBlocked(reason) + raise ReviewBlocked("boundary-contract artifact remains expected BLOCKED") _validate_telemetry_contract(manifest) paths = _fixture_paths(manifest, repo_root) for label, path in paths.items(): @@ -1199,6 +2002,17 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: raise ReviewBlocked(f"{label} fixture path missing: {declared}") _validate_fixture_path(path, repo_root) fixture = _load_json(path) + _require_exact_keys(fixture, FIXTURE_ALLOWED_FIELDS, f"{label} fixture") + if not isinstance(fixture.get("events"), list) or not fixture["events"]: + raise ReviewBlocked(f"{label} fixture events must be a non-empty list") + if not all( + isinstance(event, dict) + and event + and all(isinstance(key, str) and isinstance(value, (str, int, float, bool, type(None))) for key, value in event.items()) + for event in fixture["events"] + ): + raise ReviewBlocked(f"{label} fixture event shape is unsupported") + _validate_recursive_boundaries(fixture, f"{label} fixture") _validate_no_private_markers(fixture, f"{label} fixture") @@ -1219,6 +2033,7 @@ def _validate_telemetry_contract(manifest: dict[str, Any]) -> None: contract = manifest.get("telemetry_contract") if not isinstance(contract, dict): raise ReviewBlocked("telemetry_contract must be an object") + _require_exact_keys(contract, TELEMETRY_CONTRACT_ALLOWED_FIELDS, "telemetry_contract") if not isinstance(contract.get("source"), str) or not contract.get("source"): raise ReviewBlocked("telemetry_contract.source must be a non-empty fixture metadata source") event_ids = contract.get("event_ids") @@ -1255,12 +2070,7 @@ def _validate_claims(manifest: dict[str, Any]) -> None: for field, expected in expected_owners.items(): if field in manifest and manifest.get(field) != expected: raise ReviewBlocked(f"{field} must be the exact source-owned repository {expected}") - scan_manifest = { - key: value - for key, value in manifest.items() - if key != "blocked_claim_classes" and not (key == "expected_block_reason" and manifest.get("expected_review_outcome") == "BLOCKED") - } - requested_text = json.dumps(scan_manifest, sort_keys=True) + requested_text = json.dumps(requested_claims, sort_keys=True) for label, pattern in PROHIBITED_CLAIM_PATTERNS.items(): if pattern.search(requested_text): raise ReviewBlocked(f"requested claim is unsupported and blocked: {label}") @@ -1274,13 +2084,27 @@ def _fixture_paths(manifest: dict[str, Any], repo_root: Path) -> dict[str, Path] raw = manifest.get("fixture_paths") if not isinstance(raw, dict): raise ReviewBlocked("fixture_paths must be an object") + _require_exact_keys(raw, {"positive", "negative"}, "fixture_paths") try: - return { - "positive": _resolve_path(Path(str(raw["positive"])), repo_root), - "negative": _resolve_path(Path(str(raw["negative"])), repo_root), + paths = { + "positive": _contained_file( + repo_root, + raw["positive"], + "positive fixture path", + repo_root / "examples", + ), + "negative": _contained_file( + repo_root, + raw["negative"], + "negative fixture path", + repo_root / "examples", + ), } except KeyError as exc: raise ReviewBlocked(f"fixture_paths missing key: {exc.args[0]}") from exc + if paths["positive"] == paths["negative"]: + raise ReviewBlocked("positive and negative fixtures must be different files") + return paths def _validate_fixture_path(path: Path, repo_root: Path) -> None: @@ -1295,18 +2119,21 @@ def _validate_no_private_markers(value: Any, label: str, path: str = "") -> None for key, item in value.items(): full = f"{path}.{key}" if path else str(key) for pattern in PRIVATE_FIELD_PATTERNS: - if pattern.search(str(key)): + if pattern.search(str(key)) and not ( + _normalize_security_key(key) in SECURITY_FALSE_KEY_TOKENS and item is False + ): raise ReviewBlocked(f"{label} contains prohibited private/raw field marker") _validate_no_private_markers(item, label, full) elif isinstance(value, list): for index, item in enumerate(value): _validate_no_private_markers(item, label, f"{path}[{index}]") elif isinstance(value, str): - if ABSOLUTE_LOCAL_PATH.search(value): - raise ReviewBlocked(f"{label} contains an absolute local path") - for pattern in PRIVATE_VALUE_PATTERNS: - if pattern.search(value): - raise ReviewBlocked(f"{label} contains prohibited private/raw value marker") + for candidate in _decoded_text_variants(value): + if ABSOLUTE_LOCAL_PATH.search(candidate) or _looks_like_local_or_escaping_path(candidate): + raise ReviewBlocked(f"{label} contains an absolute local path") + for pattern in PRIVATE_VALUE_PATTERNS: + if pattern.search(candidate): + raise ReviewBlocked(f"{label} contains prohibited private/raw value marker") def _machine_state( @@ -1556,11 +2383,20 @@ def _blocked_claims() -> list[dict[str, str]]: def _prepare_output_dir(output_dir: Path, force: bool) -> None: + resolved = output_dir.resolve() + if resolved == Path(resolved.anchor) or resolved == Path.cwd().resolve(): + raise ReviewEngineError("output directory must be a dedicated run directory") + if output_dir.is_symlink(): + raise ReviewEngineError("output directory must not be a symbolic link") + marker = output_dir / ".hoxline-review-output-v1" if output_dir.exists(): if not force: - raise ReviewEngineError(f"output directory already exists: {output_dir}") + raise ReviewEngineError(f"output directory already exists: {_display_path(output_dir)}") + if not marker.is_file(): + raise ReviewEngineError("refusing to replace a directory not created by Hoxline Review Engine") shutil.rmtree(output_dir) output_dir.mkdir(parents=True, exist_ok=False) + marker.write_text("generated review output; safe to replace with --force\n", encoding="utf-8") def _sha256_file(path: Path) -> str: @@ -1569,7 +2405,11 @@ def _sha256_file(path: Path) -> str: def _write_file_map(output_dir: Path, file_map: dict[str, Any]) -> None: for name, value in file_map.items(): - path = output_dir / name + normalized = _normalize_repo_relative_path(name, "generated output path") + path = (output_dir / normalized).resolve() + if not _is_relative_to(path, output_dir.resolve()): + raise ReviewEngineError("generated output path escapes its run root") + path.parent.mkdir(parents=True, exist_ok=True) if isinstance(value, str): path.write_text(value, encoding="utf-8") else: @@ -1578,7 +2418,7 @@ def _write_file_map(output_dir: Path, file_map: dict[str, Any]) -> None: def _private_output_hits(run_dir: Path) -> list[str]: hits: list[str] = [] - for path in run_dir.iterdir(): + for path in run_dir.rglob("*"): if not path.is_file() or path.suffix not in {".json", ".md"}: continue text = path.read_text(encoding="utf-8") @@ -1613,8 +2453,11 @@ def _is_relative_to(path: Path, parent: Path) -> bool: def _load_json(path: Path) -> dict[str, Any]: - with path.open("r", encoding="utf-8") as handle: - data = json.load(handle) + try: + with path.open("r", encoding="utf-8") as handle: + data = json.load(handle, object_pairs_hook=_unique_json_object) + except json.JSONDecodeError as exc: + raise ReviewEngineError(f"input is not valid JSON: {_display_path(path)}") from exc if not isinstance(data, dict): - raise ReviewEngineError(f"input must be a JSON object: {path}") + raise ReviewEngineError(f"input must be a JSON object: {_display_path(path)}") return deepcopy(data) diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index bf5a300..055b366 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +import re from claimfirewall.policy import load_policy from claimfirewall.scanner import scan_paths @@ -36,3 +37,25 @@ def test_docs_scan_cleanly() -> None: findings = scan_paths([ROOT / "README.md", ROOT / "CLAIM_BOUNDARY.md"], policy) assert findings == [] + + +def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> None: + workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") + refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) + assert len(refs) == 6 + assert len(set(refs)) == 6 + assert "ref: main" not in workflow + assert "ref: feature/" not in workflow + assert "persist-credentials: false" in workflow + assert "permissions:\n contents: read" in workflow + for required in ( + "python -B -m unittest discover -s tests", + "python -B -m pytest", + "case-growth index", + "case-growth verify", + "case-growth diff", + "review batch run", + "review batch verify", + "git diff --check", + ): + assert required in workflow diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index 2f65dc5..a726b2f 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -7,7 +7,6 @@ import sys import tempfile import unittest -from unittest import mock from pathlib import Path try: @@ -133,8 +132,48 @@ def test_cli_prints_markdown(self) -> None: self.assertEqual(status, 0) self.assertIn("| case_id | source | validation | runtime_candidate |", stdout.getvalue()) + def test_cli_writes_content_bound_json_markdown_pair(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + pair_base = Path(temp_dir) / "case-growth" + with contextlib.redirect_stdout(io.StringIO()): + status = main( + [ + "case-growth", + "index", + "--repo-root", + str(FIXTURE_ROOT), + "--format", + "json", + "--paired-output-base", + str(pair_base), + ] + ) + self.assertEqual(status, 0) + payload = json.loads(pair_base.with_suffix(".json").read_text(encoding="utf-8")) + self.assertEqual( + pair_base.with_suffix(".md").read_text(encoding="utf-8"), + render_case_growth_markdown(payload), + ) + pair_base.with_suffix(".md").write_text("tampered\n", encoding="utf-8") + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + verify_status = main( + [ + "case-growth", + "verify", + "--repo-root", + str(FIXTURE_ROOT), + "--snapshot", + str(pair_base.with_suffix(".json")), + "--format", + "json", + ] + ) + self.assertEqual(verify_status, 1) + self.assertIn("not the exact render", stdout.getvalue()) + def test_schema_validates_sample_json(self) -> None: - sample = json.loads(SAMPLE_JSON.read_text(encoding="utf-8")) + sample = self.index schema = json.loads(SCHEMA.read_text(encoding="utf-8")) if jsonschema is not None: jsonschema.validate(sample, schema) @@ -231,10 +270,8 @@ def test_closed_count_only_counts_explicit_closed(self) -> None: self.assertTrue(all(row["case_state"] != "CLOSED" for row in self.rows)) def test_website_evidence_never_creates_proof_status(self) -> None: - website_only = row_by_id(self.index, "HO-DET-999") - self.assertEqual(website_only["source_status"], "NOT_FOUND") - self.assertEqual(website_only["proof_record_status"], "NOT_PROVEN") - self.assertEqual(website_only["proofcard_status"], "NOT_PROVEN") + case_ids = {row["case_id"] for row in self.rows} + self.assertNotIn("HO-DET-999", case_ids) def test_metrics_available_for_hox_gauntlet_fixture(self) -> None: gauntlet = row_by_id(self.index, "HOX-GAUNTLET-001") @@ -325,17 +362,19 @@ def test_diff_classifies_explicit_historical_changes_as_context(self) -> None: change = next(item for item in report["changes"] if item["field"] == "summary.proof_records_count") self.assertEqual(change["classification"], "EXPECTED_HISTORICAL_CONTEXT") - def test_diff_classifies_snapshot_commit_self_reference_as_expected(self) -> None: + def test_diff_classifies_head_rewrite_with_same_content_as_observation_only(self) -> None: snapshot = json.loads(json.dumps(self.index)) hoxline_revision = next(item for item in snapshot["source_revisions"] if item["repository"] == "hoxline") - hoxline_revision["source_commit_sha"] = hoxline_revision["source_parent_sha"] + hoxline_revision["source_commit_sha"] = "a" * 40 + hoxline_revision["source_observed_head_sha"] = "a" * 40 + hoxline_revision["current_observed_head_sha"] = "a" * 40 report = diff_case_growth_snapshot(FIXTURE_ROOT, snapshot) change = next( item for item in report["changes"] if item["source_owner"] == "hoxline" and item["field"] == "source_commit_sha" ) - self.assertEqual(change["classification"], "EXPECTED_SELF_REFERENTIAL_CONTEXT") + self.assertEqual(change["classification"], "OBSERVATION_ONLY_CONTENT_CURRENT") self.assertTrue(report["next_legal_action"].startswith("none;")) def test_cli_verify_fails_closed_on_hostile_snapshot(self) -> None: @@ -418,49 +457,44 @@ def test_missing_authorization_metric_token_is_bounded_context(self) -> None: errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, bounded) self.assertFalse(any("final authorization wording" in error for error in errors)) - def test_historical_snapshot_sha_must_resolve_in_stated_repository(self) -> None: + def test_unavailable_observed_sha_does_not_replace_content_authority(self) -> None: hostile = json.loads(json.dumps(self.index)) - hostile["historical_snapshot"] = True - hostile["current_authority"] = False hostile["source_revisions"][0]["source_commit_sha"] = "f" * 40 + hostile["source_revisions"][0]["source_observed_head_sha"] = "f" * 40 + hostile["source_revisions"][0]["current_observed_head_sha"] = "f" * 40 hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) - self.assertTrue(any("not a reachable commit" in error for error in errors)) + self.assertFalse(any("authoritative Git blob disagrees" in error for error in errors)) - def test_historical_snapshot_fingerprint_must_match_stated_commit_blob(self) -> None: + def test_current_snapshot_rejects_forged_content_identity(self) -> None: hostile = json.loads(json.dumps(self.index)) - hostile["historical_snapshot"] = True - hostile["current_authority"] = False - hostile["source_revisions"][0]["source_commit_sha"] = "a" * 40 - hostile["source_revisions"][0]["source_file_sha256"] = "0" * 64 + hostile["source_revisions"][0]["authoritative_git_blob_sha"] = "a" * 40 + hostile["source_revisions"][0]["authoritative_content_fingerprint"] = "0" * 64 hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) - with ( - mock.patch("hoxline.case_growth.collector.git_commit_exists", return_value=True), - mock.patch("hoxline.case_growth.collector.git_blob_sha256", return_value="b" * 64), - ): - errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) - self.assertTrue(any("does not match the authoritative blob at the stated commit" in error for error in errors)) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) + self.assertTrue(any("authoritative Git blob disagrees" in error for error in errors)) + self.assertTrue(any("semantic fingerprint disagrees" in error for error in errors)) def test_current_snapshot_rejects_worktree_modified_freshness(self) -> None: hostile = json.loads(json.dumps(self.index)) + hostile["current_authority"] = True + hostile["snapshot_state"]["current_authority"] = True hostile["source_revisions"][0]["source_freshness_state"] = "WORKTREE_MODIFIED" hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) self.assertTrue(any("source_freshness_state must be one of" in error for error in errors)) - def test_current_hoxline_snapshot_requires_self_reference_and_immediate_parent(self) -> None: + def test_generated_consumers_cannot_become_self_referential_authority(self) -> None: hostile = json.loads(json.dumps(self.index)) hoxline_revision = next( revision for revision in hostile["source_revisions"] if revision["repository"] == "hoxline" ) - hoxline_revision["self_referential"] = False + hoxline_revision["self_referential"] = True hoxline_revision["revision_scope"] = "authoritative_source_at_commit" - hoxline_revision["source_commit_sha"] = "a" * 40 hostile["reproducibility_sha256"] = _reproducibility_hash(hostile) errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) - self.assertTrue(any("self_referential must be true" in error for error in errors)) - self.assertTrue(any("revision_scope must be authoritative_sources_excluding_snapshot" in error for error in errors)) - self.assertTrue(any("must cite the immediate parent engine commit" in error for error in errors)) + self.assertTrue(any("generated consumers must not be self-referential authority" in error for error in errors)) + self.assertTrue(any("revision_scope must be content_addressed_authority" in error for error in errors)) def test_verify_rejects_drive_unc_and_posix_absolute_paths(self) -> None: for leaked_path in (r"D:\private\evidence.json", r"\\private-host\share\evidence.json", "/home/reviewer/evidence.json"): diff --git a/tests/test_multi_artifact_review_manifest_expansion_v1.py b/tests/test_multi_artifact_review_manifest_expansion_v1.py index c449c1d..1655fdb 100644 --- a/tests/test_multi_artifact_review_manifest_expansion_v1.py +++ b/tests/test_multi_artifact_review_manifest_expansion_v1.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import hashlib from pathlib import Path import subprocess import sys @@ -120,7 +121,7 @@ def test_batch_rejects_path_traversal_without_deleting_outside_file(tmp_path) -> assert main(["review", "batch", "run", "--index", str(hostile), "--output", str(output_dir), "--force"]) == 1 assert sentinel.read_text(encoding="utf-8") == "preserve" state = _json(output_dir / "batch-machine-state.json") - assert "invalid format" in state["block_reason"] + assert state["block_reason"] == "input rejected by path-containment boundary" def test_batch_rejects_index_manifest_artifact_id_mismatch(tmp_path) -> None: @@ -139,7 +140,7 @@ def test_boundary_contract_artifact_is_honestly_blocked(tmp_path) -> None: assert main(["review", "run", "--artifact", str(BLOCKED_MANIFEST), "--output", str(output_dir), "--force"]) == 1 state = _json(output_dir / "machine-state.json") assert state["final_status"] == "BLOCKED" - assert "boundary-contract scoped" in state["block_reason"] + assert state["block_reason"] == "boundary-contract artifact remains expected BLOCKED" assert state["public_safe_status"] == "NOT_PUBLIC_SAFE" assert state["human_review_required"] is True assert state["ai_disposition_authority"] is False @@ -305,3 +306,82 @@ def test_batch_generated_outputs_remain_ignored() -> None: ) assert result.returncode == 0 + + +def _semantic_digest(value: object) -> str: + return hashlib.sha256( + json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8") + ).hexdigest() + + +def test_batch_index_rejects_duplicate_keys_unknown_shapes_and_nested_authority(tmp_path) -> None: + original = INDEX.read_text(encoding="utf-8") + duplicate = original.replace( + '"ai_disposition_authority": false,', + '"ai_disposition_authority": false, "AI_Disposition_Authority": true,', + 1, + ) + duplicate_path = tmp_path / "duplicate-index.json" + duplicate_path.write_text(duplicate, encoding="utf-8") + duplicate_out = tmp_path / "duplicate" + assert main(["review", "batch", "run", "--index", str(duplicate_path), "--output", str(duplicate_out), "--force"]) == 1 + assert _json(duplicate_out / "batch-machine-state.json")["block_reason"] == "input rejected by strict-structure boundary" + + for name, mutator in { + "top-extension": lambda value: value.update({"metadata": {"public_safe_approved": True}}), + "artifact-extension": lambda value: value["artifacts"][0].update( + {"metadata": {"approvedByAnalyst": True}} + ), + "encoded-claim": lambda value: value.update( + {"next_gate": "final%2520authorization"} + ), + }.items(): + index = _json(INDEX) + mutator(index) + path = tmp_path / f"{name}.json" + path.write_text(json.dumps(index), encoding="utf-8") + output = tmp_path / name + assert main(["review", "batch", "run", "--index", str(path), "--output", str(output), "--force"]) == 1 + assert _json(output / "batch-machine-state.json")["final_status"] == "BLOCKED" + + +def test_batch_replay_rescans_tampered_input_even_after_hash_recalculation(tmp_path) -> None: + output = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(INDEX), "--output", str(output), "--force"]) == 0 + state_path = output / "batch-machine-state.json" + state = _json(state_path) + input_path = output / "input-index.json" + hostile = _json(input_path) + hostile["metadata"] = {"approvedByAnalyst": True} + input_path.write_text(json.dumps(hostile, sort_keys=True), encoding="utf-8") + state["input_index_sha256"] = hashlib.sha256(input_path.read_bytes()).hexdigest() + state["output_digests"]["input-index.json"] = state["input_index_sha256"] + state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(state), encoding="utf-8") + errors = verify_batch_run(state_path) + assert any("input-index replay failed closed" in error for error in errors) + + +def test_batch_replay_rejects_child_path_escape_and_aggregate_laundering(tmp_path) -> None: + output = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(INDEX), "--output", str(output), "--force"]) == 0 + state_path = output / "batch-machine-state.json" + baseline = _json(state_path) + for field, value in { + "machine_state": "../batch-machine-state.json", + "final_status": "BLOCKED", + "public_safe_status": "PUBLIC_SAFE", + "ai_disposition_authority": True, + "next_gate": "case closure", + }.items(): + hostile = json.loads(json.dumps(baseline)) + hostile["artifacts"][0][field] = value + hostile["batch_state_integrity_digest"] = _semantic_digest( + {key: item for key, item in hostile.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile), encoding="utf-8") + assert verify_batch_run(state_path), field + state_path.write_text(json.dumps(baseline), encoding="utf-8") + assert verify_batch_run(state_path) == [] diff --git a/tests/test_review_engine_v1.py b/tests/test_review_engine_v1.py index fa7350c..ab58bd3 100644 --- a/tests/test_review_engine_v1.py +++ b/tests/test_review_engine_v1.py @@ -1,6 +1,8 @@ from __future__ import annotations import json +import base64 +from copy import deepcopy from pathlib import Path import subprocess import sys @@ -75,7 +77,7 @@ def test_ho_det_010_event_and_rule_metadata_represented(tmp_path) -> None: telemetry = _json(output_dir / "telemetry-contract-check.json") manifest = _json(output_dir / "artifact-manifest.json") assert telemetry["required_source"] == "Windows Security EventChannel" - assert telemetry["event_ids"] == [4720, 4725, 4726, 4732, 4733, 4738] + assert telemetry["event_ids"] == [4732, 4733] assert telemetry["wazuh_rule_family"] == [910101, 910102, 910103] assert manifest["telemetry_contract"]["wazuh_rule_ids"] == [910101, 910102, 910103] @@ -164,7 +166,7 @@ def test_blocked_outputs_do_not_echo_private_or_raw_field_names(tmp_path) -> Non assert "raw_alert" not in combined assert "private_execution_id" not in combined assert "private_evidence\"" not in combined - assert "prohibited private/raw" in combined + assert "input rejected by strict-structure boundary" in combined def test_hostile_manifest_names_cover_required_block_classes() -> None: names = {path.name for path in HOSTILE_DIR.glob("*.json")} assert "missing-telemetry-contract.json" in names @@ -226,3 +228,105 @@ def test_local_generated_outputs_remain_ignored() -> None: ) assert result.returncode == 0 + + +def test_duplicate_and_unknown_manifest_fields_fail_closed(tmp_path) -> None: + text = MANIFEST.read_text(encoding="utf-8") + duplicate = text.replace( + '"ai_disposition_authority": false,', + '"ai_disposition_authority": false, "AI_Disposition_Authority": true,', + 1, + ) + duplicate_path = tmp_path / "duplicate.json" + duplicate_path.write_text(duplicate, encoding="utf-8") + duplicate_out = tmp_path / "duplicate-run" + assert main(["review", "run", "--artifact", str(duplicate_path), "--output", str(duplicate_out), "--force"]) == 1 + assert _json(duplicate_out / "machine-state.json")["final_status"] == "BLOCKED" + + unknown = _json(MANIFEST) + unknown["extension"] = {"harmless": True} + unknown_path = tmp_path / "unknown.json" + unknown_path.write_text(json.dumps(unknown), encoding="utf-8") + unknown_out = tmp_path / "unknown-run" + assert main(["review", "run", "--artifact", str(unknown_path), "--output", str(unknown_out), "--force"]) == 1 + assert _json(unknown_out / "machine-state.json")["block_reason"] == "input rejected by strict-structure boundary" + + +def test_nested_and_encoded_authority_laundering_fails_closed(tmp_path) -> None: + encoded_claims = [ + "public_safe_approved", + "analyst%2Dapproved%20disposition", + "final%2520authorization", + base64.urlsafe_b64encode(b"case closure").decode("ascii"), + '{"nested":{"ai_approved":true}}', + ] + for index, claim in enumerate(encoded_claims): + manifest = _json(MANIFEST) + manifest["field_mapping"] = {"nested": claim} + path = tmp_path / f"encoded-{index}.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / f"encoded-{index}" + assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + state = _json(output / "machine-state.json") + assert state["block_reason"] == "input rejected by claim-authority boundary" + combined = "\n".join(item.read_text(encoding="utf-8") for item in output.glob("*.json")) + assert claim not in combined + + manifest = _json(MANIFEST) + manifest["field_mapping"] = {"nested_authority": {"approvedByAnalyst": True}} + path = tmp_path / "nested-authority.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / "nested-authority" + assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + + +def test_encoded_mixed_and_drive_relative_paths_fail_closed(tmp_path) -> None: + attacks = [ + r"C:relative\fixture.json", + r"C:\private\fixture.json", + r"\\host\share\fixture.json", + "/tmp/fixture.json", + r"examples/review\../private.json", + "examples/review/%2e%2e/private.json", + "examples/review/%252e%252e/private.json", + "file:///C:/private/fixture.json", + ] + for index, attack in enumerate(attacks): + manifest = _json(MANIFEST) + manifest["fixture_paths"]["positive"] = attack + path = tmp_path / f"path-{index}.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / f"path-{index}" + assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + state = _json(output / "machine-state.json") + assert state["block_reason"] == "input rejected by path-containment boundary" + assert attack not in json.dumps(state) + + +def test_single_replay_binds_every_output_and_machine_state_field(tmp_path) -> None: + output = tmp_path / "review-run" + assert main(["review", "run", "--artifact", str(MANIFEST), "--output", str(output), "--force"]) == 0 + state_path = output / "machine-state.json" + baseline = _json(state_path) + + for field, value in { + "final_status": "BLOCKED", + "public_safe_status": "PUBLIC_SAFE", + "human_review_required": False, + "ai_disposition_authority": True, + "next_gate": "case closure", + "source_manifest_digest": "0" * 64, + }.items(): + hostile = deepcopy(baseline) + hostile[field] = value + state_path.write_text(json.dumps(hostile), encoding="utf-8") + assert verify_review_run(state_path), field + state_path.write_text(json.dumps(baseline), encoding="utf-8") + + for name in baseline["output_digests"]: + path = output / name + original = path.read_bytes() + path.write_bytes(original + b"\n") + assert any("digest mismatch" in error for error in verify_review_run(state_path)), name + path.write_bytes(original) + assert verify_review_run(state_path) == [] From 8652d863c34114927876f48d112ae7b0b44950c1 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 00:30:46 -0500 Subject: [PATCH 14/73] ci(hoxline): pin final platform repair head --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5867642..eb026e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: 775feb781d9ee7665c2ce623824de040e3341735 + ref: 9ee26b78e685f324b57c42a3b37836c225b29c63 path: org/hawkinsoperations-platform persist-credentials: false @@ -92,7 +92,7 @@ jobs: ".github": "fee75e7a88c0b623590786e02f69d4297a0e10b4", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "hawkinsoperations-platform": "775feb781d9ee7665c2ce623824de040e3341735", + "hawkinsoperations-platform": "9ee26b78e685f324b57c42a3b37836c225b29c63", "hawkinsoperations-proof": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", "hawkinsoperations-website": "3c8202b730836074e20c4fa1e3601741a02ee616", } From 500c47bbd14db671c95ab823aae9ba20ab56a783 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 00:36:29 -0500 Subject: [PATCH 15/73] chore(hoxline): refresh atomic case growth pair --- .../current-case-growth-index.json | 256 +++++++++++------- .../case-growth/current-case-growth-index.md | 52 ++-- 2 files changed, 182 insertions(+), 126 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index b761007..d0627b1 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T00:32:08Z", + "generated_at": "2026-07-23T05:30:55Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -9,19 +9,32 @@ "freshness": "CURRENT", "historical_snapshot": false, "current_authority": true, - "self_source_revision_semantics": "worktree-head-or-snapshot-commit-parent" + "identity_model": "repo_path_git_blob_and_semantic_fingerprint_with_separate_head_observation", + "generated_consumers_are_authority": false }, "source_revisions": [ { "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "b051aa3981e52a56b45a3078fcbf32ac34a99f92", + "source_commit_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + "source_observed_head_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + "current_observed_head_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", - "source_path": "scripts/verify-command-center-invariants.py", - "source_file_sha256": "e1ba441b72cfae9ca16f3b22f02003e77d3718c349acde736f7e8e17a7012bbc", + "revision_scope": "content_addressed_authority", + "source_path": "governance/COMMAND_CENTER_INVARIANTS.json", + "authoritative_path": "governance/COMMAND_CENTER_INVARIANTS.json", + "authoritative_git_blob_sha": "623e3f9e813b0599618a7df41dee1c9a40fb7a18", + "source_git_blob_sha": "623e3f9e813b0599618a7df41dee1c9a40fb7a18", + "source_file_sha256": "4f44687328dfaeea52270d230b513fb3195e3f3f0cecd2a32962455d875e56de", + "authoritative_content_fingerprint": "45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c", + "source_semantic_fingerprint_sha256": "45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c", + "canonical_origin": "github.com/hawkinsoperations/.github", + "observed_origin": "github.com/hawkinsoperations/.github", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -36,12 +49,24 @@ "repository": "hawkinsoperations-detections", "authority_role": "detection source truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", + "source_commit_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", + "source_observed_head_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", + "current_observed_head_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", + "revision_scope": "content_addressed_authority", "source_path": "detections/DETECTION_PROMOTION_MATRIX.yml", + "authoritative_path": "detections/DETECTION_PROMOTION_MATRIX.yml", + "authoritative_git_blob_sha": "e1d040134f139d2598c527c3584e1859e9c9553d", + "source_git_blob_sha": "e1d040134f139d2598c527c3584e1859e9c9553d", "source_file_sha256": "b73825c8655d7df62cac7737b08b3c95ec72b12c42bb6e3d265f7e74d0068e67", + "authoritative_content_fingerprint": "7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3", + "source_semantic_fingerprint_sha256": "7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3", + "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-detections", + "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-detections", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -56,12 +81,24 @@ "repository": "hawkinsoperations-validation", "authority_role": "controlled validation truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "d150cf172a4cb6065fd54c19c59beb9b581ba629", + "source_commit_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", + "source_observed_head_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", + "current_observed_head_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", + "revision_scope": "content_addressed_authority", "source_path": "validation/VALIDATION_REGISTRY.yml", - "source_file_sha256": "f907b8c46da583a851ece20b0cb6f10bdd8077d59e98491b80975cbbd4ede4aa", + "authoritative_path": "validation/VALIDATION_REGISTRY.yml", + "authoritative_git_blob_sha": "6fac3ac3d048c3ef687faaf5ebef1b04e846aad1", + "source_git_blob_sha": "6fac3ac3d048c3ef687faaf5ebef1b04e846aad1", + "source_file_sha256": "147291073c429c01bb7704c89025cf133aebebd7f735be14b95bccbe93e54878", + "authoritative_content_fingerprint": "de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3", + "source_semantic_fingerprint_sha256": "de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3", + "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-validation", + "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-validation", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -76,12 +113,24 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "d1ea495f2b010fbaefc2dccf22986526958ba424", + "source_commit_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", + "source_observed_head_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", + "current_observed_head_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", + "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", - "source_file_sha256": "7f25adcdef7f89f86bb27377de4ea86b57d7ad7f7653e56a0573e76e6cf6cd23", + "authoritative_path": "contracts/public-status-source-contract-v1.json", + "authoritative_git_blob_sha": "4a1f3401be43173582a18093efa622d0a40f9a98", + "source_git_blob_sha": "4a1f3401be43173582a18093efa622d0a40f9a98", + "source_file_sha256": "fb44489fe8b267b944191caeee89d6c262ff98e2f5bf1cbe2d14644f9026b087", + "authoritative_content_fingerprint": "98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b", + "source_semantic_fingerprint_sha256": "98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b", + "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", + "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -96,12 +145,24 @@ "repository": "hawkinsoperations-proof", "authority_role": "proof and claim-boundary truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "042a918ad4a8473cd5abcfd575072fc094639682", + "source_commit_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", + "source_observed_head_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", + "current_observed_head_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", + "revision_scope": "content_addressed_authority", "source_path": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "authoritative_path": "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml", + "authoritative_git_blob_sha": "623b93e6e5ac141684978ff4dcdc6ed1dec55678", + "source_git_blob_sha": "623b93e6e5ac141684978ff4dcdc6ed1dec55678", "source_file_sha256": "a8c24bd78bd20f318d4dfb5a930ea44bc108305bcc18b23db6ff2fc11e81a52d", + "authoritative_content_fingerprint": "68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170", + "source_semantic_fingerprint_sha256": "68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170", + "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-proof", + "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-proof", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -114,14 +175,26 @@ }, { "repository": "hawkinsoperations-website", - "authority_role": "rendering-only public status", + "authority_role": "rendering-only public status contract", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "ef7ad97279c06634e153be6e8ecee1959b9f7d78", + "source_commit_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", + "source_observed_head_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", + "current_observed_head_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", + "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, - "revision_scope": "authoritative_source_at_commit", - "source_path": "public/data/public-status.json", - "source_file_sha256": "f3f31e71520e42ce3bbbae1164c19496b20fe59e781f76bf597a1c602d2f1063", + "revision_scope": "content_addressed_authority", + "source_path": "schemas/public-status-v0.schema.json", + "authoritative_path": "schemas/public-status-v0.schema.json", + "authoritative_git_blob_sha": "75b3e5ba72b87e8980d62bc7e00014704f9031a0", + "source_git_blob_sha": "75b3e5ba72b87e8980d62bc7e00014704f9031a0", + "source_file_sha256": "7c776ec5e23235bf453b2b904a3e925dd675de0e7fc1d12320868c71cb9e0666", + "authoritative_content_fingerprint": "0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e", + "source_semantic_fingerprint_sha256": "0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e", + "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-website", + "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-website", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -136,12 +209,24 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "ab42606514011c3fe0ede9772d4a03b41e03e4e4", - "source_parent_sha": "4c96f52f28a3f0af767a94529cff165fc2042ca2", - "self_referential": true, - "revision_scope": "authoritative_sources_excluding_snapshot", + "source_commit_sha": "8652d863c34114927876f48d112ae7b0b44950c1", + "source_observed_head_sha": "8652d863c34114927876f48d112ae7b0b44950c1", + "current_observed_head_sha": "8652d863c34114927876f48d112ae7b0b44950c1", + "source_observation_kind": "reviewed_immutable_commit", + "source_parent_sha": null, + "self_referential": false, + "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", - "source_file_sha256": "da1f3bcdeb763f988dd5d80011bec1c3b965a7fe9e9424265dc31191e29ef8dc", + "authoritative_path": "src/hoxline/case_growth/collector.py", + "authoritative_git_blob_sha": "c302284aeb690fffe86f8c4aac49ab014c9cfd88", + "source_git_blob_sha": "c302284aeb690fffe86f8c4aac49ab014c9cfd88", + "source_file_sha256": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", + "authoritative_content_fingerprint": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", + "source_semantic_fingerprint_sha256": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", + "canonical_origin": "github.com/hawkinsoperations/hoxline", + "observed_origin": "github.com/hawkinsoperations/hoxline", + "repository_dirty_observed": false, + "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", "historical_snapshot": false, @@ -153,6 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], + "source_manifest_digest": "d9f2fb28f34e354d04ff71ecaa036c492a105fcf81eb2c0b4bd8ce62914707e3", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -164,7 +250,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "org metadata and reviewer routing only; not proof authority", - "files_scanned": 34 + "files_scanned": 37 }, { "repo": "hawkinsoperations-detections", @@ -173,7 +259,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "source package and source status authority only", - "files_scanned": 92 + "files_scanned": 93 }, { "repo": "hawkinsoperations-validation", @@ -182,7 +268,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "controlled validation authority only", - "files_scanned": 198 + "files_scanned": 201 }, { "repo": "hawkinsoperations-platform", @@ -191,7 +277,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "platform runtime-candidate, collector, receipt, and ledger contract authority only", - "files_scanned": 119 + "files_scanned": 121 }, { "repo": "hawkinsoperations-proof", @@ -200,7 +286,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "proof ceiling, proof record, ProofCard, public-safe, blocked-claim, and next-gate authority", - "files_scanned": 126 + "files_scanned": 128 }, { "repo": "hawkinsoperations-website", @@ -209,7 +295,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", - "files_scanned": 270 + "files_scanned": 271 }, { "repo": "hoxline", @@ -229,10 +315,10 @@ "hawkinsoperations_github_sibling_exists": false, "wording": "seven expected repo slots evaluated; seven present local repos scanned" }, - "source_files_scanned_count": 1020, - "case_ids_discovered_count": 27, + "source_files_scanned_count": 3, + "case_ids_discovered_count": 26, "summary": { - "cases_total": 27, + "cases_total": 26, "source_packages_count": 14, "controlled_validations_count": 12, "runtime_candidate_lanes_count": 5, @@ -246,24 +332,24 @@ "closed_cases_count": 0, "blocked_claims_count": 295, "cases_with_next_gate_count": 26, - "cases_missing_proof_record_count": 16, - "cases_missing_proofcard_count": 15, - "cases_not_public_safe_count": 27, - "unknown_state_count": 1 + "cases_missing_proof_record_count": 15, + "cases_missing_proofcard_count": 14, + "cases_not_public_safe_count": 26, + "unknown_state_count": 0 }, "case_growth_health": { "validation_coverage_percent": 85.71, - "proof_record_coverage_percent": 40.74, - "proofcard_coverage_percent": 44.44, - "scheduled_collector_coverage_percent": 14.81, - "runtime_candidate_coverage_percent": 18.52, - "metrics_coverage_percent": 3.7, + "proof_record_coverage_percent": 42.31, + "proofcard_coverage_percent": 46.15, + "scheduled_collector_coverage_percent": 15.38, + "runtime_candidate_coverage_percent": 19.23, + "metrics_coverage_percent": 3.85, "public_safe_percent": 0.0, "closed_case_percent": 0.0, - "blocked_claim_density": 10.93, - "next_gate_coverage_percent": 96.3, - "missing_proof_record_percent": 59.26, - "missing_proofcard_percent": 55.56, + "blocked_claim_density": 11.35, + "next_gate_coverage_percent": 100.0, + "missing_proof_record_percent": 57.69, + "missing_proofcard_percent": 53.85, "not_public_safe_percent": 100.0, "overall_health_status": "PUBLIC_SAFE_BLOCKED", "strongest_lane": "controlled_validation", @@ -332,12 +418,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "separate proof scope before any live-cloud or public route approval", "evidence_confidence": "HIGH", "notes": [ "detections: Fixture validation is external validation truth; this matrix records source package enforcement only.", - "validation: Controlled CloudTrail-style fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove live AWS, IdP, SIEM, runtime, signal, or public-safe status.", + "validation: Controlled CloudTrail-style fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation. This does not prove live AWS, IdP, SIEM, runtime, signal, or public-safe status.", "proof: Existing proof record supports controlled fixture validation only. It does not prove live AWS runtime or signal status.", "website route/rendering mention observed at hawkinsoperations-website/components/FeaturedWork.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/src/data/artifactFamilies.ts; not treated as proof", @@ -413,12 +499,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:42:44-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ "detections: Matrix records source package enforcement only; controlled-test validation belongs to validation and proof ceilings belong to proof.", - "validation: Controlled process-creation fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout.", + "validation: Controlled process-creation fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation.", "proof: Existing proof record supports CONTROLLED_TEST_VALIDATED. Private runtime wording is boundary context only and does not create public runtime proof.", "website route/rendering mention observed at hawkinsoperations-website/PENDING_WEBSITE-LANDING-001.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/README.md; not treated as proof", @@ -847,12 +933,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-23T00:30:10-05:00", "next_gate": "separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording", "evidence_confidence": "HIGH", "notes": [ "detections: Matrix records source package enforcement only; controlled-test validation belongs to validation and no runtime, signal, or public-safe claim is made here.", - "validation: Controlled Windows local user creation fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or account lifecycle completeness.", + "validation: Controlled Windows local user creation fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or account lifecycle completeness.", "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform scheduled-collector context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof" ] @@ -927,12 +1013,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-23T00:30:10-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ "detections: Source package exists for Windows local Administrators group membership changes. Runtime, signal, public-safe, production, and disposition claims remain blocked.", - "validation: Controlled Windows local Administrators group membership fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", + "validation: Controlled Windows local Administrators group membership fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform private-runtime candidate context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof" ] @@ -1003,12 +1089,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-23T00:30:10-05:00", "next_gate": "proof-card or public reviewer route only after separate human-approved proof scope", "evidence_confidence": "HIGH", "notes": [ "detections: Source package remains NOT_PUBLIC_SAFE; private runtime material is not promoted by this source matrix.", - "validation: Controlled Windows service-creation fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", + "validation: Controlled Windows service-creation fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation. This does not prove runtime-active, signal-observed, production-ready, or public-safe status.", "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Private runtime evidence capture remains non-public and this card does not create public runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/app/detections/page.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof", @@ -1093,12 +1179,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-23T00:30:10-05:00", "next_gate": "blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion", "evidence_confidence": "HIGH", "notes": [ "detections: Controlled-test validation is external validation truth. The proof repo records a CONTROLLED_TEST_VALIDATED proof record for HO-DET-012; this source matrix does not promote runtime, signal, or public-safe claims.", - "validation: Controlled service-account misuse fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout.", + "validation: Controlled service-account misuse fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation.", "proof: Existing proof record supports CONTROLLED_TEST_VALIDATED for controlled scheduled-task validation only. It does not promote runtime, signal, public-safe runtime, production, autonomous SOC, AI-approved, or analyst-approved status.", "website route/rendering mention observed at hawkinsoperations-website/app/detections/page.tsx; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/app/hoxline/page.tsx; not treated as proof", @@ -1185,12 +1271,12 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ "detections: Controlled-test validation is external validation truth; this source matrix does not promote runtime, signal, proof, or public-safe claims.", - "validation: Controlled telemetry/security-control tamper fixture validation only. Sibling source contract is checked when present and skipped only when the entire sibling source surface is absent from a single-repo CI checkout. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or control coverage completeness.", + "validation: Controlled telemetry/security-control tamper fixture validation only. The canonical detection source handoff is required and content-addressed; missing source blocks validation. This does not prove runtime-active, signal-observed, production-ready, public-safe status, or control coverage completeness.", "proof: ProofCard backfill from an existing proof record and repo-visible evidence only. Platform private-runtime candidate context remains non-promotional and this card does not create runtime, signal, public_safe, customer, production, approval, or closure claims.", "website route/rendering mention observed at hawkinsoperations-website/docs/design/WEBSITE_PR68_ORG_SYSTEM_REVIEW.md; not treated as proof", "website route/rendering mention observed at hawkinsoperations-website/docs/planning/WEBSITE_CLAUDE_DESIGN_BRIEF.md; not treated as proof", @@ -1328,36 +1414,6 @@ "detections: Planned index row only; no package path is claimed to exist." ] }, - { - "case_id": "HO-DET-999", - "detection_id": "HO-DET-999", - "case_kind": "detection", - "source_status": "NOT_FOUND", - "source_evidence_refs": [], - "validation_status": "NOT_FOUND", - "validation_evidence_refs": [], - "runtime_candidate_status": "NOT_INDEXED", - "runtime_evidence_refs": [], - "scheduled_collector_status": "NOT_INDEXED", - "scheduled_collector_evidence_refs": [], - "signal_status": "NOT_PROVEN", - "signal_evidence_refs": [], - "proof_record_status": "NOT_PROVEN", - "proof_record_path": null, - "proofcard_status": "NOT_PROVEN", - "proofcard_path": null, - "claim_authority_status": "NOT_INDEXED", - "blocked_claim_count": 0, - "blocked_claims": [], - "public_safe_status": "NOT_PUBLIC_SAFE", - "case_state": "UNKNOWN_WITH_REASON", - "metrics_available": false, - "metrics_refs": [], - "last_updated": "UNKNOWN_WITH_REASON: no git history available", - "next_gate": "UNKNOWN_WITH_REASON: no next gate indexed", - "evidence_confidence": "LOW", - "notes": [] - }, { "case_id": "HO-NDR-001", "detection_id": "HO-NDR-001", @@ -1408,7 +1464,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "captured cross-source corroboration evidence under separate proof scope", "evidence_confidence": "MEDIUM", "notes": [ @@ -1530,7 +1586,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval", "evidence_confidence": "MEDIUM", "notes": [ @@ -1584,7 +1640,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "preserve hero baseline while successor HO-DET-001 remains the current reviewed source package", "evidence_confidence": "MEDIUM", "notes": [ @@ -1695,7 +1751,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1779,7 +1835,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1858,7 +1914,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1937,7 +1993,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:34-05:00", + "last_updated": "2026-07-22T23:40:46-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1968,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "2f21fa37b84454e2dc47cf2dddac630e62d85cd48a164ef206c4ef11cbb2c7ed" + "reproducibility_sha256": "6214875f0295c1d5899efe5e1048dd6ec5c9b4bda1bb5d47ad7e6a5e75785d5e" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 3fbd38e..147e01b 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,17 +1,18 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T00:32:37Z` +Generated: `2026-07-23T05:30:55Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Reproducibility SHA-256: `d2b500c8db243e40366512e14393ce0e0f2cf4ff3ee9304b3c0b2f5938d5cdac` +Source manifest digest: `d9f2fb28f34e354d04ff71ecaa036c492a105fcf81eb2c0b4bd8ce62914707e3` +Reproducibility SHA-256: `6214875f0295c1d5899efe5e1048dd6ec5c9b4bda1bb5d47ad7e6a5e75785d5e` ## Summary | Metric | Count | | --- | ---: | -| `cases_total` | 27 | +| `cases_total` | 26 | | `source_packages_count` | 14 | | `controlled_validations_count` | 12 | | `runtime_candidate_lanes_count` | 5 | @@ -25,22 +26,22 @@ Reproducibility SHA-256: `d2b500c8db243e40366512e14393ce0e0f2cf4ff3ee9304b3c0b2f | `closed_cases_count` | 0 | | `blocked_claims_count` | 295 | | `cases_with_next_gate_count` | 26 | -| `cases_missing_proof_record_count` | 16 | -| `cases_missing_proofcard_count` | 15 | -| `cases_not_public_safe_count` | 27 | -| `unknown_state_count` | 1 | +| `cases_missing_proof_record_count` | 15 | +| `cases_missing_proofcard_count` | 14 | +| `cases_not_public_safe_count` | 26 | +| `unknown_state_count` | 0 | ## Source Revisions -| Repository | Authority role | Source path | Source revision | Source freshness | Snapshot freshness | -| --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `scripts/verify-command-center-invariants.py` | `b051aa3981e52a56b45a3078fcbf32ac34a99f92` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `d150cf172a4cb6065fd54c19c59beb9b581ba629` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `d1ea495f2b010fbaefc2dccf22986526958ba424` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `CURRENT` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status` | `public/data/public-status.json` | `ef7ad97279c06634e153be6e8ecee1959b9f7d78` | `CURRENT` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `ab42606514011c3fe0ede9772d4a03b41e03e4e4` | `CURRENT_SELF_REFERENTIAL` | `CURRENT` | +| Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | +| --- | --- | --- | --- | --- | --- | --- | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fee75e7a88c0b623590786e02f69d4297a0e10b4` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | +| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `9ee26b78e685f324b57c42a3b37836c225b29c63` | `4a1f3401be43173582a18093efa622d0a40f9a98` | `98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b` | `CURRENT` | +| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `8ff2c1a2f1762e7635fe24194617f110275c2f0c` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `3c8202b730836074e20c4fa1e3601741a02ee616` | `75b3e5ba72b87e8980d62bc7e00014704f9031a0` | `0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `8652d863c34114927876f48d112ae7b0b44950c1` | `c302284aeb690fffe86f8c4aac49ab014c9cfd88` | `b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f` | `CURRENT` | ## Convergence Findings @@ -53,17 +54,17 @@ Next legal action: none; current source-controlled inputs converge | Health metric | Value | | --- | ---: | | `validation_coverage_percent` | 85.71 | -| `proof_record_coverage_percent` | 40.74 | -| `proofcard_coverage_percent` | 44.44 | -| `scheduled_collector_coverage_percent` | 14.81 | -| `runtime_candidate_coverage_percent` | 18.52 | -| `metrics_coverage_percent` | 3.7 | +| `proof_record_coverage_percent` | 42.31 | +| `proofcard_coverage_percent` | 46.15 | +| `scheduled_collector_coverage_percent` | 15.38 | +| `runtime_candidate_coverage_percent` | 19.23 | +| `metrics_coverage_percent` | 3.85 | | `public_safe_percent` | 0.0 | | `closed_case_percent` | 0.0 | -| `blocked_claim_density` | 10.93 | -| `next_gate_coverage_percent` | 96.3 | -| `missing_proof_record_percent` | 59.26 | -| `missing_proofcard_percent` | 55.56 | +| `blocked_claim_density` | 11.35 | +| `next_gate_coverage_percent` | 100.0 | +| `missing_proof_record_percent` | 57.69 | +| `missing_proofcard_percent` | 53.85 | | `not_public_safe_percent` | 100.0 | | Assessment | Value | @@ -103,7 +104,6 @@ The health section is derived from numeric index counts only. It does not promot | `HO-DET-014` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-015` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-DET-016` | `VALIDATION_PLANNED` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | -| `HO-DET-999` | `NOT_FOUND` | `NOT_FOUND` | `NOT_INDEXED` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `UNKNOWN_WITH_REASON` | `UNKNOWN_WITH_REASON: no next gate indexed` | | `HO-NDR-001` | `EXTERNAL_BOUNDARY_CONTRACT` | `VALIDATION_CONTRACT_ENFORCED` | `NOT_PROVEN` | `NOT_INDEXED` | `NOT_PROVEN` | `PROOFCARD_EXISTS` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `captured cross-source corroboration evidence under separate proof scope` | | `HO-NDR-002` | `VALIDATION_PLANNED` | `NOT_FOUND` | `LISTED_ONLY` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `create source package under separate source-authoring approval` | | `HO-PIPE-001` | `SOURCE_EXISTS` | `VALIDATION_CONTRACT_ENFORCED` | `TELEMETRY_CONTRACT_ONLY` | `NOT_INDEXED` | `NOT_PROVEN` | `NOT_PROVEN` | false | `NOT_PUBLIC_SAFE` | `BLOCKED_WAITING_NEXT_GATE` | `proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval` | From 8ddb8eec8102cac6ef5af2e55632354ca0b8e06d Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 00:41:06 -0500 Subject: [PATCH 16/73] chore(hoxline): converge atomic reviewer snapshot --- .../current-case-growth-index.json | 28 +++++++++---------- .../case-growth/current-case-growth-index.md | 10 +++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index d0627b1..8db8ac9 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T05:30:55Z", + "generated_at": "2026-07-23T05:39:22Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -113,20 +113,20 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", - "source_observed_head_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", - "current_observed_head_sha": "9ee26b78e685f324b57c42a3b37836c225b29c63", + "source_commit_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", + "source_observed_head_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", + "current_observed_head_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", "authoritative_path": "contracts/public-status-source-contract-v1.json", - "authoritative_git_blob_sha": "4a1f3401be43173582a18093efa622d0a40f9a98", - "source_git_blob_sha": "4a1f3401be43173582a18093efa622d0a40f9a98", - "source_file_sha256": "fb44489fe8b267b944191caeee89d6c262ff98e2f5bf1cbe2d14644f9026b087", - "authoritative_content_fingerprint": "98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b", - "source_semantic_fingerprint_sha256": "98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b", + "authoritative_git_blob_sha": "b3be8100e2cf1479a2445d01e84236135d3d96a3", + "source_git_blob_sha": "b3be8100e2cf1479a2445d01e84236135d3d96a3", + "source_file_sha256": "504d0b47c7e85066ba9b7a66d5d5f8326679b3254844cfe7efe263459d9dd1c3", + "authoritative_content_fingerprint": "4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf", + "source_semantic_fingerprint_sha256": "4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "repository_dirty_observed": false, @@ -209,9 +209,9 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "8652d863c34114927876f48d112ae7b0b44950c1", - "source_observed_head_sha": "8652d863c34114927876f48d112ae7b0b44950c1", - "current_observed_head_sha": "8652d863c34114927876f48d112ae7b0b44950c1", + "source_commit_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", + "source_observed_head_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", + "current_observed_head_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "d9f2fb28f34e354d04ff71ecaa036c492a105fcf81eb2c0b4bd8ce62914707e3", + "source_manifest_digest": "3c0ab0a1b9a00da2df96ccc733c81d7bb82f34a36fd7a56353e51203c1c89010", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "6214875f0295c1d5899efe5e1048dd6ec5c9b4bda1bb5d47ad7e6a5e75785d5e" + "reproducibility_sha256": "e031a0a37dd3c50019f1f1daec6062af323e5ff0a1503557fb7465aeca3ec705" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 147e01b..888a789 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T05:30:55Z` +Generated: `2026-07-23T05:39:22Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `d9f2fb28f34e354d04ff71ecaa036c492a105fcf81eb2c0b4bd8ce62914707e3` -Reproducibility SHA-256: `6214875f0295c1d5899efe5e1048dd6ec5c9b4bda1bb5d47ad7e6a5e75785d5e` +Source manifest digest: `3c0ab0a1b9a00da2df96ccc733c81d7bb82f34a36fd7a56353e51203c1c89010` +Reproducibility SHA-256: `e031a0a37dd3c50019f1f1daec6062af323e5ff0a1503557fb7465aeca3ec705` ## Summary @@ -38,10 +38,10 @@ Reproducibility SHA-256: `6214875f0295c1d5899efe5e1048dd6ec5c9b4bda1bb5d47ad7e6a | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fee75e7a88c0b623590786e02f69d4297a0e10b4` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `9ee26b78e685f324b57c42a3b37836c225b29c63` | `4a1f3401be43173582a18093efa622d0a40f9a98` | `98a4b18b63a6e2415dd8c2d15bc283287c2f0d7385a28b875739623448dd912b` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `dce32809fcee7faef3f92da7139b2781bb9c4330` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `8ff2c1a2f1762e7635fe24194617f110275c2f0c` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `3c8202b730836074e20c4fa1e3601741a02ee616` | `75b3e5ba72b87e8980d62bc7e00014704f9031a0` | `0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `8652d863c34114927876f48d112ae7b0b44950c1` | `c302284aeb690fffe86f8c4aac49ab014c9cfd88` | `b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `500c47bbd14db671c95ab823aae9ba20ab56a783` | `c302284aeb690fffe86f8c4aac49ab014c9cfd88` | `b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f` | `CURRENT` | ## Convergence Findings From 5cf99d39a6a85f0c3882ad4f6fc635ba7d908625 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 01:09:55 -0500 Subject: [PATCH 17/73] ci(hoxline): pin final seven-source review heads --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb026e3..79fe0e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: fee75e7a88c0b623590786e02f69d4297a0e10b4 + ref: c8f7fe023aa8eb9af385a8bb222133b9997fe94e path: org/.github persist-credentials: false @@ -50,7 +50,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: 9ee26b78e685f324b57c42a3b37836c225b29c63 + ref: dce32809fcee7faef3f92da7139b2781bb9c4330 path: org/hawkinsoperations-platform persist-credentials: false @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-proof - ref: 8ff2c1a2f1762e7635fe24194617f110275c2f0c + ref: c60882552571c1664549d3e594c6994dc27dce63 path: org/hawkinsoperations-proof persist-credentials: false @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 3c8202b730836074e20c4fa1e3601741a02ee616 + ref: 9f4c03b84f2c03a305cd1e974ae405cd05218923 path: org/hawkinsoperations-website persist-credentials: false @@ -89,12 +89,12 @@ jobs: "hoxline", ] immutable = { - ".github": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + ".github": "c8f7fe023aa8eb9af385a8bb222133b9997fe94e", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "hawkinsoperations-platform": "9ee26b78e685f324b57c42a3b37836c225b29c63", - "hawkinsoperations-proof": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", - "hawkinsoperations-website": "3c8202b730836074e20c4fa1e3601741a02ee616", + "hawkinsoperations-platform": "dce32809fcee7faef3f92da7139b2781bb9c4330", + "hawkinsoperations-proof": "c60882552571c1664549d3e594c6994dc27dce63", + "hawkinsoperations-website": "9f4c03b84f2c03a305cd1e974ae405cd05218923", } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) if actual != sorted(expected): From 72de2b307d7968d74f8e7c71a6ef214ef777ad1b Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:08:35 -0500 Subject: [PATCH 18/73] fix(hoxline): bind currentness to reviewed source trees --- src/hoxline/case_growth/collector.py | 220 ++++++++++++++++++++++++++- src/hoxline/review_engine.py | 6 +- tests/test_case_growth_index_v0.py | 142 +++++++++++++++++ 3 files changed, 365 insertions(+), 3 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index c302284..32edd66 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -6,6 +6,7 @@ import json from pathlib import Path import re +import subprocess from typing import Any from .discovery import ( @@ -45,6 +46,9 @@ for repository in REPO_NAMES } +CONVERGENCE_SOURCE_MANIFEST = Path(".github/governance/CONVERGENCE_SOURCE_MANIFEST.json") +CONVERGENCE_SOURCE_MANIFEST_SCHEMA = "hawkinsoperations-convergence-source-manifest-v1" + BOUNDARY = { "runtime_public_proof_claimed": False, "signal_public_proof_claimed": False, @@ -201,6 +205,200 @@ def _normalized_origin(value: str) -> str: return origin.removesuffix(".git").rstrip("/").casefold() +def _git_output(repo: Path, *args: str) -> str | None: + try: + result = subprocess.run( + ["git", "-C", str(repo), *args], + check=False, + capture_output=True, + text=True, + ) + except OSError: + return None + return result.stdout.strip() if result.returncode == 0 else None + + +def _is_ancestor(repo: Path, ancestor: str, descendant: str) -> bool: + try: + result = subprocess.run( + ["git", "-C", str(repo), "merge-base", "--is-ancestor", ancestor, descendant], + check=False, + capture_output=True, + text=True, + ) + except OSError: + return False + return result.returncode == 0 + + +def _load_convergence_source_selections(repo_root: Path) -> tuple[dict[str, dict[str, Any]], list[str]]: + root = Path(repo_root).resolve() + manifest_path = root / CONVERGENCE_SOURCE_MANIFEST + if not manifest_path.is_file(): + return {}, [f"missing explicit seven-source selection manifest: {CONVERGENCE_SOURCE_MANIFEST.as_posix()}"] + command_center = root / ".github" + manifest_relative = "governance/CONVERGENCE_SOURCE_MANIFEST.json" + if _normalized_origin(repo_origin(command_center)) != CANONICAL_ORIGINS[".github"]: + return {}, ["seven-source selection manifest owner origin is not canonical"] + tracked_path = _git_output(command_center, "ls-files", "--error-unmatch", "--", manifest_relative) + committed_blob = _git_output(command_center, "rev-parse", f"HEAD:{manifest_relative}") + worktree_blob = _git_output(command_center, "hash-object", "--", manifest_relative) + if tracked_path != manifest_relative or committed_blob is None or worktree_blob != committed_blob: + return {}, ["seven-source selection manifest must be tracked and clean at the checked command-center head"] + try: + manifest = load_structured(manifest_path) + except (OSError, ValueError, json.JSONDecodeError) as exc: + return {}, [f"seven-source selection manifest is not strict valid JSON: {exc}"] + if not isinstance(manifest, dict): + return {}, ["seven-source selection manifest must be an object"] + errors: list[str] = [] + unknown_top_level = sorted(set(manifest) - {"schema", "manifest_id", "repositories", "constraints"}) + if unknown_top_level: + errors.append(f"seven-source selection manifest contains unsupported fields: {unknown_top_level}") + if manifest.get("schema") != CONVERGENCE_SOURCE_MANIFEST_SCHEMA: + errors.append(f"seven-source selection manifest schema must be {CONVERGENCE_SOURCE_MANIFEST_SCHEMA}") + entries = manifest.get("repositories") + if not isinstance(entries, list): + return {}, [*errors, "seven-source selection manifest repositories must be a list"] + selections: dict[str, dict[str, Any]] = {} + for entry in entries: + if not isinstance(entry, dict): + errors.append("seven-source selection manifest entries must be objects") + continue + repository = entry.get("repository") + if not isinstance(repository, str) or repository not in REPO_NAMES: + errors.append(f"seven-source selection manifest has unknown repository {repository!r}") + continue + if repository in selections: + errors.append(f"seven-source selection manifest duplicates repository {repository}") + continue + expected_canonical = f"HawkinsOperations/{repository}" + if entry.get("canonical_repository") != expected_canonical: + errors.append(f"{repository}: selected canonical repository must be {expected_canonical}") + if repository == ".github": + unknown = sorted( + set(entry) + - {"repository", "canonical_repository", "revision_source", "tree_source"} + ) + if unknown: + errors.append(f".github: selection contains unsupported fields: {unknown}") + if entry.get("revision_source") != "github_event_sha" or entry.get("tree_source") != "github_event_tree": + errors.append(".github: dynamic command-center selection must use event SHA and event tree") + else: + unknown = sorted( + set(entry) + - {"repository", "canonical_repository", "revision", "reviewed_tree_sha"} + ) + if unknown: + errors.append(f"{repository}: selection contains unsupported fields: {unknown}") + revision = entry.get("revision") + tree = entry.get("reviewed_tree_sha") + if not isinstance(revision, str) or re.fullmatch(r"[0-9a-f]{40}", revision) is None: + errors.append(f"{repository}: selected revision must be an immutable 40-character SHA") + if not isinstance(tree, str) or re.fullmatch(r"[0-9a-f]{40}", tree) is None: + errors.append(f"{repository}: reviewed tree must be a 40-character Git tree SHA") + selections[repository] = entry + missing = sorted(set(REPO_NAMES) - set(selections)) + if missing or len(selections) != len(REPO_NAMES): + errors.append(f"seven-source selection manifest must name exactly seven repositories; missing={missing}") + constraints = manifest.get("constraints") + if not isinstance(constraints, dict): + errors.append("seven-source selection manifest constraints must be an object") + else: + expected_constraint_keys = { + "exact_repository_count", + "read_only", + "default_branch_fallback", + "require_detached_exact_revision", + "record_checked_revisions", + "consumer_outputs_are_not_authority", + "proof_ceiling", + } + unknown = sorted(set(constraints) - expected_constraint_keys) + missing_constraints = sorted(expected_constraint_keys - set(constraints)) + if unknown or missing_constraints: + errors.append( + "seven-source selection manifest constraints must have exact fields; " + f"missing={missing_constraints}, unsupported={unknown}" + ) + for key, expected in { + "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, + }.items(): + if constraints.get(key) != expected: + errors.append(f"seven-source selection manifest constraint {key} must be {expected!r}") + if constraints.get("proof_ceiling") != "CONTROLLED_REPO_CONVERGENCE_AND_LOCAL_FIXTURE_REVIEW_ONLY": + errors.append("seven-source selection manifest proof ceiling is unsupported") + return selections, errors + + +def _verify_selected_source_checkout( + root: Path, + repository: str, + selections: dict[str, dict[str, Any]], +) -> list[str]: + errors: list[str] = [] + if repository not in REPO_NAMES: + return [f"unknown source repository {repository!r}"] + repo = root / repository + if not repo.is_dir(): + return [f"{repository}: selected source repository is missing"] + head = repo_head_sha(repo) + tree = _git_output(repo, "rev-parse", "HEAD^{tree}") + if head == "UNKNOWN" or tree is None: + return [f"{repository}: checked source head/tree is unavailable"] + entry = selections[repository] + if repository == ".github": + return [] + selected = str(entry["revision"]) + reviewed_tree = str(entry["reviewed_tree_sha"]) + selected_tree = _git_output(repo, "rev-parse", f"{selected}^{{tree}}") + if selected_tree is not None and selected_tree != reviewed_tree: + errors.append(f"{repository}: manifest reviewed tree disagrees with its selected revision") + selected_exists = git_commit_exists(repo, selected) + checked_head_is_behind = head != selected and selected_exists and _is_ancestor(repo, head, selected) + selected_is_ancestor_of_head = head != selected and selected_exists and _is_ancestor(repo, selected, head) + if checked_head_is_behind: + errors.append( + f"{repository}: checked head is behind the explicit selected revision; " + "an arbitrary same-blob ancestor is not current authority" + ) + rewritten_content_identity = ( + head != selected + and not checked_head_is_behind + and not selected_is_ancestor_of_head + ) + if rewritten_content_identity and tree != reviewed_tree: + errors.append( + f"{repository}: checked tree does not match the explicit reviewed tree; " + "refresh the immutable source selection after content changes" + ) + return errors + + +def verify_selected_source_checkout(repo_root: Path, repository: str) -> list[str]: + """Prove a checked source is the manifest-selected content, not an arbitrary same-blob ancestor.""" + root = Path(repo_root).resolve() + selections, errors = _load_convergence_source_selections(root) + if errors: + return errors + return _verify_selected_source_checkout(root, repository, selections) + + +def verify_all_selected_source_checkouts(repo_root: Path) -> list[str]: + root = Path(repo_root).resolve() + selections, errors = _load_convergence_source_selections(root) + if errors: + return errors + for repository in REPO_NAMES: + errors.extend(_verify_selected_source_checkout(root, repository, selections)) + return errors + + def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str, Any]]: revisions: list[dict[str, Any]] = [] for repository in REPO_NAMES: @@ -454,6 +652,17 @@ def _reproducibility_hash(index: dict[str, Any]) -> str: return hashlib.sha256(encoded).hexdigest() +def _content_normalized_cases(value: Any) -> Any: + """Remove only commit-clock observations after source currentness is verified separately.""" + if not isinstance(value, list): + return value + normalized = deepcopy(value) + for row in normalized: + if isinstance(row, dict): + row.pop("last_updated", None) + return normalized + + def _source_manifest_digest(source_revisions: list[dict[str, Any]]) -> str: manifest = [ { @@ -1049,6 +1258,10 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu errors.append(f"source_revisions must match exact seven-repository set; missing={missing}, extra={extra}") current_by_repo = {item["repository"]: item for item in current["source_revisions"]} repo_paths = resolve_repo_paths(Path(repo_root)) + selection_errors: list[str] = [] + if current_authority: + selection_errors = verify_all_selected_source_checkouts(Path(repo_root)) + errors.extend(selection_errors) for stated in stated_revisions: if not isinstance(stated, dict): errors.append("source_revisions entries must be objects") @@ -1158,7 +1371,12 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu if snapshot.get("case_ids_discovered_count") != current.get("case_ids_discovered_count"): errors.append("current snapshot case count disagrees with current authoritative repository state") for field in ("cases", "case_growth_health", "repo_slot_accuracy", "boundary"): - if snapshot.get(field) != current.get(field): + before = snapshot.get(field) + after = current.get(field) + if field == "cases" and not selection_errors: + before = _content_normalized_cases(before) + after = _content_normalized_cases(after) + if before != after: errors.append(f"current snapshot {field} disagrees with normalized current authoritative content") for finding in current.get("contradictions", []): errors.append(f"current contradiction {finding['code']}: {finding['actual']} ({finding['next_legal_action']})") diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 231c687..5294c2d 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -15,6 +15,7 @@ import yaml +from .case_growth.collector import verify_selected_source_checkout from .demo import ( BLOCKED_CLAIM_FAMILIES, PRODUCT, @@ -578,8 +579,9 @@ def _owned_authority_binding(manifest: dict[str, Any], repo_root: Path) -> dict[ origin = _canonical_origin(_git(repo, "remote", "get-url", "origin")) if origin != _canonical_origin(CANONICAL_ORIGINS[repo_name]): raise ReviewBlocked("authority repository origin is not canonical") - if _git(repo, "rev-parse", "--abbrev-ref", "HEAD") == "HEAD": - raise ReviewBlocked("detached authority repository is not accepted") + selection_errors = verify_selected_source_checkout(org_root, repo_name) + if selection_errors: + raise ReviewBlocked(f"authority repository selection is invalid: {selection_errors[0]}") cache_key = ( artifact_id, str(detection_repo), diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index a726b2f..0dd2c16 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -4,6 +4,7 @@ import io import json import shutil +import subprocess import sys import tempfile import unittest @@ -17,9 +18,11 @@ from hoxline.case_growth.collector import ( BOUNDARY, ROW_FIELDS, + _content_normalized_cases, _reproducibility_hash, build_case_growth_index, diff_case_growth_snapshot, + verify_selected_source_checkout, verify_case_growth_snapshot, ) from hoxline.case_growth.discovery import REPO_NAMES @@ -33,6 +36,68 @@ SCHEMA = ROOT / "schemas" / "case-growth-index-v0.schema.json" +def _git(repo: Path, *args: str, input_text: str | None = None) -> str: + result = subprocess.run( + ["git", "-C", str(repo), *args], + input=input_text, + text=True, + capture_output=True, + check=True, + ) + return result.stdout.strip() + + +def _write_source_selection_manifest( + org_root: Path, + selected_repository: str, + revision: str, + reviewed_tree: str, +) -> None: + entries: list[dict[str, object]] = [ + { + "repository": ".github", + "canonical_repository": "HawkinsOperations/.github", + "revision_source": "github_event_sha", + "tree_source": "github_event_tree", + } + ] + for repository in REPO_NAMES: + if repository == ".github": + continue + entries.append( + { + "repository": repository, + "canonical_repository": f"HawkinsOperations/{repository}", + "revision": revision if repository == selected_repository else "0" * 40, + "reviewed_tree_sha": reviewed_tree if repository == selected_repository else "0" * 40, + } + ) + manifest = { + "schema": "hawkinsoperations-convergence-source-manifest-v1", + "manifest_id": "TEST_EXACT_SEVEN_SOURCE_SELECTION", + "repositories": entries, + "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", + }, + } + manifest_path = org_root / ".github" / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json" + manifest_path.parent.mkdir(parents=True) + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + command_center = org_root / ".github" + _git(command_center, "init") + _git(command_center, "config", "user.name", "Hoxline Test") + _git(command_center, "config", "user.email", "hoxline-test@example.invalid") + _git(command_center, "remote", "add", "origin", "https://github.com/HawkinsOperations/.github.git") + _git(command_center, "add", "governance/CONVERGENCE_SOURCE_MANIFEST.json") + _git(command_center, "commit", "-m", "source selection") + + def row_by_id(index: dict[str, object], case_id: str) -> dict[str, object]: cases = index["cases"] assert isinstance(cases, list) @@ -377,6 +442,15 @@ def test_diff_classifies_head_rewrite_with_same_content_as_observation_only(self self.assertEqual(change["classification"], "OBSERVATION_ONLY_CONTENT_CURRENT") self.assertTrue(report["next_legal_action"].startswith("none;")) + def test_case_content_normalization_ignores_only_rewritten_commit_clock(self) -> None: + before = json.loads(json.dumps(self.rows)) + after = json.loads(json.dumps(self.rows)) + after[0]["last_updated"] = "2035-01-02T03:04:05+00:00" + self.assertEqual(_content_normalized_cases(before), _content_normalized_cases(after)) + + after[0]["source_status"] = "FORGED_SOURCE_STATUS" + self.assertNotEqual(_content_normalized_cases(before), _content_normalized_cases(after)) + def test_cli_verify_fails_closed_on_hostile_snapshot(self) -> None: hostile = json.loads(json.dumps(self.index)) hostile["repo_root"] = r"C:\Users\operator\snapshot.json" @@ -466,6 +540,74 @@ def test_unavailable_observed_sha_does_not_replace_content_authority(self) -> No errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) self.assertFalse(any("authoritative Git blob disagrees" in error for error in errors)) + def test_selected_source_checkout_accepts_exact_detached_and_content_equivalent_rewrite(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) + repository = "hawkinsoperations-detections" + repo = org_root / repository + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") + _git(repo, "add", "authority.yml") + _git(repo, "commit", "-m", "authority") + (repo / "review.txt").write_text("reviewed selection\n", encoding="utf-8") + _git(repo, "add", "review.txt") + _git(repo, "commit", "-m", "reviewed selection") + selected = _git(repo, "rev-parse", "HEAD") + reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") + _write_source_selection_manifest(org_root, repository, selected, reviewed_tree) + + _git(repo, "checkout", "--detach", selected) + self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + + selection_path = org_root / ".github" / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json" + selection_text = selection_path.read_text(encoding="utf-8") + selection_path.write_text(selection_text + "\n", encoding="utf-8") + self.assertTrue( + any( + "must be tracked and clean" in error + for error in verify_selected_source_checkout(org_root, repository) + ) + ) + selection_path.write_text(selection_text, encoding="utf-8") + + (repo / "post-selection.txt").write_text("merge descendant observation\n", encoding="utf-8") + _git(repo, "add", "post-selection.txt") + _git(repo, "commit", "-m", "merge descendant") + self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + + rewritten = _git(repo, "commit-tree", reviewed_tree, input_text="rewritten identity\n") + _git(repo, "checkout", "--detach", rewritten) + self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + + def test_selected_source_checkout_rejects_older_same_authority_blob_ancestor(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) + repository = "hawkinsoperations-detections" + repo = org_root / repository + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") + _git(repo, "add", "authority.yml") + _git(repo, "commit", "-m", "authority") + older = _git(repo, "rev-parse", "HEAD") + authority_blob = _git(repo, "rev-parse", "HEAD:authority.yml") + (repo / "review.txt").write_text("reviewed selection\n", encoding="utf-8") + _git(repo, "add", "review.txt") + _git(repo, "commit", "-m", "reviewed selection") + selected = _git(repo, "rev-parse", "HEAD") + reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") + self.assertEqual(_git(repo, "rev-parse", "HEAD:authority.yml"), authority_blob) + _write_source_selection_manifest(org_root, repository, selected, reviewed_tree) + + _git(repo, "checkout", "--detach", older) + errors = verify_selected_source_checkout(org_root, repository) + self.assertTrue(any("behind the explicit selected revision" in error for error in errors)) + def test_current_snapshot_rejects_forged_content_identity(self) -> None: hostile = json.loads(json.dumps(self.index)) hostile["source_revisions"][0]["authoritative_git_blob_sha"] = "a" * 40 From 4b890fc558def1f9c88c90baa24f321e36c5ada0 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:11:44 -0500 Subject: [PATCH 19/73] chore(hoxline): refresh current convergence pair --- .../current-case-growth-index.json | 46 +++++++++---------- .../case-growth/current-case-growth-index.md | 16 +++---- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 8db8ac9..64fc3f7 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T05:39:22Z", + "generated_at": "2026-07-23T07:08:47Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", - "source_observed_head_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", - "current_observed_head_sha": "fee75e7a88c0b623590786e02f69d4297a0e10b4", + "source_commit_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", + "source_observed_head_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", + "current_observed_head_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,9 +113,9 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", - "source_observed_head_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", - "current_observed_head_sha": "dce32809fcee7faef3f92da7139b2781bb9c4330", + "source_commit_sha": "072612135e927420473c8b779aae0e7ebab7ad68", + "source_observed_head_sha": "072612135e927420473c8b779aae0e7ebab7ad68", + "current_observed_head_sha": "072612135e927420473c8b779aae0e7ebab7ad68", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -145,9 +145,9 @@ "repository": "hawkinsoperations-proof", "authority_role": "proof and claim-boundary truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", - "source_observed_head_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", - "current_observed_head_sha": "8ff2c1a2f1762e7635fe24194617f110275c2f0c", + "source_commit_sha": "c60882552571c1664549d3e594c6994dc27dce63", + "source_observed_head_sha": "c60882552571c1664549d3e594c6994dc27dce63", + "current_observed_head_sha": "c60882552571c1664549d3e594c6994dc27dce63", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -177,9 +177,9 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status contract", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", - "source_observed_head_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", - "current_observed_head_sha": "3c8202b730836074e20c4fa1e3601741a02ee616", + "source_commit_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", + "source_observed_head_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", + "current_observed_head_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", - "source_observed_head_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", - "current_observed_head_sha": "500c47bbd14db671c95ab823aae9ba20ab56a783", + "source_commit_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", + "source_observed_head_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", + "current_observed_head_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "c302284aeb690fffe86f8c4aac49ab014c9cfd88", - "source_git_blob_sha": "c302284aeb690fffe86f8c4aac49ab014c9cfd88", - "source_file_sha256": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", - "authoritative_content_fingerprint": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", - "source_semantic_fingerprint_sha256": "b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f", + "authoritative_git_blob_sha": "32edd66f87c00af9c3b65e0922c6339f441988df", + "source_git_blob_sha": "32edd66f87c00af9c3b65e0922c6339f441988df", + "source_file_sha256": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", + "authoritative_content_fingerprint": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", + "source_semantic_fingerprint_sha256": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "3c0ab0a1b9a00da2df96ccc733c81d7bb82f34a36fd7a56353e51203c1c89010", + "source_manifest_digest": "b1ad1f18be3e7a574eb7009e854ddf3e664352e84dc8f263b3798f89ed3ebd9c", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "e031a0a37dd3c50019f1f1daec6062af323e5ff0a1503557fb7465aeca3ec705" + "reproducibility_sha256": "a798081348fda42a581b33f9aad10c63d1fc54616f410dc5f2832b5688ccb1a6" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 888a789..ffdf031 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T05:39:22Z` +Generated: `2026-07-23T07:08:47Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `3c0ab0a1b9a00da2df96ccc733c81d7bb82f34a36fd7a56353e51203c1c89010` -Reproducibility SHA-256: `e031a0a37dd3c50019f1f1daec6062af323e5ff0a1503557fb7465aeca3ec705` +Source manifest digest: `b1ad1f18be3e7a574eb7009e854ddf3e664352e84dc8f263b3798f89ed3ebd9c` +Reproducibility SHA-256: `a798081348fda42a581b33f9aad10c63d1fc54616f410dc5f2832b5688ccb1a6` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `e031a0a37dd3c50019f1f1daec6062af323e5ff0a1503557fb7465 | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fee75e7a88c0b623590786e02f69d4297a0e10b4` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fa95bbbf0985c57488b32589307eb2cabb99ea00` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `dce32809fcee7faef3f92da7139b2781bb9c4330` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | -| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `8ff2c1a2f1762e7635fe24194617f110275c2f0c` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `3c8202b730836074e20c4fa1e3601741a02ee616` | `75b3e5ba72b87e8980d62bc7e00014704f9031a0` | `0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `500c47bbd14db671c95ab823aae9ba20ab56a783` | `c302284aeb690fffe86f8c4aac49ab014c9cfd88` | `b9b3e136924df4f11c2520c70a4897ec77bac258403ec6e6416b02d7c731936f` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `072612135e927420473c8b779aae0e7ebab7ad68` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | +| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `0c9212afe4c39312dc69906c2e8fa297d3a63a5a` | `75b3e5ba72b87e8980d62bc7e00014704f9031a0` | `0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `72de2b307d7968d74f8e7c71a6ef214ef777ad1b` | `32edd66f87c00af9c3b65e0922c6339f441988df` | `8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7` | `CURRENT` | ## Convergence Findings From 3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:19:13 -0500 Subject: [PATCH 20/73] fix(hoxline): bind detached command-center observation --- src/hoxline/case_growth/collector.py | 18 ++++++++++- tests/test_case_growth_index_v0.py | 45 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 32edd66..793c254 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -4,6 +4,7 @@ from datetime import datetime, timezone import hashlib import json +import os from pathlib import Path import re import subprocess @@ -353,7 +354,22 @@ def _verify_selected_source_checkout( return [f"{repository}: checked source head/tree is unavailable"] entry = selections[repository] if repository == ".github": - return [] + observed = os.environ.get( + "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA", + "", + ).strip() + detached = repo_branch(repo).startswith("UNKNOWN_WITH_REASON:") + if detached and re.fullmatch(r"[0-9a-f]{40}", observed) is None: + errors.append( + ".github: detached command-center checkout requires " + "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA" + ) + elif observed and observed != head: + errors.append( + ".github: checked command-center head differs from the immutable " + "workflow observation" + ) + return errors selected = str(entry["revision"]) reviewed_tree = str(entry["reviewed_tree_sha"]) selected_tree = _git_output(repo, "rev-parse", f"{selected}^{{tree}}") diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index 0dd2c16..b27497c 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -8,6 +8,7 @@ import sys import tempfile import unittest +from unittest import mock from pathlib import Path try: @@ -582,6 +583,50 @@ def test_selected_source_checkout_accepts_exact_detached_and_content_equivalent_ _git(repo, "checkout", "--detach", rewritten) self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + def test_dynamic_command_center_selection_requires_exact_detached_observation(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) + repository = "hawkinsoperations-detections" + repo = org_root / repository + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") + _git(repo, "add", "authority.yml") + _git(repo, "commit", "-m", "authority") + selected = _git(repo, "rev-parse", "HEAD") + reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") + _write_source_selection_manifest(org_root, repository, selected, reviewed_tree) + + command_center = org_root / ".github" + command_head = _git(command_center, "rev-parse", "HEAD") + self.assertEqual(verify_selected_source_checkout(org_root, ".github"), []) + + _git(command_center, "checkout", "--detach", command_head) + with mock.patch.dict( + "os.environ", + {"HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": ""}, + ): + errors = verify_selected_source_checkout(org_root, ".github") + self.assertTrue(any("requires HAWKINS_COMMAND_CENTER" in error for error in errors)) + + with mock.patch.dict( + "os.environ", + {"HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": command_head}, + ): + self.assertEqual( + verify_selected_source_checkout(org_root, ".github"), + [], + ) + + with mock.patch.dict( + "os.environ", + {"HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": "f" * 40}, + ): + errors = verify_selected_source_checkout(org_root, ".github") + self.assertTrue(any("differs from the immutable workflow observation" in error for error in errors)) + def test_selected_source_checkout_rejects_older_same_authority_blob_ancestor(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: org_root = Path(temp_dir) From 352cc0b66fbd78cb1d522934a4262c4ec6c231a4 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:42:40 -0500 Subject: [PATCH 21/73] chore(hoxline): refresh final content-linked pair --- .../current-case-growth-index.json | 44 +++++++++---------- .../case-growth/current-case-growth-index.md | 12 ++--- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 64fc3f7..69fc27c 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T07:08:47Z", + "generated_at": "2026-07-23T07:41:17Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -113,9 +113,9 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "072612135e927420473c8b779aae0e7ebab7ad68", - "source_observed_head_sha": "072612135e927420473c8b779aae0e7ebab7ad68", - "current_observed_head_sha": "072612135e927420473c8b779aae0e7ebab7ad68", + "source_commit_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", + "source_observed_head_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", + "current_observed_head_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -177,20 +177,20 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status contract", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", - "source_observed_head_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", - "current_observed_head_sha": "0c9212afe4c39312dc69906c2e8fa297d3a63a5a", + "source_commit_sha": "7709c7aec560ad0195ca171c093e4f8468301796", + "source_observed_head_sha": "7709c7aec560ad0195ca171c093e4f8468301796", + "current_observed_head_sha": "7709c7aec560ad0195ca171c093e4f8468301796", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "schemas/public-status-v0.schema.json", "authoritative_path": "schemas/public-status-v0.schema.json", - "authoritative_git_blob_sha": "75b3e5ba72b87e8980d62bc7e00014704f9031a0", - "source_git_blob_sha": "75b3e5ba72b87e8980d62bc7e00014704f9031a0", - "source_file_sha256": "7c776ec5e23235bf453b2b904a3e925dd675de0e7fc1d12320868c71cb9e0666", - "authoritative_content_fingerprint": "0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e", - "source_semantic_fingerprint_sha256": "0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e", + "authoritative_git_blob_sha": "1f10e8c0948635eda720905c1f1e7476ef63bf3c", + "source_git_blob_sha": "1f10e8c0948635eda720905c1f1e7476ef63bf3c", + "source_file_sha256": "65e40be7013df6cd2fe4cec7e86c7f92da1ccdd068c54ae2dbf935a0d1f4a0c5", + "authoritative_content_fingerprint": "5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11", + "source_semantic_fingerprint_sha256": "5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-website", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-website", "repository_dirty_observed": false, @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", - "source_observed_head_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", - "current_observed_head_sha": "72de2b307d7968d74f8e7c71a6ef214ef777ad1b", + "source_commit_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", + "source_observed_head_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", + "current_observed_head_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "32edd66f87c00af9c3b65e0922c6339f441988df", - "source_git_blob_sha": "32edd66f87c00af9c3b65e0922c6339f441988df", - "source_file_sha256": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", - "authoritative_content_fingerprint": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", - "source_semantic_fingerprint_sha256": "8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7", + "authoritative_git_blob_sha": "793c254c5c662d54d2ec33fcc31279a5884f2fd8", + "source_git_blob_sha": "793c254c5c662d54d2ec33fcc31279a5884f2fd8", + "source_file_sha256": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", + "authoritative_content_fingerprint": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", + "source_semantic_fingerprint_sha256": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "b1ad1f18be3e7a574eb7009e854ddf3e664352e84dc8f263b3798f89ed3ebd9c", + "source_manifest_digest": "d8451adfcee570bccef714471ff9a85c6ed6c701bdaf1db5d0a2a872e2107c6d", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "a798081348fda42a581b33f9aad10c63d1fc54616f410dc5f2832b5688ccb1a6" + "reproducibility_sha256": "dac03d1179457c2adf259e26c9b4e5d9ba10c4b83ba0f6e0463e9e792b6889eb" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index ffdf031..d53e018 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T07:08:47Z` +Generated: `2026-07-23T07:41:17Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `b1ad1f18be3e7a574eb7009e854ddf3e664352e84dc8f263b3798f89ed3ebd9c` -Reproducibility SHA-256: `a798081348fda42a581b33f9aad10c63d1fc54616f410dc5f2832b5688ccb1a6` +Source manifest digest: `d8451adfcee570bccef714471ff9a85c6ed6c701bdaf1db5d0a2a872e2107c6d` +Reproducibility SHA-256: `dac03d1179457c2adf259e26c9b4e5d9ba10c4b83ba0f6e0463e9e792b6889eb` ## Summary @@ -38,10 +38,10 @@ Reproducibility SHA-256: `a798081348fda42a581b33f9aad10c63d1fc54616f410dc5f2832b | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fa95bbbf0985c57488b32589307eb2cabb99ea00` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `072612135e927420473c8b779aae0e7ebab7ad68` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `70ba640773e4116b1fa0e80c86c2a2a0f7287ba6` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `0c9212afe4c39312dc69906c2e8fa297d3a63a5a` | `75b3e5ba72b87e8980d62bc7e00014704f9031a0` | `0c7caf0df6f110629b43e9383a098c5995675868bb4c96de1753a1bb0429426e` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `72de2b307d7968d74f8e7c71a6ef214ef777ad1b` | `32edd66f87c00af9c3b65e0922c6339f441988df` | `8850d1cdd2fd922621f3eae9110942c802c2981159b9aa8ab542835a352874b7` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `7709c7aec560ad0195ca171c093e4f8468301796` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a` | `793c254c5c662d54d2ec33fcc31279a5884f2fd8` | `fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0` | `CURRENT` | ## Convergence Findings From 752fade273904e6f9280523a43aaf9dad5181220 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:48:29 -0500 Subject: [PATCH 22/73] chore(hoxline): bind refreshed platform authority --- .../current-case-growth-index.json | 34 +++++++++---------- .../case-growth/current-case-growth-index.md | 12 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 69fc27c..4c537f8 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T07:41:17Z", + "generated_at": "2026-07-23T07:47:05Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", - "source_observed_head_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", - "current_observed_head_sha": "fa95bbbf0985c57488b32589307eb2cabb99ea00", + "source_commit_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", + "source_observed_head_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", + "current_observed_head_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,20 +113,20 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", - "source_observed_head_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", - "current_observed_head_sha": "70ba640773e4116b1fa0e80c86c2a2a0f7287ba6", + "source_commit_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", + "source_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", + "current_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", "authoritative_path": "contracts/public-status-source-contract-v1.json", - "authoritative_git_blob_sha": "b3be8100e2cf1479a2445d01e84236135d3d96a3", - "source_git_blob_sha": "b3be8100e2cf1479a2445d01e84236135d3d96a3", - "source_file_sha256": "504d0b47c7e85066ba9b7a66d5d5f8326679b3254844cfe7efe263459d9dd1c3", - "authoritative_content_fingerprint": "4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf", - "source_semantic_fingerprint_sha256": "4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf", + "authoritative_git_blob_sha": "b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3", + "source_git_blob_sha": "b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3", + "source_file_sha256": "91e1ba4386383d7f19dada470bdd8865b64885a10152d9f68a01458b1cd1ca5c", + "authoritative_content_fingerprint": "5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b", + "source_semantic_fingerprint_sha256": "5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "repository_dirty_observed": false, @@ -209,9 +209,9 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", - "source_observed_head_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", - "current_observed_head_sha": "3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a", + "source_commit_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", + "source_observed_head_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", + "current_observed_head_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "d8451adfcee570bccef714471ff9a85c6ed6c701bdaf1db5d0a2a872e2107c6d", + "source_manifest_digest": "7319e13a69cbe9102b13a0bdfd084b2e8ab343bdb0d1a4141fa41ad0d7f3ff86", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "dac03d1179457c2adf259e26c9b4e5d9ba10c4b83ba0f6e0463e9e792b6889eb" + "reproducibility_sha256": "f62d158d74af8b114e44dcd63b908a0489d4e8d2af93e63d986b216b8e803cb4" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index d53e018..4ae8c73 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T07:41:17Z` +Generated: `2026-07-23T07:47:05Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `d8451adfcee570bccef714471ff9a85c6ed6c701bdaf1db5d0a2a872e2107c6d` -Reproducibility SHA-256: `dac03d1179457c2adf259e26c9b4e5d9ba10c4b83ba0f6e0463e9e792b6889eb` +Source manifest digest: `7319e13a69cbe9102b13a0bdfd084b2e8ab343bdb0d1a4141fa41ad0d7f3ff86` +Reproducibility SHA-256: `f62d158d74af8b114e44dcd63b908a0489d4e8d2af93e63d986b216b8e803cb4` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `dac03d1179457c2adf259e26c9b4e5d9ba10c4b83ba0f6e0463e9e | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `fa95bbbf0985c57488b32589307eb2cabb99ea00` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `b0b0bf187163edc80fa042e72c8ccd8436677a1d` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `70ba640773e4116b1fa0e80c86c2a2a0f7287ba6` | `b3be8100e2cf1479a2445d01e84236135d3d96a3` | `4a52f29172a54a0caa8d9e3b87fb15735e9bd0c0bb2ffa24da5425ce80b768bf` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `541ffcd0e94e9db94638d8ffa5934da11ae38771` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `7709c7aec560ad0195ca171c093e4f8468301796` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `3f2cf1b01f68767f9ce312e8cf31cec6a85b5d9a` | `793c254c5c662d54d2ec33fcc31279a5884f2fd8` | `fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `352cc0b66fbd78cb1d522934a4262c4ec6c231a4` | `793c254c5c662d54d2ec33fcc31279a5884f2fd8` | `fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0` | `CURRENT` | ## Convergence Findings From fd3a0b16aff03ce45318e81826f88b64fcb91f50 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 02:52:49 -0500 Subject: [PATCH 23/73] ci(hoxline): verify sealed seven-source content --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79fe0e5..aa005fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,8 @@ permissions: jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest + env: + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 9de52c3d1b8fe05c0f745c65d3817b1e50efc908 strategy: fail-fast: false matrix: @@ -26,7 +28,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: c8f7fe023aa8eb9af385a8bb222133b9997fe94e + ref: 9de52c3d1b8fe05c0f745c65d3817b1e50efc908 path: org/.github persist-credentials: false @@ -50,7 +52,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: dce32809fcee7faef3f92da7139b2781bb9c4330 + ref: 42c3a4b78548fc4f95fd017f633eaaf207fc7625 path: org/hawkinsoperations-platform persist-credentials: false @@ -66,7 +68,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 9f4c03b84f2c03a305cd1e974ae405cd05218923 + ref: 7709c7aec560ad0195ca171c093e4f8468301796 path: org/hawkinsoperations-website persist-credentials: false @@ -89,12 +91,12 @@ jobs: "hoxline", ] immutable = { - ".github": "c8f7fe023aa8eb9af385a8bb222133b9997fe94e", + ".github": "9de52c3d1b8fe05c0f745c65d3817b1e50efc908", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "hawkinsoperations-platform": "dce32809fcee7faef3f92da7139b2781bb9c4330", + "hawkinsoperations-platform": "42c3a4b78548fc4f95fd017f633eaaf207fc7625", "hawkinsoperations-proof": "c60882552571c1664549d3e594c6994dc27dce63", - "hawkinsoperations-website": "9f4c03b84f2c03a305cd1e974ae405cd05218923", + "hawkinsoperations-website": "7709c7aec560ad0195ca171c093e4f8468301796", } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) if actual != sorted(expected): From 105e904223140e299808564757aa7324424c0e53 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:00:13 -0500 Subject: [PATCH 24/73] test(hoxline): isolate command-center observation environment --- tests/test_case_growth_index_v0.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index b27497c..6061500 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -601,7 +601,14 @@ def test_dynamic_command_center_selection_requires_exact_detached_observation(se command_center = org_root / ".github" command_head = _git(command_center, "rev-parse", "HEAD") - self.assertEqual(verify_selected_source_checkout(org_root, ".github"), []) + with mock.patch.dict( + "os.environ", + {"HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": ""}, + ): + self.assertEqual( + verify_selected_source_checkout(org_root, ".github"), + [], + ) _git(command_center, "checkout", "--detach", command_head) with mock.patch.dict( From 8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:07:02 -0500 Subject: [PATCH 25/73] fix(hoxline): consume dual source identities --- .github/workflows/ci.yml | 6 +++--- src/hoxline/case_growth/collector.py | 16 +++++++++++++++- tests/test_case_growth_index_v0.py | 3 +++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa005fd..297fa6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 9de52c3d1b8fe05c0f745c65d3817b1e50efc908 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 43fd17e7e66606fac92994cfc9eeb3871a03eff1 strategy: fail-fast: false matrix: @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 9de52c3d1b8fe05c0f745c65d3817b1e50efc908 + ref: 43fd17e7e66606fac92994cfc9eeb3871a03eff1 path: org/.github persist-credentials: false @@ -91,7 +91,7 @@ jobs: "hoxline", ] immutable = { - ".github": "9de52c3d1b8fe05c0f745c65d3817b1e50efc908", + ".github": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", "hawkinsoperations-platform": "42c3a4b78548fc4f95fd017f633eaaf207fc7625", diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 793c254..6622de6 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -288,14 +288,28 @@ def _load_convergence_source_selections(repo_root: Path) -> tuple[dict[str, dict else: unknown = sorted( set(entry) - - {"repository", "canonical_repository", "revision", "reviewed_tree_sha"} + - { + "repository", + "canonical_repository", + "revision", + "authority_content_revision", + "reviewed_tree_sha", + } ) if unknown: errors.append(f"{repository}: selection contains unsupported fields: {unknown}") revision = entry.get("revision") + content_revision = entry.get("authority_content_revision") tree = entry.get("reviewed_tree_sha") if not isinstance(revision, str) or re.fullmatch(r"[0-9a-f]{40}", revision) is None: errors.append(f"{repository}: selected revision must be an immutable 40-character SHA") + if ( + not isinstance(content_revision, str) + or re.fullmatch(r"[0-9a-f]{40}", content_revision) is None + ): + errors.append( + f"{repository}: authority content revision must be an immutable 40-character SHA" + ) if not isinstance(tree, str) or re.fullmatch(r"[0-9a-f]{40}", tree) is None: errors.append(f"{repository}: reviewed tree must be a 40-character Git tree SHA") selections[repository] = entry diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index 6061500..54898a9 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -70,6 +70,9 @@ def _write_source_selection_manifest( "repository": repository, "canonical_repository": f"HawkinsOperations/{repository}", "revision": revision if repository == selected_repository else "0" * 40, + "authority_content_revision": ( + revision if repository == selected_repository else "0" * 40 + ), "reviewed_tree_sha": reviewed_tree if repository == selected_repository else "0" * 40, } ) From f6edb75fd96b4fc419a16f8357ce644c3b9f6205 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:11:44 -0500 Subject: [PATCH 26/73] chore(hoxline): refresh content-addressed case-growth pair --- .../current-case-growth-index.json | 34 +++++++++---------- .../case-growth/current-case-growth-index.md | 12 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 4c537f8..0044af4 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T07:47:05Z", + "generated_at": "2026-07-23T08:09:54Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", - "source_observed_head_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", - "current_observed_head_sha": "b0b0bf187163edc80fa042e72c8ccd8436677a1d", + "source_commit_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + "source_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + "current_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,9 +113,9 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", - "source_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", - "current_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", + "source_commit_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", + "source_observed_head_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", + "current_observed_head_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", - "source_observed_head_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", - "current_observed_head_sha": "352cc0b66fbd78cb1d522934a4262c4ec6c231a4", + "source_commit_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", + "source_observed_head_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", + "current_observed_head_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "793c254c5c662d54d2ec33fcc31279a5884f2fd8", - "source_git_blob_sha": "793c254c5c662d54d2ec33fcc31279a5884f2fd8", - "source_file_sha256": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", - "authoritative_content_fingerprint": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", - "source_semantic_fingerprint_sha256": "fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0", + "authoritative_git_blob_sha": "6622de6d38df5372b971fa928da504a0d611fc36", + "source_git_blob_sha": "6622de6d38df5372b971fa928da504a0d611fc36", + "source_file_sha256": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", + "authoritative_content_fingerprint": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", + "source_semantic_fingerprint_sha256": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "7319e13a69cbe9102b13a0bdfd084b2e8ab343bdb0d1a4141fa41ad0d7f3ff86", + "source_manifest_digest": "ecb16dbae42d8faa2fbfdba30c3c2b88b813ff058f1949335134f4f87a4f3c8f", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "f62d158d74af8b114e44dcd63b908a0489d4e8d2af93e63d986b216b8e803cb4" + "reproducibility_sha256": "c6daf34b83f157dfbf35f3abce5bd6c1fdbcc815eeec2a4982c740c2d563a8e4" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 4ae8c73..0c3e593 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T07:47:05Z` +Generated: `2026-07-23T08:09:54Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `7319e13a69cbe9102b13a0bdfd084b2e8ab343bdb0d1a4141fa41ad0d7f3ff86` -Reproducibility SHA-256: `f62d158d74af8b114e44dcd63b908a0489d4e8d2af93e63d986b216b8e803cb4` +Source manifest digest: `ecb16dbae42d8faa2fbfdba30c3c2b88b813ff058f1949335134f4f87a4f3c8f` +Reproducibility SHA-256: `c6daf34b83f157dfbf35f3abce5bd6c1fdbcc815eeec2a4982c740c2d563a8e4` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `f62d158d74af8b114e44dcd63b908a0489d4e8d2af93e63d986b21 | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `b0b0bf187163edc80fa042e72c8ccd8436677a1d` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `43fd17e7e66606fac92994cfc9eeb3871a03eff1` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `541ffcd0e94e9db94638d8ffa5934da11ae38771` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `e7451f51eae4beacdf9c22fbcc1c6983c1fa619c` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `7709c7aec560ad0195ca171c093e4f8468301796` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `352cc0b66fbd78cb1d522934a4262c4ec6c231a4` | `793c254c5c662d54d2ec33fcc31279a5884f2fd8` | `fb0f49b6ffc94062f3e8beb7b4c63398aaf3debd5a8fd505c23add52a85938e0` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd` | `6622de6d38df5372b971fa928da504a0d611fc36` | `e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c` | `CURRENT` | ## Convergence Findings From 6736f52a1e2eb1b90c79a633ea0a382e06c85fac Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:26:01 -0500 Subject: [PATCH 27/73] fix(hoxline): require command-center content identity --- src/hoxline/case_growth/collector.py | 16 +++++++++++++++- tests/test_case_growth_index_v0.py | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 6622de6..2ce8aae 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -279,12 +279,26 @@ def _load_convergence_source_selections(repo_root: Path) -> tuple[dict[str, dict if repository == ".github": unknown = sorted( set(entry) - - {"repository", "canonical_repository", "revision_source", "tree_source"} + - { + "repository", + "canonical_repository", + "revision_source", + "authority_content_revision", + "tree_source", + } ) if unknown: errors.append(f".github: selection contains unsupported fields: {unknown}") if entry.get("revision_source") != "github_event_sha" or entry.get("tree_source") != "github_event_tree": errors.append(".github: dynamic command-center selection must use event SHA and event tree") + content_revision = entry.get("authority_content_revision") + if ( + not isinstance(content_revision, str) + or re.fullmatch(r"[0-9a-f]{40}", content_revision) is None + ): + errors.append( + ".github: authority content revision must be an immutable 40-character SHA" + ) else: unknown = sorted( set(entry) diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index 54898a9..dc29e23 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -59,6 +59,7 @@ def _write_source_selection_manifest( "repository": ".github", "canonical_repository": "HawkinsOperations/.github", "revision_source": "github_event_sha", + "authority_content_revision": revision, "tree_source": "github_event_tree", } ] From 2be9e1285334ed8e440258db562bcbd9f8ff6ee0 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:28:08 -0500 Subject: [PATCH 28/73] chore(hoxline): refresh explicit content identities --- .../current-case-growth-index.json | 34 +++++++++---------- .../case-growth/current-case-growth-index.md | 12 +++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 0044af4..ab1aaba 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T08:09:54Z", + "generated_at": "2026-07-23T08:26:10Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", - "source_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", - "current_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + "source_commit_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", + "source_observed_head_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", + "current_observed_head_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,9 +113,9 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", - "source_observed_head_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", - "current_observed_head_sha": "e7451f51eae4beacdf9c22fbcc1c6983c1fa619c", + "source_commit_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", + "source_observed_head_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", + "current_observed_head_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", - "source_observed_head_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", - "current_observed_head_sha": "8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd", + "source_commit_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", + "source_observed_head_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", + "current_observed_head_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "6622de6d38df5372b971fa928da504a0d611fc36", - "source_git_blob_sha": "6622de6d38df5372b971fa928da504a0d611fc36", - "source_file_sha256": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", - "authoritative_content_fingerprint": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", - "source_semantic_fingerprint_sha256": "e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c", + "authoritative_git_blob_sha": "2ce8aaef7419ab8f155563fd67ba0a53569d14bb", + "source_git_blob_sha": "2ce8aaef7419ab8f155563fd67ba0a53569d14bb", + "source_file_sha256": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", + "authoritative_content_fingerprint": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", + "source_semantic_fingerprint_sha256": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "ecb16dbae42d8faa2fbfdba30c3c2b88b813ff058f1949335134f4f87a4f3c8f", + "source_manifest_digest": "806e26fe48b3ebad14a0cf9f9db7ca31c57cc85660207fdff05f50850c011546", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "c6daf34b83f157dfbf35f3abce5bd6c1fdbcc815eeec2a4982c740c2d563a8e4" + "reproducibility_sha256": "833b596e3f49b12f36949e91446fb16fc209cf3b38d011222d65f8dee261f286" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 0c3e593..8bf2c6f 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T08:09:54Z` +Generated: `2026-07-23T08:26:10Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `ecb16dbae42d8faa2fbfdba30c3c2b88b813ff058f1949335134f4f87a4f3c8f` -Reproducibility SHA-256: `c6daf34b83f157dfbf35f3abce5bd6c1fdbcc815eeec2a4982c740c2d563a8e4` +Source manifest digest: `806e26fe48b3ebad14a0cf9f9db7ca31c57cc85660207fdff05f50850c011546` +Reproducibility SHA-256: `833b596e3f49b12f36949e91446fb16fc209cf3b38d011222d65f8dee261f286` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `c6daf34b83f157dfbf35f3abce5bd6c1fdbcc815eeec2a4982c740 | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `43fd17e7e66606fac92994cfc9eeb3871a03eff1` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `cc1032cdf913a8cf10050d239f29e45d18a10381` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `e7451f51eae4beacdf9c22fbcc1c6983c1fa619c` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `87414ef6734993f9a4cedf4b95a03473ba336884` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `7709c7aec560ad0195ca171c093e4f8468301796` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `8a5e6bcc8b0bfff4a8661a8c663bd2832e8659dd` | `6622de6d38df5372b971fa928da504a0d611fc36` | `e47d174a56d538566820f1b75588ce00cf943f7e0c975536f01e6ad83169888c` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `6736f52a1e2eb1b90c79a633ea0a382e06c85fac` | `2ce8aaef7419ab8f155563fd67ba0a53569d14bb` | `0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1` | `CURRENT` | ## Convergence Findings From 887f0bb9f9ba6e9990e7189eb00cc46d8da0280d Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:42:02 -0500 Subject: [PATCH 29/73] fix(case-growth): anchor snapshots to authority content --- .github/workflows/ci.yml | 6 +- src/hoxline/case_growth/collector.py | 97 +++++++++++++++++++++++++--- tests/test_case_growth_index_v0.py | 44 ++++++++++--- 3 files changed, 125 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 297fa6f..66909cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 43fd17e7e66606fac92994cfc9eeb3871a03eff1 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: cc1032cdf913a8cf10050d239f29e45d18a10381 strategy: fail-fast: false matrix: @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 43fd17e7e66606fac92994cfc9eeb3871a03eff1 + ref: cc1032cdf913a8cf10050d239f29e45d18a10381 path: org/.github persist-credentials: false @@ -91,7 +91,7 @@ jobs: "hoxline", ] immutable = { - ".github": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + ".github": "cc1032cdf913a8cf10050d239f29e45d18a10381", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", "hawkinsoperations-platform": "42c3a4b78548fc4f95fd017f633eaaf207fc7625", diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 2ce8aae..9dcf086 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -145,7 +145,35 @@ def build_case_growth_index(repo_root: Path, generated_at: str | None = None) -> case_growth_health = _build_case_growth_health(summary) _add_cross_repo_quality_notes(ordered_rows, data_quality_notes) - source_revisions = _build_source_revisions(repo_paths) + source_selections: dict[str, dict[str, Any]] | None = None + selection_manifest = ( + repo_root / ".github" / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json" + ) + if selection_manifest.is_file(): + source_selections, selection_errors = _load_convergence_source_selections( + repo_root + ) + if selection_errors: + raise ValueError( + "invalid seven-source selection manifest: " + + "; ".join(selection_errors) + ) + checkout_errors: list[str] = [] + for repository in REPO_NAMES: + checkout_errors.extend( + _verify_selected_source_checkout( + repo_root, + repository, + source_selections, + ) + ) + if checkout_errors: + raise ValueError( + "selected source checkout verification failed: " + + "; ".join(checkout_errors) + ) + + source_revisions = _build_source_revisions(repo_paths, source_selections) contradictions, drift = _source_convergence_findings(repo_paths, source_revisions, summary, ordered_rows) global_current_authority = ( not contradictions @@ -381,6 +409,45 @@ def _verify_selected_source_checkout( if head == "UNKNOWN" or tree is None: return [f"{repository}: checked source head/tree is unavailable"] entry = selections[repository] + authority_path = AUTHORITY_SOURCES[repository][1] + content_revision = str(entry["authority_content_revision"]) + if not git_commit_exists(repo, content_revision): + errors.append( + f"{repository}: authority content revision is unreachable in the checked repository" + ) + return errors + current_blob = git_blob_identity(repo, head, authority_path) + content_blob = git_blob_identity(repo, content_revision, authority_path) + if current_blob is None or content_blob is None or current_blob[0] != content_blob[0]: + errors.append( + f"{repository}: authority content revision does not carry the checked authority blob" + ) + elif ( + semantic_fingerprint(authority_path, current_blob[1]) + != semantic_fingerprint(authority_path, content_blob[1]) + ): + errors.append( + f"{repository}: authority content revision semantic fingerprint disagrees with current" + ) + head_is_content_ancestor = ( + head != content_revision and _is_ancestor(repo, head, content_revision) + ) + content_is_head_ancestor = ( + head != content_revision and _is_ancestor(repo, content_revision, head) + ) + content_tree = _git_output(repo, "rev-parse", f"{content_revision}^{{tree}}") + if head_is_content_ancestor: + errors.append( + f"{repository}: checked head is behind the authority content revision" + ) + elif ( + head != content_revision + and not content_is_head_ancestor + and content_tree != tree + ): + errors.append( + f"{repository}: authority content revision is outside the reviewed current lineage" + ) if repository == ".github": observed = os.environ.get( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA", @@ -443,14 +510,22 @@ def verify_all_selected_source_checkouts(repo_root: Path) -> list[str]: return errors -def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str, Any]]: +def _build_source_revisions( + repo_paths: dict[str, Path | None], + selections: dict[str, dict[str, Any]] | None = None, +) -> list[dict[str, Any]]: revisions: list[dict[str, Any]] = [] for repository in REPO_NAMES: repo = repo_paths.get(repository) authority_role, relative_path = AUTHORITY_SOURCES[repository] source = repo / relative_path if repo is not None else None source_exists = source is not None and source.is_file() - sha = repo_head_sha(repo) if repo is not None else "UNKNOWN" + current_head = repo_head_sha(repo) if repo is not None else "UNKNOWN" + selected_content_sha = ( + str(selections[repository]["authority_content_revision"]) + if selections is not None and repository in selections + else current_head + ) branch = repo_branch(repo) if repo is not None else "NOT_FOUND" dirty = repo_dirty(repo) if repo is not None else False dirty_paths = repo_dirty_paths(repo) if repo is not None else [] @@ -463,7 +538,7 @@ def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str freshness = "MISSING_REPOSITORY" elif not source_exists: freshness = "MISSING_AUTHORITY_SOURCE" - elif sha == "UNKNOWN": + elif current_head == "UNKNOWN": freshness = "UNVERSIONED_SOURCE" elif authority_dirty: freshness = "WORKTREE_MODIFIED" @@ -471,7 +546,11 @@ def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str freshness = "REPOSITORY_IDENTITY_INVALID" else: freshness = "CURRENT" - blob_identity = git_blob_identity(repo, sha, relative_path) if repo is not None else None + blob_identity = ( + git_blob_identity(repo, selected_content_sha, relative_path) + if repo is not None + else None + ) committed_fingerprint = hashlib.sha256(blob_identity[1]).hexdigest() if blob_identity is not None else None semantic = semantic_fingerprint(relative_path, blob_identity[1]) if blob_identity is not None else None revisions.append( @@ -479,9 +558,9 @@ def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str "repository": repository, "authority_role": authority_role, "resolved_ref": branch, - "source_commit_sha": sha, - "source_observed_head_sha": sha, - "current_observed_head_sha": sha, + "source_commit_sha": selected_content_sha, + "source_observed_head_sha": selected_content_sha, + "current_observed_head_sha": current_head, "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": None, "self_referential": False, @@ -504,7 +583,7 @@ def _build_source_revisions(repo_paths: dict[str, Path | None]) -> list[dict[str "source_freshness_state": freshness, "snapshot_freshness_state": "CURRENT", "historical_snapshot": False, - "current_authority": source_exists and sha != "UNKNOWN" and canonical_origin and not authority_dirty, + "current_authority": source_exists and current_head != "UNKNOWN" and canonical_origin and not authority_dirty, "missing_source_state": not source_exists, "dangling_reference_state": repo is not None and not source_exists, "contradictions": [], diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index dc29e23..c55858d 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -54,12 +54,27 @@ def _write_source_selection_manifest( revision: str, reviewed_tree: str, ) -> None: + command_center = org_root / ".github" + command_authority_path = command_center / "governance" / "COMMAND_CENTER_INVARIANTS.json" + command_authority_path.parent.mkdir(parents=True) + command_authority_path.write_text( + json.dumps({"schema": "command-center-invariants-v1", "test_fixture": True}), + encoding="utf-8", + ) + _git(command_center, "init") + _git(command_center, "config", "user.name", "Hoxline Test") + _git(command_center, "config", "user.email", "hoxline-test@example.invalid") + _git(command_center, "remote", "add", "origin", "https://github.com/HawkinsOperations/.github.git") + _git(command_center, "add", "governance/COMMAND_CENTER_INVARIANTS.json") + _git(command_center, "commit", "-m", "command authority") + command_content_revision = _git(command_center, "rev-parse", "HEAD") + entries: list[dict[str, object]] = [ { "repository": ".github", "canonical_repository": "HawkinsOperations/.github", "revision_source": "github_event_sha", - "authority_content_revision": revision, + "authority_content_revision": command_content_revision, "tree_source": "github_event_tree", } ] @@ -92,13 +107,7 @@ def _write_source_selection_manifest( }, } manifest_path = org_root / ".github" / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json" - manifest_path.parent.mkdir(parents=True) manifest_path.write_text(json.dumps(manifest), encoding="utf-8") - command_center = org_root / ".github" - _git(command_center, "init") - _git(command_center, "config", "user.name", "Hoxline Test") - _git(command_center, "config", "user.email", "hoxline-test@example.invalid") - _git(command_center, "remote", "add", "origin", "https://github.com/HawkinsOperations/.github.git") _git(command_center, "add", "governance/CONVERGENCE_SOURCE_MANIFEST.json") _git(command_center, "commit", "-m", "source selection") @@ -554,8 +563,13 @@ def test_selected_source_checkout_accepts_exact_detached_and_content_equivalent_ _git(repo, "init") _git(repo, "config", "user.name", "Hoxline Test") _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "detections").mkdir() + (repo / "detections" / "DETECTION_PROMOTION_MATRIX.yml").write_text( + "schema: detection-promotion-matrix-v1\nentries: []\n", + encoding="utf-8", + ) (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") - _git(repo, "add", "authority.yml") + _git(repo, "add", "authority.yml", "detections/DETECTION_PROMOTION_MATRIX.yml") _git(repo, "commit", "-m", "authority") (repo / "review.txt").write_text("reviewed selection\n", encoding="utf-8") _git(repo, "add", "review.txt") @@ -596,8 +610,13 @@ def test_dynamic_command_center_selection_requires_exact_detached_observation(se _git(repo, "init") _git(repo, "config", "user.name", "Hoxline Test") _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "detections").mkdir() + (repo / "detections" / "DETECTION_PROMOTION_MATRIX.yml").write_text( + "schema: detection-promotion-matrix-v1\nentries: []\n", + encoding="utf-8", + ) (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") - _git(repo, "add", "authority.yml") + _git(repo, "add", "authority.yml", "detections/DETECTION_PROMOTION_MATRIX.yml") _git(repo, "commit", "-m", "authority") selected = _git(repo, "rev-parse", "HEAD") reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") @@ -647,8 +666,13 @@ def test_selected_source_checkout_rejects_older_same_authority_blob_ancestor(sel _git(repo, "init") _git(repo, "config", "user.name", "Hoxline Test") _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "detections").mkdir() + (repo / "detections" / "DETECTION_PROMOTION_MATRIX.yml").write_text( + "schema: detection-promotion-matrix-v1\nentries: []\n", + encoding="utf-8", + ) (repo / "authority.yml").write_text("authority: detection\n", encoding="utf-8") - _git(repo, "add", "authority.yml") + _git(repo, "add", "authority.yml", "detections/DETECTION_PROMOTION_MATRIX.yml") _git(repo, "commit", "-m", "authority") older = _git(repo, "rev-parse", "HEAD") authority_blob = _git(repo, "rev-parse", "HEAD:authority.yml") From cc0e3e11b86b8998eccaec7d7981cc4f051f156e Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 03:46:12 -0500 Subject: [PATCH 30/73] fix(case-growth): separate content and head observations --- src/hoxline/case_growth/collector.py | 26 ++++++++++++++++++++++++-- tests/test_case_growth_index_v0.py | 19 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 9dcf086..1b3fb46 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -1399,9 +1399,13 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu current_observed = stated.get("current_observed_head_sha") if stated.get("source_observation_kind") != "reviewed_immutable_commit": errors.append(f"{repository}: source_observation_kind must be reviewed_immutable_commit") - if observed_head != stated_sha or current_observed != stated_sha: + if observed_head != stated_sha: errors.append( - f"{repository}: recorded observed-head fields must identify the same reviewed source commit" + f"{repository}: source_observed_head_sha must identify the content-addressed source commit" + ) + if not re.fullmatch(r"[0-9a-f]{40}", str(current_observed or "")): + errors.append( + f"{repository}: current_observed_head_sha must be a 40-character Git SHA" ) resolved_ref = stated.get("resolved_ref") if not isinstance(resolved_ref, str) or not re.fullmatch(r"[A-Za-z0-9._/-]+", resolved_ref): @@ -1450,6 +1454,24 @@ def verify_case_growth_snapshot(repo_root: Path, snapshot: dict[str, Any]) -> tu # above keep this explicitly bounded rather than silently treating # an arbitrary ancestor as current authority. pass + if ( + repo_paths.get(repository) is not None + and re.fullmatch(r"[0-9a-f]{40}", str(current_observed or "")) + and git_commit_exists(repo_paths[repository], str(current_observed)) + ): + current_observed_identity = git_blob_identity( + repo_paths[repository], + str(current_observed), + source_path, + ) + if ( + current_observed_identity is None + or current_observed_identity[0] != current_blob + ): + errors.append( + f"{repository}: generation-time head observation does not carry " + "the checked current authoritative blob" + ) if stated.get("source_file_sha256") != current_revision.get("source_file_sha256") and not historical: errors.append( f"{repository}: authoritative source fingerprint drifted; regenerate from {current_revision['source_path']}" diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index c55858d..a6fc4a2 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -554,6 +554,25 @@ def test_unavailable_observed_sha_does_not_replace_content_authority(self) -> No errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, hostile) self.assertFalse(any("authoritative Git blob disagrees" in error for error in errors)) + def test_generation_head_observation_is_separate_from_content_identity(self) -> None: + current = json.loads(json.dumps(self.index)) + revision = current["source_revisions"][0] + revision["current_observed_head_sha"] = "e" * 40 + current["reproducibility_sha256"] = _reproducibility_hash(current) + errors, _ = verify_case_growth_snapshot(FIXTURE_ROOT, current) + self.assertFalse( + any( + "observed-head fields must identify the same reviewed source commit" in error + for error in errors + ) + ) + self.assertFalse( + any( + "current_observed_head_sha must be a 40-character Git SHA" in error + for error in errors + ) + ) + def test_selected_source_checkout_accepts_exact_detached_and_content_equivalent_rewrite(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: org_root = Path(temp_dir) From e6e6e8c28f047cb2f56c7cbd75e273ffad0ed913 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 04:10:10 -0500 Subject: [PATCH 31/73] ci(hoxline): consume stable content manifest --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66909cd..27aec7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: cc1032cdf913a8cf10050d239f29e45d18a10381 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 3afb59fc452fc1fbdf14207416068ec72b084464 strategy: fail-fast: false matrix: @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: cc1032cdf913a8cf10050d239f29e45d18a10381 + ref: 3afb59fc452fc1fbdf14207416068ec72b084464 path: org/.github persist-credentials: false @@ -91,7 +91,7 @@ jobs: "hoxline", ] immutable = { - ".github": "cc1032cdf913a8cf10050d239f29e45d18a10381", + ".github": "3afb59fc452fc1fbdf14207416068ec72b084464", "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", "hawkinsoperations-platform": "42c3a4b78548fc4f95fd017f633eaaf207fc7625", From 47c72da9f4dbbfb8f4a602136345f5428c497167 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 04:12:33 -0500 Subject: [PATCH 32/73] chore(case-growth): refresh atomic convergence pair --- .../current-case-growth-index.json | 42 +++++++++---------- .../case-growth/current-case-growth-index.md | 14 +++---- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index ab1aaba..dc9ceed 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T08:26:10Z", + "generated_at": "2026-07-23T09:10:20Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", - "source_observed_head_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", - "current_observed_head_sha": "cc1032cdf913a8cf10050d239f29e45d18a10381", + "source_commit_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + "source_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", + "current_observed_head_sha": "3afb59fc452fc1fbdf14207416068ec72b084464", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,8 +113,8 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", - "source_observed_head_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", + "source_commit_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", + "source_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", "current_observed_head_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, @@ -177,9 +177,9 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status contract", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "7709c7aec560ad0195ca171c093e4f8468301796", - "source_observed_head_sha": "7709c7aec560ad0195ca171c093e4f8468301796", - "current_observed_head_sha": "7709c7aec560ad0195ca171c093e4f8468301796", + "source_commit_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", + "source_observed_head_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", + "current_observed_head_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,23 +209,23 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", - "source_observed_head_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", - "current_observed_head_sha": "6736f52a1e2eb1b90c79a633ea0a382e06c85fac", + "source_commit_sha": "cc0e3e11b86b8998eccaec7d7981cc4f051f156e", + "source_observed_head_sha": "cc0e3e11b86b8998eccaec7d7981cc4f051f156e", + "current_observed_head_sha": "e6e6e8c28f047cb2f56c7cbd75e273ffad0ed913", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "2ce8aaef7419ab8f155563fd67ba0a53569d14bb", - "source_git_blob_sha": "2ce8aaef7419ab8f155563fd67ba0a53569d14bb", - "source_file_sha256": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", - "authoritative_content_fingerprint": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", - "source_semantic_fingerprint_sha256": "0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1", + "authoritative_git_blob_sha": "1b3fb46ad51706da678156160b88ab0e25a18294", + "source_git_blob_sha": "1b3fb46ad51706da678156160b88ab0e25a18294", + "source_file_sha256": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", + "authoritative_content_fingerprint": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", + "source_semantic_fingerprint_sha256": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", - "repository_dirty_observed": false, + "repository_dirty_observed": true, "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "806e26fe48b3ebad14a0cf9f9db7ca31c57cc85660207fdff05f50850c011546", + "source_manifest_digest": "c92dcc2cd305861c487473e0eb7afb29d800e1c175b19f0413ca6c48e4765ac2", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -302,7 +302,7 @@ "path": "hoxline", "exists": true, "branch": "feature/hoxline-case-growth-convergence-v1", - "dirty": false, + "dirty": true, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", "files_scanned": 181 } @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "833b596e3f49b12f36949e91446fb16fc209cf3b38d011222d65f8dee261f286" + "reproducibility_sha256": "9d34b6d27c5d6425e347058f207400d8431d47d236c7a57d17071bb3e85fe949" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 8bf2c6f..5088fe2 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T08:26:10Z` +Generated: `2026-07-23T09:10:20Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `806e26fe48b3ebad14a0cf9f9db7ca31c57cc85660207fdff05f50850c011546` -Reproducibility SHA-256: `833b596e3f49b12f36949e91446fb16fc209cf3b38d011222d65f8dee261f286` +Source manifest digest: `c92dcc2cd305861c487473e0eb7afb29d800e1c175b19f0413ca6c48e4765ac2` +Reproducibility SHA-256: `9d34b6d27c5d6425e347058f207400d8431d47d236c7a57d17071bb3e85fe949` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `833b596e3f49b12f36949e91446fb16fc209cf3b38d011222d65f8 | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `cc1032cdf913a8cf10050d239f29e45d18a10381` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `43fd17e7e66606fac92994cfc9eeb3871a03eff1` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `87414ef6734993f9a4cedf4b95a03473ba336884` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `541ffcd0e94e9db94638d8ffa5934da11ae38771` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `7709c7aec560ad0195ca171c093e4f8468301796` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `6736f52a1e2eb1b90c79a633ea0a382e06c85fac` | `2ce8aaef7419ab8f155563fd67ba0a53569d14bb` | `0024da90dcc53017f0d488ec16bd4bb1e4796c6f93cd6223df9b241ba5de56e1` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `0230c42fb4aa40aa3c917505457848da4f9883c2` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `cc0e3e11b86b8998eccaec7d7981cc4f051f156e` | `1b3fb46ad51706da678156160b88ab0e25a18294` | `471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65` | `CURRENT` | ## Convergence Findings From d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 04:43:29 -0500 Subject: [PATCH 33/73] fix(case-growth): verify reviewed rewrite projections --- src/hoxline/case_growth/collector.py | 22 ++++++ tests/test_case_growth_index_v0.py | 105 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 1b3fb46..1eed4c1 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -436,6 +436,26 @@ def _verify_selected_source_checkout( head != content_revision and _is_ancestor(repo, content_revision, head) ) content_tree = _git_output(repo, "rev-parse", f"{content_revision}^{{tree}}") + rewritten_reviewed_projection = False + if ( + repository != ".github" + and head != content_revision + and not head_is_content_ancestor + and not content_is_head_ancestor + ): + selected = str(entry["revision"]) + reviewed_tree = str(entry["reviewed_tree_sha"]) + selected_tree = _git_output(repo, "rev-parse", f"{selected}^{{tree}}") + content_is_selected_ancestor = ( + content_revision == selected + or _is_ancestor(repo, content_revision, selected) + ) + rewritten_reviewed_projection = ( + selected_tree is not None + and selected_tree == reviewed_tree + and tree == reviewed_tree + and content_is_selected_ancestor + ) if head_is_content_ancestor: errors.append( f"{repository}: checked head is behind the authority content revision" @@ -444,6 +464,8 @@ def _verify_selected_source_checkout( head != content_revision and not content_is_head_ancestor and content_tree != tree + and repository != ".github" + and not rewritten_reviewed_projection ): errors.append( f"{repository}: authority content revision is outside the reviewed current lineage" diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index a6fc4a2..d8622c1 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -676,6 +676,111 @@ def test_dynamic_command_center_selection_requires_exact_detached_observation(se errors = verify_selected_source_checkout(org_root, ".github") self.assertTrue(any("differs from the immutable workflow observation" in error for error in errors)) + def test_command_center_content_identity_survives_exact_rewritten_event_tree(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) + repository = "hawkinsoperations-detections" + repo = org_root / repository + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "detections").mkdir() + (repo / "detections" / "DETECTION_PROMOTION_MATRIX.yml").write_text( + "schema: detection-promotion-matrix-v1\nentries: []\n", + encoding="utf-8", + ) + _git(repo, "add", "detections/DETECTION_PROMOTION_MATRIX.yml") + _git(repo, "commit", "-m", "authority") + selected = _git(repo, "rev-parse", "HEAD") + reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") + _write_source_selection_manifest(org_root, repository, selected, reviewed_tree) + + command_center = org_root / ".github" + command_tree = _git(command_center, "rev-parse", "HEAD^{tree}") + rewritten = _git( + command_center, + "commit-tree", + command_tree, + input_text="rewritten command event\n", + ) + _git(command_center, "checkout", "--detach", rewritten) + with mock.patch.dict( + "os.environ", + {"HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": rewritten}, + ): + self.assertEqual( + verify_selected_source_checkout(org_root, ".github"), + [], + ) + + def test_content_commit_must_belong_to_selected_reviewed_lineage(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) + repository = "hawkinsoperations-detections" + repo = org_root / repository + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + (repo / "detections").mkdir() + authority_path = repo / "detections" / "DETECTION_PROMOTION_MATRIX.yml" + authority_path.write_text( + "schema: detection-promotion-matrix-v1\nentries: []\n", + encoding="utf-8", + ) + _git(repo, "add", "detections/DETECTION_PROMOTION_MATRIX.yml") + _git(repo, "commit", "-m", "authority content") + content_revision = _git(repo, "rev-parse", "HEAD") + content_tree = _git(repo, "rev-parse", "HEAD^{tree}") + (repo / "review.txt").write_text("reviewed final\n", encoding="utf-8") + _git(repo, "add", "review.txt") + _git(repo, "commit", "-m", "reviewed final") + selected = _git(repo, "rev-parse", "HEAD") + reviewed_tree = _git(repo, "rev-parse", "HEAD^{tree}") + _write_source_selection_manifest(org_root, repository, selected, reviewed_tree) + + command_center = org_root / ".github" + manifest_path = command_center / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json" + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + selected_entry = next( + entry + for entry in manifest["repositories"] + if entry["repository"] == repository + ) + selected_entry["authority_content_revision"] = content_revision + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + _git(command_center, "add", "governance/CONVERGENCE_SOURCE_MANIFEST.json") + _git(command_center, "commit", "-m", "select content ancestor") + + rewritten = _git(repo, "commit-tree", reviewed_tree, input_text="rewritten final\n") + _git(repo, "checkout", "--detach", rewritten) + self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + + unrelated_content = _git( + repo, + "commit-tree", + content_tree, + input_text="unrelated same authority content\n", + ) + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + selected_entry = next( + entry + for entry in manifest["repositories"] + if entry["repository"] == repository + ) + selected_entry["authority_content_revision"] = unrelated_content + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + _git(command_center, "add", "governance/CONVERGENCE_SOURCE_MANIFEST.json") + _git(command_center, "commit", "-m", "hostile unrelated content") + errors = verify_selected_source_checkout(org_root, repository) + self.assertTrue( + any( + "outside the reviewed current lineage" in error + for error in errors + ) + ) + def test_selected_source_checkout_rejects_older_same_authority_blob_ancestor(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: org_root = Path(temp_dir) From 1387938ccb10e02edc3e8d4d5353017d46b3704f Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 07:05:27 -0500 Subject: [PATCH 34/73] fix(hoxline): bind review authority and replay integrity --- .github/workflows/ci.yml | 7 + examples/demo/ho-det-010-safe-fixture.json | 4 +- .../ho-det-010-safe-negative-fixture.json | 4 +- .../aws-det-001-artifact-manifest-v1.json | 10 +- .../fixtures/aws-det-001-safe-fixture.json | 6 +- .../aws-det-001-safe-negative-fixture.json | 6 +- .../fixtures/ho-det-009-safe-fixture.json | 4 +- .../ho-det-009-safe-negative-fixture.json | 4 +- .../fixtures/ho-det-011-safe-fixture.json | 4 +- .../ho-det-011-safe-negative-fixture.json | 4 +- .../fixtures/ho-det-012-safe-fixture.json | 4 +- .../ho-det-012-safe-negative-fixture.json | 4 +- .../fixtures/ho-det-013-safe-fixture.json | 6 +- .../ho-det-013-safe-negative-fixture.json | 6 +- .../fixtures/id-det-001-safe-fixture.json | 6 +- .../id-det-001-safe-negative-fixture.json | 6 +- .../fixtures/id-det-002-safe-fixture.json | 6 +- .../id-det-002-safe-negative-fixture.json | 6 +- .../fixtures/id-det-003-safe-fixture.json | 6 +- .../id-det-003-safe-negative-fixture.json | 6 +- .../fixtures/id-det-004-safe-fixture.json | 6 +- .../id-det-004-safe-negative-fixture.json | 6 +- .../ho-det-009-artifact-manifest-v1.json | 12 +- .../ho-det-010-artifact-manifest-v1.json | 6 +- .../ho-det-011-artifact-manifest-v1.json | 12 +- .../ho-det-012-artifact-manifest-v1.json | 12 +- .../ho-det-013-artifact-manifest-v1.json | 10 +- .../ho-ndr-001-artifact-manifest-v1.json | 4 +- .../id-det-001-artifact-manifest-v1.json | 10 +- .../id-det-002-artifact-manifest-v1.json | 10 +- .../id-det-003-artifact-manifest-v1.json | 10 +- .../id-det-004-artifact-manifest-v1.json | 10 +- .../multi-artifact-review-index-v1.json | 2 +- schemas/artifact-manifest-v1.schema.json | 2 +- src/hoxline/demo.py | 52 +- src/hoxline/review_engine.py | 739 +++++++++++++++--- ...i_artifact_review_manifest_expansion_v1.py | 116 +++ tests/test_one_command_reviewer_demo_v0.py | 2 +- tests/test_review_engine_v1.py | 279 ++++++- 39 files changed, 1172 insertions(+), 237 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27aec7a..8585a6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: org/hoxline + fetch-depth: 0 persist-credentials: false - name: Check out command center @@ -30,6 +31,7 @@ jobs: repository: HawkinsOperations/.github ref: 3afb59fc452fc1fbdf14207416068ec72b084464 path: org/.github + fetch-depth: 0 persist-credentials: false - name: Check out detection authority @@ -38,6 +40,7 @@ jobs: repository: HawkinsOperations/hawkinsoperations-detections ref: 16d4432eb46d457939f1cfe573c0c5d712650efb path: org/hawkinsoperations-detections + fetch-depth: 0 persist-credentials: false - name: Check out validation authority @@ -46,6 +49,7 @@ jobs: repository: HawkinsOperations/hawkinsoperations-validation ref: 34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc path: org/hawkinsoperations-validation + fetch-depth: 0 persist-credentials: false - name: Check out platform authority @@ -54,6 +58,7 @@ jobs: repository: HawkinsOperations/hawkinsoperations-platform ref: 42c3a4b78548fc4f95fd017f633eaaf207fc7625 path: org/hawkinsoperations-platform + fetch-depth: 0 persist-credentials: false - name: Check out proof authority @@ -62,6 +67,7 @@ jobs: repository: HawkinsOperations/hawkinsoperations-proof ref: c60882552571c1664549d3e594c6994dc27dce63 path: org/hawkinsoperations-proof + fetch-depth: 0 persist-credentials: false - name: Check out website rendering contract @@ -70,6 +76,7 @@ jobs: repository: HawkinsOperations/hawkinsoperations-website ref: 7709c7aec560ad0195ca171c093e4f8468301796 path: org/hawkinsoperations-website + fetch-depth: 0 persist-credentials: false - name: Verify and record exact seven-repository checkout diff --git a/examples/demo/ho-det-010-safe-fixture.json b/examples/demo/ho-det-010-safe-fixture.json index a79fa4c..ccb022d 100644 --- a/examples/demo/ho-det-010-safe-fixture.json +++ b/examples/demo/ho-det-010-safe-fixture.json @@ -2,14 +2,14 @@ "schema_version": "hoxline-demo-fixture-v0", "fixture_id": "ho-det-010-safe-positive-fixture-v0", "artifact_id": "HO-DET-010", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "safe_fixture": true, "expected_detection": true, "endpoint_mutation": false, "runtime_required": false, "network_required": false, "host": "demo-host-010", - "description": "Synthetic local Administrators membership change fixture for the one-command reviewer demo.", + "description": "Controlled-test local Administrators membership change fixture for the one-command reviewer demo.", "events": [ { "event_id": 4732, diff --git a/examples/demo/ho-det-010-safe-negative-fixture.json b/examples/demo/ho-det-010-safe-negative-fixture.json index bed9fae..566961d 100644 --- a/examples/demo/ho-det-010-safe-negative-fixture.json +++ b/examples/demo/ho-det-010-safe-negative-fixture.json @@ -2,14 +2,14 @@ "schema_version": "hoxline-demo-fixture-v0", "fixture_id": "ho-det-010-safe-negative-fixture-v0", "artifact_id": "HO-DET-010", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "safe_fixture": true, "expected_detection": false, "endpoint_mutation": false, "runtime_required": false, "network_required": false, "host": "demo-host-010", - "description": "Synthetic non-admin group membership fixture that must not fire the demo detection.", + "description": "Controlled-test non-admin group membership fixture that must not fire the demo detection.", "events": [ { "event_id": 4732, diff --git a/examples/review/aws-det-001-artifact-manifest-v1.json b/examples/review/aws-det-001-artifact-manifest-v1.json index 8c52815..b75d29b 100644 --- a/examples/review/aws-det-001-artifact-manifest-v1.json +++ b/examples/review/aws-det-001-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "AWS-DET-001", "artifact_name": "AWS IAM denied action fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_key": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/aws-det-001-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "AWS-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "AWS-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the AWS-DET-001 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the AWS-DET-001 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/fixtures/aws-det-001-safe-fixture.json b/examples/review/fixtures/aws-det-001-safe-fixture.json index f69c031..ccd6f6b 100644 --- a/examples/review/fixtures/aws-det-001-safe-fixture.json +++ b/examples/review/fixtures/aws-det-001-safe-fixture.json @@ -6,13 +6,13 @@ "event_key": "CreateUser", "channel": "AWS CloudTrail fixture", "action": "iam_action", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "aws-det-001-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/aws-det-001-safe-negative-fixture.json b/examples/review/fixtures/aws-det-001-safe-negative-fixture.json index f4f9517..23f2e0d 100644 --- a/examples/review/fixtures/aws-det-001-safe-negative-fixture.json +++ b/examples/review/fixtures/aws-det-001-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_key": "ListUsers", "channel": "AWS CloudTrail fixture", "action": "iam_action", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "aws-det-001-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/ho-det-009-safe-fixture.json b/examples/review/fixtures/ho-det-009-safe-fixture.json index 438500a..648f06b 100644 --- a/examples/review/fixtures/ho-det-009-safe-fixture.json +++ b/examples/review/fixtures/ho-det-009-safe-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-009", - "description": "Synthetic fixture for HO-DET-009; local review only and not runtime evidence.", + "description": "Controlled-test fixture for HO-DET-009; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -13,7 +13,7 @@ ], "expected_detection": true, "fixture_id": "ho-det-009-safe-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-009", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-009-safe-negative-fixture.json b/examples/review/fixtures/ho-det-009-safe-negative-fixture.json index 250fa6c..aebe4fa 100644 --- a/examples/review/fixtures/ho-det-009-safe-negative-fixture.json +++ b/examples/review/fixtures/ho-det-009-safe-negative-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-009", - "description": "Synthetic negative fixture for HO-DET-009; local review only and not runtime evidence.", + "description": "Controlled-test negative fixture for HO-DET-009; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -13,7 +13,7 @@ ], "expected_detection": false, "fixture_id": "ho-det-009-safe-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-009", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-011-safe-fixture.json b/examples/review/fixtures/ho-det-011-safe-fixture.json index d8c25ea..8148538 100644 --- a/examples/review/fixtures/ho-det-011-safe-fixture.json +++ b/examples/review/fixtures/ho-det-011-safe-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-011", - "description": "Synthetic fixture for HO-DET-011; local review only and not runtime evidence.", + "description": "Controlled-test fixture for HO-DET-011; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -14,7 +14,7 @@ ], "expected_detection": true, "fixture_id": "ho-det-011-safe-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-011", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-011-safe-negative-fixture.json b/examples/review/fixtures/ho-det-011-safe-negative-fixture.json index 3ae3f76..5e1f299 100644 --- a/examples/review/fixtures/ho-det-011-safe-negative-fixture.json +++ b/examples/review/fixtures/ho-det-011-safe-negative-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-011", - "description": "Synthetic negative fixture for HO-DET-011; local review only and not runtime evidence.", + "description": "Controlled-test negative fixture for HO-DET-011; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -14,7 +14,7 @@ ], "expected_detection": false, "fixture_id": "ho-det-011-safe-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-011", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-012-safe-fixture.json b/examples/review/fixtures/ho-det-012-safe-fixture.json index 0b5b176..2d70846 100644 --- a/examples/review/fixtures/ho-det-012-safe-fixture.json +++ b/examples/review/fixtures/ho-det-012-safe-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-012", - "description": "Synthetic fixture for HO-DET-012; local review only and not runtime evidence.", + "description": "Controlled-test fixture for HO-DET-012; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -14,7 +14,7 @@ ], "expected_detection": true, "fixture_id": "ho-det-012-safe-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-012", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-012-safe-negative-fixture.json b/examples/review/fixtures/ho-det-012-safe-negative-fixture.json index 4e2a353..680b25c 100644 --- a/examples/review/fixtures/ho-det-012-safe-negative-fixture.json +++ b/examples/review/fixtures/ho-det-012-safe-negative-fixture.json @@ -1,6 +1,6 @@ { "artifact_id": "HO-DET-012", - "description": "Synthetic negative fixture for HO-DET-012; local review only and not runtime evidence.", + "description": "Controlled-test negative fixture for HO-DET-012; local review only and not runtime evidence.", "endpoint_mutation": false, "events": [ { @@ -14,7 +14,7 @@ ], "expected_detection": false, "fixture_id": "ho-det-012-safe-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "host": "demo-host-012", "network_required": false, "runtime_required": false, diff --git a/examples/review/fixtures/ho-det-013-safe-fixture.json b/examples/review/fixtures/ho-det-013-safe-fixture.json index 6a1eedf..a6dc3bc 100644 --- a/examples/review/fixtures/ho-det-013-safe-fixture.json +++ b/examples/review/fixtures/ho-det-013-safe-fixture.json @@ -6,13 +6,13 @@ "event_id": 1102, "channel": "Windows Security", "action": "security_control_tamper", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "ho-det-013-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/ho-det-013-safe-negative-fixture.json b/examples/review/fixtures/ho-det-013-safe-negative-fixture.json index c975f4d..6e163bd 100644 --- a/examples/review/fixtures/ho-det-013-safe-negative-fixture.json +++ b/examples/review/fixtures/ho-det-013-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_id": 7036, "channel": "Windows Security", "action": "security_control_tamper", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "ho-det-013-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-001-safe-fixture.json b/examples/review/fixtures/id-det-001-safe-fixture.json index 371c33b..e8a822e 100644 --- a/examples/review/fixtures/id-det-001-safe-fixture.json +++ b/examples/review/fixtures/id-det-001-safe-fixture.json @@ -6,13 +6,13 @@ "event_key": "impossible_travel", "channel": "Identity fixture", "action": "identity_session_context", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "id-det-001-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-001-safe-negative-fixture.json b/examples/review/fixtures/id-det-001-safe-negative-fixture.json index 39cbef6..6ef9c88 100644 --- a/examples/review/fixtures/id-det-001-safe-negative-fixture.json +++ b/examples/review/fixtures/id-det-001-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_key": "known_device_login", "channel": "Identity fixture", "action": "identity_session_context", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "id-det-001-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-002-safe-fixture.json b/examples/review/fixtures/id-det-002-safe-fixture.json index fc0c48f..9d4783d 100644 --- a/examples/review/fixtures/id-det-002-safe-fixture.json +++ b/examples/review/fixtures/id-det-002-safe-fixture.json @@ -6,13 +6,13 @@ "event_key": "mfa_push_fatigue_volume", "channel": "Identity fixture", "action": "authentication_control_change", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "id-det-002-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-002-safe-negative-fixture.json b/examples/review/fixtures/id-det-002-safe-negative-fixture.json index f584eab..8d453fc 100644 --- a/examples/review/fixtures/id-det-002-safe-negative-fixture.json +++ b/examples/review/fixtures/id-det-002-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_key": "approved_authentication_change", "channel": "Identity fixture", "action": "authentication_control_change", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "id-det-002-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-003-safe-fixture.json b/examples/review/fixtures/id-det-003-safe-fixture.json index bb79978..2e3f667 100644 --- a/examples/review/fixtures/id-det-003-safe-fixture.json +++ b/examples/review/fixtures/id-det-003-safe-fixture.json @@ -6,13 +6,13 @@ "event_key": "privileged_role_assignment", "channel": "Identity fixture", "action": "privilege_change", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "id-det-003-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-003-safe-negative-fixture.json b/examples/review/fixtures/id-det-003-safe-negative-fixture.json index e8104f5..b3dcb2e 100644 --- a/examples/review/fixtures/id-det-003-safe-negative-fixture.json +++ b/examples/review/fixtures/id-det-003-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_key": "approved_role_assignment", "channel": "Identity fixture", "action": "privilege_change", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "id-det-003-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-004-safe-fixture.json b/examples/review/fixtures/id-det-004-safe-fixture.json index 5d0e4cb..649c671 100644 --- a/examples/review/fixtures/id-det-004-safe-fixture.json +++ b/examples/review/fixtures/id-det-004-safe-fixture.json @@ -6,13 +6,13 @@ "event_key": "impossible_travel", "channel": "Identity fixture", "action": "travel_session_context", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": true, "fixture_id": "id-det-004-positive-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/fixtures/id-det-004-safe-negative-fixture.json b/examples/review/fixtures/id-det-004-safe-negative-fixture.json index fa7480a..6789f67 100644 --- a/examples/review/fixtures/id-det-004-safe-negative-fixture.json +++ b/examples/review/fixtures/id-det-004-safe-negative-fixture.json @@ -6,13 +6,13 @@ "event_key": "approved_travel", "channel": "Identity fixture", "action": "travel_session_context", - "actor": "synthetic-review-actor" + "actor": "controlled-test-review-actor" } ], "expected_detection": false, "fixture_id": "id-det-004-negative-fixture-v1", - "fixture_kind": "synthetic-demo-only", - "host": "synthetic-fixture-host", + "fixture_kind": "controlled-test-demo-only", + "host": "controlled-test-fixture-host", "network_required": false, "runtime_required": false, "safe_fixture": true, diff --git a/examples/review/ho-det-009-artifact-manifest-v1.json b/examples/review/ho-det-009-artifact-manifest-v1.json index acebc4a..760a3f0 100644 --- a/examples/review/ho-det-009-artifact-manifest-v1.json +++ b/examples/review/ho-det-009-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "HO-DET-009", "artifact_name": "Windows local user account creation detection", "artifact_type": "detection-review-artifact", @@ -39,7 +39,7 @@ ], "field_mapping": { "action": "account lifecycle action", - "actor": "synthetic actor label", + "actor": "controlled-test actor label", "event_id": "event identifier", "target_account": "local account under review" }, @@ -58,12 +58,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "HO-DET-009 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "HO-DET-009 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [ @@ -79,14 +79,14 @@ ], "scope": "fixture-only metadata contract", "source": "Windows Security EventChannel", - "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only synthetic fixture metadata.", + "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only controlled-test fixture metadata.", "wazuh_rule_ids": [ 910091, 910092, 910093 ] }, - "triage_what_happened": "A synthetic fixture represented a local user account creation pattern.", + "triage_what_happened": "A controlled-test fixture represented a local user account creation pattern.", "triage_why_it_matters": "Unexpected local account creation can indicate account lifecycle abuse when backed by governed evidence.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/ho-det-010-artifact-manifest-v1.json b/examples/review/ho-det-010-artifact-manifest-v1.json index 15b6052..adc3a3a 100644 --- a/examples/review/ho-det-010-artifact-manifest-v1.json +++ b/examples/review/ho-det-010-artifact-manifest-v1.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -45,7 +45,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -70,6 +70,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/ho-det-011-artifact-manifest-v1.json b/examples/review/ho-det-011-artifact-manifest-v1.json index 37418ac..69898ad 100644 --- a/examples/review/ho-det-011-artifact-manifest-v1.json +++ b/examples/review/ho-det-011-artifact-manifest-v1.json @@ -4,7 +4,7 @@ ], "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "HO-DET-011", "artifact_name": "Windows service creation or service binary change detection", "artifact_type": "detection-review-artifact", @@ -42,7 +42,7 @@ ], "field_mapping": { "action": "service creation action", - "actor": "synthetic actor label", + "actor": "controlled-test actor label", "event_id": "event identifier", "service_image_path": "service binary path label", "service_name": "service under review" @@ -62,12 +62,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "HO-DET-011 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "HO-DET-011 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [ @@ -83,7 +83,7 @@ ], "scope": "fixture-only metadata contract", "source": "Windows Security EventChannel", - "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only synthetic fixture metadata.", + "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only controlled-test fixture metadata.", "wazuh_rule_ids": [ 910011, 910012, @@ -91,7 +91,7 @@ 910014 ] }, - "triage_what_happened": "A synthetic fixture represented a Windows service creation pattern.", + "triage_what_happened": "A controlled-test fixture represented a Windows service creation pattern.", "triage_why_it_matters": "Unexpected service creation can indicate persistence when backed by governed evidence.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/ho-det-012-artifact-manifest-v1.json b/examples/review/ho-det-012-artifact-manifest-v1.json index 50009b7..df01cc0 100644 --- a/examples/review/ho-det-012-artifact-manifest-v1.json +++ b/examples/review/ho-det-012-artifact-manifest-v1.json @@ -4,7 +4,7 @@ ], "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "HO-DET-012", "artifact_name": "Suspicious scheduled task creation or update detection", "artifact_type": "detection-review-artifact", @@ -42,7 +42,7 @@ ], "field_mapping": { "action": "task lifecycle action", - "actor": "synthetic actor label", + "actor": "controlled-test actor label", "event_id": "event identifier", "task_action": "task action label", "task_name": "scheduled task under review" @@ -62,12 +62,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "HO-DET-012 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "HO-DET-012 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [ @@ -84,14 +84,14 @@ ], "scope": "fixture-only metadata contract", "source": "Windows Security EventChannel", - "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only synthetic fixture metadata.", + "source_control_note": "Source-controlled detection and validation metadata exists outside Hoxline; this manifest carries only controlled-test fixture metadata.", "wazuh_rule_ids": [ 910021, 910022, 910023 ] }, - "triage_what_happened": "A synthetic fixture represented a scheduled task creation pattern.", + "triage_what_happened": "A controlled-test fixture represented a scheduled task creation pattern.", "triage_why_it_matters": "Unexpected scheduled tasks can indicate persistence when backed by governed evidence.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/ho-det-013-artifact-manifest-v1.json b/examples/review/ho-det-013-artifact-manifest-v1.json index 3ca38a3..3e42d8b 100644 --- a/examples/review/ho-det-013-artifact-manifest-v1.json +++ b/examples/review/ho-det-013-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "HO-DET-013", "artifact_name": "Security control tamper fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_id": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/ho-det-013-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "HO-DET-013 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "HO-DET-013 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [ @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the HO-DET-013 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the HO-DET-013 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/ho-ndr-001-artifact-manifest-v1.json b/examples/review/ho-ndr-001-artifact-manifest-v1.json index ede758c..4eaffd7 100644 --- a/examples/review/ho-ndr-001-artifact-manifest-v1.json +++ b/examples/review/ho-ndr-001-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "boundary contract review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "HO-NDR-001", "artifact_name": "Security Onion NDR boundary contract", "artifact_type": "boundary-review-artifact", @@ -43,7 +43,7 @@ ], "runtime_boundary": "No runtime execution, Security Onion access, endpoint mutation, SIEM action, or live signal action.", "runtime_proof": false, - "signal_boundary": "No synthetic or live signal is claimed.", + "signal_boundary": "No controlled-test or live signal is claimed.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], diff --git a/examples/review/id-det-001-artifact-manifest-v1.json b/examples/review/id-det-001-artifact-manifest-v1.json index 4a06150..429b784 100644 --- a/examples/review/id-det-001-artifact-manifest-v1.json +++ b/examples/review/id-det-001-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "ID-DET-001", "artifact_name": "Identity session context fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_key": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/id-det-001-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "ID-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "ID-DET-001 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the ID-DET-001 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the ID-DET-001 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/id-det-002-artifact-manifest-v1.json b/examples/review/id-det-002-artifact-manifest-v1.json index 771c9e8..da2d8b2 100644 --- a/examples/review/id-det-002-artifact-manifest-v1.json +++ b/examples/review/id-det-002-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "ID-DET-002", "artifact_name": "Identity authentication control fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_key": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/id-det-002-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "ID-DET-002 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "ID-DET-002 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the ID-DET-002 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the ID-DET-002 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/id-det-003-artifact-manifest-v1.json b/examples/review/id-det-003-artifact-manifest-v1.json index 9b7c6e2..2473385 100644 --- a/examples/review/id-det-003-artifact-manifest-v1.json +++ b/examples/review/id-det-003-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "ID-DET-003", "artifact_name": "Identity privilege change fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_key": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/id-det-003-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "ID-DET-003 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "ID-DET-003 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the ID-DET-003 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the ID-DET-003 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/id-det-004-artifact-manifest-v1.json b/examples/review/id-det-004-artifact-manifest-v1.json index e145e5a..d890ef0 100644 --- a/examples/review/id-det-004-artifact-manifest-v1.json +++ b/examples/review/id-det-004-artifact-manifest-v1.json @@ -1,7 +1,7 @@ { "ai_disposition_authority": false, "allowed_claim_class": "controlled local fixture review only", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "artifact_id": "ID-DET-004", "artifact_name": "Identity travel context fixture review", "artifact_type": "detection-review-artifact", @@ -30,7 +30,7 @@ "event_key": "fixture selector", "channel": "fixture source label", "action": "bounded fixture action", - "actor": "synthetic actor label" + "actor": "controlled-test actor label" }, "fixture_paths": { "negative": "examples/review/fixtures/id-det-004-safe-negative-fixture.json", @@ -47,12 +47,12 @@ "public_proof_promoted": false, "public_safe_status": "NOT_PUBLIC_SAFE", "requested_claims": [ - "ID-DET-004 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled synthetic fixtures only." + "ID-DET-004 can be reviewed locally through deterministic Hoxline Review Engine v1 using bundled controlled-test fixtures only." ], "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", "runtime_proof": false, "severity": "medium", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "source_owner": "hawkinsoperations-detections", "telemetry_contract": { "event_ids": [], @@ -71,7 +71,7 @@ "source_control_note": "Source and controlled-validation metadata remain owned by their repositories; this manifest carries sanitized fixture metadata only.", "wazuh_rule_ids": [] }, - "triage_what_happened": "A synthetic fixture represented the ID-DET-004 controlled review pattern.", + "triage_what_happened": "A controlled-test fixture represented the ID-DET-004 controlled review pattern.", "triage_why_it_matters": "The bounded pattern is useful for deterministic reviewer reproduction when kept separate from runtime and proof claims.", "validation_owner": "hawkinsoperations-validation", "wazuh_mutation": false diff --git a/examples/review/multi-artifact-review-index-v1.json b/examples/review/multi-artifact-review-index-v1.json index a4e503d..00b007d 100644 --- a/examples/review/multi-artifact-review-index-v1.json +++ b/examples/review/multi-artifact-review-index-v1.json @@ -76,5 +76,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/schemas/artifact-manifest-v1.schema.json b/schemas/artifact-manifest-v1.schema.json index 718852f..15eddc2 100644 --- a/schemas/artifact-manifest-v1.schema.json +++ b/schemas/artifact-manifest-v1.schema.json @@ -38,7 +38,7 @@ }, "artifact_name": { "type": "string", "minLength": 1 }, "artifact_type": { "const": "detection-review-artifact" }, - "artifact_family": { "const": "synthetic-review-only" }, + "artifact_family": { "const": "controlled-test-review-only" }, "source_owner": { "const": "hawkinsoperations-detections" }, "validation_owner": { "const": "hawkinsoperations-validation" }, "platform_owner": { "const": "hawkinsoperations-platform" }, diff --git a/src/hoxline/demo.py b/src/hoxline/demo.py index e6fc172..ea030d8 100644 --- a/src/hoxline/demo.py +++ b/src/hoxline/demo.py @@ -13,7 +13,7 @@ SCHEMA_VERSION = "hoxline-demo-run-v0" DEMO_ID = "hoxline-one-command-reviewer-demo-v0" ARTIFACT_ID = "HO-DET-010" -ARTIFACT_TYPE = "synthetic-local-admin-membership-change-detection" +ARTIFACT_TYPE = "controlled-test-local-admin-membership-change-detection" PROOF_CEILING = "CONTROLLED_FIXTURE_VALIDATED" PUBLIC_SAFE_STATUS = "NOT_PUBLIC_SAFE" SAFE_ALLOWED_CLAIM = ( @@ -45,7 +45,7 @@ "evidence-graph.json", "telemetry-contract-check.json", "validation-result.json", - "synthetic-signal.json", + "controlled-test-signal.json", "enrichment.json", "triage-summary.md", "proofcard.json", @@ -118,7 +118,7 @@ def build_demo_run( intake = _artifact_intake() telemetry = _telemetry_contract_check(fixture) validation = _controlled_validation(fixture, negative_fixture, telemetry) - signal = _synthetic_signal(fixture, validation) + signal = _controlled_test_signal(fixture, validation) enrichment = _enrichment(fixture) triage = _triage(signal, enrichment, validation) proofcard = _proofcard(intake, telemetry, validation, signal, enrichment, triage) @@ -129,7 +129,7 @@ def build_demo_run( "evidence_graph": evidence_graph, "telemetry_contract_check": telemetry, "validation_result": validation, - "synthetic_signal": signal, + "controlled_test_signal": signal, "enrichment": enrichment, "triage_summary": _triage_markdown(triage), "proofcard": proofcard, @@ -153,7 +153,7 @@ def write_demo_run(output_dir: Path, run: dict[str, Any], force: bool = False) - "evidence-graph.json": run["evidence_graph"], "telemetry-contract-check.json": run["telemetry_contract_check"], "validation-result.json": run["validation_result"], - "synthetic-signal.json": run["synthetic_signal"], + "controlled-test-signal.json": run["controlled_test_signal"], "enrichment.json": run["enrichment"], "triage-summary.md": run["triage_summary"], "proofcard.json": run["proofcard"], @@ -185,7 +185,7 @@ def verify_demo_run_dir(input_path: Path) -> list[str]: claim_authority = _load_json(run_dir / "claim-authority.json") telemetry = _load_json(run_dir / "telemetry-contract-check.json") validation = _load_json(run_dir / "validation-result.json") - signal = _load_json(run_dir / "synthetic-signal.json") + signal = _load_json(run_dir / "controlled-test-signal.json") reviewer_pack = (run_dir / "reviewer-pack.md").read_text(encoding="utf-8") except (OSError, DemoError) as exc: return [str(exc)] @@ -217,7 +217,7 @@ def verify_demo_run_dir(input_path: Path) -> list[str]: if validation.get("result") != "pass" or validation.get("endpoint_mutation") is not False: errors.append("validation must pass without endpoint mutation") if signal.get("detection_fired") is not True or signal.get("source") != "safe bundled fixture": - errors.append("synthetic signal must fire from safe bundled fixture only") + errors.append("controlled-test signal must fire from safe bundled fixture only") if proofcard.get("public_safe_status") != PUBLIC_SAFE_STATUS: errors.append("ProofCard must keep public_safe_status NOT_PUBLIC_SAFE") if proofcard.get("human_review_required") is not True: @@ -248,7 +248,7 @@ def render_quickstart_console(output_dir: Path, run: dict[str, Any]) -> str: "2. Evidence graph linked.", "3. Telemetry contract checked.", "4. Controlled validation passed using bundled fixture.", - "5. Safe synthetic signal/detection event fired.", + "5. Safe controlled-test signal/detection event fired.", "6. Enrichment attached ATT&CK / source / field mapping.", "7. Triage summary generated.", "8. ProofCard rendered.", @@ -270,14 +270,14 @@ def _artifact_intake() -> dict[str, Any]: "artifact_id": ARTIFACT_ID, "artifact_type": ARTIFACT_TYPE, "source_owner": "hawkinsoperations-detections", - "source_label": "synthetic demo fixture for local Administrators membership change logic", + "source_label": "controlled-test demo fixture for local Administrators membership change logic", "ai_assisted": True, "initial_claim_ceiling": PROOF_CEILING, "public_safe_status": PUBLIC_SAFE_STATUS, "human_review_required": True, "ai_disposition_authority": False, "notes": [ - "Fixture is synthetic and local-only.", + "Fixture is controlled-test and local-only.", "No users, groups, endpoints, Wazuh systems, or private infrastructure are touched.", ], } @@ -318,15 +318,15 @@ def _controlled_validation(fixture: dict[str, Any], negative_fixture: dict[str, "endpoint_mutation": False, "runtime_rerun": False, "wazuh_mutation": False, - "explanation": "Validation evaluates bundled synthetic fixture records only.", + "explanation": "Validation evaluates bundled controlled-test fixture records only.", } -def _synthetic_signal(fixture: dict[str, Any], validation: dict[str, Any]) -> dict[str, Any]: +def _controlled_test_signal(fixture: dict[str, Any], validation: dict[str, Any]) -> dict[str, Any]: return { - "schema_version": "synthetic-signal-v0", + "schema_version": "controlled-test-signal-v0", "artifact_id": ARTIFACT_ID, - "signal_id": "synthetic-signal-ho-det-010-demo-v0", + "signal_id": "controlled-test-signal-ho-det-010-demo-v0", "source": "safe bundled fixture", "detection_fired": validation["result"] == "pass" and _fixture_matches_detection(fixture), "simulation_only": True, @@ -349,8 +349,8 @@ def _enrichment(fixture: dict[str, Any]) -> dict[str, Any]: "4733": "member removed from local group", "4738": "user account changed", }, - "source_mapping": {"channel": "Windows Security EventChannel", "fixture_host": fixture["host"], "fixture_scope": "synthetic demo host"}, - "field_mapping": {"event_id": "event identifier", "target_account": "account under review", "group_name": "local group name", "action": "membership or account action", "actor": "synthetic actor label"}, + "source_mapping": {"channel": "Windows Security EventChannel", "fixture_host": fixture["host"], "fixture_scope": "controlled-test demo host"}, + "field_mapping": {"event_id": "event identifier", "target_account": "account under review", "group_name": "local group name", "action": "membership or account action", "actor": "controlled-test actor label"}, "confidence": "bounded-demo-high", "severity": "medium", } @@ -360,9 +360,9 @@ def _triage(signal: dict[str, Any], enrichment: dict[str, Any], validation: dict return { "schema_version": "triage-summary-v0", "artifact_id": ARTIFACT_ID, - "what_happened": "A synthetic fixture represented a local Administrators membership change pattern.", + "what_happened": "A controlled-test fixture represented a local Administrators membership change pattern.", "why_it_matters": "Unexpected local administrator membership changes can indicate account or privilege manipulation.", - "evidence_exists": ["artifact intake record", "evidence graph", "telemetry contract check", "positive and negative synthetic fixtures", "fixture-derived synthetic signal", "enrichment mapping", "ProofCard", "Claim Authority decision"], + "evidence_exists": ["artifact intake record", "evidence graph", "telemetry contract check", "positive and negative controlled-test fixtures", "fixture-derived controlled-test signal", "enrichment mapping", "ProofCard", "Claim Authority decision"], "missing_evidence": ["public-safe runtime proof", "public signal proof", "human review gate completion", "final authorization record"], "next_gate": "human_review_gate", "detection_fired": signal["detection_fired"], @@ -382,10 +382,10 @@ def _proofcard(intake: dict[str, Any], telemetry: dict[str, Any], validation: di "proof_ceiling_meaning": "LOCAL_FIXTURE_DEMONSTRATION_ONLY", "review_lane": "ONE_COMMAND_REVIEWER_DEMO_V0", "review_version": "v0", - "owner_split": {"source_truth": "hawkinsoperations-detections", "behavior_truth": "bundled synthetic fixture", "platform_runtime_truth": "not asserted", "proof_authority": "not asserted by demo", "rendering": "local generated files only"}, + "owner_split": {"source_truth": "hawkinsoperations-detections", "behavior_truth": "bundled controlled-test fixture", "platform_runtime_truth": "not asserted", "proof_authority": "not asserted by demo", "rendering": "local generated files only"}, "telemetry_contract": telemetry, "controlled_validation": validation, - "synthetic_signal": signal, + "controlled_test_signal": signal, "enrichment": enrichment, "triage": triage, "allowed_claims": [SAFE_ALLOWED_CLAIM], @@ -420,7 +420,7 @@ def _evidence_graph(intake: dict[str, Any], telemetry: dict[str, Any], validatio _node("artifact-intake", "artifact_intake", intake["source_owner"], "PASS"), _node("telemetry-contract-check", "telemetry_contract_check", "hoxline-demo-fixture", telemetry["result"].upper()), _node("controlled-validation", "controlled_validation", "hoxline-demo-fixture", validation["result"].upper()), - _node("synthetic-signal", "synthetic_signal", "hoxline-demo-fixture", "PASS"), + _node("controlled-test-signal", "controlled_test_signal", "hoxline-demo-fixture", "PASS"), _node("proofcard", "proofcard", proofcard["proof_owner"], "PASS"), _node("claim-authority", "claim_authority", "hoxline", "PASS"), ] @@ -433,8 +433,8 @@ def _evidence_graph(intake: dict[str, Any], telemetry: dict[str, Any], validatio "edges": [ {"from": "artifact-intake", "to": "telemetry-contract-check", "relationship": "declares assumptions"}, {"from": "telemetry-contract-check", "to": "controlled-validation", "relationship": "bounds fixture validation"}, - {"from": "controlled-validation", "to": "synthetic-signal", "relationship": "creates fixture-only signal"}, - {"from": "synthetic-signal", "to": "proofcard", "relationship": "summarized by"}, + {"from": "controlled-validation", "to": "controlled-test-signal", "relationship": "creates fixture-only signal"}, + {"from": "controlled-test-signal", "to": "proofcard", "relationship": "summarized by"}, {"from": "proofcard", "to": "claim-authority", "relationship": "constrains claims"}, ], "missing_evidence": proofcard["missing_evidence"], @@ -461,14 +461,14 @@ def _run_summary(intake: dict[str, Any], evidence_graph: dict[str, Any], telemet "public_proof_promoted": False, "lifetime_ledger_changed": False, "website_rendering_is_proof": False, - "stage_results": {"intake": intake["intake_id"], "evidence_graph": evidence_graph["graph_id"], "telemetry_contract_check": telemetry["result"], "controlled_validation": validation["result"], "synthetic_signal": signal["detection_fired"], "enrichment": enrichment["confidence"], "triage": triage["next_gate"], "proofcard": proofcard["proofcard_id"], "claim_authority": claim_authority["decision_id"]}, + "stage_results": {"intake": intake["intake_id"], "evidence_graph": evidence_graph["graph_id"], "telemetry_contract_check": telemetry["result"], "controlled_validation": validation["result"], "controlled_test_signal": signal["detection_fired"], "enrichment": enrichment["confidence"], "triage": triage["next_gate"], "proofcard": proofcard["proofcard_id"], "claim_authority": claim_authority["decision_id"]}, } def _reviewer_pack(proofcard: dict[str, Any], claim_authority: dict[str, Any], triage: dict[str, Any]) -> str: lines = [ "# Hoxline One-Command Reviewer Demo v0", "", f"Product: {PRODUCT}", "", f"Doctrine: {DOCTRINE}", "", f"Artifact: `{ARTIFACT_ID}`", "", "## 30-Second Talk Track", "", TALK_TRACK, "", - "## What This Proves", "", "- A reviewer can run Hoxline locally against bundled synthetic fixtures.", "- Hoxline can produce intake, graph, telemetry, validation, signal simulation, enrichment, triage, ProofCard, Claim Authority, and reviewer-pack outputs.", "- Claim Authority allows only bounded demo wording and blocks stronger public claims.", "", + "## What This Proves", "", "- A reviewer can run Hoxline locally against bundled controlled-test fixtures.", "- Hoxline can produce intake, graph, telemetry, validation, signal simulation, enrichment, triage, ProofCard, Claim Authority, and reviewer-pack outputs.", "- Claim Authority allows only bounded demo wording and blocks stronger public claims.", "", "## What This Does Not Prove", "", "- It does not prove live runtime behavior.", "- It does not prove public signal observation.", "- It does not prove public-safe status, production readiness, deployment, approval, authorization, or case closure.", "- It does not touch endpoints, users, groups, Wazuh, Splunk, Cribl, private infrastructure, or ledgers.", "", "## Triage", "", f"- What happened: {triage['what_happened']}", f"- Why it matters: {triage['why_it_matters']}", f"- Next gate: `{triage['next_gate']}`", "", "## Allowed Claim", "", f"- {claim_authority['allowed_claims'][0]}", "", "## Blocked Claims", "", ] @@ -542,7 +542,7 @@ def _node(node_id: str, node_type: str, owner: str, status: str) -> dict[str, st def _validate_fixture(fixture: dict[str, Any], expected_detection: bool) -> None: - expected = {"schema_version": "hoxline-demo-fixture-v0", "artifact_id": ARTIFACT_ID, "fixture_kind": "synthetic-demo-only", "safe_fixture": True, "endpoint_mutation": False, "runtime_required": False, "network_required": False} + expected = {"schema_version": "hoxline-demo-fixture-v0", "artifact_id": ARTIFACT_ID, "fixture_kind": "controlled-test-demo-only", "safe_fixture": True, "endpoint_mutation": False, "runtime_required": False, "network_required": False} for field, value in expected.items(): if fixture.get(field) != value: raise DemoError(f"fixture field {field} must be {value!r}") diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 5294c2d..6503abe 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -11,6 +11,7 @@ import shutil import subprocess from typing import Any +import unicodedata from urllib.parse import unquote, urlsplit import yaml @@ -39,7 +40,7 @@ "evidence-graph.json", "telemetry-contract-check.json", "validation-result.json", - "synthetic-signal.json", + "controlled-test-signal.json", "enrichment.json", "triage-summary.md", "proofcard.json", @@ -62,7 +63,7 @@ "evidence_graph", "telemetry_contract_check", "controlled_validation", - "synthetic_signal", + "controlled_test_signal", "enrichment", "triage", "proofcard", @@ -103,7 +104,10 @@ re.IGNORECASE, ), "runtime promotion": re.compile(r"\bruntime[-_ ]active\b", re.IGNORECASE), - "signal promotion": re.compile(r"\bsignal[-_ ]observed\b|\bsignal[-_ ]proof\b", re.IGNORECASE), + "signal promotion": re.compile( + r"\bsignal(?:[-_ ](?:was[-_ ]?)?observed|[-_ ]proof)\b", + re.IGNORECASE, + ), "production": re.compile(r"\bproduction(?:[- ]ready| readiness)?\b", re.IGNORECASE), "customer deployment": re.compile(r"\bcustomer(?:[- ]deployed| deployment)?\b", re.IGNORECASE), "SOCaaS deployment": re.compile(r"\bSOCaaS(?:[- ]ready| deployed| deployment)?\b", re.IGNORECASE), @@ -144,6 +148,48 @@ r"\bprivate execution ID\b", ) ] +PRIVATE_KEY_TOKENS = { + "endpointlog", + "generatedpassword", + "password", + "privateevidence", + "privateexecutionid", + "privatepacket", + "privatepayload", + "rawalert", + "rawwazuh", + "secret", + "token", +} +PASS_OUTPUT_ROLES = { + "artifact_manifest": "artifact-manifest.json", + "artifact_intake": "intake.json", + "evidence_graph": "evidence-graph.json", + "telemetry_contract_check": "telemetry-contract-check.json", + "controlled_validation": "validation-result.json", + "controlled_test_signal": "controlled-test-signal.json", + "enrichment": "enrichment.json", + "triage": "triage-summary.md", + "proofcard": "proofcard.json", + "proofcard_markdown": "proofcard.md", + "claim_authority": "claim-authority.json", + "reviewer_pack": "reviewer-pack.md", + "machine_state": "machine-state.json", + "run_summary": "run-summary.json", +} +BLOCKED_OUTPUT_ROLES = { + "artifact_manifest": "artifact-manifest.json", + "machine_state": "machine-state.json", + "blocked_review": "blocked-review.md", + "run_summary": "run-summary.json", +} +BATCH_OUTPUT_ROLES = { + "input_index": "input-index.json", + "machine_state": "batch-machine-state.json", + "summary": "batch-summary.md", + "reviewer_pack": "batch-reviewer-pack.md", + "run_summary": "batch-run-summary.json", +} ABSOLUTE_LOCAL_PATH = re.compile( r"(?i)(?:[A-Z]:[\\/]|(?]+)" @@ -253,6 +299,105 @@ "lifetime_ledger_changed": False, "private_evidence_committed": False, } +COMPOSITIONAL_PROMOTION_KEYS = { + "aidisposition", + "aiauthority", + "analystapproval", + "analystauthority", + "productionlive", + "productionstatus", + "customerdeployment", + "customerstatus", + "socaasdeployment", + "socaasstatus", + "runtimestatus", + "signalstatus", + "approvalstatus", + "closurestatus", + "casestatus", + "publicsaferuntime", + "finalauthorized", + "finalauthorizationstatus", +} +BOUNDED_PROMOTION_VALUES = { + "blocked", + "controlledtestonly", + "false", + "notactive", + "notapproved", + "notauthorized", + "notclosed", + "notdeployed", + "notobserved", + "notpublicsafe", + "notready", + "unsupported", +} +REVIEW_MACHINE_STATE_BASE_FIELDS = { + "schema_version", + "engine_version", + "run_id", + "artifact_id", + "manifest_path", + "final_status", + "block_reason", + "stages", + "allowed_claim", + "requested_claims", + "blocked_claims", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "endpoint_mutation", + "wazuh_mutation", + "runtime_proof", + "private_evidence_committed", + "public_proof_promoted", + "lifetime_ledger_changed", + "proof_boundary", + "runtime_boundary", + "signal_boundary", + "next_gate", + "outputs", + "product", + "output_digests", + "state_integrity_digest", +} +BATCH_MACHINE_STATE_FIELDS = { + "schema_version", + "engine_version", + "batch_id", + "index_id", + "index_path", + "artifacts", + "expected_pass_artifacts", + "expected_blocked_artifacts", + "final_status", + "actual_pass_artifacts", + "actual_blocked_artifacts", + "block_reason", + "batch_claim_boundary", + "proof_boundary", + "runtime_boundary", + "signal_boundary", + "public_safe_status", + "human_review_required", + "ai_disposition_authority", + "endpoint_mutation", + "wazuh_mutation", + "runtime_proof", + "private_evidence_committed", + "public_proof_promoted", + "lifetime_ledger_changed", + "website_changed", + "outputs", + "next_gate", + "product", + "source_manifest_digest", + "input_index_sha256", + "output_digests", + "batch_state_integrity_digest", +} _AUTHORITY_BINDING_CACHE: dict[tuple[str, ...], dict[str, Any]] = {} @@ -273,7 +418,7 @@ def _construct_unique_mapping(loader: _UniqueKeyLoader, node: yaml.MappingNode, result: dict[Any, Any] = {} seen: set[str] = set() for key, value in pairs: - normalized = str(key).casefold() + normalized = _normalized_key_identity(key) if normalized in seen: raise ReviewBlocked("structured input contains a duplicate key") seen.add(normalized) @@ -291,7 +436,7 @@ def _unique_json_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]: result: dict[str, Any] = {} seen: set[str] = set() for key, value in pairs: - normalized = key.casefold() + normalized = _normalized_key_identity(key) if normalized in seen: raise ReviewBlocked("structured input contains a duplicate key") seen.add(normalized) @@ -306,29 +451,107 @@ def _require_exact_keys(value: dict[str, Any], allowed: set[str], label: str) -> def _normalize_security_key(value: Any) -> str: - decoded = _decoded_text_variants(str(value))[-1] + decoded = _decoded_text_variants( + unicodedata.normalize("NFKC", str(value)), + strict_base64=False, + )[-1] return re.sub(r"[^a-z0-9]", "", decoded.casefold()) -def _decoded_text_variants(value: str) -> list[str]: - variants = [value] - current = value +def _normalized_key_identity(value: Any) -> str: + return _normalize_security_key(unicodedata.normalize("NFKC", str(value))) + + +def _is_promotion_key(normalized_key: str) -> bool: + if normalized_key == "finalstatus": + return False + if any( + token in normalized_key + for token in SECURITY_FALSE_KEY_TOKENS | COMPOSITIONAL_PROMOTION_KEYS + ): + return True + authority_prefixes = ("production", "customer", "socaas", "runtime", "signal", "approval", "closure", "case", "publicsafe", "final") + promotion_suffixes = ("active", "approved", "authorized", "closed", "deployed", "live", "observed", "proof", "ready", "runtime", "status") + return any(prefix in normalized_key for prefix in authority_prefixes) and any( + normalized_key.endswith(suffix) for suffix in promotion_suffixes + ) + + +def _is_bounded_promotion_value(value: Any) -> bool: + if value in (False, None, 0, "", [], {}): + return True + if isinstance(value, str): + return _normalize_security_key(value) in BOUNDED_PROMOTION_VALUES + return False + + +def _canonical_base64_text(value: str, *, strict: bool) -> str | None: + compact = value.strip() + if len(compact) < 8 or not re.fullmatch(r"[A-Za-z0-9+/=_-]+", compact): + return None + encoded_signal = "=" in compact or bool( + re.match(r"(?i)^(?:eyj|w3s|y2fz|chvi|chjp|zmlu|yw5h|quk|l3|qzpc)", compact) + ) + unpadded = compact.rstrip("=") + if "=" in unpadded or len(compact) - len(unpadded) > 2: + if strict and encoded_signal: + raise ReviewBlocked("structured input contains non-canonical base64") + return None + remainder = len(unpadded) % 4 + if remainder == 1: + # A one-character remainder can never be valid base64. Limit rejection + # to values that carry strong encoded-data signals so ordinary labels + # are not reclassified as encodings. + if encoded_signal: + if strict: + raise ReviewBlocked("structured input contains invalid base64 length") + return None + padded = unpadded + "=" * ((4 - remainder) % 4) + alphabet = "urlsafe" if re.search(r"[-_]", unpadded) else "standard" + try: + if alphabet == "urlsafe": + decoded_bytes = base64.b64decode(padded.translate(str.maketrans("-_", "+/")), validate=True) + canonical = base64.urlsafe_b64encode(decoded_bytes).decode("ascii").rstrip("=") + else: + decoded_bytes = base64.b64decode(padded, validate=True) + canonical = base64.b64encode(decoded_bytes).decode("ascii").rstrip("=") + except (binascii.Error, ValueError) as exc: + if strict and encoded_signal: + raise ReviewBlocked("structured input contains invalid base64") from exc + return None + if canonical != unpadded: + if strict and encoded_signal: + raise ReviewBlocked("structured input contains non-canonical base64") + return None + try: + decoded = decoded_bytes.decode("utf-8") + except UnicodeDecodeError: + return None + if not decoded or not all(char.isprintable() or char.isspace() for char in decoded): + return None + return decoded + + +def _decoded_text_variants(value: str, *, strict_base64: bool = True) -> list[str]: + initial = unicodedata.normalize("NFKC", value) + variants = [initial] + frontier = [initial] for _ in range(3): - decoded = unquote(current) - if decoded == current: + next_frontier: list[str] = [] + for current in frontier: + decoded_url = unicodedata.normalize("NFKC", unquote(current)) + if decoded_url != current and decoded_url not in variants: + variants.append(decoded_url) + next_frontier.append(decoded_url) + decoded_b64 = _canonical_base64_text(current, strict=strict_base64) + if decoded_b64 is not None: + decoded_b64 = unicodedata.normalize("NFKC", decoded_b64) + if decoded_b64 not in variants: + variants.append(decoded_b64) + next_frontier.append(decoded_b64) + if not next_frontier: break - variants.append(decoded) - current = decoded - compact = value.strip() - if len(compact) >= 8 and len(compact) % 4 == 0 and re.fullmatch(r"[A-Za-z0-9+/=_-]+", compact): - try: - padded = compact + "=" * ((4 - len(compact) % 4) % 4) - decoded_bytes = base64.urlsafe_b64decode(padded.encode("ascii")) - decoded = decoded_bytes.decode("utf-8") - if decoded and decoded not in variants and all(char.isprintable() or char.isspace() for char in decoded): - variants.append(decoded) - except (binascii.Error, UnicodeDecodeError, ValueError): - pass + frontier = next_frontier return variants @@ -354,7 +577,7 @@ def _validate_recursive_boundaries(value: Any, label: str, path: str = "") -> No for key, item in value.items(): key_path = f"{path}.{key}" if path else str(key) normalized_key = _normalize_security_key(key) - if normalized_key in SECURITY_FALSE_KEY_TOKENS and item not in (False, None, 0, "", [], {}): + if _is_promotion_key(normalized_key) and not _is_bounded_promotion_value(item): raise ReviewBlocked(f"{label} contains prohibited authority promotion") for canonical, expected in SECURITY_FIXED_FIELDS.items(): if normalized_key == _normalize_security_key(canonical) and item != expected: @@ -371,6 +594,135 @@ def _validate_recursive_boundaries(value: Any, label: str, path: str = "") -> No raise ReviewBlocked(f"{label} contains an unsupported claim") +def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") -> bool: + normalized_path = _normalized_key_identity(path) + if re.fullmatch( + r"\s*-?\s*public[_ -]?safe[_ -]?status(?:\s+remains|\s*:)?\s*`?NOT_PUBLIC_SAFE`?\.?\s*", + unicodedata.normalize("NFKC", value), + re.IGNORECASE, + ): + return True + if any( + token in normalized_path + for token in ( + "blockedclaims", + "blockedclaimclasses", + "missingevidence", + "saferwording", + "whathoxlineblocked", + ) + ): + return True + if normalized_path.endswith( + ("path", "machinestate", "outputdir", "reviewerpack", "blockedreview", "runsummary") + ) and re.fullmatch(r"[A-Za-z0-9._/-]+", value): + return True + + local_negative = re.compile( + r"\b(?:not asserted|not prove|does not prove|not public proof|not runtime proof|" + r"unsupported|prohibited|remain false|remains false|claims block|blocked claim|" + r"claim authority blocks|do not say)\b", + re.IGNORECASE, + ) + inherited_negative = re.compile( + r"\b(?:not asserted|not prove|does not prove|claims block|blocked claim|" + r"claim authority blocks|do not say)\b", + re.IGNORECASE, + ) + trailing_negative = re.compile( + r"^\s*:?\s*(?:is|are|remains?)\s+(?:unsupported|prohibited|false|not asserted)\b", + re.IGNORECASE, + ) + clause_separator = re.compile(r"[,;:/\u2013\u2014]|\b(?:while|but|however)\b", re.IGNORECASE) + hard_separator = re.compile(r"[;:/\u2013\u2014]|\b(?:while|but|however)\b", re.IGNORECASE) + unsafe_seen = False + for candidate in _decoded_text_variants(value): + normalized = unicodedata.normalize("NFKC", candidate) + for pattern in PROHIBITED_CLAIM_PATTERNS.values(): + for match in pattern.finditer(normalized): + unsafe_seen = True + normalized_folded = normalized.casefold() + token_start = normalized_folded.rfind("not_public_safe", 0, match.start() + 1) + if token_start >= 0 and token_start <= match.start() < token_start + len("not_public_safe"): + continue + separators = [item for item in clause_separator.finditer(normalized) if item.end() <= match.start()] + clause_start = separators[-1].end() if separators else 0 + prefix = normalized[clause_start : match.start()] + suffix = normalized[match.end() :] + if local_negative.search(prefix) or trailing_negative.search(suffix): + continue + # A scoped denial such as "does not prove production readiness, + # deployment, or approval" can govern a comma-delimited list. + # Weak phrases such as "unsupported note" do not carry across + # punctuation, and hard delimiters always reset the scope. + if separators and separators[-1].group() == ",": + hard = [item for item in hard_separator.finditer(normalized) if item.end() <= match.start()] + hard_start = hard[-1].end() if hard else 0 + if inherited_negative.search(normalized[hard_start : match.start()]): + continue + return False + return unsafe_seen + + +def _validate_generated_output_security(value: Any, label: str, path: str = "") -> None: + if isinstance(value, dict): + seen: set[str] = set() + for key, item in value.items(): + normalized_key = _normalized_key_identity(key) + if normalized_key in seen: + raise ReviewBlocked(f"{label} contains normalized-key collision") + seen.add(normalized_key) + key_path = f"{path}.{key}" if path else str(key) + if _is_promotion_key(normalized_key) and not _is_bounded_promotion_value(item): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") + for canonical, expected in REVIEW_OUTPUT_SECURITY_FIELDS.items(): + if normalized_key == _normalized_key_identity(canonical) and item != expected: + raise ReviewBlocked(f"{label} violates a fixed authority boundary") + _validate_generated_output_security(item, label, key_path) + return + if isinstance(value, list): + for index, item in enumerate(value): + _validate_generated_output_security(item, label, f"{path}[{index}]") + return + if isinstance(value, str): + _validate_declared_string(value, label) + lines = value.splitlines() if "\n" in value or "\r" in value else [value] + markdown_section = "" + for line in lines: + if line.lstrip().startswith("#"): + markdown_section = line.lstrip("#").strip() + elif re.fullmatch(r"[A-Za-z][A-Za-z ]+:", line.strip()): + markdown_section = line.strip().rstrip(":") + line_path = f"{path}.{markdown_section}" if markdown_section else path + if _string_has_unsafe_claim(line) and not _is_bounded_negative_claim( + line, path=line_path, label=label + ): + raise ReviewBlocked(f"{label} contains an unsupported claim") + + +def _expected_file_text(value: Any) -> str: + if isinstance(value, str): + return value + return json.dumps(value, indent=2, sort_keys=True) + "\n" + + +def _verify_generated_file(path: Path, expected: Any, label: str, errors: list[str]) -> None: + if not path.is_file() or path.is_symlink(): + return + try: + actual_text = path.read_text(encoding="utf-8") + if actual_text != _expected_file_text(expected): + errors.append(f"{label} does not match deterministic engine output") + if path.suffix == ".json": + actual: Any = json.loads(actual_text, object_pairs_hook=_unique_json_object) + else: + actual = actual_text + _validate_generated_output_security(actual, label) + _validate_no_private_markers(actual, label) + except (OSError, UnicodeError, json.JSONDecodeError, ReviewEngineError) as exc: + errors.append(f"{label} security validation failed: {_sanitize_block_reason(str(exc))}") + + def _validate_declared_string(value: str, label: str) -> None: for candidate in _decoded_text_variants(value): if "\x00" in candidate or any(ord(char) < 32 and char not in "\t\r\n" for char in candidate): @@ -767,16 +1119,26 @@ def verify_review_run(machine_state_path: Path) -> list[str]: final_status = state.get("final_status") if final_status not in {"PASS", "BLOCKED"}: errors.append("machine-state final_status must be PASS or BLOCKED") + expected_state_fields = set(REVIEW_MACHINE_STATE_BASE_FIELDS) + if final_status == "PASS": + expected_state_fields.update({"authority_binding", "input_digests", "source_manifest_digest"}) + if set(state) != expected_state_fields: + errors.append("machine-state fields must exactly match the deterministic engine contract") if state.get("schema_version") != MACHINE_STATE_VERSION: errors.append(f"machine-state schema_version must be {MACHINE_STATE_VERSION}") if state.get("engine_version") != ENGINE_VERSION: errors.append(f"machine-state engine_version must be {ENGINE_VERSION}") + if state.get("run_id") != _safe_run_identity(run_dir.name): + errors.append("machine-state run_id must match the sanitized canonical run directory identity") + if final_status == "PASS" and state.get("block_reason") is not None: + errors.append("PASS machine-state block_reason must be null") if [stage.get("stage_name") for stage in state.get("stages", [])] != STAGE_REGISTRY: errors.append("machine-state stages must match review engine stage registry") - expected_outputs = EXPECTED_PASS_OUTPUTS if final_status == "PASS" else EXPECTED_BLOCKED_OUTPUTS + expected_roles = PASS_OUTPUT_ROLES if final_status == "PASS" else BLOCKED_OUTPUT_ROLES + expected_outputs = list(expected_roles.values()) output_refs = state.get("outputs") - if not isinstance(output_refs, dict) or set(output_refs.values()) != set(expected_outputs) - {"proofcard.md"}: - errors.append("machine-state output references must exactly match the engine contract") + if output_refs != expected_roles: + errors.append("machine-state output role-to-path mapping must exactly match the engine contract") declared_output_names = set(state.get("output_digests", {})) expected_digest_names = set(expected_outputs) - {"machine-state.json"} if declared_output_names != expected_digest_names: @@ -814,12 +1176,7 @@ def verify_review_run(machine_state_path: Path) -> list[str]: if state.get(field) != expected: errors.append(f"machine-state field {field} must be {expected!r}") try: - scan_state = { - key: value - for key, value in state.items() - if key not in {"blocked_claims", "stages", "proof_boundary", "runtime_boundary", "signal_boundary"} - } - _validate_recursive_boundaries(scan_state, "machine-state") + _validate_generated_output_security(state, "machine-state") _validate_no_private_markers(state, "machine-state") except ReviewBlocked as exc: errors.append(_sanitize_block_reason(str(exc))) @@ -845,10 +1202,88 @@ def verify_review_run(machine_state_path: Path) -> list[str]: errors.append("machine-state source manifest digest mismatch") if state.get("authority_binding") != authority: errors.append("machine-state authority binding does not match current owned sources") + regenerated = _build_review_outputs(manifest, fixture_paths, authority) + expected_files: dict[str, Any] = { + "artifact-manifest.json": manifest, + "intake.json": regenerated["intake"], + "evidence-graph.json": regenerated["evidence_graph"], + "telemetry-contract-check.json": regenerated["telemetry_contract_check"], + "validation-result.json": regenerated["validation_result"], + "controlled-test-signal.json": regenerated["controlled_test_signal"], + "enrichment.json": regenerated["enrichment"], + "triage-summary.md": regenerated["triage_summary"], + "proofcard.json": regenerated["proofcard"], + "proofcard.md": regenerated["proofcard_markdown"], + "claim-authority.json": regenerated["claim_authority"], + "reviewer-pack.md": _reviewer_pack(manifest, state), + "run-summary.json": _run_summary(manifest, state, EXPECTED_PASS_OUTPUTS), + } + for name, expected in expected_files.items(): + _verify_generated_file(run_dir / name, expected, name, errors) + expected_state_fields = { + "run_id": _safe_run_identity(run_dir.name), + "artifact_id": manifest["artifact_id"], + "stages": _pass_stages(PASS_OUTPUT_ROLES), + "outputs": PASS_OUTPUT_ROLES, + "final_status": "PASS", + "block_reason": None, + "allowed_claim": _allowed_claims_for(manifest)[0], + "requested_claims": manifest["requested_claims"], + "blocked_claims": _blocked_claims(), + "proof_boundary": manifest["proof_boundary"], + "runtime_boundary": manifest["runtime_boundary"], + "signal_boundary": manifest["signal_boundary"], + "next_gate": manifest["next_gate"], + "product": PRODUCT, + } + for key, expected in expected_state_fields.items(): + if state.get(key) != expected: + errors.append(f"machine-state field {key} does not match deterministic engine output") + try: + recorded_manifest_path = _normalize_repo_relative_path( + state.get("manifest_path"), "machine-state manifest_path" + ) + recorded_manifest = (Path(__file__).resolve().parents[2] / recorded_manifest_path).resolve() + if not recorded_manifest.is_file() or _semantic_digest(_load_json(recorded_manifest)) != _semantic_digest(manifest): + errors.append("machine-state manifest_path does not identify the bound manifest content") + except (OSError, ReviewEngineError) as exc: + errors.append(f"machine-state manifest_path failed closed: {_sanitize_block_reason(str(exc))}") except (OSError, ReviewEngineError) as exc: errors.append(f"owned input replay failed closed: {_sanitize_block_reason(str(exc))}") - if final_status == "BLOCKED" and not state.get("block_reason"): - errors.append("BLOCKED run must include block_reason") + if final_status == "BLOCKED": + if not state.get("block_reason"): + errors.append("BLOCKED run must include block_reason") + else: + safe_manifest = _safe_blocked_manifest( + {"artifact_id": state.get("artifact_id")}, + Path("blocked-input.json"), + state["block_reason"], + ) + expected_state_fields = { + "run_id": _safe_run_identity(run_dir.name), + "artifact_id": safe_manifest["artifact_id"], + "manifest_path": "blocked-input.json", + "stages": _blocked_stages(state["block_reason"], BLOCKED_OUTPUT_ROLES), + "outputs": BLOCKED_OUTPUT_ROLES, + "final_status": "BLOCKED", + "allowed_claim": None, + "requested_claims": [], + "blocked_claims": _blocked_claims(), + "proof_boundary": "fixture-only; not public proof", + "runtime_boundary": "runtime prohibited", + "signal_boundary": "controlled-test fixture signal only", + "next_gate": "human_review_gate", + "product": PRODUCT, + } + for key, expected in expected_state_fields.items(): + if state.get(key) != expected: + errors.append(f"BLOCKED machine-state field {key} does not match deterministic engine output") + for name, expected in { + "artifact-manifest.json": safe_manifest, + "blocked-review.md": _blocked_review(state), + "run-summary.json": _run_summary(safe_manifest, state, EXPECTED_BLOCKED_OUTPUTS), + }.items(): + _verify_generated_file(run_dir / name, expected, name, errors) private_hits = _private_output_hits(run_dir) if private_hits: errors.append(f"private/raw markers found in review outputs: {', '.join(private_hits)}") @@ -987,8 +1422,16 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: errors.append(f"batch-machine-state schema_version must be {BATCH_MACHINE_STATE_VERSION}") if state.get("engine_version") != BATCH_ENGINE_VERSION: errors.append(f"batch-machine-state engine_version must be {BATCH_ENGINE_VERSION}") + if set(state) != BATCH_MACHINE_STATE_FIELDS: + errors.append("batch-machine-state fields must exactly match the deterministic engine contract") if state.get("final_status") not in {"PASS", "MIXED", "BLOCKED"}: errors.append("batch-machine-state final_status must be PASS, MIXED, or BLOCKED") + if state.get("batch_id") != _safe_run_identity(run_dir.name): + errors.append("batch-machine-state batch_id must match the sanitized canonical run directory identity") + if state.get("final_status") != "BLOCKED" and state.get("block_reason") is not None: + errors.append("non-BLOCKED batch-machine-state block_reason must be null") + if state.get("outputs") != BATCH_OUTPUT_ROLES: + errors.append("batch output role-to-path mapping must exactly match the engine contract") for name in BATCH_EXPECTED_OUTPUTS: path = (run_dir / name).resolve() if not _is_relative_to(path, run_dir.resolve()) or path.is_symlink(): @@ -1025,6 +1468,11 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: }.items(): if state.get(field) != expected: errors.append(f"batch-machine-state field {field} must be {expected!r}") + try: + _validate_generated_output_security(state, "batch-machine-state") + _validate_no_private_markers(state, "batch-machine-state") + except ReviewBlocked as exc: + errors.append(_sanitize_block_reason(str(exc))) if state.get("final_status") == "BLOCKED" and not state.get("block_reason"): errors.append("BLOCKED batch run must include block_reason") artifacts = state.get("artifacts", []) @@ -1084,6 +1532,21 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: for false_field in ("endpoint_mutation", "wazuh_mutation", "runtime_proof", "public_proof_promoted", "lifetime_ledger_changed", "private_evidence_committed"): if artifact.get(false_field) is not False: errors.append(f"{artifact_id}: {false_field} must be false") + expected_artifact_root = f"artifacts/{artifact_id}" + expected_paths = { + "output_dir": expected_artifact_root, + "machine_state": f"{expected_artifact_root}/machine-state.json", + "run_summary": f"{expected_artifact_root}/run-summary.json", + "reviewer_pack": f"{expected_artifact_root}/reviewer-pack.md" + if child_state.get("final_status") == "PASS" + else None, + "blocked_review": f"{expected_artifact_root}/blocked-review.md" + if child_state.get("final_status") == "BLOCKED" + else None, + } + for key, expected in expected_paths.items(): + if artifact.get(key) != expected: + errors.append(f"{artifact_id}: aggregate {key} does not match canonical output role path") errors.extend(_batch_expectation_errors(state)) expected_aggregate = _semantic_digest(sorted(source_digests)) if source_digests else None if state.get("source_manifest_digest") != expected_aggregate: @@ -1101,8 +1564,85 @@ def verify_batch_run(batch_machine_state_path: Path) -> list[str]: errors.append("batch expected PASS list does not match input index") if state.get("expected_blocked_artifacts") != index.get("expected_blocked_artifacts"): errors.append("batch expected BLOCKED list does not match input index") + expected_state_fields = { + "index_id": index["index_id"], + "batch_claim_boundary": index["batch_claim_boundary"], + "proof_boundary": index["proof_boundary"], + "runtime_boundary": index["runtime_boundary"], + "signal_boundary": index["signal_boundary"], + "next_gate": index["next_gate"], + "outputs": BATCH_OUTPUT_ROLES, + "product": PRODUCT, + } + for key, expected in expected_state_fields.items(): + if state.get(key) != expected: + errors.append(f"batch-machine-state field {key} does not match deterministic engine output") + recorded_index_path = _normalize_repo_relative_path( + state.get("index_path"), "batch-machine-state index_path" + ) + recorded_index = (Path(__file__).resolve().parents[2] / recorded_index_path).resolve() + if not recorded_index.is_file() or _semantic_digest(_load_json(recorded_index)) != _semantic_digest(index): + errors.append("batch-machine-state index_path does not identify the bound index content") + indexed_paths = { + entry["artifact_id"]: _normalize_repo_relative_path( + entry["manifest_path"], "batch artifact manifest path" + ) + for entry in index["artifacts"] + } + if [artifact.get("artifact_id") for artifact in artifacts] != [ + entry["artifact_id"] for entry in index["artifacts"] + ]: + errors.append("batch aggregate artifact order must match the bound input index") + for artifact in artifacts: + artifact_id = str(artifact.get("artifact_id")) + if artifact.get("manifest_path") != indexed_paths.get(artifact_id): + errors.append(f"{artifact_id}: aggregate manifest_path does not match input index") + for name, expected in { + "input-index.json": index, + "batch-summary.md": _batch_summary_markdown(state), + "batch-reviewer-pack.md": _batch_reviewer_pack(state), + "batch-run-summary.json": _batch_run_summary(state), + }.items(): + _verify_generated_file(run_dir / name, expected, name, errors) except (OSError, ReviewEngineError) as exc: errors.append(f"batch input-index replay failed closed: {_sanitize_block_reason(str(exc))}") + else: + try: + safe_index = _safe_blocked_index( + {"index_id": state.get("index_id")}, + Path("blocked-input-index.json"), + str(state.get("block_reason")), + ) + expected_state_fields = { + "batch_id": _safe_run_identity(run_dir.name), + "index_id": safe_index["index_id"], + "index_path": "blocked-input-index.json", + "artifacts": [], + "expected_pass_artifacts": [], + "expected_blocked_artifacts": [], + "actual_pass_artifacts": [], + "actual_blocked_artifacts": [], + "batch_claim_boundary": None, + "proof_boundary": safe_index["proof_boundary"], + "runtime_boundary": safe_index["runtime_boundary"], + "signal_boundary": safe_index["signal_boundary"], + "outputs": BATCH_OUTPUT_ROLES, + "next_gate": safe_index["next_gate"], + "product": PRODUCT, + "source_manifest_digest": None, + } + for key, expected in expected_state_fields.items(): + if state.get(key) != expected: + errors.append(f"blocked batch-machine-state field {key} does not match deterministic engine output") + for name, expected in { + "input-index.json": safe_index, + "batch-summary.md": _batch_summary_markdown(state), + "batch-reviewer-pack.md": _batch_reviewer_pack(state), + "batch-run-summary.json": _batch_run_summary(state), + }.items(): + _verify_generated_file(run_dir / name, expected, name, errors) + except (OSError, ReviewEngineError) as exc: + errors.append(f"blocked batch replay failed closed: {_sanitize_block_reason(str(exc))}") private_hits = _private_output_hits(run_dir) if private_hits: errors.append(f"private/raw markers found in batch outputs: {', '.join(private_hits)}") @@ -1232,7 +1772,7 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa return { "schema_version": BATCH_MACHINE_STATE_VERSION, "engine_version": BATCH_ENGINE_VERSION, - "batch_id": output_dir.name, + "batch_id": _safe_run_identity(output_dir.name), "index_id": index.get("index_id"), "index_path": _display_path(index_path), "artifacts": artifacts, @@ -1260,9 +1800,8 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa "public_proof_promoted": False, "lifetime_ledger_changed": False, "website_changed": False, - "outputs": BATCH_EXPECTED_OUTPUTS, + "outputs": deepcopy(BATCH_OUTPUT_ROLES), "next_gate": index.get("next_gate"), - "created_at": datetime.now(timezone.utc).isoformat(), "product": PRODUCT, "source_manifest_digest": _semantic_digest( sorted( @@ -1278,7 +1817,7 @@ def _batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Pa def _blocked_batch_machine_state(index: dict[str, Any], index_path: Path, output_dir: Path, block_reason: str) -> dict[str, Any]: state = _batch_machine_state(index, index_path, output_dir, []) - state["batch_id"] = "blocked-batch-run" + state["index_path"] = "blocked-input-index.json" state["final_status"] = "BLOCKED" state["block_reason"] = block_reason return state @@ -1413,7 +1952,7 @@ def _batch_run_summary(state: dict[str, Any]) -> dict[str, Any]: "final_status": state["final_status"], "block_reason": state.get("block_reason"), "artifacts": state.get("artifacts", []), - "outputs": BATCH_EXPECTED_OUTPUTS, + "outputs": deepcopy(BATCH_OUTPUT_ROLES), "public_safe_status": state["public_safe_status"], "human_review_required": state["human_review_required"], "ai_disposition_authority": state["ai_disposition_authority"], @@ -1443,7 +1982,7 @@ def _safe_blocked_index(index: dict[str, Any], index_path: Path, block_reason: s "human_review_required": True, "ai_disposition_authority": False, "runtime_boundary": "runtime prohibited", - "signal_boundary": "synthetic fixture signal only", + "signal_boundary": "controlled-test fixture signal only", "proof_boundary": "not public proof", "next_gate": "index_fix_required", } @@ -1457,22 +1996,8 @@ def _build_pass_run( fixture_paths: dict[str, Path], authority_binding: dict[str, Any], ) -> dict[str, Any]: - run_id = output_dir.name - output_refs = { - "artifact_manifest": "artifact-manifest.json", - "artifact_intake": "intake.json", - "evidence_graph": "evidence-graph.json", - "telemetry_contract_check": "telemetry-contract-check.json", - "controlled_validation": "validation-result.json", - "synthetic_signal": "synthetic-signal.json", - "enrichment": "enrichment.json", - "triage": "triage-summary.md", - "proofcard": "proofcard.json", - "claim_authority": "claim-authority.json", - "reviewer_pack": "reviewer-pack.md", - "machine_state": "machine-state.json", - "run_summary": "run-summary.json", - } + run_id = _safe_run_identity(output_dir.name) + output_refs = deepcopy(PASS_OUTPUT_ROLES) state = _machine_state( manifest=manifest, manifest_path=manifest_path, @@ -1502,13 +2027,8 @@ def _build_pass_run( def _build_blocked_run(manifest: dict[str, Any], manifest_path: Path, output_dir: Path, block_reason: str) -> dict[str, Any]: - run_id = "blocked-review-run" - output_refs = { - "artifact_manifest": "artifact-manifest.json", - "machine_state": "machine-state.json", - "blocked_review": "blocked-review.md", - "run_summary": "run-summary.json", - } + run_id = _safe_run_identity(output_dir.name) + output_refs = deepcopy(BLOCKED_OUTPUT_ROLES) safe_manifest = _safe_blocked_manifest(manifest, manifest_path, block_reason) state = _machine_state( manifest=safe_manifest, @@ -1554,7 +2074,7 @@ def _write_pass_outputs(output_dir: Path, run: dict[str, Any], force: bool) -> N "evidence-graph.json": outputs["evidence_graph"], "telemetry-contract-check.json": outputs["telemetry_contract_check"], "validation-result.json": outputs["validation_result"], - "synthetic-signal.json": outputs["synthetic_signal"], + "controlled-test-signal.json": outputs["controlled_test_signal"], "enrichment.json": outputs["enrichment"], "triage-summary.md": outputs["triage_summary"], "proofcard.json": outputs["proofcard"], @@ -1610,7 +2130,7 @@ def _build_review_outputs( intake = _artifact_intake(manifest) telemetry = _telemetry_contract_check(manifest, positive_fixture) validation = _controlled_validation(manifest, positive_fixture, negative_fixture, telemetry, authority_binding) - signal = _synthetic_signal(manifest, positive_fixture, validation) + signal = _controlled_test_signal(manifest, positive_fixture, validation) enrichment = _enrichment(manifest, positive_fixture) triage = _triage(manifest, signal, enrichment, validation) proofcard = _proofcard(manifest, intake, telemetry, validation, signal, enrichment, triage) @@ -1621,7 +2141,7 @@ def _build_review_outputs( "evidence_graph": evidence_graph, "telemetry_contract_check": telemetry, "validation_result": validation, - "synthetic_signal": signal, + "controlled_test_signal": signal, "enrichment": enrichment, "triage_summary": _triage_markdown(manifest, triage), "proofcard": proofcard, @@ -1645,7 +2165,7 @@ def _artifact_intake(manifest: dict[str, Any]) -> dict[str, Any]: "human_review_required": True, "ai_disposition_authority": False, "notes": [ - "Fixture is synthetic and local-only.", + "Fixture is controlled-test and local-only.", "No users, groups, endpoints, Wazuh systems, or private infrastructure are touched.", ], } @@ -1699,16 +2219,16 @@ def _controlled_validation( "endpoint_mutation": False, "runtime_rerun": False, "wazuh_mutation": False, - "explanation": "Validation evaluates bundled synthetic fixture records only.", + "explanation": "Validation evaluates bundled controlled-test fixture records only.", } -def _synthetic_signal(manifest: dict[str, Any], fixture: dict[str, Any], validation: dict[str, Any]) -> dict[str, Any]: +def _controlled_test_signal(manifest: dict[str, Any], fixture: dict[str, Any], validation: dict[str, Any]) -> dict[str, Any]: artifact_id = manifest["artifact_id"] return { - "schema_version": "synthetic-signal-v0", + "schema_version": "controlled-test-signal-v0", "artifact_id": artifact_id, - "signal_id": f"synthetic-signal-{artifact_id.lower()}-review-v1", + "signal_id": f"controlled-test-signal-{artifact_id.lower()}-review-v1", "source": "safe bundled fixture", "detection_fired": validation["result"] == "pass" and _fixture_matches_manifest(fixture, manifest), "simulation_only": True, @@ -1724,14 +2244,14 @@ def _enrichment(manifest: dict[str, Any], fixture: dict[str, Any]) -> dict[str, return { "schema_version": "enrichment-v0", "artifact_id": manifest["artifact_id"], - "attack_mapping": list(manifest.get("attack_mapping", [{"technique_id": "T0000", "technique": "synthetic fixture review", "scope": "review mapping only"}])), + "attack_mapping": list(manifest.get("attack_mapping", [{"technique_id": "T0000", "technique": "controlled-test fixture review", "scope": "review mapping only"}])), "event_id_mapping": event_mapping, "source_mapping": { "channel": manifest["telemetry_contract"]["source"], "fixture_host": fixture["host"], - "fixture_scope": "synthetic demo host", + "fixture_scope": "controlled-test demo host", }, - "field_mapping": dict(manifest.get("field_mapping", {"event_id": "event identifier", "action": "review action", "actor": "synthetic actor label"})), + "field_mapping": dict(manifest.get("field_mapping", {"event_id": "event identifier", "action": "review action", "actor": "controlled-test actor label"})), "confidence": manifest.get("confidence", "bounded-demo-high"), "severity": manifest.get("severity", "medium"), } @@ -1742,14 +2262,14 @@ def _triage(manifest: dict[str, Any], signal: dict[str, Any], enrichment: dict[s return { "schema_version": "triage-summary-v0", "artifact_id": artifact_id, - "what_happened": manifest.get("triage_what_happened", f"A synthetic fixture represented the {artifact_id} review pattern."), + "what_happened": manifest.get("triage_what_happened", f"A controlled-test fixture represented the {artifact_id} review pattern."), "why_it_matters": manifest.get("triage_why_it_matters", "The pattern can matter during security review when backed by appropriate evidence."), "evidence_exists": [ "artifact intake record", "evidence graph", "telemetry contract check", - "positive and negative synthetic fixtures", - "fixture-derived synthetic signal", + "positive and negative controlled-test fixtures", + "fixture-derived controlled-test signal", "enrichment mapping", "ProofCard", "Claim Authority decision", @@ -1784,14 +2304,14 @@ def _proofcard( "review_version": "v1", "owner_split": { "source_truth": manifest["source_owner"], - "behavior_truth": "bundled synthetic fixture", + "behavior_truth": "bundled controlled-test fixture", "platform_runtime_truth": "not asserted", "proof_authority": "not asserted by review engine", "rendering": "local generated files only", }, "telemetry_contract": telemetry, "controlled_validation": validation, - "synthetic_signal": signal, + "controlled_test_signal": signal, "enrichment": enrichment, "triage": triage, "allowed_claims": _allowed_claims_for(manifest), @@ -1837,7 +2357,7 @@ def _evidence_graph( _node("artifact-intake", "artifact_intake", intake["source_owner"], "PASS"), _node("telemetry-contract-check", "telemetry_contract_check", "hoxline-review-fixture", telemetry["result"].upper()), _node("controlled-validation", "controlled_validation", "hoxline-review-fixture", validation["result"].upper()), - _node("synthetic-signal", "synthetic_signal", "hoxline-review-fixture", "PASS"), + _node("controlled-test-signal", "controlled_test_signal", "hoxline-review-fixture", "PASS"), _node("proofcard", "proofcard", proofcard["proof_owner"], "PASS"), _node("claim-authority", "claim_authority", "hoxline", "PASS"), ] @@ -1850,8 +2370,8 @@ def _evidence_graph( "edges": [ {"from": "artifact-intake", "to": "telemetry-contract-check", "relationship": "declares assumptions"}, {"from": "telemetry-contract-check", "to": "controlled-validation", "relationship": "bounds fixture validation"}, - {"from": "controlled-validation", "to": "synthetic-signal", "relationship": "creates fixture-only signal"}, - {"from": "synthetic-signal", "to": "proofcard", "relationship": "summarized by"}, + {"from": "controlled-validation", "to": "controlled-test-signal", "relationship": "creates fixture-only signal"}, + {"from": "controlled-test-signal", "to": "proofcard", "relationship": "summarized by"}, {"from": "proofcard", "to": "claim-authority", "relationship": "constrains claims"}, ], "missing_evidence": proofcard["missing_evidence"], @@ -1907,7 +2427,7 @@ def _validate_review_fixture(fixture: dict[str, Any], manifest: dict[str, Any], expected = { "schema_version": "hoxline-demo-fixture-v0", "artifact_id": manifest["artifact_id"], - "fixture_kind": "synthetic-demo-only", + "fixture_kind": "controlled-test-demo-only", "safe_fixture": True, "endpoint_mutation": False, "runtime_required": False, @@ -1974,8 +2494,8 @@ def _validate_manifest(manifest: dict[str, Any], manifest_path: Path, repo_root: _require_exact_keys(manifest, MANIFEST_ALLOWED_FIELDS, "artifact manifest") if manifest["manifest_version"] != MANIFEST_VERSION: raise ReviewBlocked(f"manifest_version must be {MANIFEST_VERSION}") - if manifest["artifact_id"] != ARTIFACT_ID and manifest.get("artifact_family") != "synthetic-review-only": - raise ReviewBlocked("unknown artifact requires explicit synthetic-review-only artifact_family") + if manifest["artifact_id"] != ARTIFACT_ID and manifest.get("artifact_family") != "controlled-test-review-only": + raise ReviewBlocked("unknown artifact requires explicit controlled-test-review-only artifact_family") if manifest.get("public_safe_status") != PUBLIC_SAFE_STATUS: raise ReviewBlocked("public_safe_status must remain NOT_PUBLIC_SAFE") if manifest.get("human_review_required") is not True: @@ -2120,11 +2640,16 @@ def _validate_no_private_markers(value: Any, label: str, path: str = "") -> None if isinstance(value, dict): for key, item in value.items(): full = f"{path}.{key}" if path else str(key) - for pattern in PRIVATE_FIELD_PATTERNS: - if pattern.search(str(key)) and not ( - _normalize_security_key(key) in SECURITY_FALSE_KEY_TOKENS and item is False - ): - raise ReviewBlocked(f"{label} contains prohibited private/raw field marker") + normalized_key = _normalize_security_key(key) + private_key = any(token in normalized_key for token in PRIVATE_KEY_TOKENS) or any( + pattern.search(candidate) + for candidate in _decoded_text_variants(str(key), strict_base64=False) + for pattern in PRIVATE_FIELD_PATTERNS + ) + if private_key and not ( + _is_promotion_key(normalized_key) and _is_bounded_promotion_value(item) + ): + raise ReviewBlocked(f"{label} contains prohibited private/raw field marker") _validate_no_private_markers(item, label, full) elif isinstance(value, list): for index, item in enumerate(value): @@ -2133,9 +2658,19 @@ def _validate_no_private_markers(value: Any, label: str, path: str = "") -> None for candidate in _decoded_text_variants(value): if ABSOLUTE_LOCAL_PATH.search(candidate) or _looks_like_local_or_escaping_path(candidate): raise ReviewBlocked(f"{label} contains an absolute local path") + normalized_candidate = _normalize_security_key(candidate) + if any(token in normalized_candidate for token in PRIVATE_KEY_TOKENS): + raise ReviewBlocked(f"{label} contains prohibited private/raw value marker") for pattern in PRIVATE_VALUE_PATTERNS: if pattern.search(candidate): raise ReviewBlocked(f"{label} contains prohibited private/raw value marker") + stripped = candidate.strip() + if stripped.startswith(("{", "[")): + try: + nested = json.loads(stripped, object_pairs_hook=_unique_json_object) + except (json.JSONDecodeError, ReviewEngineError): + continue + _validate_no_private_markers(nested, label, path) def _machine_state( @@ -2163,7 +2698,7 @@ def _machine_state( "blocked_claims": _blocked_claims(), "proof_boundary": manifest.get("proof_boundary", "fixture-only; not public proof") if manifest else "manifest blocked before proof boundary established", "runtime_boundary": manifest.get("runtime_boundary", "runtime prohibited") if manifest else "runtime prohibited", - "signal_boundary": manifest.get("signal_boundary", "synthetic fixture signal only") if manifest else "synthetic fixture signal only", + "signal_boundary": manifest.get("signal_boundary", "controlled-test fixture signal only") if manifest else "controlled-test fixture signal only", "public_safe_status": PUBLIC_SAFE_STATUS, "human_review_required": True, "ai_disposition_authority": False, @@ -2174,7 +2709,6 @@ def _machine_state( "public_proof_promoted": False, "lifetime_ledger_changed": False, "next_gate": manifest.get("next_gate", "human_review_gate") if manifest else "manifest_fix_required", - "created_at": datetime.now(timezone.utc).isoformat(), "product": PRODUCT, } @@ -2187,7 +2721,7 @@ def _stage(stage_name: str, status: str, output_ref: str | None, summary: str, f "output_ref": output_ref, "proof_boundary": "fixture-only; NOT_PUBLIC_SAFE; not runtime proof", "failure_mode": failure_mode, - "claim_boundary": "unsupported public, runtime, production, customer, SOCaaS, autonomous, approval, authorization, and case-closure claims block", + "claim_boundary": "claims block unsupported public, runtime, production, customer, SOCaaS, autonomous, approval, authorization, and case-closure wording", "summary": summary, } @@ -2198,7 +2732,7 @@ def _pass_stages(outputs: dict[str, str]) -> list[dict[str, Any]]: "evidence_graph": "evidence graph linked local fixture review nodes", "telemetry_contract_check": "declared fixture selector and source metadata checked", "controlled_validation": "positive and negative bundled fixtures validated", - "synthetic_signal": "safe fixture signal simulated without endpoint mutation", + "controlled_test_signal": "safe fixture signal simulated without endpoint mutation", "enrichment": "ATT&CK, event, source, and field mapping attached", "triage": "reviewer-readable triage summary generated", "proofcard": "ProofCard rendered under fixture-only ceiling", @@ -2231,7 +2765,7 @@ def _reviewer_pack(manifest: dict[str, Any], state: dict[str, Any]) -> str: "", "## What Happened", "", - f"Artifact `{state['artifact_id']}` ran through artifact intake, evidence graph, telemetry contract check, controlled validation, synthetic signal, enrichment, triage, ProofCard, Claim Authority, reviewer pack, and machine-state stages.", + f"Artifact `{state['artifact_id']}` ran through artifact intake, evidence graph, telemetry contract check, controlled validation, controlled-test signal, enrichment, triage, ProofCard, Claim Authority, reviewer pack, and machine-state stages.", "", "## Stage Table", "", @@ -2257,7 +2791,7 @@ def _reviewer_pack(manifest: dict[str, Any], state: dict[str, Any]) -> str: "## What This Proves", "", "- The artifact manifest can be reviewed by deterministic local Hoxline stages.", - "- The engine can generate replayable machine-state and reviewer artifacts from public-safe synthetic fixtures.", + "- The engine can generate replayable machine-state and reviewer artifacts from public-safe controlled-test fixtures.", "- Claim Authority blocks unsupported public, runtime, production, customer, SOCaaS, autonomous, approval, authorization, and case-closure claims.", "", "## What This Does Not Prove", @@ -2334,7 +2868,7 @@ def _verify_pass_outputs(run_dir: Path, state: dict[str, Any], errors: list[str] manifest = _load_json(run_dir / "artifact-manifest.json") telemetry = _load_json(run_dir / "telemetry-contract-check.json") validation = _load_json(run_dir / "validation-result.json") - signal = _load_json(run_dir / "synthetic-signal.json") + signal = _load_json(run_dir / "controlled-test-signal.json") proofcard = _load_json(run_dir / "proofcard.json") claim_authority = _load_json(run_dir / "claim-authority.json") reviewer_pack = (run_dir / "reviewer-pack.md").read_text(encoding="utf-8") @@ -2349,7 +2883,7 @@ def _verify_pass_outputs(run_dir: Path, state: dict[str, Any], errors: list[str] if validation.get("result") != "pass" or validation.get("endpoint_mutation") is not False: errors.append("controlled validation must pass without endpoint mutation") if signal.get("source") != "safe bundled fixture" or signal.get("detection_fired") is not True: - errors.append("synthetic signal must fire only from safe bundled fixture") + errors.append("controlled-test signal must fire only from safe bundled fixture") if proofcard.get("public_safe_status") != PUBLIC_SAFE_STATUS: errors.append("ProofCard must preserve NOT_PUBLIC_SAFE") blocked = {item.get("claim") for item in claim_authority.get("blocked_claims", [])} @@ -2440,6 +2974,13 @@ def _display_path(path: Path) -> str: return resolved.name +def _safe_run_identity(name: str) -> str: + normalized = _normalize_security_key(name) + if any(token in normalized for token in PRIVATE_KEY_TOKENS) or _string_has_unsafe_claim(name): + return f"sanitized-run-{hashlib.sha256(name.encode('utf-8')).hexdigest()[:12]}" + return name + + def _resolve_path(path: Path, base: Path) -> Path: if path.is_absolute(): return path diff --git a/tests/test_multi_artifact_review_manifest_expansion_v1.py b/tests/test_multi_artifact_review_manifest_expansion_v1.py index 1655fdb..5bcd710 100644 --- a/tests/test_multi_artifact_review_manifest_expansion_v1.py +++ b/tests/test_multi_artifact_review_manifest_expansion_v1.py @@ -385,3 +385,119 @@ def test_batch_replay_rejects_child_path_escape_and_aggregate_laundering(tmp_pat assert verify_batch_run(state_path), field state_path.write_text(json.dumps(baseline), encoding="utf-8") assert verify_batch_run(state_path) == [] + + +def test_batch_replay_rejects_rehashed_output_and_role_laundering(tmp_path) -> None: + output = tmp_path / "batch" + assert main(["review", "batch", "run", "--index", str(INDEX), "--output", str(output), "--force"]) == 0 + state_path = output / "batch-machine-state.json" + baseline = _json(state_path) + + for name in baseline["output_digests"]: + path = output / name + original = path.read_text(encoding="utf-8") + if path.suffix == ".json": + hostile = json.loads(original) + hostile["replay_extension"] = {"approvedByAnalyst": True} + path.write_text(json.dumps(hostile, indent=2, sort_keys=True) + "\n", encoding="utf-8") + else: + path.write_text(original + "\nproduction ready\n", encoding="utf-8") + state = json.loads(json.dumps(baseline)) + state["output_digests"][name] = hashlib.sha256(path.read_bytes()).hexdigest() + state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path), name + path.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["outputs"]["summary"] = "batch-reviewer-pack.md" + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["replay_extension"] = "bounded-looking extra field" + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) + + for field, value in { + "created_at": "2099-01-01T00:00:00Z", + "batch_id": "laundered-batch", + "block_reason": "unexpected non-blocked reason", + }.items(): + hostile_state = json.loads(json.dumps(baseline)) + hostile_state[field] = value + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: item for key, item in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path), field + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["artifacts"] = list(reversed(hostile_state["artifacts"])) + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) + + +def test_blocked_batch_replay_rejects_rehashed_output_and_role_laundering(tmp_path) -> None: + index = HOSTILE_BATCH_DIR / "unsafe-batch-public-safe-claim-index.json" + output = tmp_path / "blocked-batch" + assert main(["review", "batch", "run", "--index", str(index), "--output", str(output), "--force"]) == 1 + state_path = output / "batch-machine-state.json" + baseline = _json(state_path) + assert baseline["final_status"] == "BLOCKED" + assert verify_batch_run(state_path) == [] + + for name in baseline["output_digests"]: + path = output / name + original = path.read_text(encoding="utf-8") + if path.suffix == ".json": + hostile = json.loads(original) + hostile["replay_extension"] = {"approvedByAnalyst": True} + path.write_text(json.dumps(hostile, indent=2, sort_keys=True) + "\n", encoding="utf-8") + else: + path.write_text(original + "\ncase closure\n", encoding="utf-8") + state = json.loads(json.dumps(baseline)) + state["output_digests"][name] = hashlib.sha256(path.read_bytes()).hexdigest() + state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path), name + path.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["outputs"]["summary"] = "batch-reviewer-pack.md" + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["replay_extension"] = "bounded-looking extra field" + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) + + hostile_state = json.loads(json.dumps(baseline)) + hostile_state["created_at"] = "2099-01-01T00:00:00Z" + hostile_state["batch_state_integrity_digest"] = _semantic_digest( + {key: value for key, value in hostile_state.items() if key != "batch_state_integrity_digest"} + ) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_batch_run(state_path) diff --git a/tests/test_one_command_reviewer_demo_v0.py b/tests/test_one_command_reviewer_demo_v0.py index 33e9503..dd2bf67 100644 --- a/tests/test_one_command_reviewer_demo_v0.py +++ b/tests/test_one_command_reviewer_demo_v0.py @@ -59,7 +59,7 @@ def test_detection_fires_only_from_safe_fixture(tmp_path) -> None: output_dir = tmp_path / "demo-run" assert main(["demo", "quickstart", "--output", str(output_dir), "--force"]) == 0 - signal = _json(output_dir / "synthetic-signal.json") + signal = _json(output_dir / "controlled-test-signal.json") validation = _json(output_dir / "validation-result.json") assert signal["source"] == "safe bundled fixture" assert signal["detection_fired"] is True diff --git a/tests/test_review_engine_v1.py b/tests/test_review_engine_v1.py index ab58bd3..f6030e7 100644 --- a/tests/test_review_engine_v1.py +++ b/tests/test_review_engine_v1.py @@ -3,12 +3,24 @@ import json import base64 from copy import deepcopy +import hashlib from pathlib import Path import subprocess import sys +from urllib.parse import quote + +import pytest from hoxline.cli import main -from hoxline.review_engine import EXPECTED_PASS_OUTPUTS, STAGE_REGISTRY, verify_review_run +from hoxline.review_engine import ( + EXPECTED_PASS_OUTPUTS, + STAGE_REGISTRY, + ReviewBlocked, + _validate_generated_output_security, + _validate_no_private_markers, + _validate_recursive_boundaries, + verify_review_run, +) ROOT = Path(__file__).resolve().parents[1] @@ -21,6 +33,16 @@ def _json(path: Path) -> dict[str, object]: return json.load(handle) +def _integrity(value: dict[str, object], field: str = "state_integrity_digest") -> str: + return hashlib.sha256( + json.dumps( + {key: item for key, item in value.items() if key != field}, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + ).hexdigest() + + def test_valid_ho_det_010_manifest_run_passes(tmp_path, capsys) -> None: output_dir = tmp_path / "review-run" @@ -82,11 +104,11 @@ def test_ho_det_010_event_and_rule_metadata_represented(tmp_path) -> None: assert manifest["telemetry_contract"]["wazuh_rule_ids"] == [910101, 910102, 910103] -def test_synthetic_signal_only_and_blocked_claims_enforced(tmp_path) -> None: +def test_controlled_test_signal_only_and_blocked_claims_enforced(tmp_path) -> None: output_dir = tmp_path / "review-run" assert main(["review", "run", "--artifact", str(MANIFEST), "--output", str(output_dir), "--force"]) == 0 - signal = _json(output_dir / "synthetic-signal.json") + signal = _json(output_dir / "controlled-test-signal.json") state = _json(output_dir / "machine-state.json") blocked = {item["claim"] for item in state["blocked_claims"]} assert signal["source"] == "safe bundled fixture" @@ -253,11 +275,19 @@ def test_duplicate_and_unknown_manifest_fields_fail_closed(tmp_path) -> None: def test_nested_and_encoded_authority_laundering_fails_closed(tmp_path) -> None: + def encoded(raw: bytes) -> str: + return base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=") + encoded_claims = [ "public_safe_approved", "analyst%2Dapproved%20disposition", "final%2520authorization", base64.urlsafe_b64encode(b"case closure").decode("ascii"), + encoded(b"case closure!"), + encoded(b"final authorization!"), + encoded(encoded(b"AI-approved disposition").encode("ascii")), + encoded(b"private execution ID"), + encoded(b"C:\\private\\fixture.json"), '{"nested":{"ai_approved":true}}', ] for index, claim in enumerate(encoded_claims): @@ -268,7 +298,11 @@ def test_nested_and_encoded_authority_laundering_fails_closed(tmp_path) -> None: output = tmp_path / f"encoded-{index}" assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 state = _json(output / "machine-state.json") - assert state["block_reason"] == "input rejected by claim-authority boundary" + assert state["block_reason"] in { + "input rejected by claim-authority boundary", + "input rejected by private-data boundary", + "input rejected by path-containment boundary", + } combined = "\n".join(item.read_text(encoding="utf-8") for item in output.glob("*.json")) assert claim not in combined @@ -279,6 +313,151 @@ def test_nested_and_encoded_authority_laundering_fails_closed(tmp_path) -> None: output = tmp_path / "nested-authority" assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + canonical = encoded(b"case closure!") + noncanonical = canonical[:-1] + chr(ord(canonical[-1]) + 1) + for index, hostile_value in enumerate((noncanonical, "eyJaaaaaa")): + manifest = _json(MANIFEST) + manifest["field_mapping"] = {"encoded": hostile_value} + path = tmp_path / f"invalid-base64-{index}.json" + path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / f"invalid-base64-{index}" + assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + + +def test_nfkc_security_keys_and_normalized_collisions_fail_closed(tmp_path) -> None: + attacks = [ + {"public_safe": True}, + {"ai_disposition_authority": True}, + {"public_safe": True}, + {"public_safe": False, "public_safe": True}, + ] + for index, attack in enumerate(attacks): + manifest = _json(MANIFEST) + manifest["field_mapping"] = attack + path = tmp_path / f"nfkc-{index}.json" + path.write_text(json.dumps(manifest, ensure_ascii=False), encoding="utf-8") + output = tmp_path / f"nfkc-{index}" + assert main(["review", "run", "--artifact", str(path), "--output", str(output), "--force"]) == 1 + assert _json(output / "machine-state.json")["final_status"] == "BLOCKED" + + +def test_generic_negation_cannot_launder_an_adjacent_positive_claim() -> None: + attacks = [ + "pending documentation, production is live", + "unsupported note \u2014 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 attack in attacks: + with pytest.raises(ReviewBlocked): + _validate_generated_output_security({"note": attack}, "hostile generated output") + + for bounded in ( + "This does not prove production readiness, customer deployment, or signal-observed proof.", + "Production readiness is unsupported.", + "Claims block customer deployment.", + ): + _validate_generated_output_security({"note": bounded}, "bounded generated output") + + with pytest.raises(ReviewBlocked): + _validate_generated_output_security( + {"extra": "not_public_safe; production is live"}, + "hostile generated output", + ) + + +def test_compositional_promotion_keys_and_embedded_structures_fail_closed() -> None: + keys = [ + "production_live", + "customer_deployment", + "socaas_deployment", + "runtime_status", + "signal_status", + "approval_status", + "closure_status", + "case_status", + "public_safe_runtime", + "final_authorized", + "%70roduction_live", + "production_live_flag", + "customer_deployment_enabled", + "runtime_status_value", + "signal_observed_state", + "public_safe_runtime_flag", + "final_authorized_flag", + "case_closed_value", + "ai_approval_state", + "ai_disposition_enabled", + "analyst_authority_state", + "analyst_approval_value", + "approved_by_analyst_flag", + ] + for key in keys: + payload = {"metadata": {key: True}} + encoded_json = json.dumps(payload, separators=(",", ":")) + variants = [ + payload, + encoded_json, + quote(encoded_json, safe=""), + base64.urlsafe_b64encode(encoded_json.encode("utf-8")).decode("ascii").rstrip("="), + ] + for variant in variants: + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries({"extension": variant}, "hostile input") + with pytest.raises(ReviewBlocked): + _validate_generated_output_security({"extension": variant}, "hostile output") + + _validate_recursive_boundaries( + {"metadata": {"production_live": False, "case_status": "BLOCKED"}}, + "bounded input", + ) + _validate_generated_output_security( + {"metadata": {"production_live": False, "case_status": "BLOCKED"}}, + "bounded output", + ) + + +def test_private_marker_keys_fail_closed_through_recursive_encodings() -> None: + keys = [ + "raw_wazuh", + "private_evidence", + "%72aw_wazuh", + "%2572aw_wazuh", + "%70rivate_evidence", + "%2570rivate_evidence", + ] + for key in keys: + payload = {"metadata": {key: "hostile marker"}} + encoded_json = json.dumps(payload, separators=(",", ":")) + variants = [ + payload, + encoded_json, + quote(encoded_json, safe=""), + quote(quote(encoded_json, safe=""), safe=""), + base64.urlsafe_b64encode(encoded_json.encode("utf-8")).decode("ascii").rstrip("="), + ] + for variant in variants: + with pytest.raises(ReviewBlocked): + _validate_no_private_markers({"extension": variant}, "hostile private input") + + for value in ( + "raw_wazuh", + "private_evidence", + "endpoint_log", + "generated_password", + "private_payload", + "raw-alert", + ): + for variant in ( + value, + quote(value, safe=""), + base64.urlsafe_b64encode(value.encode("utf-8")).decode("ascii").rstrip("="), + ): + with pytest.raises(ReviewBlocked): + _validate_no_private_markers({"note": variant}, "hostile private value") + def test_encoded_mixed_and_drive_relative_paths_fail_closed(tmp_path) -> None: attacks = [ @@ -330,3 +509,95 @@ def test_single_replay_binds_every_output_and_machine_state_field(tmp_path) -> N assert any("digest mismatch" in error for error in verify_review_run(state_path)), name path.write_bytes(original) assert verify_review_run(state_path) == [] + + +def test_single_replay_rejects_rehashed_output_and_guardrail_laundering(tmp_path) -> None: + output = tmp_path / "review-run" + assert main(["review", "run", "--artifact", str(MANIFEST), "--output", str(output), "--force"]) == 0 + state_path = output / "machine-state.json" + baseline = _json(state_path) + + for name in baseline["output_digests"]: + path = output / name + original = path.read_text(encoding="utf-8") + if path.suffix == ".json": + hostile = json.loads(original) + hostile["replay_extension"] = {"approvedByAnalyst": True} + path.write_text(json.dumps(hostile, indent=2, sort_keys=True) + "\n", encoding="utf-8") + else: + path.write_text(original + "\nproduction ready\n", encoding="utf-8") + state = deepcopy(baseline) + state["output_digests"][name] = hashlib.sha256(path.read_bytes()).hexdigest() + state["state_integrity_digest"] = _integrity(state) + state_path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path), name + path.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + for mutator in ( + lambda state: state["outputs"].update({"proofcard_markdown": "proofcard.json"}), + lambda state: state["stages"][0].update({"proof_boundary": "production ready"}), + lambda state: state["blocked_claims"][0].update({"safer_wording": "production ready"}), + lambda state: state.update({"proof_boundary": "production ready"}), + lambda state: state.update({"replay_extension": "bounded-looking extra field"}), + lambda state: state.update({"created_at": "2099-01-01T00:00:00Z"}), + ): + hostile_state = deepcopy(baseline) + mutator(hostile_state) + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) + + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) == [] + + +def test_blocked_replay_rejects_rehashed_output_and_role_laundering(tmp_path) -> None: + manifest = _json(MANIFEST) + manifest["requested_claims"] = {"nested": {"approvedByAnalyst": True}} + manifest_path = tmp_path / "blocked-input.json" + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / "blocked-run" + assert main(["review", "run", "--artifact", str(manifest_path), "--output", str(output), "--force"]) == 1 + state_path = output / "machine-state.json" + baseline = _json(state_path) + assert baseline["final_status"] == "BLOCKED" + assert verify_review_run(state_path) == [] + + for name in baseline["output_digests"]: + path = output / name + original = path.read_text(encoding="utf-8") + if path.suffix == ".json": + hostile = json.loads(original) + hostile["replay_extension"] = {"publicSafe": True} + path.write_text(json.dumps(hostile, indent=2, sort_keys=True) + "\n", encoding="utf-8") + else: + path.write_text(original + "\nfinal authorization\n", encoding="utf-8") + state = deepcopy(baseline) + state["output_digests"][name] = hashlib.sha256(path.read_bytes()).hexdigest() + state["state_integrity_digest"] = _integrity(state) + state_path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path), name + path.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + hostile_state = deepcopy(baseline) + hostile_state["outputs"]["blocked_review"] = "run-summary.json" + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) + + hostile_state = deepcopy(baseline) + hostile_state["replay_extension"] = "bounded-looking extra field" + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) + + hostile_state = deepcopy(baseline) + hostile_state["created_at"] = "2099-01-01T00:00:00Z" + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text(json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) + + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + assert verify_review_run(state_path) == [] From 6d1581393deb8a1ffe1894b2761056d3af792746 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 09:53:11 -0500 Subject: [PATCH 35/73] fix(hoxline): harden recursive review authority --- .github/workflows/ci.yml | 14 +- README.md | 8 +- .../HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md | 8 +- docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md | 12 +- docs/gauntlet/HOXLINE_GAUNTLET_V0.md | 14 +- .../review-engine/HOXLINE_REVIEW_ENGINE_V1.md | 14 +- docs/reviewer/HOXLINE_REVIEWER_START_HERE.md | 2 +- examples/gauntlet/controlled-test-events.json | 155 +++++++++ .../gauntlet/expected-detection-results.json | 4 +- examples/gauntlet/sample-artifact.json | 14 +- examples/gauntlet/sample-evidence-graph.json | 4 +- examples/gauntlet/sample-proofcard.json | 6 +- examples/gauntlet/synthetic-events.json | 155 --------- .../duplicate-artifact-id-index.json | 2 +- .../hostile-batch/missing-manifest-index.json | 2 +- .../private-evidence-batch-index.json | 4 +- .../production-claim-batch-index.json | 2 +- .../unexpected-blocked-pass-index.json | 2 +- .../hostile-batch/unexpected-pass-index.json | 2 +- .../unsafe-batch-public-safe-claim-index.json | 2 +- .../review/hostile/missing-fixture-path.json | 6 +- .../hostile/missing-telemetry-contract.json | 6 +- .../private-evidence-field-attempt.json | 8 +- .../private-execution-id-field-attempt.json | 8 +- .../hostile/raw-alert-like-field-attempt.json | 8 +- .../hostile/requested-ai-approved-claim.json | 4 +- .../requested-analyst-approved-claim.json | 4 +- .../requested-autonomous-soc-claim.json | 4 +- .../hostile/requested-case-closure-claim.json | 4 +- .../hostile/requested-customer-claim.json | 4 +- .../requested-final-authorization-claim.json | 4 +- .../hostile/requested-production-claim.json | 4 +- .../hostile/requested-socaas-claim.json | 4 +- .../hostile/unsafe-public-safe-status.json | 6 +- src/hoxline/cli.py | 6 +- src/hoxline/metrics/__init__.py | 4 +- src/hoxline/metrics/evaluator.py | 28 +- src/hoxline/metrics/report.py | 6 +- src/hoxline/review_engine.py | 304 +++++++++++++++-- tests/fixtures/case_growth/README.md | 2 +- tests/test_gauntlet_metrics_v0.py | 10 +- tests/test_gauntlet_v0.py | 4 +- ...i_artifact_review_manifest_expansion_v1.py | 31 ++ tests/test_review_engine_v1.py | 309 +++++++++++++++++- 44 files changed, 905 insertions(+), 299 deletions(-) create mode 100644 examples/gauntlet/controlled-test-events.json delete mode 100644 examples/gauntlet/synthetic-events.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8585a6d..bc3d439 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,13 +128,25 @@ jobs: - name: Install package and declared test dependencies working-directory: org/hoxline run: | - python -m pip install --upgrade pip python -m pip install -e ".[test]" - name: Compile working-directory: org/hoxline run: python -B -m compileall src tests + - name: Reject retired tracked vocabulary + working-directory: org/hoxline + run: | + python -B - <<'PY' + from pathlib import Path + from hoxline.review_engine import verify_tracked_vocabulary + + errors = verify_tracked_vocabulary(Path(".")) + if errors: + raise SystemExit("\n".join(errors)) + print("Tracked vocabulary verification: PASS") + PY + - name: Run unittest suite working-directory: org/hoxline run: python -B -m unittest discover -s tests diff --git a/README.md b/README.md index 655b412..2552612 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ python -B -m hoxline demo quickstart --output .hoxline/demo-runs/self-test --for python -B -m hoxline demo verify --input .hoxline/demo-runs/self-test/run-summary.json ``` -The command writes `.hoxline/demo-runs//` with `intake.json`, `evidence-graph.json`, `telemetry-contract-check.json`, `validation-result.json`, `synthetic-signal.json`, `enrichment.json`, `triage-summary.md`, `proofcard.json`, `proofcard.md`, `claim-authority.json`, `reviewer-pack.md`, and `run-summary.json`. +The command writes `.hoxline/demo-runs//` with `intake.json`, `evidence-graph.json`, `telemetry-contract-check.json`, `validation-result.json`, `controlled-test-signal.json`, `enrichment.json`, `triage-summary.md`, `proofcard.json`, `proofcard.md`, `claim-authority.json`, `reviewer-pack.md`, and `run-summary.json`. -What it proves: Hoxline can carry a synthetic HO-DET-010 fixture through intake, evidence graph, telemetry contract check, controlled validation, fixture-only signal simulation, enrichment, triage, ProofCard, Claim Authority, blocked claims, and reviewer packaging. +What it proves: Hoxline can carry a controlled-test HO-DET-010 fixture through intake, evidence graph, telemetry contract check, controlled validation, fixture-only signal simulation, enrichment, triage, ProofCard, Claim Authority, blocked claims, and reviewer packaging. What it does not prove: live runtime behavior, public signal observation, public-safe status, production readiness, SOCaaS deployment, customer deployment, autonomous SOC operation, AI approval, analyst approval, final authorization, or case closure. The demo does not touch endpoints, users, groups, Wazuh, Splunk, Cribl, private infrastructure, ledgers, or website proof state. @@ -46,7 +46,7 @@ python -B -m hoxline review verify --run .hoxline/runs//machine-state.js The command writes `.hoxline/runs//` with `artifact-manifest.json`, stage outputs, `proofcard.json`, `proofcard.md`, `claim-authority.json`, `reviewer-pack.md`, `machine-state.json`, and `run-summary.json`. -What it proves: Hoxline can take a public sanitized synthetic artifact manifest, run deterministic local review stages, write replayable machine state, generate reviewer artifacts, and block unsupported claims. +What it proves: Hoxline can take a public sanitized controlled-test artifact manifest, run deterministic local review stages, write replayable machine state, generate reviewer artifacts, and block unsupported claims. What it does not prove: live runtime behavior, public signal observation, public-safe status, production readiness, SOCaaS deployment, customer deployment, autonomous SOC operation, AI approval, analyst approval, final authorization, or case closure. @@ -141,7 +141,7 @@ References are carried from `hawkinsoperations-platform#64` and `hawkinsoperatio Hoxline also supports private runtime candidate review for artifacts whose source, telemetry contract, validation, private signal, packet verification, and scheduled collector inclusion have been established internally but are not public-safe proof. -Separate from the one-command fixture demo, HO-DET-010 also has private runtime-candidate context that is not published here. The public demo uses only synthetic fixture records and must not be confused with private runtime candidate evidence. HO-DET-010 remains `NOT_PUBLIC_SAFE`; human review is required; AI has no disposition authority; no public proof, ledger append, website proof promotion, production, customer, SOCaaS, fleet, analyst-approved, AI-approved, or case-closure claim is made. +Separate from the one-command fixture demo, HO-DET-010 also has private runtime-candidate context that is not published here. The public demo uses only controlled-test fixture records and must not be confused with private runtime candidate evidence. HO-DET-010 remains `NOT_PUBLIC_SAFE`; human review is required; AI has no disposition authority; no public proof, ledger append, website proof promotion, production, customer, SOCaaS, fleet, analyst-approved, AI-approved, or case-closure claim is made. ## Claim Firewall Claim Firewall is the first Claim Authority enforcement capability inside Hoxline. diff --git a/docs/demo/HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md b/docs/demo/HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md index 876002e..3d3b214 100644 --- a/docs/demo/HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md +++ b/docs/demo/HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md @@ -12,7 +12,7 @@ ai_disposition_authority: false ## Purpose -This demo lets a reviewer clone Hoxline, run one command from the repo root, and see the governed ProofOps loop in about 30 seconds. It uses a bundled synthetic HO-DET-010 fixture for a local Administrators membership-change pattern. It does not create users, change groups, touch endpoints, connect to Wazuh, publish private evidence, or claim live runtime proof. +This demo lets a reviewer clone Hoxline, run one command from the repo root, and see the governed ProofOps loop in about 30 seconds. It uses a bundled controlled-test HO-DET-010 fixture for a local Administrators membership-change pattern. It does not create users, change groups, touch endpoints, connect to Wazuh, publish private evidence, or claim live runtime proof. ## Command @@ -37,7 +37,7 @@ The command writes `.hoxline/demo-runs//` with: - `evidence-graph.json` - `telemetry-contract-check.json` - `validation-result.json` -- `synthetic-signal.json` +- `controlled-test-signal.json` - `enrichment.json` - `triage-summary.md` - `proofcard.json` @@ -64,7 +64,7 @@ The command writes `.hoxline/demo-runs//` with: ## Supported Artifact -The demo supports `HO-DET-010` with a synthetic local Administrators membership-change fixture: +The demo supports `HO-DET-010` with a controlled-test local Administrators membership-change fixture: - positive fixture: `examples/demo/ho-det-010-safe-fixture.json` - negative fixture: `examples/demo/ho-det-010-safe-negative-fixture.json` @@ -73,7 +73,7 @@ The telemetry contract represents Windows Security EventChannel assumptions for ## What It Proves -- Hoxline can generate the reviewer path locally from synthetic fixtures approved for public demo use. +- Hoxline can generate the reviewer path locally from controlled-test fixtures approved for public demo use. - The demo produces structured records for intake, graph linkage, telemetry assumptions, validation, signal simulation, enrichment, triage, ProofCard, Claim Authority, and reviewer packaging. - Claim Authority allows bounded demo wording and blocks unsupported public claims. diff --git a/docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md b/docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md index 88eee19..65915ac 100644 --- a/docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md +++ b/docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md @@ -6,9 +6,9 @@ Proof ceiling: `CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY`. ## What Is Measured -The metrics engine evaluates the controlled synthetic event fixture for the browser-cache / script-interpreter detection-review scenario. It emits numeric JSON for: +The metrics engine evaluates the controlled controlled-test event fixture for the browser-cache / script-interpreter detection-review scenario. It emits numeric JSON for: -* synthetic event volume +* controlled-test event volume * expected positive and negative counts * true positive, true negative, false positive, and false negative counts * precision, recall, F1, and false-positive rate @@ -22,7 +22,7 @@ The metrics engine evaluates the controlled synthetic event fixture for the brow | Metric | Value | Meaning | | --- | ---: | --- | -| events_total | 12 | synthetic events evaluated | +| events_total | 12 | controlled-test events evaluated | | expected_positive | 4 | events expected to match the controlled review rule | | expected_negative | 8 | events expected not to match the controlled review rule | | true_positive | 4 | expected positive events matched | @@ -64,7 +64,7 @@ proof_ceiling: CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY ## What The Numbers Prove -The numbers prove that Hoxline can run a deterministic controlled-fixture evaluation for this one synthetic detection-review artifact and emit measurable JSON. They also show that the fixture has all required telemetry fields, that Claim Authority blocks unsupported wording in the bad release note, and that the ProofCard has all required sections. +The numbers prove that Hoxline can run a deterministic controlled-fixture evaluation for this one controlled-test detection-review artifact and emit measurable JSON. They also show that the fixture has all required telemetry fields, that Claim Authority blocks unsupported wording in the bad release note, and that the ProofCard has all required sections. ## What The Numbers Do Not Prove @@ -74,7 +74,7 @@ This artifact does not prove runtime evidence, signal evidence, customer deploym ```powershell python -B -m hoxline.cli gauntlet metrics ` - --events examples/gauntlet/synthetic-events.json ` + --events examples/gauntlet/controlled-test-events.json ` --artifact examples/gauntlet/sample-artifact.json ` --proofcard examples/gauntlet/sample-proofcard.json ` --claim-output examples/gauntlet/sample-claim-authority-output.json ` @@ -85,7 +85,7 @@ To write the report: ```powershell python -B -m hoxline.cli gauntlet metrics ` - --events examples/gauntlet/synthetic-events.json ` + --events examples/gauntlet/controlled-test-events.json ` --artifact examples/gauntlet/sample-artifact.json ` --proofcard examples/gauntlet/sample-proofcard.json ` --claim-output examples/gauntlet/sample-claim-authority-output.json ` diff --git a/docs/gauntlet/HOXLINE_GAUNTLET_V0.md b/docs/gauntlet/HOXLINE_GAUNTLET_V0.md index 6dc32f1..5d403c7 100644 --- a/docs/gauntlet/HOXLINE_GAUNTLET_V0.md +++ b/docs/gauntlet/HOXLINE_GAUNTLET_V0.md @@ -4,7 +4,7 @@ This gauntlet is a controlled product demo artifact for Hoxline. It shows how an Artifact ID: `HOX-GAUNTLET-001` -Scenario: an AI assistant drafts a synthetic Splunk/SOC detection-review artifact and release note for a browser-cache / ClickFix-style payload extraction detection idea. The fixture is sanitized, contains no malware code, contains no exploit instructions, and does not depend on live telemetry. +Scenario: an AI assistant drafts a controlled-test Splunk/SOC detection-review artifact and release note for a browser-cache / ClickFix-style payload extraction detection idea. The fixture is sanitized, contains no malware code, contains no exploit instructions, and does not depend on live telemetry. Proof ceiling: `CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY`. @@ -31,7 +31,7 @@ AI-assisted security work ## Three-Minute Reviewer Path 1. Read this page for the boundary and stage map. -2. Open `examples/gauntlet/sample-artifact.json` for the synthetic detection artifact and telemetry contract. +2. Open `examples/gauntlet/sample-artifact.json` for the controlled-test detection artifact and telemetry contract. 3. Compare `examples/gauntlet/bad-release-note.md` with `examples/gauntlet/safe-release-note.md`. 4. Open `examples/gauntlet/sample-evidence-graph.json`, `examples/gauntlet/sample-promotion-state.json`, `examples/gauntlet/sample-proofcard.json`, and `examples/gauntlet/sample-claim-authority-output.json`. 5. Open `docs/gauntlet/HOXLINE_GAUNTLET_METRICS_V0.md` for the numeric Work Impact Metrics v0 output. @@ -55,7 +55,7 @@ Purpose: demonstrate how Hoxline governs an AI-assisted Splunk/SOC detection dra The sample artifact has: -* A synthetic Splunk-style detection review object. +* A controlled-test Splunk-style detection review object. * A telemetry contract describing required fields and fixture-only scope. * Controlled validation with deterministic positive and negative fixture expectations. * Runtime candidate state recorded as `NOT_PROMOTED`. @@ -70,7 +70,7 @@ The sample artifact has: | 1 | AI-assisted security work | Work is marked `ai_assisted=true`. | Intake required. | | 2 | Artifact Intake | Artifact identity, source-control path, scope, and proposed claims are recorded. | Evidence graph node created. | | 3 | Evidence Graph | Artifact, telemetry contract, validation, runtime candidate, signal observation, review, ProofCard, and claim decision nodes are linked. | Traceable state exists. | -| 4 | Telemetry Contract Check | Status is `PASSED_SYNTHETIC_CONTRACT`. | Required synthetic fields are declared. | +| 4 | Telemetry Contract Check | Status is `CONTROLLED_TEST_VALIDATED`. | Required controlled-test fields are declared. | | 5 | Controlled Validation | Status is `PASSED_CONTROLLED_FIXTURES`. | Fixture-only validation supports the safe claim. | | 6 | Runtime Candidate Ledger | Candidate state is `NOT_PROMOTED`. | Runtime proof remains unavailable. | | 7 | Signal Observation | Signal state is `NOT_OBSERVED`. | Signal proof remains unavailable. | @@ -86,7 +86,7 @@ The sample artifact has: * `examples/gauntlet/sample-evidence-graph.json` * `examples/gauntlet/sample-proofcard.json` * `examples/gauntlet/sample-claim-authority-output.json` -* `examples/gauntlet/synthetic-events.json` +* `examples/gauntlet/controlled-test-events.json` * `examples/gauntlet/expected-detection-results.json` * `examples/gauntlet/sample-work-impact-metrics.json` * `examples/gauntlet/bad-release-note.md` @@ -96,10 +96,10 @@ The sample JSON files keep runtime observation, signal observation, external pro ## Work Impact Metrics -`HOX-GAUNTLET-001` now emits numeric Work Impact Metrics v0 for the controlled fixture: 12 synthetic events, 4 expected positives, 8 expected negatives, 4 true positives, 8 true negatives, 0 false positives, 0 false negatives, 1.0 precision, 1.0 recall, 1.0 F1, 0.0 false-positive rate, 100.0% telemetry coverage, 7 claims scanned, 1 claim allowed, 6 claims blocked, and 100.0% ProofCard completeness. +`HOX-GAUNTLET-001` now emits numeric Work Impact Metrics v0 for the controlled fixture: 12 controlled-test events, 4 expected positives, 8 expected negatives, 4 true positives, 8 true negatives, 0 false positives, 0 false negatives, 1.0 precision, 1.0 recall, 1.0 F1, 0.0 false-positive rate, 100.0% telemetry coverage, 7 claims scanned, 1 claim allowed, 6 claims blocked, and 100.0% ProofCard completeness. Run: ```powershell -python -B -m hoxline.cli gauntlet metrics --events examples/gauntlet/synthetic-events.json --artifact examples/gauntlet/sample-artifact.json --proofcard examples/gauntlet/sample-proofcard.json --claim-output examples/gauntlet/sample-claim-authority-output.json --format json +python -B -m hoxline.cli gauntlet metrics --events examples/gauntlet/controlled-test-events.json --artifact examples/gauntlet/sample-artifact.json --proofcard examples/gauntlet/sample-proofcard.json --claim-output examples/gauntlet/sample-claim-authority-output.json --format json ``` diff --git a/docs/review-engine/HOXLINE_REVIEW_ENGINE_V1.md b/docs/review-engine/HOXLINE_REVIEW_ENGINE_V1.md index 68db2a7..26b2ca4 100644 --- a/docs/review-engine/HOXLINE_REVIEW_ENGINE_V1.md +++ b/docs/review-engine/HOXLINE_REVIEW_ENGINE_V1.md @@ -50,7 +50,7 @@ Required fields include `manifest_version`, `artifact_id`, `artifact_name`, `art 2. `evidence_graph` 3. `telemetry_contract_check` 4. `controlled_validation` -5. `synthetic_signal` +5. `controlled_test_signal` 6. `enrichment` 7. `triage` 8. `proofcard` @@ -72,7 +72,7 @@ No proof-boundary violation is warning-only. Violations produce `final_status=BL ## Generated Outputs -PASS runs write `artifact-manifest.json`, `intake.json`, `evidence-graph.json`, `telemetry-contract-check.json`, `validation-result.json`, `synthetic-signal.json`, `enrichment.json`, `triage-summary.md`, `proofcard.json`, `proofcard.md`, `claim-authority.json`, `reviewer-pack.md`, `machine-state.json`, and `run-summary.json`. +PASS runs write `artifact-manifest.json`, `intake.json`, `evidence-graph.json`, `telemetry-contract-check.json`, `validation-result.json`, `controlled-test-signal.json`, `enrichment.json`, `triage-summary.md`, `proofcard.json`, `proofcard.md`, `claim-authority.json`, `reviewer-pack.md`, `machine-state.json`, and `run-summary.json`. BLOCKED runs write sanitized `artifact-manifest.json`, `machine-state.json`, `blocked-review.md`, and `run-summary.json` when safe. @@ -82,7 +82,7 @@ BLOCKED runs write sanitized `artifact-manifest.json`, `machine-state.json`, `bl ## Hostile Fixture Behavior -Synthetic hostile manifests under `examples/review/hostile/` intentionally request unsafe claims, omit telemetry, point to missing fixtures, or include private/raw-like fields. They are expected to block. They are not evidence and are not runtime material. +Controlled-test hostile manifests under `examples/review/hostile/` intentionally request unsafe claims, omit telemetry, point to missing fixtures, or include private/raw-like fields. They are expected to block. They are not evidence and are not runtime material. ## Clean-Room Expectation @@ -110,19 +110,19 @@ A batch exits zero only when actual artifact outcomes match `expected_pass_artif ## Hostile Batch Behavior -Synthetic hostile indexes under `examples/review/hostile-batch/` cover duplicate artifact IDs, missing manifests, expectation mismatches, unsafe batch status, private-marker attempts, and production wording. They are expected to block fail-closed. +Controlled-test hostile indexes under `examples/review/hostile-batch/` cover duplicate artifact IDs, missing manifests, expectation mismatches, unsafe batch status, private-marker attempts, and production wording. They are expected to block fail-closed. ## Adding The Next Artifact Safely -Add a manifest only when source-controlled metadata exists or when the manifest is explicitly synthetic and fixture-only. Add positive and negative synthetic fixtures under `examples/review/fixtures/`, list every blocked claim class, keep all governance flags bounded, add the artifact to the index, and add a hostile case for the most likely unsafe claim. If the artifact cannot satisfy telemetry or fixture gates, list it as expected BLOCKED instead of pretending it is review-passable. +Add a manifest only when source-controlled metadata exists or when the manifest is explicitly controlled-test and fixture-only. Add positive and negative controlled-test fixtures under `examples/review/fixtures/`, list every blocked claim class, keep all governance flags bounded, add the artifact to the index, and add a hostile case for the most likely unsafe claim. If the artifact cannot satisfy telemetry or fixture gates, list it as expected BLOCKED instead of pretending it is review-passable. ## Future Detection Plug-In Path -To add a future detection, create a synthetic fixture manifest with telemetry assumptions, allowed example fixture paths, expected event/rule metadata, requested bounded claim wording, blocked claim classes, and explicit proof/runtime/signal boundaries. The engine should block until every required field and gate is satisfied. +To add a future detection, create a controlled-test fixture manifest with telemetry assumptions, allowed example fixture paths, expected event/rule metadata, requested bounded claim wording, blocked claim classes, and explicit proof/runtime/signal boundaries. The engine should block until every required field and gate is satisfied. ## What It Proves -It proves Hoxline can deterministically review a public sanitized synthetic artifact manifest through machine-checkable stages, generate reviewer artifacts, emit replayable machine state, and block unsupported claims. +It proves Hoxline can deterministically review a public sanitized controlled-test artifact manifest through machine-checkable stages, generate reviewer artifacts, emit replayable machine state, and block unsupported claims. ## What It Does Not Prove diff --git a/docs/reviewer/HOXLINE_REVIEWER_START_HERE.md b/docs/reviewer/HOXLINE_REVIEWER_START_HERE.md index e4376b7..0872c65 100644 --- a/docs/reviewer/HOXLINE_REVIEWER_START_HERE.md +++ b/docs/reviewer/HOXLINE_REVIEWER_START_HERE.md @@ -123,7 +123,7 @@ Current private scheduled collector scope: - HO-DET-011 - HO-DET-012 -HO-DET-010 has private runtime-candidate context outside this public fixture demo. Do not confuse the one-command synthetic fixture output with private runtime candidate evidence. HO-DET-010 remains `NOT_PUBLIC_SAFE`, `human_review_required=true`, and `ai_disposition_authority=false` pending governed review. +HO-DET-010 has private runtime-candidate context outside this public fixture demo. Do not confuse the one-command controlled-test fixture output with private runtime candidate evidence. HO-DET-010 remains `NOT_PUBLIC_SAFE`, `human_review_required=true`, and `ai_disposition_authority=false` pending governed review. Do not copy private packet contents, raw Wazuh alerts, endpoint logs, command lines, generated credentials, private payloads, execution identifiers, or private telemetry into public documentation. ## Website Route diff --git a/examples/gauntlet/controlled-test-events.json b/examples/gauntlet/controlled-test-events.json new file mode 100644 index 0000000..e866385 --- /dev/null +++ b/examples/gauntlet/controlled-test-events.json @@ -0,0 +1,155 @@ +{ + "schema_version": "controlled-test-events-v0", + "artifact_id": "HOX-GAUNTLET-001", + "dataset_scope": "controlled controlled-test fixture only", + "contains_malware_code": false, + "contains_exploit_instructions": false, + "contains_customer_data": false, + "contains_private_runtime_evidence": false, + "events": [ + { + "event_id": "controlled-test-event-001", + "event_time": "2026-06-27T00:00:01Z", + "host": "controlled-test-host-01", + "user": "controlled-test-user-a", + "parent_process_name": "controlled_test_browser.exe", + "process_name": "powershell.exe", + "process_command_line": "powershell.exe -NoProfile -File C:\\Controlled-test\\Review\\cache-followup.ps1", + "file_path": "C:\\Users\\controlled-test-user-a\\AppData\\Local\\Browser\\Cache\\cache-item-001.tmp", + "expected_detection_match": true, + "rationale": "Browser-like parent, cache-like path, and script interpreter child match the controlled review rule." + }, + { + "event_id": "controlled-test-event-002", + "event_time": "2026-06-27T00:00:02Z", + "host": "controlled-test-host-01", + "user": "controlled-test-user-b", + "parent_process_name": "chrome.exe", + "process_name": "cmd.exe", + "process_command_line": "cmd.exe /c C:\\Controlled-test\\Review\\cache-followup.cmd", + "file_path": "C:\\Users\\controlled-test-user-b\\AppData\\Local\\Chrome\\User Data\\Default\\Cache\\cache-item-002.bin", + "expected_detection_match": true, + "rationale": "Chrome parent, browser cache path, and command interpreter child match the controlled review rule." + }, + { + "event_id": "controlled-test-event-003", + "event_time": "2026-06-27T00:00:03Z", + "host": "controlled-test-host-02", + "user": "controlled-test-user-c", + "parent_process_name": "msedge.exe", + "process_name": "wscript.exe", + "process_command_line": "wscript.exe C:\\Controlled-test\\Review\\cache-followup.vbs", + "file_path": "C:\\Users\\controlled-test-user-c\\AppData\\Local\\Edge\\User Data\\Default\\Cache\\cache-item-003.dat", + "expected_detection_match": true, + "rationale": "Edge parent, cache path, and Windows script host child match the controlled review rule." + }, + { + "event_id": "controlled-test-event-004", + "event_time": "2026-06-27T00:00:04Z", + "host": "controlled-test-host-02", + "user": "controlled-test-user-d", + "parent_process_name": "firefox.exe", + "process_name": "pwsh.exe", + "process_command_line": "pwsh.exe -File C:\\Controlled-test\\Review\\cache-followup.ps1", + "file_path": "C:\\Users\\controlled-test-user-d\\AppData\\Local\\Firefox\\Profiles\\controlled-test\\cache2\\cache-item-004.bin", + "expected_detection_match": true, + "rationale": "Firefox parent, cache path, and PowerShell child match the controlled review rule." + }, + { + "event_id": "controlled-test-event-005", + "event_time": "2026-06-27T00:00:05Z", + "host": "controlled-test-host-03", + "user": "controlled-test-user-e", + "parent_process_name": "controlled_test_browser.exe", + "process_name": "notepad.exe", + "process_command_line": "notepad.exe C:\\Controlled-test\\Review\\cache-note.txt", + "file_path": "C:\\Users\\controlled-test-user-e\\AppData\\Local\\Browser\\Cache\\cache-note.txt", + "expected_detection_match": false, + "rationale": "Benign browser-cache event uses a non-script child process and must not match." + }, + { + "event_id": "controlled-test-event-006", + "event_time": "2026-06-27T00:00:06Z", + "host": "controlled-test-host-03", + "user": "controlled-test-user-f", + "parent_process_name": "explorer.exe", + "process_name": "powershell.exe", + "process_command_line": "powershell.exe -NoProfile -File C:\\Controlled-test\\Review\\admin-note.ps1", + "file_path": "C:\\Controlled-test\\Review\\admin-note.ps1", + "expected_detection_match": false, + "rationale": "Script interpreter child exists, but the parent is not browser-like and the file path is not cache-like." + }, + { + "event_id": "controlled-test-event-007", + "event_time": "2026-06-27T00:00:07Z", + "host": "controlled-test-host-04", + "user": "controlled-test-user-g", + "parent_process_name": "chrome.exe", + "process_name": "notepad.exe", + "process_command_line": "notepad.exe C:\\Controlled-test\\Review\\cache-text.txt", + "file_path": "C:\\Users\\controlled-test-user-g\\AppData\\Local\\Chrome\\User Data\\Default\\Cache\\cache-text.txt", + "expected_detection_match": false, + "rationale": "Browser parent and cache path exist, but the child process is not a script interpreter." + }, + { + "event_id": "controlled-test-event-008", + "event_time": "2026-06-27T00:00:08Z", + "host": "controlled-test-host-04", + "user": "controlled-test-user-h", + "parent_process_name": "msedge.exe", + "process_name": "powershell.exe", + "process_command_line": "powershell.exe -NoProfile -File C:\\Controlled-test\\Review\\download-followup.ps1", + "file_path": "C:\\Users\\controlled-test-user-h\\Downloads\\download-followup.ps1", + "expected_detection_match": false, + "rationale": "Browser parent and script interpreter child exist, but the file path is not cache-like." + }, + { + "event_id": "controlled-test-event-009", + "event_time": "2026-06-27T00:00:09Z", + "host": "controlled-test-host-05", + "user": "controlled-test-user-i", + "parent_process_name": "controlled_test_browser.exe", + "process_name": "rundll32.exe", + "process_command_line": "rundll32.exe C:\\Controlled-test\\Review\\sample.dll,ReviewEntry", + "file_path": "C:\\Users\\controlled-test-user-i\\AppData\\Local\\Browser\\Cache\\cache-item-009.bin", + "expected_detection_match": false, + "rationale": "Browser parent and cache path exist, but the child process is outside the controlled script-interpreter list." + }, + { + "event_id": "controlled-test-event-010", + "event_time": "2026-06-27T00:00:10Z", + "host": "controlled-test-host-05", + "user": "controlled-test-user-j", + "parent_process_name": "outlook.exe", + "process_name": "wscript.exe", + "process_command_line": "wscript.exe C:\\Controlled-test\\Review\\mail-followup.vbs", + "file_path": "C:\\Controlled-test\\Review\\mail-followup.vbs", + "expected_detection_match": false, + "rationale": "Script host child exists, but neither browser parent nor cache path is present." + }, + { + "event_id": "controlled-test-event-011", + "event_time": "2026-06-27T00:00:11Z", + "host": "controlled-test-host-06", + "user": "controlled-test-user-k", + "parent_process_name": "firefox.exe", + "process_name": "calc.exe", + "process_command_line": "calc.exe", + "file_path": "C:\\Users\\controlled-test-user-k\\AppData\\Local\\Firefox\\Profiles\\controlled-test\\cache2\\cache-item-011.bin", + "expected_detection_match": false, + "rationale": "Browser parent and cache path exist, but the child process is a benign calculator process." + }, + { + "event_id": "controlled-test-event-012", + "event_time": "2026-06-27T00:00:12Z", + "host": "controlled-test-host-06", + "user": "controlled-test-user-l", + "parent_process_name": "services.exe", + "process_name": "cmd.exe", + "process_command_line": "cmd.exe /c C:\\Controlled-test\\Review\\service-check.cmd", + "file_path": "C:\\Controlled-test\\Review\\service-check.cmd", + "expected_detection_match": false, + "rationale": "Command interpreter child exists, but the parent and path do not match the browser-cache scenario." + } + ] +} diff --git a/examples/gauntlet/expected-detection-results.json b/examples/gauntlet/expected-detection-results.json index d235f79..eaebc5e 100644 --- a/examples/gauntlet/expected-detection-results.json +++ b/examples/gauntlet/expected-detection-results.json @@ -1,8 +1,8 @@ { "schema_version": "expected-detection-results-v0", "artifact_id": "HOX-GAUNTLET-001", - "fixture_scope": "controlled synthetic fixture only", - "detector_note": "Controlled fixture perfect means the deterministic review rule matches this synthetic dataset exactly; it does not claim production perfection.", + "fixture_scope": "controlled controlled-test fixture only", + "detector_note": "Controlled fixture perfect means the deterministic review rule matches this controlled-test dataset exactly; it does not claim production perfection.", "events_total": 12, "expected_positive": 4, "expected_negative": 8, diff --git a/examples/gauntlet/sample-artifact.json b/examples/gauntlet/sample-artifact.json index cd56b70..852c8ad 100644 --- a/examples/gauntlet/sample-artifact.json +++ b/examples/gauntlet/sample-artifact.json @@ -1,11 +1,11 @@ { "schema_version": "sample-artifact-v0", "artifact_id": "HOX-GAUNTLET-001", - "title": "Synthetic Splunk SOC detection review for browser-cache payload extraction", + "title": "Controlled-test Splunk SOC detection review for browser-cache payload extraction", "ai_assisted": true, "scenario": "An AI assistant drafts a Splunk/SOC-style detection idea and release note for browser-cache / ClickFix-style payload extraction review.", "public_safety": { - "synthetic_only": true, + "controlled_test_only": true, "contains_malware_code": false, "contains_exploit_instructions": false, "contains_customer_data": false, @@ -17,13 +17,13 @@ "status": "SOURCE_CONTROLLED_SAMPLE" }, "detection_artifact": { - "type": "synthetic_splunk_detection_review", + "type": "controlled_test_detection_review", "platform": "Splunk", "soc_queue": "detection-engineering-review", "name": "Browser cache extraction followed by script interpreter staging", - "description": "Synthetic review object for suspicious browser cache file access followed by script interpreter staging. The object is a detection-review fixture, not deployable detection logic.", + "description": "Controlled-test review object for suspicious browser cache file access followed by script interpreter staging. The object is a detection-review fixture, not deployable detection logic.", "risk_hypothesis": "A user interaction lure may lead to cached payload extraction and local interpreter staging.", - "splunk_search_pseudocode": "index=synthetic_endpoint sourcetype=synthetic_process (parent_process_name IN (browser.exe, synthetic_browser.exe)) AND (process_name IN (powershell.exe, cmd.exe, wscript.exe)) AND file_path=\"*\\\\Browser\\\\Cache\\\\*\"", + "splunk_search_pseudocode": "index=controlled_test_endpoint sourcetype=controlled_test_process (parent_process_name IN (browser.exe, controlled_test_browser.exe)) AND (process_name IN (powershell.exe, cmd.exe, wscript.exe)) AND file_path=\"*\\\\Browser\\\\Cache\\\\*\"", "required_fields": [ "event_time", "host", @@ -43,8 +43,8 @@ }, "telemetry_contract": { "contract_id": "HOX-GAUNTLET-001-TELEMETRY-CONTRACT", - "status": "PASSED_SYNTHETIC_CONTRACT", - "fixture_scope": "synthetic Splunk-style events only", + "status": "CONTROLLED_TEST_VALIDATED", + "fixture_scope": "controlled-test Splunk-style events only", "required_fields_present": true, "runtime_source_required_for_stronger_claims": true }, diff --git a/examples/gauntlet/sample-evidence-graph.json b/examples/gauntlet/sample-evidence-graph.json index cf19ede..38f697a 100644 --- a/examples/gauntlet/sample-evidence-graph.json +++ b/examples/gauntlet/sample-evidence-graph.json @@ -19,7 +19,7 @@ "id": "HOX-GAUNTLET-001-TELEMETRY-CONTRACT", "type": "telemetry_contract_check", "label": "Telemetry Contract Check", - "status": "PASSED_SYNTHETIC_CONTRACT" + "status": "CONTROLLED_TEST_VALIDATED" }, { "id": "HOX-GAUNTLET-001-CONTROLLED-VALIDATION", @@ -107,7 +107,7 @@ ], "telemetry_contract": { "contract_id": "HOX-GAUNTLET-001-TELEMETRY-CONTRACT", - "status": "PASSED_SYNTHETIC_CONTRACT", + "status": "CONTROLLED_TEST_VALIDATED", "required_fields": [ "event_time", "host", diff --git a/examples/gauntlet/sample-proofcard.json b/examples/gauntlet/sample-proofcard.json index 6a28a04..d3a6942 100644 --- a/examples/gauntlet/sample-proofcard.json +++ b/examples/gauntlet/sample-proofcard.json @@ -5,13 +5,13 @@ "title": "Hoxline Gauntlet v0 Splunk ProofOps lab ProofCard", "proof_ceiling": "CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY", "what_exists": [ - "Synthetic Splunk/SOC-style detection-review artifact in source control.", + "Controlled-test Splunk/SOC-style detection-review artifact in source control.", "Bad AI-assisted release note with unsupported claims.", "Safe release note constrained to controlled validation and claim-boundary workflow.", "Evidence graph, promotion state, telemetry contract, controlled validation result, and Claim Authority output." ], "what_was_tested": [ - "Required synthetic telemetry fields are declared.", + "Required controlled-test telemetry fields are declared.", "Positive fixture expectation equals four matches.", "Negative fixture expectation equals eight non-matches.", "Bad release note is blocked by Claim Authority policy.", @@ -20,7 +20,7 @@ "validation_result": { "status": "PASSED_CONTROLLED_FIXTURES", "deterministic": true, - "scope": "local synthetic fixture validation only", + "scope": "local controlled-test fixture validation only", "positive_fixture_expected_matches": 4, "positive_fixture_actual_matches": 4, "negative_fixture_expected_matches": 8, diff --git a/examples/gauntlet/synthetic-events.json b/examples/gauntlet/synthetic-events.json deleted file mode 100644 index 0016753..0000000 --- a/examples/gauntlet/synthetic-events.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "schema_version": "synthetic-events-v0", - "artifact_id": "HOX-GAUNTLET-001", - "dataset_scope": "controlled synthetic fixture only", - "contains_malware_code": false, - "contains_exploit_instructions": false, - "contains_customer_data": false, - "contains_private_runtime_evidence": false, - "events": [ - { - "event_id": "synthetic-event-001", - "event_time": "2026-06-27T00:00:01Z", - "host": "synthetic-host-01", - "user": "synthetic-user-a", - "parent_process_name": "synthetic_browser.exe", - "process_name": "powershell.exe", - "process_command_line": "powershell.exe -NoProfile -File C:\\Synthetic\\Review\\cache-followup.ps1", - "file_path": "C:\\Users\\synthetic-user-a\\AppData\\Local\\Browser\\Cache\\cache-item-001.tmp", - "expected_detection_match": true, - "rationale": "Browser-like parent, cache-like path, and script interpreter child match the controlled review rule." - }, - { - "event_id": "synthetic-event-002", - "event_time": "2026-06-27T00:00:02Z", - "host": "synthetic-host-01", - "user": "synthetic-user-b", - "parent_process_name": "chrome.exe", - "process_name": "cmd.exe", - "process_command_line": "cmd.exe /c C:\\Synthetic\\Review\\cache-followup.cmd", - "file_path": "C:\\Users\\synthetic-user-b\\AppData\\Local\\Chrome\\User Data\\Default\\Cache\\cache-item-002.bin", - "expected_detection_match": true, - "rationale": "Chrome parent, browser cache path, and command interpreter child match the controlled review rule." - }, - { - "event_id": "synthetic-event-003", - "event_time": "2026-06-27T00:00:03Z", - "host": "synthetic-host-02", - "user": "synthetic-user-c", - "parent_process_name": "msedge.exe", - "process_name": "wscript.exe", - "process_command_line": "wscript.exe C:\\Synthetic\\Review\\cache-followup.vbs", - "file_path": "C:\\Users\\synthetic-user-c\\AppData\\Local\\Edge\\User Data\\Default\\Cache\\cache-item-003.dat", - "expected_detection_match": true, - "rationale": "Edge parent, cache path, and Windows script host child match the controlled review rule." - }, - { - "event_id": "synthetic-event-004", - "event_time": "2026-06-27T00:00:04Z", - "host": "synthetic-host-02", - "user": "synthetic-user-d", - "parent_process_name": "firefox.exe", - "process_name": "pwsh.exe", - "process_command_line": "pwsh.exe -File C:\\Synthetic\\Review\\cache-followup.ps1", - "file_path": "C:\\Users\\synthetic-user-d\\AppData\\Local\\Firefox\\Profiles\\synthetic\\cache2\\cache-item-004.bin", - "expected_detection_match": true, - "rationale": "Firefox parent, cache path, and PowerShell child match the controlled review rule." - }, - { - "event_id": "synthetic-event-005", - "event_time": "2026-06-27T00:00:05Z", - "host": "synthetic-host-03", - "user": "synthetic-user-e", - "parent_process_name": "synthetic_browser.exe", - "process_name": "notepad.exe", - "process_command_line": "notepad.exe C:\\Synthetic\\Review\\cache-note.txt", - "file_path": "C:\\Users\\synthetic-user-e\\AppData\\Local\\Browser\\Cache\\cache-note.txt", - "expected_detection_match": false, - "rationale": "Benign browser-cache event uses a non-script child process and must not match." - }, - { - "event_id": "synthetic-event-006", - "event_time": "2026-06-27T00:00:06Z", - "host": "synthetic-host-03", - "user": "synthetic-user-f", - "parent_process_name": "explorer.exe", - "process_name": "powershell.exe", - "process_command_line": "powershell.exe -NoProfile -File C:\\Synthetic\\Review\\admin-note.ps1", - "file_path": "C:\\Synthetic\\Review\\admin-note.ps1", - "expected_detection_match": false, - "rationale": "Script interpreter child exists, but the parent is not browser-like and the file path is not cache-like." - }, - { - "event_id": "synthetic-event-007", - "event_time": "2026-06-27T00:00:07Z", - "host": "synthetic-host-04", - "user": "synthetic-user-g", - "parent_process_name": "chrome.exe", - "process_name": "notepad.exe", - "process_command_line": "notepad.exe C:\\Synthetic\\Review\\cache-text.txt", - "file_path": "C:\\Users\\synthetic-user-g\\AppData\\Local\\Chrome\\User Data\\Default\\Cache\\cache-text.txt", - "expected_detection_match": false, - "rationale": "Browser parent and cache path exist, but the child process is not a script interpreter." - }, - { - "event_id": "synthetic-event-008", - "event_time": "2026-06-27T00:00:08Z", - "host": "synthetic-host-04", - "user": "synthetic-user-h", - "parent_process_name": "msedge.exe", - "process_name": "powershell.exe", - "process_command_line": "powershell.exe -NoProfile -File C:\\Synthetic\\Review\\download-followup.ps1", - "file_path": "C:\\Users\\synthetic-user-h\\Downloads\\download-followup.ps1", - "expected_detection_match": false, - "rationale": "Browser parent and script interpreter child exist, but the file path is not cache-like." - }, - { - "event_id": "synthetic-event-009", - "event_time": "2026-06-27T00:00:09Z", - "host": "synthetic-host-05", - "user": "synthetic-user-i", - "parent_process_name": "synthetic_browser.exe", - "process_name": "rundll32.exe", - "process_command_line": "rundll32.exe C:\\Synthetic\\Review\\sample.dll,ReviewEntry", - "file_path": "C:\\Users\\synthetic-user-i\\AppData\\Local\\Browser\\Cache\\cache-item-009.bin", - "expected_detection_match": false, - "rationale": "Browser parent and cache path exist, but the child process is outside the controlled script-interpreter list." - }, - { - "event_id": "synthetic-event-010", - "event_time": "2026-06-27T00:00:10Z", - "host": "synthetic-host-05", - "user": "synthetic-user-j", - "parent_process_name": "outlook.exe", - "process_name": "wscript.exe", - "process_command_line": "wscript.exe C:\\Synthetic\\Review\\mail-followup.vbs", - "file_path": "C:\\Synthetic\\Review\\mail-followup.vbs", - "expected_detection_match": false, - "rationale": "Script host child exists, but neither browser parent nor cache path is present." - }, - { - "event_id": "synthetic-event-011", - "event_time": "2026-06-27T00:00:11Z", - "host": "synthetic-host-06", - "user": "synthetic-user-k", - "parent_process_name": "firefox.exe", - "process_name": "calc.exe", - "process_command_line": "calc.exe", - "file_path": "C:\\Users\\synthetic-user-k\\AppData\\Local\\Firefox\\Profiles\\synthetic\\cache2\\cache-item-011.bin", - "expected_detection_match": false, - "rationale": "Browser parent and cache path exist, but the child process is a benign calculator process." - }, - { - "event_id": "synthetic-event-012", - "event_time": "2026-06-27T00:00:12Z", - "host": "synthetic-host-06", - "user": "synthetic-user-l", - "parent_process_name": "services.exe", - "process_name": "cmd.exe", - "process_command_line": "cmd.exe /c C:\\Synthetic\\Review\\service-check.cmd", - "file_path": "C:\\Synthetic\\Review\\service-check.cmd", - "expected_detection_match": false, - "rationale": "Command interpreter child exists, but the parent and path do not match the browser-cache scenario." - } - ] -} diff --git a/examples/review/hostile-batch/duplicate-artifact-id-index.json b/examples/review/hostile-batch/duplicate-artifact-id-index.json index 23c0172..7fe7ac5 100644 --- a/examples/review/hostile-batch/duplicate-artifact-id-index.json +++ b/examples/review/hostile-batch/duplicate-artifact-id-index.json @@ -32,5 +32,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/missing-manifest-index.json b/examples/review/hostile-batch/missing-manifest-index.json index 3743d86..efbf9f0 100644 --- a/examples/review/hostile-batch/missing-manifest-index.json +++ b/examples/review/hostile-batch/missing-manifest-index.json @@ -40,5 +40,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/private-evidence-batch-index.json b/examples/review/hostile-batch/private-evidence-batch-index.json index 4693706..51d09c5 100644 --- a/examples/review/hostile-batch/private-evidence-batch-index.json +++ b/examples/review/hostile-batch/private-evidence-batch-index.json @@ -37,9 +37,9 @@ "index_id": "hoxline-multi-artifact-review-index-v1", "index_version": "multi-artifact-review-index-v1", "next_gate": "human_review_gate", - "private_evidence_attempt": "blocked synthetic hostile field marker", + "private_evidence_attempt": "blocked controlled-test hostile field marker", "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/production-claim-batch-index.json b/examples/review/hostile-batch/production-claim-batch-index.json index 2bf5b6c..6923ea2 100644 --- a/examples/review/hostile-batch/production-claim-batch-index.json +++ b/examples/review/hostile-batch/production-claim-batch-index.json @@ -40,5 +40,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/unexpected-blocked-pass-index.json b/examples/review/hostile-batch/unexpected-blocked-pass-index.json index 1cef231..68923b6 100644 --- a/examples/review/hostile-batch/unexpected-blocked-pass-index.json +++ b/examples/review/hostile-batch/unexpected-blocked-pass-index.json @@ -41,5 +41,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/unexpected-pass-index.json b/examples/review/hostile-batch/unexpected-pass-index.json index 95e6729..e40a7f5 100644 --- a/examples/review/hostile-batch/unexpected-pass-index.json +++ b/examples/review/hostile-batch/unexpected-pass-index.json @@ -41,5 +41,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "NOT_PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile-batch/unsafe-batch-public-safe-claim-index.json b/examples/review/hostile-batch/unsafe-batch-public-safe-claim-index.json index abf3e13..fe1bc4e 100644 --- a/examples/review/hostile-batch/unsafe-batch-public-safe-claim-index.json +++ b/examples/review/hostile-batch/unsafe-batch-public-safe-claim-index.json @@ -40,5 +40,5 @@ "proof_boundary": "Batch output is local reviewer evidence of engine behavior only; it is not public proof.", "public_safe_status": "PUBLIC_SAFE", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signals only; not public signal observation." + "signal_boundary": "Controlled-test fixture signals only; not public signal observation." } diff --git a/examples/review/hostile/missing-fixture-path.json b/examples/review/hostile/missing-fixture-path.json index 97dea18..c5813c6 100644 --- a/examples/review/hostile/missing-fixture-path.json +++ b/examples/review/hostile/missing-fixture-path.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -53,7 +53,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/missing-telemetry-contract.json b/examples/review/hostile/missing-telemetry-contract.json index f3be51d..c56b1f2 100644 --- a/examples/review/hostile/missing-telemetry-contract.json +++ b/examples/review/hostile/missing-telemetry-contract.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -28,7 +28,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -53,6 +53,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/private-evidence-field-attempt.json b/examples/review/hostile/private-evidence-field-attempt.json index 41d69fd..50f16be 100644 --- a/examples/review/hostile/private-evidence-field-attempt.json +++ b/examples/review/hostile/private-evidence-field-attempt.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -53,7 +53,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -78,7 +78,7 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate", - "private_evidence": "synthetic hostile private evidence field attempt" + "private_evidence": "controlled-test hostile private evidence field attempt" } diff --git a/examples/review/hostile/private-execution-id-field-attempt.json b/examples/review/hostile/private-execution-id-field-attempt.json index 02407af..7ed39de 100644 --- a/examples/review/hostile/private-execution-id-field-attempt.json +++ b/examples/review/hostile/private-execution-id-field-attempt.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -53,7 +53,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -78,7 +78,7 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate", - "private_execution_id": "synthetic-hostile-private-execution-id" + "private_execution_id": "controlled-test-hostile-private-execution-id" } diff --git a/examples/review/hostile/raw-alert-like-field-attempt.json b/examples/review/hostile/raw-alert-like-field-attempt.json index 246b4d2..8ffdfa1 100644 --- a/examples/review/hostile/raw-alert-like-field-attempt.json +++ b/examples/review/hostile/raw-alert-like-field-attempt.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -53,7 +53,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -78,7 +78,7 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate", - "raw_alert": "synthetic hostile raw alert-like field attempt" + "raw_alert": "controlled-test hostile raw alert-like field attempt" } diff --git a/examples/review/hostile/requested-ai-approved-claim.json b/examples/review/hostile/requested-ai-approved-claim.json index b314199..df8f223 100644 --- a/examples/review/hostile/requested-ai-approved-claim.json +++ b/examples/review/hostile/requested-ai-approved-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-analyst-approved-claim.json b/examples/review/hostile/requested-analyst-approved-claim.json index c278d62..f9ec1e1 100644 --- a/examples/review/hostile/requested-analyst-approved-claim.json +++ b/examples/review/hostile/requested-analyst-approved-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-autonomous-soc-claim.json b/examples/review/hostile/requested-autonomous-soc-claim.json index 9f2698f..f8ef378 100644 --- a/examples/review/hostile/requested-autonomous-soc-claim.json +++ b/examples/review/hostile/requested-autonomous-soc-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-case-closure-claim.json b/examples/review/hostile/requested-case-closure-claim.json index 9f393c7..d89d929 100644 --- a/examples/review/hostile/requested-case-closure-claim.json +++ b/examples/review/hostile/requested-case-closure-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-customer-claim.json b/examples/review/hostile/requested-customer-claim.json index b37f5fd..e4fa99d 100644 --- a/examples/review/hostile/requested-customer-claim.json +++ b/examples/review/hostile/requested-customer-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-final-authorization-claim.json b/examples/review/hostile/requested-final-authorization-claim.json index ce00316..84a72ec 100644 --- a/examples/review/hostile/requested-final-authorization-claim.json +++ b/examples/review/hostile/requested-final-authorization-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-production-claim.json b/examples/review/hostile/requested-production-claim.json index d8cc69a..ade4495 100644 --- a/examples/review/hostile/requested-production-claim.json +++ b/examples/review/hostile/requested-production-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/requested-socaas-claim.json b/examples/review/hostile/requested-socaas-claim.json index 9aa0bc9..ac3ca5b 100644 --- a/examples/review/hostile/requested-socaas-claim.json +++ b/examples/review/hostile/requested-socaas-claim.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/examples/review/hostile/unsafe-public-safe-status.json b/examples/review/hostile/unsafe-public-safe-status.json index be57c15..fe3417e 100644 --- a/examples/review/hostile/unsafe-public-safe-status.json +++ b/examples/review/hostile/unsafe-public-safe-status.json @@ -3,7 +3,7 @@ "artifact_id": "HO-DET-010", "artifact_name": "Windows local Administrators group membership change detection", "artifact_type": "detection-review-artifact", - "artifact_family": "synthetic-review-only", + "artifact_family": "controlled-test-review-only", "source_owner": "hawkinsoperations-detections", "validation_owner": "hawkinsoperations-validation", "platform_owner": "hawkinsoperations-platform", @@ -53,7 +53,7 @@ ], "allowed_claim_class": "controlled local fixture review only", "requested_claims": [ - "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe synthetic fixtures only." + "HO-DET-010 can be reviewed locally through deterministic Hoxline Review Engine v1 using public-safe controlled-test fixtures only." ], "blocked_claim_classes": [ "production ready", @@ -78,6 +78,6 @@ "lifetime_ledger_changed": false, "proof_boundary": "Fixture-only local review output; not public proof and not proof authority.", "runtime_boundary": "No runtime execution, endpoint mutation, VM, Wazuh, Splunk, Cribl, private infrastructure, or live signal action.", - "signal_boundary": "Synthetic fixture signal only; not public signal observation.", + "signal_boundary": "Controlled-test fixture signal only; not public signal observation.", "next_gate": "human_review_gate" } diff --git a/src/hoxline/cli.py b/src/hoxline/cli.py index 0b1c0a0..159661a 100644 --- a/src/hoxline/cli.py +++ b/src/hoxline/cli.py @@ -86,7 +86,7 @@ def _build_parser() -> argparse.ArgumentParser: run_parser.add_argument("--output", help="optional output file path") metrics_parser = gauntlet_subparsers.add_parser("metrics", help="emit Hoxline Gauntlet work-impact metrics") - metrics_parser.add_argument("--events", required=True, help="synthetic events fixture path") + metrics_parser.add_argument("--events", required=True, help="controlled-test events fixture path") metrics_parser.add_argument("--artifact", required=True, help="sample artifact JSON path") metrics_parser.add_argument("--proofcard", required=True, help="sample ProofCard JSON path") metrics_parser.add_argument("--claim-output", required=True, help="sample Claim Authority output JSON path") @@ -534,10 +534,10 @@ def _build_gauntlet_v0_lab_report(artifact_path: Path) -> dict[str, object]: "scenario": artifact.get("scenario"), "proof_ceiling": proof_ceiling, "stages": [ - {"stage": "AI-assisted security work", "state": "SOURCE_CONTROLLED_SYNTHETIC_DRAFT"}, + {"stage": "AI-assisted security work", "state": "SOURCE_CONTROLLED_TEST_DRAFT"}, {"stage": "Artifact Intake", "state": "ACCEPTED"}, {"stage": "Evidence Graph", "state": "PRESENT"}, - {"stage": "Telemetry Contract Check", "state": "PASSED_SYNTHETIC_CONTRACT"}, + {"stage": "Telemetry Contract Check", "state": "CONTROLLED_TEST_VALIDATED"}, {"stage": "Controlled Validation", "state": "PASSED_CONTROLLED_FIXTURES"}, {"stage": "Runtime Candidate Ledger", "state": "NOT_PROMOTED"}, {"stage": "Signal Observation", "state": "NOT_OBSERVED"}, diff --git a/src/hoxline/metrics/__init__.py b/src/hoxline/metrics/__init__.py index cf75a7d..047db9a 100644 --- a/src/hoxline/metrics/__init__.py +++ b/src/hoxline/metrics/__init__.py @@ -1,11 +1,11 @@ from __future__ import annotations -from .evaluator import DetectionMetrics, evaluate_detection_fixture, load_synthetic_events +from .evaluator import DetectionMetrics, evaluate_detection_fixture, load_controlled_test_events from .report import build_work_impact_report __all__ = [ "DetectionMetrics", "build_work_impact_report", "evaluate_detection_fixture", - "load_synthetic_events", + "load_controlled_test_events", ] diff --git a/src/hoxline/metrics/evaluator.py b/src/hoxline/metrics/evaluator.py index 90706f2..c146f5c 100644 --- a/src/hoxline/metrics/evaluator.py +++ b/src/hoxline/metrics/evaluator.py @@ -25,7 +25,7 @@ "edge.exe", "firefox.exe", "msedge.exe", - "synthetic_browser.exe", + "controlled_test_browser.exe", } SCRIPT_INTERPRETER_NAMES = { @@ -43,8 +43,8 @@ "/chrome/user data/default/cache/", "\\edge\\user data\\default\\cache\\", "/edge/user data/default/cache/", - "\\firefox\\profiles\\synthetic\\cache2\\", - "/firefox/profiles/synthetic/cache2/", + "\\firefox\\profiles\\controlled-test\\cache2\\", + "/firefox/profiles/controlled-test/cache2/", ) @@ -78,33 +78,33 @@ def as_dict(self) -> dict[str, int | float]: } -def load_synthetic_events(path: str | Path) -> dict[str, Any]: +def load_controlled_test_events(path: str | Path) -> dict[str, Any]: with Path(path).open("r", encoding="utf-8") as handle: data = json.load(handle) if not isinstance(data, dict): - raise ValueError("synthetic event fixture must be a JSON object") - if data.get("schema_version") != "synthetic-events-v0": - raise ValueError("synthetic event fixture schema_version must be synthetic-events-v0") + raise ValueError("controlled-test event fixture must be a JSON object") + if data.get("schema_version") != "controlled-test-events-v0": + raise ValueError("controlled-test event fixture schema_version must be controlled-test-events-v0") if data.get("artifact_id") != "HOX-GAUNTLET-001": - raise ValueError("synthetic event fixture artifact_id must be HOX-GAUNTLET-001") + raise ValueError("controlled-test event fixture artifact_id must be HOX-GAUNTLET-001") events = data.get("events") if not isinstance(events, list): - raise ValueError("synthetic event fixture must include an events list") + raise ValueError("controlled-test event fixture must include an events list") for event in events: _validate_event(event) return data def evaluate_detection_fixture(path: str | Path) -> DetectionMetrics: - fixture = load_synthetic_events(path) + fixture = load_controlled_test_events(path) events = fixture["events"] if not isinstance(events, list): - raise ValueError("synthetic event fixture must include an events list") + raise ValueError("controlled-test event fixture must include an events list") true_positive = true_negative = false_positive = false_negative = 0 for event in events: if not isinstance(event, dict): - raise ValueError("synthetic event must be an object") + raise ValueError("controlled-test event must be an object") expected = event["expected_detection_match"] is True observed = matches_browser_cache_script_interpreter(event) if expected and observed: @@ -169,10 +169,10 @@ def telemetry_coverage(events: list[dict[str, Any]], required_fields: list[str]) def _validate_event(event: object) -> None: if not isinstance(event, dict): - raise ValueError("synthetic event must be an object") + raise ValueError("controlled-test event must be an object") missing = [field for field in REQUIRED_EVENT_FIELDS if field not in event] if missing: - raise ValueError(f"synthetic event missing required fields: {', '.join(missing)}") + raise ValueError(f"controlled-test event missing required fields: {', '.join(missing)}") if not isinstance(event["expected_detection_match"], bool): raise ValueError("expected_detection_match must be boolean") diff --git a/src/hoxline/metrics/report.py b/src/hoxline/metrics/report.py index f0b8545..f650be2 100644 --- a/src/hoxline/metrics/report.py +++ b/src/hoxline/metrics/report.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any -from .evaluator import evaluate_detection_fixture, load_synthetic_events, telemetry_coverage +from .evaluator import evaluate_detection_fixture, load_controlled_test_events, telemetry_coverage PROOF_CEILING = "CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY" @@ -40,10 +40,10 @@ def build_work_impact_report( artifact = _load_json_object(artifact_path) proofcard = _load_json_object(proofcard_path) claim_output = _load_json_object(claim_output_path) - events_fixture = load_synthetic_events(events_path) + events_fixture = load_controlled_test_events(events_path) events = events_fixture["events"] if not isinstance(events, list): - raise ValueError("synthetic event fixture must include an events list") + raise ValueError("controlled-test event fixture must include an events list") _require(artifact, "artifact_id", "HOX-GAUNTLET-001") _require(artifact, "proof_ceiling", PROOF_CEILING) diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 6503abe..843e9b8 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -100,20 +100,29 @@ PROHIBITED_CLAIM_PATTERNS = { "public-safe runtime proof": re.compile(r"public[- ]safe runtime proof", re.IGNORECASE), "public-safe promotion": re.compile( - r"\bpublic[-_ ]safe(?:[-_ ](?:approved|promotion|promoted|proof|status|true))\b", + r"\bpublic[-_ ]safe(?:[-_ ](?:is[-_ ])?(?:approved|confirmed|promotion|promoted|proof|status|true))\b", + re.IGNORECASE, + ), + "runtime promotion": re.compile( + r"\bruntime(?:[-_ ]active|\s+(?:is|was)\s+active)\b", re.IGNORECASE, ), - "runtime promotion": re.compile(r"\bruntime[-_ ]active\b", re.IGNORECASE), "signal promotion": re.compile( - r"\bsignal(?:[-_ ](?:was[-_ ]?)?observed|[-_ ]proof)\b", + r"\bsignal(?:[-_ ](?:was[-_ ]?)?observed|[-_ ]proof|\s+(?:is|was)\s+observed)\b", re.IGNORECASE, ), "production": re.compile(r"\bproduction(?:[- ]ready| readiness)?\b", re.IGNORECASE), "customer deployment": re.compile(r"\bcustomer(?:[- ]deployed| deployment)?\b", re.IGNORECASE), "SOCaaS deployment": re.compile(r"\bSOCaaS(?:[- ]ready| deployed| deployment)?\b", re.IGNORECASE), "autonomous SOC": re.compile(r"\bautonomous SOC\b", re.IGNORECASE), - "AI-approved disposition": re.compile(r"\bAI[-_ ]approved\b", re.IGNORECASE), - "analyst-approved disposition": re.compile(r"\banalyst[-_ ]approved\b", re.IGNORECASE), + "AI-approved disposition": re.compile( + r"\bAI(?:[-_ ]approved|[-_ ](?:authority|disposition)(?:[-_ ](?:enabled|approved|true))?)\b", + re.IGNORECASE, + ), + "analyst-approved disposition": re.compile( + r"\banalyst(?:[-_ ]approved|[-_ ]approval(?:[-_ ](?:granted|approved|true))?)\b", + re.IGNORECASE, + ), "final authorization": re.compile(r"\bfinal[-_ ]authorization\b", re.IGNORECASE), "case closure": re.compile(r"\bcase[-_ ]closure\b|\bcase[-_ ]closed\b", re.IGNORECASE), "live cloud claim": re.compile(r"\blive (?:AWS|cloud)(?: runtime| proof| signal)?\b", re.IGNORECASE), @@ -149,6 +158,7 @@ ) ] PRIVATE_KEY_TOKENS = { + "customeridentifier", "endpointlog", "generatedpassword", "password", @@ -161,6 +171,15 @@ "secret", "token", } +RETIRED_VOCABULARY = "syn" + "thetic" +TRACKED_TEXT_EXTENSIONS = { + ".cfg", ".css", ".csv", ".html", ".ini", ".js", ".json", ".md", ".mjs", + ".ps1", ".py", ".rst", ".sh", ".toml", ".ts", ".tsx", ".txt", ".yaml", ".yml", +} +TRACKED_BINARY_EXTENSIONS = {".gif", ".ico", ".jpeg", ".jpg", ".pdf", ".png", ".webp"} +TRACKED_TEXT_FILENAMES = { + ".editorconfig", ".gitattributes", ".gitignore", "LICENSE", "MANIFEST.in", "Makefile", +} PASS_OUTPUT_ROLES = { "artifact_manifest": "artifact-manifest.json", "artifact_intake": "intake.json", @@ -462,6 +481,20 @@ def _normalized_key_identity(value: Any) -> str: return _normalize_security_key(unicodedata.normalize("NFKC", str(value))) +def _security_scan_text(value: str) -> str: + normalized = unicodedata.normalize("NFKD", value) + scanned: list[str] = [] + for character in normalized: + if character in "\t\r\n": + scanned.append(" ") + continue + category = unicodedata.category(character) + if category.startswith(("C", "M")): + continue + scanned.append(character) + return "".join(scanned) + + def _is_promotion_key(normalized_key: str) -> bool: if normalized_key == "finalstatus": return False @@ -470,10 +503,56 @@ def _is_promotion_key(normalized_key: str) -> bool: for token in SECURITY_FALSE_KEY_TOKENS | COMPOSITIONAL_PROMOTION_KEYS ): return True - authority_prefixes = ("production", "customer", "socaas", "runtime", "signal", "approval", "closure", "case", "publicsafe", "final") - promotion_suffixes = ("active", "approved", "authorized", "closed", "deployed", "live", "observed", "proof", "ready", "runtime", "status") - return any(prefix in normalized_key for prefix in authority_prefixes) and any( - normalized_key.endswith(suffix) for suffix in promotion_suffixes + anchored_prefixes = ("ai", "analyst", "review", "final") + contained_prefixes = ( + "production", "customer", "socaas", "runtime", "signal", "approval", + "closure", "case", "publicsafe", + ) + promotion_indicators = ( + "active", + "approved", + "authority", + "authorization", + "authorized", + "closed", + "decision", + "deployed", + "disposition", + "enabled", + "live", + "observed", + "proof", + "ready", + "state", + "status", + ) + authority_match = any(normalized_key.startswith(prefix) for prefix in anchored_prefixes) or any( + prefix in normalized_key for prefix in contained_prefixes + ) + return authority_match and any( + indicator in normalized_key for indicator in promotion_indicators + ) + + +def _is_promotion_path(path: str) -> bool: + segments = [ + _normalize_security_key(segment) + for segment in re.split(r"\.|\[\d+\]", path) + if segment + ] + if any(_is_promotion_key(segment) for segment in segments): + return True + authority_segments = { + "ai", "analyst", "review", "final", "production", "customer", "socaas", + "runtime", "signal", "approval", "closure", "case", "publicsafe", + } + indicator_segments = { + "active", "approved", "authority", "authorization", "authorized", "closed", + "decision", "deployed", "disposition", "enabled", "live", "observed", + "proof", "ready", "state", "status", + } + return any(segment in authority_segments for segment in segments) and any( + segment in indicator_segments for segment in segments ) @@ -533,19 +612,19 @@ def _canonical_base64_text(value: str, *, strict: bool) -> str | None: def _decoded_text_variants(value: str, *, strict_base64: bool = True) -> list[str]: - initial = unicodedata.normalize("NFKC", value) + initial = _security_scan_text(value) variants = [initial] frontier = [initial] for _ in range(3): next_frontier: list[str] = [] for current in frontier: - decoded_url = unicodedata.normalize("NFKC", unquote(current)) + decoded_url = _security_scan_text(unquote(current)) if decoded_url != current and decoded_url not in variants: variants.append(decoded_url) next_frontier.append(decoded_url) decoded_b64 = _canonical_base64_text(current, strict=strict_base64) if decoded_b64 is not None: - decoded_b64 = unicodedata.normalize("NFKC", decoded_b64) + decoded_b64 = _security_scan_text(decoded_b64) if decoded_b64 not in variants: variants.append(decoded_b64) next_frontier.append(decoded_b64) @@ -577,7 +656,13 @@ def _validate_recursive_boundaries(value: Any, label: str, path: str = "") -> No for key, item in value.items(): key_path = f"{path}.{key}" if path else str(key) normalized_key = _normalize_security_key(key) - if _is_promotion_key(normalized_key) and not _is_bounded_promotion_value(item): + canonical_final_status = normalized_key == "finalstatus" and item in { + "PASS", "MIXED", "BLOCKED", + } + if ( + not canonical_final_status + and (_is_promotion_key(normalized_key) or _is_promotion_path(key_path)) + ) and not isinstance(item, (dict, list)) and not _is_bounded_promotion_value(item): raise ReviewBlocked(f"{label} contains prohibited authority promotion") for canonical, expected in SECURITY_FIXED_FIELDS.items(): if normalized_key == _normalize_security_key(canonical) and item != expected: @@ -589,9 +674,14 @@ def _validate_recursive_boundaries(value: Any, label: str, path: str = "") -> No _validate_recursive_boundaries(item, label, f"{path}[{index}]") return if isinstance(value, str): + if _is_promotion_path(path) and not _is_bounded_promotion_value(value): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") _validate_declared_string(value, label) if _string_has_unsafe_claim(value): raise ReviewBlocked(f"{label} contains an unsupported claim") + return + if _is_promotion_path(path) and not _is_bounded_promotion_value(value): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") -> bool: @@ -602,7 +692,7 @@ def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") - re.IGNORECASE, ): return True - if any( + blocked_context = any( token in normalized_path for token in ( "blockedclaims", @@ -611,8 +701,7 @@ def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") - "saferwording", "whathoxlineblocked", ) - ): - return True + ) if normalized_path.endswith( ("path", "machinestate", "outputdir", "reviewerpack", "blockedreview", "runsummary") ) and re.fullmatch(r"[A-Za-z0-9._/-]+", value): @@ -630,14 +719,87 @@ def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") - re.IGNORECASE, ) trailing_negative = re.compile( - r"^\s*:?\s*(?:is|are|remains?)\s+(?:unsupported|prohibited|false|not asserted)\b", + r"^\s*:?\s*(?:is|are|remains?)\s+(?:unsupported|prohibited|false|not asserted|NOT_PUBLIC_SAFE)\b", re.IGNORECASE, ) - clause_separator = re.compile(r"[,;:/\u2013\u2014]|\b(?:while|but|however)\b", re.IGNORECASE) - hard_separator = re.compile(r"[;:/\u2013\u2014]|\b(?:while|but|however)\b", re.IGNORECASE) + clause_separator = re.compile( + r"[,;:/\u2013\u2014]|\b(?:while|but|however|and|plus|though)\b", + re.IGNORECASE, + ) + hard_separator = re.compile( + r"[;:/\u2013\u2014]|\b(?:while|but|however|though)\b", + re.IGNORECASE, + ) + authority_subject = ( + r"(?:public[-_ ]safe(?:[-_ ](?:runtime|proof|status))?|runtime|signal|production|" + r"customer(?:[-_ ](?:environment|deployment))?|SOCaaS(?:[-_ ]deployment)?|" + r"AI[-_ ](?:authority|approval|disposition)|analyst[-_ ](?:authority|approval|disposition)|" + r"final[-_ ]authorization|case[-_ ]closure)" + ) + affirmative_predicate = re.compile( + rf"\b{authority_subject}\b" + r"(?:\s+(?:is|are|was|were|has|have|becomes?|became))?\s+" + r"(?:enabled|granted|confirmed|received|approved|active|deployed|live|observed|closed|true)\b", + re.IGNORECASE, + ) + blocked_suffix = re.compile( + r"\b(?:claim|claims|claim classes|claim families|identified claims)\s+" + r"(?:remain|remains|are)\s+(?:blocked|unsupported|prohibited|not asserted)\b", + re.IGNORECASE, + ) + global_negative_predicate = re.compile( + r"\b(?:is|are|remains?)\s+" + r"(?:unsupported|prohibited|false|not asserted|NOT_PUBLIC_SAFE)\b", + re.IGNORECASE, + ) + exact_bounded_nouns = { + *(_security_scan_text(item).strip().casefold() for item in BLOCKED_CLAIM_FAMILIES), + "public signal proof", + "human review gate completion", + "final authorization record", + } unsafe_seen = False for candidate in _decoded_text_variants(value): normalized = unicodedata.normalize("NFKC", candidate) + canonical_value = re.sub( + r"\s+", + " ", + normalized.strip().strip(" \t`*_-.:;"), + ).casefold() + explicit_affirmative = affirmative_predicate.search(normalized) + if blocked_context and canonical_value in exact_bounded_nouns: + return True + if blocked_context and ":" in normalized: + claim_label, bounded_explanation = normalized.split(":", 1) + canonical_label = re.sub( + r"\s+", + " ", + claim_label.strip().strip(" \t`*_-.:;"), + ).casefold() + if ( + canonical_label in exact_bounded_nouns + and global_negative_predicate.search(bounded_explanation) is not None + ): + return True + if explicit_affirmative is not None: + return False + if global_negative_predicate.search(normalized) is not None and any( + pattern.search(normalized) for pattern in PROHIBITED_CLAIM_PATTERNS.values() + ): + return True + if blocked_suffix.search(normalized) is not None and any( + pattern.search(normalized) for pattern in PROHIBITED_CLAIM_PATTERNS.values() + ): + return True + negative_intro = inherited_negative.search(normalized) + if ( + negative_intro is not None + and affirmative_predicate.search(normalized, negative_intro.end()) is not None + ): + # A denial governs only the authority nouns it directly scopes. Any + # later explicit affirmative authority predicate is a new claim, + # regardless of the connector used to join the clauses. + return False for pattern in PROHIBITED_CLAIM_PATTERNS.values(): for match in pattern.finditer(normalized): unsafe_seen = True @@ -655,10 +817,22 @@ def _is_bounded_negative_claim(value: str, *, path: str = "", label: str = "") - # deployment, or approval" can govern a comma-delimited list. # Weak phrases such as "unsupported note" do not carry across # punctuation, and hard delimiters always reset the scope. - if separators and separators[-1].group() == ",": + if separators and separators[-1].group().casefold() in {",", "and", "plus"}: hard = [item for item in hard_separator.finditer(normalized) if item.end() <= match.start()] hard_start = hard[-1].end() if hard else 0 - if inherited_negative.search(normalized[hard_start : match.start()]): + next_separator = next( + ( + item + for item in clause_separator.finditer(normalized, match.end()) + if item.start() >= match.end() + ), + None, + ) + fragment_end = next_separator.start() if next_separator else len(normalized) + fragment = normalized[clause_start:fragment_end] + if inherited_negative.search( + normalized[hard_start : match.start()] + ) and not affirmative_predicate.search(fragment): continue return False return unsafe_seen @@ -673,7 +847,13 @@ def _validate_generated_output_security(value: Any, label: str, path: str = "") raise ReviewBlocked(f"{label} contains normalized-key collision") seen.add(normalized_key) key_path = f"{path}.{key}" if path else str(key) - if _is_promotion_key(normalized_key) and not _is_bounded_promotion_value(item): + canonical_final_status = normalized_key == "finalstatus" and item in { + "PASS", "MIXED", "BLOCKED", + } + if ( + not canonical_final_status + and (_is_promotion_key(normalized_key) or _is_promotion_path(key_path)) + ) and not isinstance(item, (dict, list)) and not _is_bounded_promotion_value(item): raise ReviewBlocked(f"{label} contains prohibited authority promotion") for canonical, expected in REVIEW_OUTPUT_SECURITY_FIELDS.items(): if normalized_key == _normalized_key_identity(canonical) and item != expected: @@ -685,7 +865,13 @@ def _validate_generated_output_security(value: Any, label: str, path: str = "") _validate_generated_output_security(item, label, f"{path}[{index}]") return if isinstance(value, str): + if _is_promotion_path(path) and not _is_bounded_promotion_value(value): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") _validate_declared_string(value, label) + value_is_bounded = ( + _string_has_unsafe_claim(value) + and _is_bounded_negative_claim(value, path=path, label=label) + ) lines = value.splitlines() if "\n" in value or "\r" in value else [value] markdown_section = "" for line in lines: @@ -696,8 +882,11 @@ def _validate_generated_output_security(value: Any, label: str, path: str = "") line_path = f"{path}.{markdown_section}" if markdown_section else path if _string_has_unsafe_claim(line) and not _is_bounded_negative_claim( line, path=line_path, label=label - ): + ) and not value_is_bounded: raise ReviewBlocked(f"{label} contains an unsupported claim") + return + if _is_promotion_path(path) and not _is_bounded_promotion_value(value): + raise ReviewBlocked(f"{label} contains prohibited authority promotion") def _expected_file_text(value: Any) -> str: @@ -2981,6 +3170,75 @@ def _safe_run_identity(name: str) -> str: return name +def verify_tracked_vocabulary(repo_root: Path) -> list[str]: + root = repo_root.resolve() + try: + result = subprocess.run( + [ + "git", "-C", str(root), "ls-files", "--cached", "--others", + "--exclude-standard", "-z", + ], + check=False, + capture_output=True, + ) + except OSError as exc: + return [f"tracked vocabulary inventory failed: {exc}"] + if result.returncode != 0: + detail = result.stderr.decode("utf-8", errors="replace").strip() + return [f"tracked vocabulary inventory failed: {detail or 'git ls-files failed'}"] + deleted_result = subprocess.run( + ["git", "-C", str(root), "ls-files", "--deleted", "-z"], + check=False, + capture_output=True, + ) + if deleted_result.returncode != 0: + detail = deleted_result.stderr.decode("utf-8", errors="replace").strip() + return [f"tracked deleted-path inventory failed: {detail or 'git ls-files --deleted failed'}"] + try: + tracked = result.stdout.decode("utf-8", errors="strict").split("\0") + deleted = { + item + for item in deleted_result.stdout.decode("utf-8", errors="strict").split("\0") + if item + } + except UnicodeDecodeError as exc: + return [f"tracked filename inventory is not strict UTF-8: {exc}"] + + errors: list[str] = [] + for relative in sorted(item for item in tracked if item): + if relative in deleted: + continue + normalized_relative = _security_scan_text(relative) + if RETIRED_VOCABULARY in normalized_relative.casefold(): + errors.append(f"tracked filename contains retired vocabulary: {relative}") + path = root / Path(relative) + try: + if not path.is_file(): + errors.append(f"tracked path is missing or not a regular file: {relative}") + continue + suffix = path.suffix.casefold() + if suffix in TRACKED_BINARY_EXTENSIONS: + continue + if suffix not in TRACKED_TEXT_EXTENSIONS and path.name not in TRACKED_TEXT_FILENAMES: + errors.append(f"tracked file type is not explicitly classified: {relative}") + continue + payload = path.read_bytes() + except OSError as exc: + errors.append(f"tracked file read failed: {relative}: {exc}") + continue + if b"\0" in payload: + errors.append(f"tracked text file contains NUL bytes: {relative}") + continue + try: + text = payload.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + errors.append(f"tracked text file is not strict UTF-8: {relative}: {exc}") + continue + if RETIRED_VOCABULARY in _security_scan_text(text).casefold(): + errors.append(f"tracked file contains retired vocabulary: {relative}") + return errors + + def _resolve_path(path: Path, base: Path) -> Path: if path.is_absolute(): return path diff --git a/tests/fixtures/case_growth/README.md b/tests/fixtures/case_growth/README.md index 2c70b4b..20e61f9 100644 --- a/tests/fixtures/case_growth/README.md +++ b/tests/fixtures/case_growth/README.md @@ -1,3 +1,3 @@ # Case Growth Fixture -Minimal seven-repo fixture for the Hoxline Case Growth Index v0 tests. The fixture uses public release-safe synthetic files only and intentionally includes missing proof/card states. +Minimal seven-repo fixture for the Hoxline Case Growth Index v0 tests. The fixture uses public release-safe controlled-test files only and intentionally includes missing proof/card states. diff --git a/tests/test_gauntlet_metrics_v0.py b/tests/test_gauntlet_metrics_v0.py index 425a90c..6070bc2 100644 --- a/tests/test_gauntlet_metrics_v0.py +++ b/tests/test_gauntlet_metrics_v0.py @@ -12,21 +12,21 @@ sys.path.insert(0, str(ROOT / "src")) from hoxline.cli import main -from hoxline.metrics import build_work_impact_report, evaluate_detection_fixture, load_synthetic_events +from hoxline.metrics import build_work_impact_report, evaluate_detection_fixture, load_controlled_test_events ARTIFACT = ROOT / "examples" / "gauntlet" / "sample-artifact.json" CLAIM_OUTPUT = ROOT / "examples" / "gauntlet" / "sample-claim-authority-output.json" EXPECTED_RESULTS = ROOT / "examples" / "gauntlet" / "expected-detection-results.json" -EVENTS = ROOT / "examples" / "gauntlet" / "synthetic-events.json" +EVENTS = ROOT / "examples" / "gauntlet" / "controlled-test-events.json" PROOFCARD = ROOT / "examples" / "gauntlet" / "sample-proofcard.json" SAMPLE_METRICS = ROOT / "examples" / "gauntlet" / "sample-work-impact-metrics.json" SCHEMA = ROOT / "schemas" / "work-impact-metrics-v0.schema.json" class HoxlineGauntletMetricsV0Test(unittest.TestCase): - def test_synthetic_event_fixture_loads(self) -> None: - fixture = load_synthetic_events(EVENTS) + def test_controlled_test_event_fixture_loads(self) -> None: + fixture = load_controlled_test_events(EVENTS) events = fixture["events"] self.assertEqual(fixture["artifact_id"], "HOX-GAUNTLET-001") @@ -36,7 +36,7 @@ def test_synthetic_event_fixture_loads(self) -> None: self.assertGreaterEqual(sum(1 for event in events if not event["expected_detection_match"]), 6) self.assertTrue( any( - event["parent_process_name"] == "synthetic_browser.exe" + event["parent_process_name"] == "controlled_test_browser.exe" and event["process_name"] == "notepad.exe" and event["expected_detection_match"] is False for event in events diff --git a/tests/test_gauntlet_v0.py b/tests/test_gauntlet_v0.py index b5cd197..864c9b9 100644 --- a/tests/test_gauntlet_v0.py +++ b/tests/test_gauntlet_v0.py @@ -27,14 +27,14 @@ class HoxlineGauntletV0Test(unittest.TestCase): - def test_sample_artifact_is_synthetic_splunk_soc_detection_artifact(self) -> None: + def test_sample_artifact_is_controlled_test_splunk_soc_detection_artifact(self) -> None: artifact = _load_json(ARTIFACT) self.assertEqual(artifact["artifact_id"], "HOX-GAUNTLET-001") self.assertTrue(artifact["ai_assisted"]) self.assertEqual(artifact["detection_artifact"]["platform"], "Splunk") self.assertEqual(artifact["proof_ceiling"], "CONTROLLED_VALIDATION_PRODUCT_DEMO_ONLY") - self.assertTrue(artifact["public_safety"]["synthetic_only"]) + self.assertTrue(artifact["public_safety"]["controlled_test_only"]) self.assertFalse(artifact["public_safety"]["contains_malware_code"]) self.assertFalse(artifact["public_safety"]["contains_exploit_instructions"]) diff --git a/tests/test_multi_artifact_review_manifest_expansion_v1.py b/tests/test_multi_artifact_review_manifest_expansion_v1.py index 5bcd710..d3a679d 100644 --- a/tests/test_multi_artifact_review_manifest_expansion_v1.py +++ b/tests/test_multi_artifact_review_manifest_expansion_v1.py @@ -344,6 +344,37 @@ def test_batch_index_rejects_duplicate_keys_unknown_shapes_and_nested_authority( assert main(["review", "batch", "run", "--index", str(path), "--output", str(output), "--force"]) == 1 assert _json(output / "batch-machine-state.json")["final_status"] == "BLOCKED" + for index_number, attack in enumerate( + ( + {"final": {"review": {"authorization": True}}}, + {"ai": {"metadata": {"authority": True}}}, + {"review": {"metadata": {"disposition": "APPROVED"}}}, + ) + ): + index = _json(INDEX) + index["batch_claim_boundary"] = json.dumps(attack, separators=(",", ":")) + path = tmp_path / f"neutral-wrapper-index-{index_number}.json" + path.write_text(json.dumps(index), encoding="utf-8") + output = tmp_path / f"neutral-wrapper-index-{index_number}" + assert main(["review", "batch", "run", "--index", str(path), "--output", str(output), "--force"]) == 1 + assert _json(output / "batch-machine-state.json")["final_status"] == "BLOCKED" + + for index_number, attack in enumerate( + ( + {"production_live": [True]}, + {"ai_authority": ["APPROVED"]}, + {"review_disposition": [True]}, + {"final_authorization": [1]}, + ) + ): + index = _json(INDEX) + index["batch_claim_boundary"] = json.dumps(attack, separators=(",", ":")) + path = tmp_path / f"promotion-array-index-{index_number}.json" + path.write_text(json.dumps(index), encoding="utf-8") + output = tmp_path / f"promotion-array-index-{index_number}" + assert main(["review", "batch", "run", "--index", str(path), "--output", str(output), "--force"]) == 1 + assert _json(output / "batch-machine-state.json")["final_status"] == "BLOCKED" + def test_batch_replay_rescans_tampered_input_even_after_hash_recalculation(tmp_path) -> None: output = tmp_path / "batch" diff --git a/tests/test_review_engine_v1.py b/tests/test_review_engine_v1.py index f6030e7..8b2d50a 100644 --- a/tests/test_review_engine_v1.py +++ b/tests/test_review_engine_v1.py @@ -19,6 +19,7 @@ _validate_generated_output_security, _validate_no_private_markers, _validate_recursive_boundaries, + verify_tracked_vocabulary, verify_review_run, ) @@ -341,7 +342,24 @@ def test_nfkc_security_keys_and_normalized_collisions_fail_closed(tmp_path) -> N assert _json(output / "machine-state.json")["final_status"] == "BLOCKED" -def test_generic_negation_cannot_launder_an_adjacent_positive_claim() -> None: +def test_generic_negation_cannot_launder_an_adjacent_positive_claim(tmp_path) -> None: + connector_attacks = [ + f"does not prove runtime {connector} customer deployment is active" + for connector in ( + "because", + "therefore", + "meanwhile", + "furthermore", + "also", + "nevertheless", + "nonetheless", + "except", + "despite that", + "in fact", + "whereas", + "yet", + ) + ] attacks = [ "pending documentation, production is live", "unsupported note \u2014 customer environment deployed", @@ -349,8 +367,22 @@ def test_generic_negation_cannot_launder_an_adjacent_positive_claim() -> None: "missing receipt while production is live", "no proof currently, customer environment deployed", "not approved / production is live", + "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", + "runtime is active and not simulated", + "signal is observed and not inferred", + *connector_attacks, ] for attack in attacks: + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries({"note": attack}, "hostile direct input") with pytest.raises(ReviewBlocked): _validate_generated_output_security({"note": attack}, "hostile generated output") @@ -358,17 +390,80 @@ def test_generic_negation_cannot_launder_an_adjacent_positive_claim() -> None: "This does not prove production readiness, customer deployment, or signal-observed proof.", "Production readiness is unsupported.", "Claims block customer deployment.", + "This does not prove runtime, customer deployment, AI approval, final authorization, or case closure.", + "This does not prove runtime-active status, signal-observed status, production-ready status, public-safe status, AI-approved status, or analyst-approved status.", + "This does not prove runtime, customer deployment, AI approval, or case closure. These claims remain blocked.", + "This does not prove runtime, customer deployment, AI approval, or case closure; all identified claims remain blocked.", + "This does not prove runtime,\ncustomer deployment,\tAI approval, or case closure.", + "Public-safe runtime proof remains NOT_PUBLIC_SAFE.", ): _validate_generated_output_security({"note": bounded}, "bounded generated output") + blocked_paths = ( + "blocked_claims", + "blocked_claim_classes", + "missing_evidence", + "safer_wording", + "what_hoxline_blocked", + ) + path_laundering_claims = ( + "customer deployment is active", + "AI authority is enabled", + "public safe is confirmed", + "case closure approved", + "runtime is active", + ) + for blocked_path in blocked_paths: + for claim in path_laundering_claims: + with pytest.raises(ReviewBlocked): + _validate_generated_output_security( + {blocked_path: [claim]}, + "hostile blocked-path output", + ) + + for bounded in ( + "production ready", + "public-safe runtime proof", + "final authorization record", + "Runtime, signal, public-safe, production, customer, AI approval, final authorization, and case closure claims remain blocked.", + "Runtime, signal, public-safe, live IdP, production identity coverage, autonomous SOC, AI-approved disposition, and analyst-approved disposition claims remain blocked.", + ): + _validate_generated_output_security( + {"blocked_claims": [bounded]}, + "bounded blocked-path output", + ) + with pytest.raises(ReviewBlocked): _validate_generated_output_security( {"extra": "not_public_safe; production is live"}, "hostile generated output", ) + output = tmp_path / "review-run" + assert main(["review", "run", "--artifact", str(MANIFEST), "--output", str(output), "--force"]) == 0 + state_path = output / "machine-state.json" + baseline = _json(state_path) + reviewer_pack = output / "reviewer-pack.md" + original = reviewer_pack.read_text(encoding="utf-8") + for attack in attacks: + reviewer_pack.write_text(original + f"\n{attack}\n", encoding="utf-8") + hostile_state = deepcopy(baseline) + hostile_state["output_digests"]["reviewer-pack.md"] = hashlib.sha256( + reviewer_pack.read_bytes() + ).hexdigest() + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text( + json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + errors = verify_review_run(state_path) + assert errors, attack + assert any("security validation failed" in error for error in errors), attack + reviewer_pack.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + -def test_compositional_promotion_keys_and_embedded_structures_fail_closed() -> None: +def test_compositional_promotion_keys_and_embedded_structures_fail_closed(tmp_path) -> None: keys = [ "production_live", "customer_deployment", @@ -393,6 +488,13 @@ def test_compositional_promotion_keys_and_embedded_structures_fail_closed() -> N "analyst_authority_state", "analyst_approval_value", "approved_by_analyst_flag", + "runtime_state", + "approval_state", + "extension_production_active_metadata", + "extension_runtime_state_metadata", + "extension_approval_state_metadata", + "%72untime_state", + "%2572untime_state", ] for key in keys: payload = {"metadata": {key: True}} @@ -418,6 +520,96 @@ def test_compositional_promotion_keys_and_embedded_structures_fail_closed() -> N "bounded output", ) + for payload in ( + {"runtime": {"state": True}}, + {"approval": {"status": True}}, + {"production": {"active": True}}, + ): + encoded_json = json.dumps(payload, separators=(",", ":")) + for variant in ( + payload, + encoded_json, + quote(encoded_json, safe=""), + quote(quote(encoded_json, safe=""), safe=""), + base64.urlsafe_b64encode(encoded_json.encode("utf-8")).decode("ascii").rstrip("="), + ): + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries({"extension": variant}, "hostile split input") + with pytest.raises(ReviewBlocked): + _validate_generated_output_security({"extension": variant}, "hostile split output") + + _validate_recursive_boundaries( + {"runtime": {"state": False}, "approval": {"status": "BLOCKED"}}, + "bounded split input", + ) + _validate_recursive_boundaries( + {"final_status": "PASS", "artifacts": [{"final_status": "PASS"}]}, + "canonical final status", + ) + _validate_generated_output_security( + {"final_status": "PASS", "artifacts": [{"final_status": "PASS"}]}, + "canonical final status", + ) + + neutral_wrapper_attacks = [ + {"final": {"review": {"authorization": True}}}, + {"ai": {"metadata": {"authority": True}}}, + {"review": {"metadata": {"disposition": "APPROVED"}}}, + ] + for index, attack in enumerate(neutral_wrapper_attacks): + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries(attack, "hostile neutral wrapper") + with pytest.raises(ReviewBlocked): + _validate_generated_output_security(attack, "hostile neutral wrapper") + + manifest = _json(MANIFEST) + manifest["field_mapping"] = attack + manifest_path = tmp_path / f"neutral-wrapper-manifest-{index}.json" + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / f"neutral-wrapper-manifest-{index}" + assert main( + ["review", "run", "--artifact", str(manifest_path), "--output", str(output), "--force"] + ) == 1 + + _validate_recursive_boundaries( + { + "final": {"review": {"authorization": False}}, + "ai": {"metadata": {"authority": False}}, + "review": {"metadata": {"disposition": "BLOCKED"}}, + }, + "bounded neutral wrappers", + ) + + array_attacks = [ + {"production_live": [True]}, + {"ai_authority": ["APPROVED"]}, + {"review_disposition": [True]}, + {"final_authorization": [1]}, + ] + for index, attack in enumerate(array_attacks): + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries(attack, "hostile promotion array") + with pytest.raises(ReviewBlocked): + _validate_generated_output_security(attack, "hostile promotion array") + + manifest = _json(MANIFEST) + manifest["field_mapping"] = attack + manifest_path = tmp_path / f"promotion-array-manifest-{index}.json" + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + output = tmp_path / f"promotion-array-manifest-{index}" + assert main( + ["review", "run", "--artifact", str(manifest_path), "--output", str(output), "--force"] + ) == 1 + + bounded_array = { + "production_live": [False], + "ai_authority": ["NOT_APPROVED"], + "review_disposition": ["BLOCKED"], + "final_authorization": [False], + } + _validate_recursive_boundaries(bounded_array, "bounded promotion array") + _validate_generated_output_security(bounded_array, "bounded promotion array") + def test_private_marker_keys_fail_closed_through_recursive_encodings() -> None: keys = [ @@ -459,6 +651,119 @@ def test_private_marker_keys_fail_closed_through_recursive_encodings() -> None: _validate_no_private_markers({"note": variant}, "hostile private value") +def test_unicode_format_characters_cannot_obfuscate_claims_or_private_markers(tmp_path) -> None: + scan_ignorable_characters = ( + "\u200b", "\u200c", "\u200d", "\u2060", "\ufeff", + "\u034f", "\u0301", "\ufe0f", + "\x00", "\x08", "\x1f", "\x7f", + ) + claim_templates = ( + "public{format} safe is confirmed", + "case{format} closure approved", + "AI{format} authority is enabled", + "runtime{format} active", + ) + attacks = [ + template.format(format=character) + for character in scan_ignorable_characters + for template in claim_templates + ] + for attack in attacks: + for variant in (attack, quote(attack, safe="")): + nested = {"nested": [{"note": variant}]} + with pytest.raises(ReviewBlocked): + _validate_recursive_boundaries(nested, "format-obfuscated input") + with pytest.raises(ReviewBlocked): + _validate_generated_output_security(nested, "format-obfuscated output") + + for character in scan_ignorable_characters: + for marker in ( + f"PRIVATE{character}_EVIDENCE", + f"RAW{character}_WAZUH", + f"CUSTOMER{character}_IDENTIFIER", + ): + with pytest.raises(ReviewBlocked): + _validate_no_private_markers({"note": marker}, "format-obfuscated private marker") + + raw_plain = "public safe is confirmed" + raw_obfuscated = "public\u200b safe is confirmed" + assert hashlib.sha256(raw_plain.encode("utf-8")).hexdigest() != hashlib.sha256( + raw_obfuscated.encode("utf-8") + ).hexdigest() + _validate_generated_output_security( + { + "note": "valid family emoji: \U0001f468\u200d\U0001f469\u200d\U0001f467\ufe0f", + "accented": "r\u00e9sum\u00e9 review \U0001f469\u200d\U0001f4bb only", + "multiline": "controlled-test\nreview\tcomplete\rbounded", + }, + "valid emoji control", + ) + + output = tmp_path / "review-run" + assert main(["review", "run", "--artifact", str(MANIFEST), "--output", str(output), "--force"]) == 0 + state_path = output / "machine-state.json" + baseline = _json(state_path) + reviewer_pack = output / "reviewer-pack.md" + original = reviewer_pack.read_text(encoding="utf-8") + for attack in attacks: + reviewer_pack.write_text(original + f"\n{attack}\n", encoding="utf-8") + hostile_state = deepcopy(baseline) + hostile_state["output_digests"]["reviewer-pack.md"] = hashlib.sha256( + reviewer_pack.read_bytes() + ).hexdigest() + hostile_state["state_integrity_digest"] = _integrity(hostile_state) + state_path.write_text( + json.dumps(hostile_state, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + errors = verify_review_run(state_path) + assert errors, attack + assert any("security validation failed" in error for error in errors), attack + reviewer_pack.write_text(original, encoding="utf-8") + state_path.write_text(json.dumps(baseline, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def _tracked_vocabulary_repo(tmp_path: Path, directory: str, name: str, content: bytes) -> Path: + root = tmp_path / directory + root.mkdir() + subprocess.run(["git", "init", "-q", str(root)], check=True) + (root / name).write_bytes(content) + subprocess.run(["git", "-C", str(root), "add", "--", name], check=True) + return root + + +def test_tracked_vocabulary_guard_is_nfkc_filename_utf8_and_nul_fail_closed(tmp_path) -> None: + accepted = _tracked_vocabulary_repo( + tmp_path, + "accepted", + "fixture.md", + "controlled-test résumé 中文 family 👩‍💻\n".encode("utf-8"), + ) + assert verify_tracked_vocabulary(accepted) == [] + + retired = "syn" + "thetic" + cases = [ + ("ascii", "fixture.md", f"retired {retired} fixture\n".encode("utf-8")), + ( + "nfkc", + "fixture.md", + "retired \uff53\uff59\uff4e\uff54\uff48\uff45\uff54\uff49\uff43 fixture\n".encode("utf-8"), + ), + ("filename", f"{retired}-name.md", b"controlled-test fixture\n"), + ("format-filename", "syn\u200bthetic-name.md", b"controlled-test fixture\n"), + ("mark-filename", "synthe\u0301tic-name.md", b"controlled-test fixture\n"), + ("format-content", "fixture.md", "retired syn\u200bthetic fixture\n".encode("utf-8")), + ("mark-content", "fixture.md", "retired synthe\u0301tic fixture\n".encode("utf-8")), + ("control-content", "fixture.md", b"retired syn\x08thetic fixture\n"), + ("utf16", "fixture.md", f"{retired} fixture".encode("utf-16")), + ("nul-byte", "fixture.md", b"controlled-test\0fixture"), + ("unknown-extension", "fixture.blobx", b"controlled-test fixture"), + ] + for directory, name, content in cases: + root = _tracked_vocabulary_repo(tmp_path, directory, name, content) + assert verify_tracked_vocabulary(root), directory + + def test_encoded_mixed_and_drive_relative_paths_fail_closed(tmp_path) -> None: attacks = [ r"C:relative\fixture.json", From d8aeb256b0642ad210550a0f21e3bdd9a93b5d49 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 09:56:08 -0500 Subject: [PATCH 36/73] ci(hoxline): pin hardened authority sources --- .github/workflows/ci.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc3d439..d99562b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 3afb59fc452fc1fbdf14207416068ec72b084464 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 4c04ace8ddfe9aca9e6ad91e7984995210fa4025 strategy: fail-fast: false matrix: @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 3afb59fc452fc1fbdf14207416068ec72b084464 + ref: 4c04ace8ddfe9aca9e6ad91e7984995210fa4025 path: org/.github fetch-depth: 0 persist-credentials: false @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-detections - ref: 16d4432eb46d457939f1cfe573c0c5d712650efb + ref: 11a481afbc739c994ce19cdb12052a307a6eec77 path: org/hawkinsoperations-detections fetch-depth: 0 persist-credentials: false @@ -47,7 +47,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation - ref: 34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc + ref: 2e833f340e183e8e2357c860314509c6d1cc2dd3 path: org/hawkinsoperations-validation fetch-depth: 0 persist-credentials: false @@ -56,7 +56,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: 42c3a4b78548fc4f95fd017f633eaaf207fc7625 + ref: bca89adb3222c34b81458e319e5272402256ec6f path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -65,7 +65,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-proof - ref: c60882552571c1664549d3e594c6994dc27dce63 + ref: 79d4bff2dccb24c2b29a68f4a3be7a1e916414b4 path: org/hawkinsoperations-proof fetch-depth: 0 persist-credentials: false @@ -74,7 +74,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 7709c7aec560ad0195ca171c093e4f8468301796 + ref: 048a11ea7e0404a7d426e900f2d9584b842e4bb3 path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -98,12 +98,12 @@ jobs: "hoxline", ] immutable = { - ".github": "3afb59fc452fc1fbdf14207416068ec72b084464", - "hawkinsoperations-detections": "16d4432eb46d457939f1cfe573c0c5d712650efb", - "hawkinsoperations-validation": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "hawkinsoperations-platform": "42c3a4b78548fc4f95fd017f633eaaf207fc7625", - "hawkinsoperations-proof": "c60882552571c1664549d3e594c6994dc27dce63", - "hawkinsoperations-website": "7709c7aec560ad0195ca171c093e4f8468301796", + ".github": "4c04ace8ddfe9aca9e6ad91e7984995210fa4025", + "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", + "hawkinsoperations-validation": "2e833f340e183e8e2357c860314509c6d1cc2dd3", + "hawkinsoperations-platform": "bca89adb3222c34b81458e319e5272402256ec6f", + "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", + "hawkinsoperations-website": "048a11ea7e0404a7d426e900f2d9584b842e4bb3", } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) if actual != sorted(expected): From f725daec88593f9b1dce5276556c33099d9787e3 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 10:09:34 -0500 Subject: [PATCH 37/73] ci(hoxline): consume final reviewed code matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d99562b..986c3b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 4c04ace8ddfe9aca9e6ad91e7984995210fa4025 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 76f2897b30d0e1b92799cd4c2d87ea7700fd2779 strategy: fail-fast: false matrix: @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 4c04ace8ddfe9aca9e6ad91e7984995210fa4025 + ref: 76f2897b30d0e1b92799cd4c2d87ea7700fd2779 path: org/.github fetch-depth: 0 persist-credentials: false @@ -98,7 +98,7 @@ jobs: "hoxline", ] immutable = { - ".github": "4c04ace8ddfe9aca9e6ad91e7984995210fa4025", + ".github": "76f2897b30d0e1b92799cd4c2d87ea7700fd2779", "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", "hawkinsoperations-validation": "2e833f340e183e8e2357c860314509c6d1cc2dd3", "hawkinsoperations-platform": "bca89adb3222c34b81458e319e5272402256ec6f", From 088e559877c86d1893b9bb38370552e8f64f3cf8 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 10:32:02 -0500 Subject: [PATCH 38/73] ci(hoxline): pin final website review matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 986c3b0..86eb2d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 76f2897b30d0e1b92799cd4c2d87ea7700fd2779 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: a93f3b99477507063c8321fdc20a64ef57d0a801 strategy: fail-fast: false matrix: @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 76f2897b30d0e1b92799cd4c2d87ea7700fd2779 + ref: a93f3b99477507063c8321fdc20a64ef57d0a801 path: org/.github fetch-depth: 0 persist-credentials: false @@ -98,7 +98,7 @@ jobs: "hoxline", ] immutable = { - ".github": "76f2897b30d0e1b92799cd4c2d87ea7700fd2779", + ".github": "a93f3b99477507063c8321fdc20a64ef57d0a801", "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", "hawkinsoperations-validation": "2e833f340e183e8e2357c860314509c6d1cc2dd3", "hawkinsoperations-platform": "bca89adb3222c34b81458e319e5272402256ec6f", From ee17f78ae96ba6d252524e40ec75af46f33da69d Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 11:01:11 -0500 Subject: [PATCH 39/73] ci(hoxline): pin final generator review matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86eb2d1..8e6f196 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: a93f3b99477507063c8321fdc20a64ef57d0a801 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: e806e91e3047a9ec235b2aa83c19a340034eb578 strategy: fail-fast: false matrix: @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: a93f3b99477507063c8321fdc20a64ef57d0a801 + ref: e806e91e3047a9ec235b2aa83c19a340034eb578 path: org/.github fetch-depth: 0 persist-credentials: false @@ -98,7 +98,7 @@ jobs: "hoxline", ] immutable = { - ".github": "a93f3b99477507063c8321fdc20a64ef57d0a801", + ".github": "e806e91e3047a9ec235b2aa83c19a340034eb578", "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", "hawkinsoperations-validation": "2e833f340e183e8e2357c860314509c6d1cc2dd3", "hawkinsoperations-platform": "bca89adb3222c34b81458e319e5272402256ec6f", From fb0cb3bd45fd046d82742e53d858800c58ef8c7f Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 11:28:54 -0500 Subject: [PATCH 40/73] chore(case-growth): refresh hardened atomic snapshot --- .../current-case-growth-index.json | 86 +++++++++---------- .../case-growth/current-case-growth-index.md | 18 ++-- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index dc9ceed..5dddd98 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T09:10:20Z", + "generated_at": "2026-07-23T16:26:37Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -17,9 +17,9 @@ "repository": ".github", "authority_role": "org command-center routing", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", - "source_observed_head_sha": "43fd17e7e66606fac92994cfc9eeb3871a03eff1", - "current_observed_head_sha": "3afb59fc452fc1fbdf14207416068ec72b084464", + "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", + "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", + "current_observed_head_sha": "e806e91e3047a9ec235b2aa83c19a340034eb578", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -49,9 +49,9 @@ "repository": "hawkinsoperations-detections", "authority_role": "detection source truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", - "source_observed_head_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", - "current_observed_head_sha": "16d4432eb46d457939f1cfe573c0c5d712650efb", + "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", + "source_observed_head_sha": "c2790ab007279faff6130a60dc68818343c255ee", + "current_observed_head_sha": "11a481afbc739c994ce19cdb12052a307a6eec77", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -81,9 +81,9 @@ "repository": "hawkinsoperations-validation", "authority_role": "controlled validation truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "source_observed_head_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", - "current_observed_head_sha": "34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc", + "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", + "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", + "current_observed_head_sha": "2e833f340e183e8e2357c860314509c6d1cc2dd3", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", "source_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", - "current_observed_head_sha": "87414ef6734993f9a4cedf4b95a03473ba336884", + "current_observed_head_sha": "bca89adb3222c34b81458e319e5272402256ec6f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -145,9 +145,9 @@ "repository": "hawkinsoperations-proof", "authority_role": "proof and claim-boundary truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "c60882552571c1664549d3e594c6994dc27dce63", - "source_observed_head_sha": "c60882552571c1664549d3e594c6994dc27dce63", - "current_observed_head_sha": "c60882552571c1664549d3e594c6994dc27dce63", + "source_commit_sha": "042a918ad4a8473cd5abcfd575072fc094639682", + "source_observed_head_sha": "042a918ad4a8473cd5abcfd575072fc094639682", + "current_observed_head_sha": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -177,9 +177,9 @@ "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status contract", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", - "source_observed_head_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", - "current_observed_head_sha": "0230c42fb4aa40aa3c917505457848da4f9883c2", + "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", + "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", + "current_observed_head_sha": "8be70f1614d37eec590fdb1bb9234de312711021", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,23 +209,23 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "cc0e3e11b86b8998eccaec7d7981cc4f051f156e", - "source_observed_head_sha": "cc0e3e11b86b8998eccaec7d7981cc4f051f156e", - "current_observed_head_sha": "e6e6e8c28f047cb2f56c7cbd75e273ffad0ed913", + "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", + "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", + "current_observed_head_sha": "ee17f78ae96ba6d252524e40ec75af46f33da69d", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "1b3fb46ad51706da678156160b88ab0e25a18294", - "source_git_blob_sha": "1b3fb46ad51706da678156160b88ab0e25a18294", - "source_file_sha256": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", - "authoritative_content_fingerprint": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", - "source_semantic_fingerprint_sha256": "471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65", + "authoritative_git_blob_sha": "1eed4c1d3433667670a130278ca8a804e00c5243", + "source_git_blob_sha": "1eed4c1d3433667670a130278ca8a804e00c5243", + "source_file_sha256": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", + "authoritative_content_fingerprint": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", + "source_semantic_fingerprint_sha256": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", - "repository_dirty_observed": true, + "repository_dirty_observed": false, "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "c92dcc2cd305861c487473e0eb7afb29d800e1c175b19f0413ca6c48e4765ac2", + "source_manifest_digest": "567d17964d9d6d7c46a2b96ef1be1a7f35c58d96fa154072c28be4d540aad226", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -268,7 +268,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "controlled validation authority only", - "files_scanned": 201 + "files_scanned": 202 }, { "repo": "hawkinsoperations-platform", @@ -295,14 +295,14 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", - "files_scanned": 271 + "files_scanned": 272 }, { "repo": "hoxline", "path": "hoxline", "exists": true, "branch": "feature/hoxline-case-growth-convergence-v1", - "dirty": true, + "dirty": false, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", "files_scanned": 181 } @@ -418,7 +418,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "separate proof scope before any live-cloud or public route approval", "evidence_confidence": "HIGH", "notes": [ @@ -499,7 +499,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:42:44-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -933,7 +933,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T00:30:10-05:00", + "last_updated": "2026-07-23T06:37:51-05:00", "next_gate": "separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording", "evidence_confidence": "HIGH", "notes": [ @@ -1013,7 +1013,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T00:30:10-05:00", + "last_updated": "2026-07-23T06:37:51-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1089,7 +1089,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T00:30:10-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "proof-card or public reviewer route only after separate human-approved proof scope", "evidence_confidence": "HIGH", "notes": [ @@ -1179,7 +1179,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T00:30:10-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion", "evidence_confidence": "HIGH", "notes": [ @@ -1271,7 +1271,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T06:37:51-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1586,7 +1586,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval", "evidence_confidence": "MEDIUM", "notes": [ @@ -1751,7 +1751,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1835,7 +1835,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1914,7 +1914,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1993,7 +1993,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-23T05:15:50-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "9d34b6d27c5d6425e347058f207400d8431d47d236c7a57d17071bb3e85fe949" + "reproducibility_sha256": "7c86e8683040a2b76485ac2cccbe761691a48d047d15cd01870d6a7e349fe607" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 5088fe2..46ea1c8 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T09:10:20Z` +Generated: `2026-07-23T16:26:37Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `c92dcc2cd305861c487473e0eb7afb29d800e1c175b19f0413ca6c48e4765ac2` -Reproducibility SHA-256: `9d34b6d27c5d6425e347058f207400d8431d47d236c7a57d17071bb3e85fe949` +Source manifest digest: `567d17964d9d6d7c46a2b96ef1be1a7f35c58d96fa154072c28be4d540aad226` +Reproducibility SHA-256: `7c86e8683040a2b76485ac2cccbe761691a48d047d15cd01870d6a7e349fe607` ## Summary @@ -35,13 +35,13 @@ Reproducibility SHA-256: `9d34b6d27c5d6425e347058f207400d8431d47d236c7a57d17071b | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | -| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `43fd17e7e66606fac92994cfc9eeb3871a03eff1` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | -| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `16d4432eb46d457939f1cfe573c0c5d712650efb` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | -| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `34bbfc4f48f75f647d5946b4cbe6835abf5e7dbc` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | +| `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `6e6763a81d6af09c2e4588462b56117ce82c2f88` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | +| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | +| `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `ebf52f7c6c9b78de767272cc56fccdc584f5c4e0` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `541ffcd0e94e9db94638d8ffa5934da11ae38771` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | -| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `c60882552571c1664549d3e594c6994dc27dce63` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | -| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `0230c42fb4aa40aa3c917505457848da4f9883c2` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `cc0e3e11b86b8998eccaec7d7981cc4f051f156e` | `1b3fb46ad51706da678156160b88ab0e25a18294` | `471c62ecb6a4a62e54f11538dae660a8b98cb818877de86b91733b54045c4b65` | `CURRENT` | +| `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | +| `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7` | `1eed4c1d3433667670a130278ca8a804e00c5243` | `8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324` | `CURRENT` | ## Convergence Findings From 768fd2263a123fe6a508849a9f5eaed37f403135 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 12:00:09 -0500 Subject: [PATCH 41/73] chore(hoxline): refresh converged Case Growth pair --- .../current-case-growth-index.json | 28 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 5dddd98..93119c9 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T16:26:37Z", + "generated_at": "2026-07-23T16:56:09Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "e806e91e3047a9ec235b2aa83c19a340034eb578", + "current_observed_head_sha": "503d3af7b84b9fe9c76ca06db0ae2a87925fb32f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,20 +113,20 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", - "source_observed_head_sha": "541ffcd0e94e9db94638d8ffa5934da11ae38771", - "current_observed_head_sha": "bca89adb3222c34b81458e319e5272402256ec6f", + "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", + "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", + "current_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", "authoritative_path": "contracts/public-status-source-contract-v1.json", - "authoritative_git_blob_sha": "b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3", - "source_git_blob_sha": "b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3", - "source_file_sha256": "91e1ba4386383d7f19dada470bdd8865b64885a10152d9f68a01458b1cd1ca5c", - "authoritative_content_fingerprint": "5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b", - "source_semantic_fingerprint_sha256": "5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b", + "authoritative_git_blob_sha": "577dd64c65324f45248ce36e4550cba04b9ff056", + "source_git_blob_sha": "577dd64c65324f45248ce36e4550cba04b9ff056", + "source_file_sha256": "6825af757ac6ae3a8980200a70a93cb60ae9dc41058468c7d7601d1a976c0c52", + "authoritative_content_fingerprint": "8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95", + "source_semantic_fingerprint_sha256": "8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "repository_dirty_observed": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "8be70f1614d37eec590fdb1bb9234de312711021", + "current_observed_head_sha": "50cb1a2fd39887de9e413b450a8af37090953102", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "ee17f78ae96ba6d252524e40ec75af46f33da69d", + "current_observed_head_sha": "fb0cb3bd45fd046d82742e53d858800c58ef8c7f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "567d17964d9d6d7c46a2b96ef1be1a7f35c58d96fa154072c28be4d540aad226", + "source_manifest_digest": "ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "7c86e8683040a2b76485ac2cccbe761691a48d047d15cd01870d6a7e349fe607" + "reproducibility_sha256": "74fce75126434a61ae2916a0ce22229f7f311f2c0241b4461a0bebf6917390e6" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 46ea1c8..5c908fc 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T16:26:37Z` +Generated: `2026-07-23T16:56:09Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `567d17964d9d6d7c46a2b96ef1be1a7f35c58d96fa154072c28be4d540aad226` -Reproducibility SHA-256: `7c86e8683040a2b76485ac2cccbe761691a48d047d15cd01870d6a7e349fe607` +Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` +Reproducibility SHA-256: `74fce75126434a61ae2916a0ce22229f7f311f2c0241b4461a0bebf6917390e6` ## Summary @@ -38,7 +38,7 @@ Reproducibility SHA-256: `7c86e8683040a2b76485ac2cccbe761691a48d047d15cd01870d6a | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `6e6763a81d6af09c2e4588462b56117ce82c2f88` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `ebf52f7c6c9b78de767272cc56fccdc584f5c4e0` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `541ffcd0e94e9db94638d8ffa5934da11ae38771` | `b4dc8749d9e51592a05b1ebe83c87b86a8cc01b3` | `5e57cd9e7eeee3fac51d5f89fa2a84f664b5e8d0bab3e0c475052bbcbf3a064b` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `651a43a4dfe0776605d5bd7b85ef4f8381c42b64` | `577dd64c65324f45248ce36e4550cba04b9ff056` | `8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | | `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7` | `1eed4c1d3433667670a130278ca8a804e00c5243` | `8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324` | `CURRENT` | From 0b40f45ec7b5275effb8270ab3382fa6df218e95 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 12:07:45 -0500 Subject: [PATCH 42/73] chore(hoxline): seal final converged Case Growth pair --- examples/case-growth/current-case-growth-index.json | 10 +++++----- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 93119c9..dbd9bbd 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T16:56:09Z", + "generated_at": "2026-07-23T17:05:44Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "503d3af7b84b9fe9c76ca06db0ae2a87925fb32f", + "current_observed_head_sha": "da64b4638b46696274206cc85ecb44427b2c9566", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "50cb1a2fd39887de9e413b450a8af37090953102", + "current_observed_head_sha": "8a08611f9562e9b34a7422a7c3faeabfe69aba0a", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "fb0cb3bd45fd046d82742e53d858800c58ef8c7f", + "current_observed_head_sha": "768fd2263a123fe6a508849a9f5eaed37f403135", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "74fce75126434a61ae2916a0ce22229f7f311f2c0241b4461a0bebf6917390e6" + "reproducibility_sha256": "aba0c2f21807937bc27ab0c9fb8bdbe1b410d9d89190f98f6c2daecf31d96ef9" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 5c908fc..80a923a 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T16:56:09Z` +Generated: `2026-07-23T17:05:44Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `74fce75126434a61ae2916a0ce22229f7f311f2c0241b4461a0bebf6917390e6` +Reproducibility SHA-256: `aba0c2f21807937bc27ab0c9fb8bdbe1b410d9d89190f98f6c2daecf31d96ef9` ## Summary From 84c7be52b3077d4b9075954de8900693813c725e Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 12:27:54 -0500 Subject: [PATCH 43/73] chore(hoxline): record merge-rehearsal observation --- examples/case-growth/current-case-growth-index.json | 12 ++++++------ examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index dbd9bbd..b8df2cc 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T17:05:44Z", + "generated_at": "2026-07-23T17:22:53Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "da64b4638b46696274206cc85ecb44427b2c9566", + "current_observed_head_sha": "7ed69fa7656121f3e595b83de61705557aa073d7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", + "current_observed_head_sha": "8ad2ec22595c3cccf6696a07d9708c3397d60175", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "8a08611f9562e9b34a7422a7c3faeabfe69aba0a", + "current_observed_head_sha": "ad76debe8f183477f1f8244c04d19177cce3aede", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "768fd2263a123fe6a508849a9f5eaed37f403135", + "current_observed_head_sha": "0b40f45ec7b5275effb8270ab3382fa6df218e95", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "aba0c2f21807937bc27ab0c9fb8bdbe1b410d9d89190f98f6c2daecf31d96ef9" + "reproducibility_sha256": "f313b41532c29870c152549e127bad740a71f3be60aa2a29ae64f4b1e84ea521" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 80a923a..9fefa08 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T17:05:44Z` +Generated: `2026-07-23T17:22:53Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `aba0c2f21807937bc27ab0c9fb8bdbe1b410d9d89190f98f6c2daecf31d96ef9` +Reproducibility SHA-256: `f313b41532c29870c152549e127bad740a71f3be60aa2a29ae64f4b1e84ea521` ## Summary From 92e40bfe6c516c2f67f0efd1478e355f1b7c0374 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 12:33:48 -0500 Subject: [PATCH 44/73] chore(hoxline): seal final strategy-resilient Case Growth pair --- examples/case-growth/current-case-growth-index.json | 12 ++++++------ examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index b8df2cc..b99a26b 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T17:22:53Z", + "generated_at": "2026-07-23T17:31:31Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "7ed69fa7656121f3e595b83de61705557aa073d7", + "current_observed_head_sha": "bba6e6a2afd13baae36c339edb023109f0eb184f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "8ad2ec22595c3cccf6696a07d9708c3397d60175", + "current_observed_head_sha": "1662b888858a605057f2189ac00e4a0d8fb78873", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "ad76debe8f183477f1f8244c04d19177cce3aede", + "current_observed_head_sha": "4d66cfe7401aa90589b378960941cc0d402a7cde", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "0b40f45ec7b5275effb8270ab3382fa6df218e95", + "current_observed_head_sha": "84c7be52b3077d4b9075954de8900693813c725e", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "f313b41532c29870c152549e127bad740a71f3be60aa2a29ae64f4b1e84ea521" + "reproducibility_sha256": "fd0658440376caf2cd4dd1418106c1a18aa4260dbb5b36339aa7c1397b84130a" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 9fefa08..cd5602d 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T17:22:53Z` +Generated: `2026-07-23T17:31:31Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `f313b41532c29870c152549e127bad740a71f3be60aa2a29ae64f4b1e84ea521` +Reproducibility SHA-256: `fd0658440376caf2cd4dd1418106c1a18aa4260dbb5b36339aa7c1397b84130a` ## Summary From 385f3a09c9bae92ac481123ebd40d20ca2643451 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 13:07:29 -0500 Subject: [PATCH 45/73] chore(hoxline): seal explicit-observation Case Growth pair --- examples/case-growth/current-case-growth-index.json | 12 ++++++------ examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index b99a26b..0b06c85 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T17:31:31Z", + "generated_at": "2026-07-23T18:05:50Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "bba6e6a2afd13baae36c339edb023109f0eb184f", + "current_observed_head_sha": "0868ba3903f5f50c4e50a5806957247abd60cbfd", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "1662b888858a605057f2189ac00e4a0d8fb78873", + "current_observed_head_sha": "152a28ca790ca16c81e3815fa3139fd6b4a26610", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "4d66cfe7401aa90589b378960941cc0d402a7cde", + "current_observed_head_sha": "6719cc6b54e77cb3d46231b12ac19718ca3c3343", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "84c7be52b3077d4b9075954de8900693813c725e", + "current_observed_head_sha": "92e40bfe6c516c2f67f0efd1478e355f1b7c0374", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "fd0658440376caf2cd4dd1418106c1a18aa4260dbb5b36339aa7c1397b84130a" + "reproducibility_sha256": "312854f8827a3e634885adaa54f0aa1b3ad046bce8990d457639eb37ecfa2087" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index cd5602d..1002883 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T17:31:31Z` +Generated: `2026-07-23T18:05:50Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `fd0658440376caf2cd4dd1418106c1a18aa4260dbb5b36339aa7c1397b84130a` +Reproducibility SHA-256: `312854f8827a3e634885adaa54f0aa1b3ad046bce8990d457639eb37ecfa2087` ## Summary From 804b19eead9a71066a9a4f28286015e0a2397aae Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 13:59:56 -0500 Subject: [PATCH 46/73] chore(case-growth): refresh rewrite-resilient authority pair --- examples/case-growth/current-case-growth-index.json | 12 ++++++------ examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 0b06c85..5d5e867 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T18:05:50Z", + "generated_at": "2026-07-23T18:58:21Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "0868ba3903f5f50c4e50a5806957247abd60cbfd", + "current_observed_head_sha": "a54f2c19913bdd010de5a752b348a0115f4abd93", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "152a28ca790ca16c81e3815fa3139fd6b4a26610", + "current_observed_head_sha": "7357a0cc8f87bb4b455969279122d9a195da49c8", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "6719cc6b54e77cb3d46231b12ac19718ca3c3343", + "current_observed_head_sha": "3aaf8d38e51d2fb59403acb99b01e04badd173ba", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "92e40bfe6c516c2f67f0efd1478e355f1b7c0374", + "current_observed_head_sha": "385f3a09c9bae92ac481123ebd40d20ca2643451", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "312854f8827a3e634885adaa54f0aa1b3ad046bce8990d457639eb37ecfa2087" + "reproducibility_sha256": "27a0a83145cf018fa53f1fa4c43d44f5539544c7462d34851275ddf3adeb204c" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 1002883..1b15aba 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T18:05:50Z` +Generated: `2026-07-23T18:58:21Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `312854f8827a3e634885adaa54f0aa1b3ad046bce8990d457639eb37ecfa2087` +Reproducibility SHA-256: `27a0a83145cf018fa53f1fa4c43d44f5539544c7462d34851275ddf3adeb204c` ## Summary From 6acb75142ea4337f02c54dc74830f6797b83518f Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 15:18:42 -0500 Subject: [PATCH 47/73] fix(case-growth): model historical and blocked index states --- schemas/case-growth-index-v0.schema.json | 15 ++++++++++-- tests/test_case_growth_index_v0.py | 31 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/schemas/case-growth-index-v0.schema.json b/schemas/case-growth-index-v0.schema.json index bd0c5cf..5548d76 100644 --- a/schemas/case-growth-index-v0.schema.json +++ b/schemas/case-growth-index-v0.schema.json @@ -33,8 +33,8 @@ "generated_at": { "type": "string", "minLength": 1 }, "repo_root": { "type": "string", "minLength": 1 }, "proof_ceiling": { "const": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY" }, - "historical_snapshot": { "const": false }, - "current_authority": { "const": true }, + "historical_snapshot": { "type": "boolean" }, + "current_authority": { "type": "boolean" }, "snapshot_state": { "type": "object" }, "source_revisions": { "type": "array", @@ -260,6 +260,17 @@ } } }, + "allOf": [ + { + "not": { + "required": ["historical_snapshot", "current_authority"], + "properties": { + "historical_snapshot": { "const": true }, + "current_authority": { "const": true } + } + } + } + ], "$defs": { "string_array": { "type": "array", diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index d8622c1..d8b8623 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -1,6 +1,7 @@ from __future__ import annotations import contextlib +import copy import io import json import shutil @@ -34,6 +35,7 @@ ROOT = Path(__file__).resolve().parents[1] FIXTURE_ROOT = ROOT / "tests" / "fixtures" / "case_growth" / "org" SAMPLE_JSON = ROOT / "examples" / "case-growth" / "sample-case-growth-index.json" +CURRENT_JSON = ROOT / "examples" / "case-growth" / "current-case-growth-index.json" SCHEMA = ROOT / "schemas" / "case-growth-index-v0.schema.json" @@ -251,7 +253,7 @@ def test_cli_writes_content_bound_json_markdown_pair(self) -> None: self.assertEqual(verify_status, 1) self.assertIn("not the exact render", stdout.getvalue()) - def test_schema_validates_sample_json(self) -> None: + def test_schema_validates_fail_closed_diagnostic_index(self) -> None: sample = self.index schema = json.loads(SCHEMA.read_text(encoding="utf-8")) if jsonschema is not None: @@ -260,6 +262,33 @@ def test_schema_validates_sample_json(self) -> None: for field in schema["required"]: self.assertIn(field, sample) + def test_schema_validates_checked_current_and_historical_states(self) -> None: + schema = json.loads(SCHEMA.read_text(encoding="utf-8")) + current = json.loads(CURRENT_JSON.read_text(encoding="utf-8")) + historical = copy.deepcopy(current) + historical["historical_snapshot"] = True + historical["current_authority"] = False + historical["snapshot_state"]["historical_snapshot"] = True + historical["snapshot_state"]["current_authority"] = False + if jsonschema is not None: + jsonschema.validate(current, schema) + jsonschema.validate(historical, schema) + else: + self.assertFalse(current["historical_snapshot"]) + self.assertTrue(current["current_authority"]) + self.assertTrue(historical["historical_snapshot"]) + self.assertFalse(historical["current_authority"]) + + def test_schema_rejects_historical_snapshot_claiming_current_authority(self) -> None: + if jsonschema is None: + self.skipTest("jsonschema is not installed") + schema = json.loads(SCHEMA.read_text(encoding="utf-8")) + contradictory = json.loads(CURRENT_JSON.read_text(encoding="utf-8")) + contradictory["historical_snapshot"] = True + contradictory["current_authority"] = True + with self.assertRaises(jsonschema.ValidationError): + jsonschema.validate(contradictory, schema) + def test_rows_include_every_required_field(self) -> None: for row in self.rows: for field in ROW_FIELDS: From 7c842a1ad7f30f3c516082b31ed9fc892ca70a58 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 15:20:26 -0500 Subject: [PATCH 48/73] chore(case-growth): refresh schema-aware index pair --- examples/case-growth/current-case-growth-index.json | 8 ++++---- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 5d5e867..aa70e19 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T18:58:21Z", + "generated_at": "2026-07-23T20:18:50Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "a54f2c19913bdd010de5a752b348a0115f4abd93", + "current_observed_head_sha": "cbf4daea4f7cb5580b507dbb5276db2b04e8b257", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "385f3a09c9bae92ac481123ebd40d20ca2643451", + "current_observed_head_sha": "6acb75142ea4337f02c54dc74830f6797b83518f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "27a0a83145cf018fa53f1fa4c43d44f5539544c7462d34851275ddf3adeb204c" + "reproducibility_sha256": "c9f481d92b2d4421b97103cf094f44efabf9447d6d027588192573a3fb54582b" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 1b15aba..8995b5f 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T18:58:21Z` +Generated: `2026-07-23T20:18:50Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `27a0a83145cf018fa53f1fa4c43d44f5539544c7462d34851275ddf3adeb204c` +Reproducibility SHA-256: `c9f481d92b2d4421b97103cf094f44efabf9447d6d027588192573a3fb54582b` ## Summary From ad8c4507304bd0796bab3ff95daadab3b1ad50b5 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 16:00:54 -0500 Subject: [PATCH 49/73] chore(case-growth): refresh final source observations --- examples/case-growth/current-case-growth-index.json | 10 +++++----- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index aa70e19..c29894b 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T20:18:50Z", + "generated_at": "2026-07-23T20:57:21Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "cbf4daea4f7cb5580b507dbb5276db2b04e8b257", + "current_observed_head_sha": "071ec49ddef3643fed12b7047eba5635ee783a11", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "3aaf8d38e51d2fb59403acb99b01e04badd173ba", + "current_observed_head_sha": "5e887c88ddc4ae5325fdcc927fb7552e699aed02", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "6acb75142ea4337f02c54dc74830f6797b83518f", + "current_observed_head_sha": "7c842a1ad7f30f3c516082b31ed9fc892ca70a58", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "c9f481d92b2d4421b97103cf094f44efabf9447d6d027588192573a3fb54582b" + "reproducibility_sha256": "0ca95603a4e72c764b36d1f93963c31c9ba9fee2c20a9ada4afbf22e8155028a" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 8995b5f..3a24693 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T20:18:50Z` +Generated: `2026-07-23T20:57:21Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `c9f481d92b2d4421b97103cf094f44efabf9447d6d027588192573a3fb54582b` +Reproducibility SHA-256: `0ca95603a4e72c764b36d1f93963c31c9ba9fee2c20a9ada4afbf22e8155028a` ## Summary From 29772b1da7acf684ac476ea6e671de48e9b0e04f Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 16:55:38 -0500 Subject: [PATCH 50/73] chore(case-growth): refresh CI repair observations --- .../case-growth/current-case-growth-index.json | 16 ++++++++-------- .../case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index c29894b..4171cc2 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T20:57:21Z", + "generated_at": "2026-07-23T21:53:49Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "071ec49ddef3643fed12b7047eba5635ee783a11", + "current_observed_head_sha": "5dd76305b291ecaa0e17273d48e58a32b97d2990", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "2e833f340e183e8e2357c860314509c6d1cc2dd3", + "current_observed_head_sha": "46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "7357a0cc8f87bb4b455969279122d9a195da49c8", + "current_observed_head_sha": "c624877d5e61d02ca4593f50b92655e318d25830", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "5e887c88ddc4ae5325fdcc927fb7552e699aed02", + "current_observed_head_sha": "c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "7c842a1ad7f30f3c516082b31ed9fc892ca70a58", + "current_observed_head_sha": "ad8c4507304bd0796bab3ff95daadab3b1ad50b5", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -499,7 +499,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-23T16:45:14-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "0ca95603a4e72c764b36d1f93963c31c9ba9fee2c20a9ada4afbf22e8155028a" + "reproducibility_sha256": "40c53af1f0020f54e6b811f36494e6050679a70698f535e84e4637c75ab128eb" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 3a24693..1d30b94 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T20:57:21Z` +Generated: `2026-07-23T21:53:49Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `0ca95603a4e72c764b36d1f93963c31c9ba9fee2c20a9ada4afbf22e8155028a` +Reproducibility SHA-256: `40c53af1f0020f54e6b811f36494e6050679a70698f535e84e4637c75ab128eb` ## Summary From 228135bd1ecafe3bee7b4c28a7ad4aade536a68b Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 17:04:34 -0500 Subject: [PATCH 51/73] ci(hoxline): verify the exact seven-head source set --- .github/workflows/ci.yml | 24 ++++++++++++++---------- tests/test_case_growth_index_v0.py | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e6f196..d793b36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,8 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: e806e91e3047a9ec235b2aa83c19a340034eb578 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7 + HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false matrix: @@ -22,6 +23,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: path: org/hoxline + ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 persist-credentials: false @@ -29,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: e806e91e3047a9ec235b2aa83c19a340034eb578 + ref: 657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7 path: org/.github fetch-depth: 0 persist-credentials: false @@ -47,7 +49,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation - ref: 2e833f340e183e8e2357c860314509c6d1cc2dd3 + ref: 46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a path: org/hawkinsoperations-validation fetch-depth: 0 persist-credentials: false @@ -56,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: bca89adb3222c34b81458e319e5272402256ec6f + ref: c624877d5e61d02ca4593f50b92655e318d25830 path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -74,7 +76,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 048a11ea7e0404a7d426e900f2d9584b842e4bb3 + ref: c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -85,6 +87,7 @@ jobs: run: | python - <<'PY' from pathlib import Path + import os import subprocess root = Path("..").resolve() @@ -98,12 +101,13 @@ jobs: "hoxline", ] immutable = { - ".github": "e806e91e3047a9ec235b2aa83c19a340034eb578", + ".github": "657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7", "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", - "hawkinsoperations-validation": "2e833f340e183e8e2357c860314509c6d1cc2dd3", - "hawkinsoperations-platform": "bca89adb3222c34b81458e319e5272402256ec6f", + "hawkinsoperations-validation": "46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a", + "hawkinsoperations-platform": "c624877d5e61d02ca4593f50b92655e318d25830", "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "hawkinsoperations-website": "048a11ea7e0404a7d426e900f2d9584b842e4bb3", + "hawkinsoperations-website": "c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c", + "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) if actual != sorted(expected): @@ -114,7 +118,7 @@ jobs: text=True, ).strip() print(f"{name}={sha}") - if name in immutable and sha != immutable[name]: + if sha != immutable[name]: raise SystemExit( f"{name} checkout mismatch: expected {immutable[name]}, got {sha}" ) diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index d8b8623..e8770e6 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -37,6 +37,7 @@ SAMPLE_JSON = ROOT / "examples" / "case-growth" / "sample-case-growth-index.json" CURRENT_JSON = ROOT / "examples" / "case-growth" / "current-case-growth-index.json" SCHEMA = ROOT / "schemas" / "case-growth-index-v0.schema.json" +CI_WORKFLOW = ROOT / ".github" / "workflows" / "ci.yml" def _git(repo: Path, *args: str, input_text: str | None = None) -> str: @@ -188,6 +189,29 @@ def derived_health(summary: dict[str, int]) -> dict[str, float]: class CaseGrowthIndexV0Tests(unittest.TestCase): + def test_ci_checks_the_exact_pr_head_and_all_seven_repositories(self) -> None: + workflow = CI_WORKFLOW.read_text(encoding="utf-8") + pr_head = "${{ github.event.pull_request.head.sha || github.sha }}" + + def issues(value: str) -> list[str]: + findings = [] + if value.count("uses: actions/checkout@") != 7: + findings.append("checkout_count") + for required in ( + f"HAWKINS_HOXLINE_EVENT_SHA: {pr_head}", + f"ref: {pr_head}", + '"hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"]', + "if sha != immutable[name]:", + ): + if required not in value: + findings.append(required) + return findings + + self.assertEqual([], issues(workflow)) + + merge_ref_attack = workflow.replace(f"ref: {pr_head}", "ref: ${{ github.sha }}", 1) + self.assertNotEqual([], issues(merge_ref_attack)) + @classmethod def setUpClass(cls) -> None: cls.index = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") From fc1f65828c7c67d47cdc066d970822524759cbf0 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 17:06:28 -0500 Subject: [PATCH 52/73] chore(case-growth): refresh exact CI source observations --- examples/case-growth/current-case-growth-index.json | 8 ++++---- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 4171cc2..d88726b 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T21:53:49Z", + "generated_at": "2026-07-23T22:04:45Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "5dd76305b291ecaa0e17273d48e58a32b97d2990", + "current_observed_head_sha": "657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "ad8c4507304bd0796bab3ff95daadab3b1ad50b5", + "current_observed_head_sha": "228135bd1ecafe3bee7b4c28a7ad4aade536a68b", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "40c53af1f0020f54e6b811f36494e6050679a70698f535e84e4637c75ab128eb" + "reproducibility_sha256": "7a631cb26a4ccb28ddb1dae331f7744e9183c21adc196a4fd040e1eb237f329f" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index 1d30b94..ae57191 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T21:53:49Z` +Generated: `2026-07-23T22:04:45Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `40c53af1f0020f54e6b811f36494e6050679a70698f535e84e4637c75ab128eb` +Reproducibility SHA-256: `7a631cb26a4ccb28ddb1dae331f7744e9183c21adc196a4fd040e1eb237f329f` ## Summary From 5204ef31849d83ad790a142b68ea84e1451087d1 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 17:12:14 -0500 Subject: [PATCH 53/73] chore(case-growth): refresh exact website observations --- examples/case-growth/current-case-growth-index.json | 10 +++++----- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index d88726b..422f3be 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T22:04:45Z", + "generated_at": "2026-07-23T22:10:35Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7", + "current_observed_head_sha": "fbec4fdee5f36e7f5c9d377f0868e0b79cde8469", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c", + "current_observed_head_sha": "66ec4034005a76fc64f6ad8e6e0a4001cba68fbc", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "228135bd1ecafe3bee7b4c28a7ad4aade536a68b", + "current_observed_head_sha": "fc1f65828c7c67d47cdc066d970822524759cbf0", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "7a631cb26a4ccb28ddb1dae331f7744e9183c21adc196a4fd040e1eb237f329f" + "reproducibility_sha256": "3d436b4687568d8e8fd34c84a0ddced7e8f5201f75f1483dd092eab53faae2c4" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index ae57191..e1ed9eb 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T22:04:45Z` +Generated: `2026-07-23T22:10:35Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `7a631cb26a4ccb28ddb1dae331f7744e9183c21adc196a4fd040e1eb237f329f` +Reproducibility SHA-256: `3d436b4687568d8e8fd34c84a0ddced7e8f5201f75f1483dd092eab53faae2c4` ## Summary From b0899fe48f0c50989e60dbbfb69c6822e7e88d85 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 17:38:16 -0500 Subject: [PATCH 54/73] fix(hoxline): make review paths checkout-root invariant --- .../claim_authority/evaluator.py | 21 +++++++++++++++---- src/claimfirewall/scanner.py | 21 +++++++++++++++---- src/hoxline/review_engine.py | 6 ++++++ tests/test_blocked_claims.py | 3 ++- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/claimfirewall/claim_authority/evaluator.py b/src/claimfirewall/claim_authority/evaluator.py index 590b71d..ca16b63 100644 --- a/src/claimfirewall/claim_authority/evaluator.py +++ b/src/claimfirewall/claim_authority/evaluator.py @@ -167,13 +167,22 @@ def _iter_scan_files(paths: Iterable[str | Path], exclude_patterns: Iterable[str for raw_path in paths: path = Path(raw_path) if path.is_file(): - if _is_supported(path) and not _is_excluded(path, excludes): + if _is_supported(path) and not _is_excluded( + path, + excludes, + path.parent, + ): yield path continue if path.is_dir(): + scan_root = path.resolve() for child in sorted(path.rglob("*")): - if child.is_file() and _is_supported(child) and not _is_excluded(child, excludes): + if child.is_file() and _is_supported(child) and not _is_excluded( + child, + excludes, + scan_root, + ): yield child continue @@ -184,10 +193,14 @@ def _is_supported(path: Path) -> bool: return path.suffix.lower() in SUPPORTED_EXTENSIONS -def _is_excluded(path: Path, exclude_patterns: Iterable[str]) -> bool: +def _is_excluded( + path: Path, + exclude_patterns: Iterable[str], + scan_root: Path, +) -> bool: normalized = path.as_posix() try: - relative = path.resolve().relative_to(Path.cwd().resolve()).as_posix() + relative = path.resolve().relative_to(scan_root.resolve()).as_posix() except ValueError: relative = normalized name = path.name diff --git a/src/claimfirewall/scanner.py b/src/claimfirewall/scanner.py index c8ffceb..ab3a9ce 100644 --- a/src/claimfirewall/scanner.py +++ b/src/claimfirewall/scanner.py @@ -76,13 +76,22 @@ def _iter_scan_files(paths: Iterable[str | Path], exclude_patterns: Iterable[str for raw_path in paths: path = Path(raw_path) if path.is_file(): - if _is_supported(path) and not _is_excluded(path, excludes): + if _is_supported(path) and not _is_excluded( + path, + excludes, + path.parent, + ): yield path continue if path.is_dir(): + scan_root = path.resolve() for child in sorted(path.rglob("*")): - if child.is_file() and _is_supported(child) and not _is_excluded(child, excludes): + if child.is_file() and _is_supported(child) and not _is_excluded( + child, + excludes, + scan_root, + ): yield child continue @@ -93,10 +102,14 @@ def _is_supported(path: Path) -> bool: return path.suffix.lower() in SUPPORTED_EXTENSIONS -def _is_excluded(path: Path, exclude_patterns: Iterable[str]) -> bool: +def _is_excluded( + path: Path, + exclude_patterns: Iterable[str], + scan_root: Path, +) -> bool: normalized = path.as_posix() try: - relative = path.resolve().relative_to(Path.cwd().resolve()).as_posix() + relative = path.resolve().relative_to(scan_root.resolve()).as_posix() except ValueError: relative = normalized name = path.name diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 843e9b8..0824e21 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -1064,6 +1064,12 @@ def _review_repo_root(input_path: Path, explicit_root: Path | None = None) -> Pa for candidate in (resolved.parent, *resolved.parents): if (candidate / "pyproject.toml").is_file() and (candidate / "examples" / "review").is_dir(): return candidate + package_root = Path(__file__).resolve().parents[2] + if ( + (package_root / "pyproject.toml").is_file() + and (package_root / "examples" / "review").is_dir() + ): + return package_root return Path.cwd().resolve() diff --git a/tests/test_blocked_claims.py b/tests/test_blocked_claims.py index d74b18d..405e802 100644 --- a/tests/test_blocked_claims.py +++ b/tests/test_blocked_claims.py @@ -45,8 +45,9 @@ def test_failing_example_reports_blocked_claims() -> None: assert all(finding.line_number > 0 for finding in findings) -def test_directory_scan_and_exclude() -> None: +def test_directory_scan_and_exclude(tmp_path, monkeypatch) -> None: policy = load_policy(POLICY) + monkeypatch.chdir(tmp_path) findings = scan_paths([ROOT], policy, exclude_patterns=["examples/fail.md", "policy/blocked_claims.yml"]) From b0945c4c3db7312f7d50a51d4abab9e65ac454da Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 18:26:47 -0500 Subject: [PATCH 55/73] fix(case-growth): bind detached refs to exact heads --- src/hoxline/case_growth/collector.py | 7 +- tests/test_case_growth_index_v0.py | 139 +++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index 1eed4c1..b4d8f87 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -549,6 +549,11 @@ def _build_source_revisions( else current_head ) branch = repo_branch(repo) if repo is not None else "NOT_FOUND" + resolved_ref = ( + current_head + if branch.startswith("UNKNOWN_WITH_REASON:") + else branch + ) dirty = repo_dirty(repo) if repo is not None else False dirty_paths = repo_dirty_paths(repo) if repo is not None else [] authority_dirty = relative_path.replace("\\", "/").casefold() in { @@ -579,7 +584,7 @@ def _build_source_revisions( { "repository": repository, "authority_role": authority_role, - "resolved_ref": branch, + "resolved_ref": resolved_ref, "source_commit_sha": selected_content_sha, "source_observed_head_sha": selected_content_sha, "current_observed_head_sha": current_head, diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index e8770e6..d90955d 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -673,6 +673,145 @@ def test_selected_source_checkout_accepts_exact_detached_and_content_equivalent_ _git(repo, "checkout", "--detach", rewritten) self.assertEqual(verify_selected_source_checkout(org_root, repository), []) + def test_detached_seven_source_generation_records_exact_resolved_refs(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + org_root = Path(temp_dir) / "org" + shutil.copytree(FIXTURE_ROOT, org_root) + missing_authority_fixtures = { + ".github/governance/COMMAND_CENTER_INVARIANTS.json": "{}\n", + ( + "hawkinsoperations-platform/contracts/" + "public-status-source-contract-v1.json" + ): "{}\n", + "hawkinsoperations-website/schemas/public-status-v0.schema.json": "{}\n", + "hoxline/src/hoxline/case_growth/collector.py": ( + "# detached authority fixture\n" + ), + } + for relative, content in missing_authority_fixtures.items(): + authority_path = org_root / relative + authority_path.parent.mkdir(parents=True, exist_ok=True) + authority_path.write_text(content, encoding="utf-8") + expected_heads: dict[str, str] = {} + for repository in REPO_NAMES: + repo = org_root / repository + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-test@example.invalid") + _git( + repo, + "remote", + "add", + "origin", + f"https://github.com/HawkinsOperations/{repository}.git", + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "detached authority fixture") + head = _git(repo, "rev-parse", "HEAD") + expected_heads[repository] = head + _git(repo, "checkout", "--detach", head) + + selections: list[dict[str, object]] = [] + for repository in REPO_NAMES: + head = expected_heads[repository] + if repository == ".github": + selections.append( + { + "repository": repository, + "canonical_repository": "HawkinsOperations/.github", + "revision_source": "github_event_sha", + "authority_content_revision": head, + "tree_source": "github_event_tree", + } + ) + else: + selections.append( + { + "repository": repository, + "canonical_repository": ( + f"HawkinsOperations/{repository}" + ), + "revision": head, + "authority_content_revision": head, + "reviewed_tree_sha": _git( + org_root / repository, + "rev-parse", + "HEAD^{tree}", + ), + } + ) + manifest = { + "schema": "hawkinsoperations-convergence-source-manifest-v1", + "manifest_id": "TEST_DETACHED_EXACT_SEVEN_SOURCE_SELECTION", + "repositories": selections, + "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" + ), + }, + } + manifest_path = ( + org_root + / ".github" + / "governance" + / "CONVERGENCE_SOURCE_MANIFEST.json" + ) + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + command_center = org_root / ".github" + _git( + command_center, + "add", + "governance/CONVERGENCE_SOURCE_MANIFEST.json", + ) + _git(command_center, "commit", "-m", "detached source selection") + expected_heads[".github"] = _git(command_center, "rev-parse", "HEAD") + + with mock.patch.dict( + "os.environ", + { + "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": ( + expected_heads[".github"] + ) + }, + ): + generated = build_case_growth_index( + org_root, + generated_at="2026-06-27T00:00:00Z", + ) + for revision in generated["source_revisions"]: + repository = revision["repository"] + with self.subTest(repository=repository): + self.assertEqual( + expected_heads[repository], + revision["resolved_ref"], + ) + self.assertEqual( + revision["current_observed_head_sha"], + revision["resolved_ref"], + ) + self.assertFalse( + str(revision["resolved_ref"]).startswith( + "UNKNOWN_WITH_REASON:" + ) + ) + + with mock.patch.dict( + "os.environ", + { + "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA": ( + expected_heads[".github"] + ) + }, + ): + errors, _ = verify_case_growth_snapshot(org_root, generated) + self.assertEqual([], errors, errors) + def test_dynamic_command_center_selection_requires_exact_detached_observation(self) -> None: with tempfile.TemporaryDirectory() as temp_dir: org_root = Path(temp_dir) From 58a891578ee296d0cbd332797aad8a8928e69a5c Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 18:33:08 -0500 Subject: [PATCH 56/73] chore(case-growth): refresh detached-source pair --- .../current-case-growth-index.json | 34 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 ++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 422f3be..80d07f8 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T22:10:35Z", + "generated_at": "2026-07-23T23:30:59Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "fbec4fdee5f36e7f5c9d377f0868e0b79cde8469", + "current_observed_head_sha": "b56c7d694b9fc84e53635636976db4b2e929e796", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -51,7 +51,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", "source_observed_head_sha": "c2790ab007279faff6130a60dc68818343c255ee", - "current_observed_head_sha": "11a481afbc739c994ce19cdb12052a307a6eec77", + "current_observed_head_sha": "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a", + "current_observed_head_sha": "03d8b909b2304a9a570129a559f9cd44e98dc2b1", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "c624877d5e61d02ca4593f50b92655e318d25830", + "current_observed_head_sha": "26ae0fd0112d2123f90197b9aa5078db0d35db65", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "66ec4034005a76fc64f6ad8e6e0a4001cba68fbc", + "current_observed_head_sha": "722f9393f766267e81309210208673ead248251c", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "source_observed_head_sha": "d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7", - "current_observed_head_sha": "fc1f65828c7c67d47cdc066d970822524759cbf0", + "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", + "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", + "current_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "1eed4c1d3433667670a130278ca8a804e00c5243", - "source_git_blob_sha": "1eed4c1d3433667670a130278ca8a804e00c5243", - "source_file_sha256": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", - "authoritative_content_fingerprint": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", - "source_semantic_fingerprint_sha256": "8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324", + "authoritative_git_blob_sha": "b4d8f87864c4039fa756fe13da1b6a2371ca6e14", + "source_git_blob_sha": "b4d8f87864c4039fa756fe13da1b6a2371ca6e14", + "source_file_sha256": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", + "authoritative_content_fingerprint": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", + "source_semantic_fingerprint_sha256": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093", + "source_manifest_digest": "b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -499,7 +499,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T16:45:14-05:00", + "last_updated": "2026-07-23T17:49:24-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "3d436b4687568d8e8fd34c84a0ddced7e8f5201f75f1483dd092eab53faae2c4" + "reproducibility_sha256": "e34e6ca619acc857b4f0f542a193c82c3e99f23ccd7f721755b6493c7b647f7e" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index e1ed9eb..b06a0ab 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T22:10:35Z` +Generated: `2026-07-23T23:30:59Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `ad6f20c070aee750d8d45ba8682822ae2dcf6d5e6730f411c4721cc5ec86e093` -Reproducibility SHA-256: `3d436b4687568d8e8fd34c84a0ddced7e8f5201f75f1483dd092eab53faae2c4` +Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` +Reproducibility SHA-256: `e34e6ca619acc857b4f0f542a193c82c3e99f23ccd7f721755b6493c7b647f7e` ## Summary @@ -41,7 +41,7 @@ Reproducibility SHA-256: `3d436b4687568d8e8fd34c84a0ddced7e8f5201f75f1483dd092ea | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `651a43a4dfe0776605d5bd7b85ef4f8381c42b64` | `577dd64c65324f45248ce36e4550cba04b9ff056` | `8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `d2ce9d977e832e327b4bdbfef55f04a4fd7c35e7` | `1eed4c1d3433667670a130278ca8a804e00c5243` | `8eee6ebefd06667c8d4c4814a03fa1b159215d6189a136b9fe74d9bd8702c324` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `b0945c4c3db7312f7d50a51d4abab9e65ac454da` | `b4d8f87864c4039fa756fe13da1b6a2371ca6e14` | `e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428` | `CURRENT` | ## Convergence Findings From 193f929633635f351b2ef0e2ca5d8c0166e595ff Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 19:28:31 -0500 Subject: [PATCH 57/73] ci(hoxline): verify reviewed seven-source heads --- .github/workflows/ci.yml | 22 +++++++++++----------- tests/test_action_contract.py | 14 +++++++++++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d793b36..fd5c1dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: b87acd4487ff779bc0cb8b96c783ada0d712a0ed HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7 + ref: b87acd4487ff779bc0cb8b96c783ada0d712a0ed path: org/.github fetch-depth: 0 persist-credentials: false @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-detections - ref: 11a481afbc739c994ce19cdb12052a307a6eec77 + ref: 015a9dc359b3fa4c4853cd1b18372c3e44eacec9 path: org/hawkinsoperations-detections fetch-depth: 0 persist-credentials: false @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation - ref: 46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a + ref: 03d8b909b2304a9a570129a559f9cd44e98dc2b1 path: org/hawkinsoperations-validation fetch-depth: 0 persist-credentials: false @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: c624877d5e61d02ca4593f50b92655e318d25830 + ref: 26ae0fd0112d2123f90197b9aa5078db0d35db65 path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -76,7 +76,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c + ref: 5ee3fe553e78ca35b3a2422b2231c627c1ee1f99 path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -101,12 +101,12 @@ jobs: "hoxline", ] immutable = { - ".github": "657b041e55cb1c8e31e1ba4ebf0c620ab8a00cc7", - "hawkinsoperations-detections": "11a481afbc739c994ce19cdb12052a307a6eec77", - "hawkinsoperations-validation": "46a7ed39e3c0ea74ad102a1547a8bca2d00f3b4a", - "hawkinsoperations-platform": "c624877d5e61d02ca4593f50b92655e318d25830", + ".github": "b87acd4487ff779bc0cb8b96c783ada0d712a0ed", + "hawkinsoperations-detections": "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", + "hawkinsoperations-validation": "03d8b909b2304a9a570129a559f9cd44e98dc2b1", + "hawkinsoperations-platform": "26ae0fd0112d2123f90197b9aa5078db0d35db65", "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "hawkinsoperations-website": "c23b03a3f3f5373c1d2d918f43ca65eb8ac74e6c", + "hawkinsoperations-website": "5ee3fe553e78ca35b3a2422b2231c627c1ee1f99", "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 055b366..3959d82 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -42,8 +42,20 @@ def test_docs_scan_cleanly() -> None: def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> None: workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) + reviewed_refs = { + "b87acd4487ff779bc0cb8b96c783ada0d712a0ed", + "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", + "03d8b909b2304a9a570129a559f9cd44e98dc2b1", + "26ae0fd0112d2123f90197b9aa5078db0d35db65", + "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", + "5ee3fe553e78ca35b3a2422b2231c627c1ee1f99", + } assert len(refs) == 6 - assert len(set(refs)) == 6 + assert set(refs) == reviewed_refs + assert ( + "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: " + "b87acd4487ff779bc0cb8b96c783ada0d712a0ed" + ) in workflow assert "ref: main" not in workflow assert "ref: feature/" not in workflow assert "persist-credentials: false" in workflow From 6616877345c169ed3201ffeb951d2f710b04bba8 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 20:10:52 -0500 Subject: [PATCH 58/73] chore(case-growth): refresh reviewed head observations --- .../case-growth/current-case-growth-index.json | 14 +++++++------- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 80d07f8..3f9b008 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-23T23:30:59Z", + "generated_at": "2026-07-24T01:08:19Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "b56c7d694b9fc84e53635636976db4b2e929e796", + "current_observed_head_sha": "b86cc465faa375a8bc4bd78dbb9954a9620f922f", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "26ae0fd0112d2123f90197b9aa5078db0d35db65", + "current_observed_head_sha": "13c31e4eb60885420564e411965bfdb17b46f491", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "722f9393f766267e81309210208673ead248251c", + "current_observed_head_sha": "ecc73ff1c762c1aa6d8ebfca8b2fcb2124935e48", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "current_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", + "current_observed_head_sha": "193f929633635f351b2ef0e2ca5d8c0166e595ff", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -295,7 +295,7 @@ "branch": "feature/hoxline-case-growth-convergence-v1", "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", - "files_scanned": 272 + "files_scanned": 273 }, { "repo": "hoxline", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "e34e6ca619acc857b4f0f542a193c82c3e99f23ccd7f721755b6493c7b647f7e" + "reproducibility_sha256": "27b09115fa4db447b7bb467f12a18d60ecdc5a9eade78c0d6f8129da8ba36024" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index b06a0ab..f11e7e0 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-23T23:30:59Z` +Generated: `2026-07-24T01:08:19Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` -Reproducibility SHA-256: `e34e6ca619acc857b4f0f542a193c82c3e99f23ccd7f721755b6493c7b647f7e` +Reproducibility SHA-256: `27b09115fa4db447b7bb467f12a18d60ecdc5a9eade78c0d6f8129da8ba36024` ## Summary From 9a8ff9bfbf523ea61e7ce2c2142ec40c5a192c09 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 20:19:09 -0500 Subject: [PATCH 59/73] chore(case-growth): finalize settled source observations --- examples/case-growth/current-case-growth-index.json | 10 +++++----- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 3f9b008..a679199 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T01:08:19Z", + "generated_at": "2026-07-24T01:17:06Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "b86cc465faa375a8bc4bd78dbb9954a9620f922f", + "current_observed_head_sha": "4e6995774daba161c032e573634607afe807c8ef", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "13c31e4eb60885420564e411965bfdb17b46f491", + "current_observed_head_sha": "f2f1517c40669c621830ab3dcd00dd4d60b60cbc", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "current_observed_head_sha": "193f929633635f351b2ef0e2ca5d8c0166e595ff", + "current_observed_head_sha": "6616877345c169ed3201ffeb951d2f710b04bba8", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "27b09115fa4db447b7bb467f12a18d60ecdc5a9eade78c0d6f8129da8ba36024" + "reproducibility_sha256": "4cc222d41c706d812029dd7f64c715492069fe58e8228bc62bd2ed6506420289" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index f11e7e0..cdb5aa4 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T01:08:19Z` +Generated: `2026-07-24T01:17:06Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` -Reproducibility SHA-256: `27b09115fa4db447b7bb467f12a18d60ecdc5a9eade78c0d6f8129da8ba36024` +Reproducibility SHA-256: `4cc222d41c706d812029dd7f64c715492069fe58e8228bc62bd2ed6506420289` ## Summary From cad8114853ce3e6570149b6762fba4e8f5c396ae Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 20:37:17 -0500 Subject: [PATCH 60/73] chore(case-growth): finalize merge-resilient observations --- examples/case-growth/current-case-growth-index.json | 10 +++++----- examples/case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index a679199..6d1e154 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T01:17:06Z", + "generated_at": "2026-07-24T01:35:00Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "4e6995774daba161c032e573634607afe807c8ef", + "current_observed_head_sha": "c3bc35693f88f4cb907c2c6c53281631dcbdb6b8", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "f2f1517c40669c621830ab3dcd00dd4d60b60cbc", + "current_observed_head_sha": "eb43cd3ce4ad6668dbc9a175bf3de714fcb40c63", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "current_observed_head_sha": "6616877345c169ed3201ffeb951d2f710b04bba8", + "current_observed_head_sha": "9a8ff9bfbf523ea61e7ce2c2142ec40c5a192c09", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "4cc222d41c706d812029dd7f64c715492069fe58e8228bc62bd2ed6506420289" + "reproducibility_sha256": "d1d899ea7366fff5380712893f16fcba8938b4275563507e5fd2a12c9b832514" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index cdb5aa4..e0c4a48 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T01:17:06Z` +Generated: `2026-07-24T01:35:00Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` -Reproducibility SHA-256: `4cc222d41c706d812029dd7f64c715492069fe58e8228bc62bd2ed6506420289` +Reproducibility SHA-256: `d1d899ea7366fff5380712893f16fcba8938b4275563507e5fd2a12c9b832514` ## Summary From dbfd4a195ce00cb4e4b96494f114f7f487bb029a Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 21:46:22 -0500 Subject: [PATCH 61/73] fix(case-growth): bind authority to stored repository origin --- src/hoxline/case_growth/discovery.py | 30 +++++++++++++++++++++-- src/hoxline/review_engine.py | 3 ++- tests/test_case_growth_index_v0.py | 36 +++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/hoxline/case_growth/discovery.py b/src/hoxline/case_growth/discovery.py index d691a6e..5359f7e 100644 --- a/src/hoxline/case_growth/discovery.py +++ b/src/hoxline/case_growth/discovery.py @@ -201,8 +201,34 @@ def semantic_fingerprint(repo_relative_path: str, raw: bytes) -> str: def repo_origin(repo_path: Path) -> str: - lines = git_lines(repo_path, ["remote", "get-url", "origin"]) - return lines[0] if lines else "UNKNOWN" + # Inspect only the value physically stored in this repository. `git remote + # get-url` applies ambient url.*.insteadOf rewriting and can conceal a + # non-canonical stored owner. + try: + result = subprocess.run( + [ + "git", + "-C", + str(repo_path), + "config", + "--local", + "--null", + "--get-all", + "remote.origin.url", + ], + check=True, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + except (OSError, subprocess.CalledProcessError): + return "UNKNOWN" + values = result.stdout.split("\0") + if values and values[-1] == "": + values.pop() + stripped = [value.strip() for value in values] + return stripped[0] if len(stripped) == 1 and stripped[0] else "UNKNOWN" def file_sha256(path: Path) -> str | None: diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index 0824e21..a864c92 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -17,6 +17,7 @@ import yaml from .case_growth.collector import verify_selected_source_checkout +from .case_growth.discovery import repo_origin from .demo import ( BLOCKED_CLAIM_FAMILIES, PRODUCT, @@ -1123,7 +1124,7 @@ def _owned_authority_binding(manifest: dict[str, Any], repo_root: Path) -> dict[ ): if repo.parent != org_root or not (repo / ".git").exists(): raise ReviewBlocked("required authority repository is missing") - origin = _canonical_origin(_git(repo, "remote", "get-url", "origin")) + origin = _canonical_origin(repo_origin(repo)) if origin != _canonical_origin(CANONICAL_ORIGINS[repo_name]): raise ReviewBlocked("authority repository origin is not canonical") selection_errors = verify_selected_source_checkout(org_root, repo_name) diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index d90955d..ee52c9a 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -4,6 +4,7 @@ import copy import io import json +import os import shutil import subprocess import sys @@ -27,7 +28,7 @@ verify_selected_source_checkout, verify_case_growth_snapshot, ) -from hoxline.case_growth.discovery import REPO_NAMES +from hoxline.case_growth.discovery import REPO_NAMES, repo_origin from hoxline.case_growth.render import render_case_growth_markdown from hoxline.cli import main @@ -212,6 +213,39 @@ def issues(value: str) -> list[str]: merge_ref_attack = workflow.replace(f"ref: {pr_head}", "ref: ${{ github.sha }}", 1) self.assertNotEqual([], issues(merge_ref_attack)) + def test_repo_origin_rejects_ambient_instead_of_laundering(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + repo = Path(temp_dir) / "repo" + repo.mkdir() + _git(repo, "init") + stored_origin = "C:/hostile/local-hoxline" + canonical = "https://github.com/HawkinsOperations/hoxline.git" + _git(repo, "remote", "add", "origin", stored_origin) + hostile_env = { + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": f"url.{canonical}.insteadOf", + "GIT_CONFIG_VALUE_0": stored_origin, + } + with mock.patch.dict(os.environ, hostile_env, clear=False): + effective = _git(repo, "remote", "get-url", "origin") + self.assertEqual(effective, canonical) + self.assertEqual(repo_origin(repo), stored_origin) + + def test_repo_origin_rejects_empty_duplicate_in_both_orders(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + repo = Path(temp_dir) / "repo" + repo.mkdir() + _git(repo, "init") + canonical = "https://github.com/HawkinsOperations/hoxline.git" + _git(repo, "config", "--add", "remote.origin.url", canonical) + _git(repo, "config", "--add", "remote.origin.url", "") + self.assertEqual(repo_origin(repo), "UNKNOWN") + + _git(repo, "config", "--unset-all", "remote.origin.url") + _git(repo, "config", "--add", "remote.origin.url", "") + _git(repo, "config", "--add", "remote.origin.url", canonical) + self.assertEqual(repo_origin(repo), "UNKNOWN") + @classmethod def setUpClass(cls) -> None: cls.index = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") From 17f03c4acf3bb111e625d5955d2ec619fb1ea198 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 21:54:40 -0500 Subject: [PATCH 62/73] chore(case-growth): refresh atomic convergence pair --- .../case-growth/current-case-growth-index.json | 16 ++++++++-------- .../case-growth/current-case-growth-index.md | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 6d1e154..5316c90 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T01:35:00Z", + "generated_at": "2026-07-24T02:52:42Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "c3bc35693f88f4cb907c2c6c53281631dcbdb6b8", + "current_observed_head_sha": "404b667f364db666cdadf40edd5a32829e3d1499", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -51,7 +51,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", "source_observed_head_sha": "c2790ab007279faff6130a60dc68818343c255ee", - "current_observed_head_sha": "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", + "current_observed_head_sha": "e0360e6a5011e28417855677fbd4b331ae156966", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "03d8b909b2304a9a570129a559f9cd44e98dc2b1", + "current_observed_head_sha": "073e8c0899d306b6d845d48bc47cbd7f5f78c3c7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "eb43cd3ce4ad6668dbc9a175bf3de714fcb40c63", + "current_observed_head_sha": "77b2bbde6c0678e69c16ee91d5b2614cd5fd167b", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "ecc73ff1c762c1aa6d8ebfca8b2fcb2124935e48", + "current_observed_head_sha": "71dcdabb0cab9bc8a08ec838a91482c5b50aaea7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "current_observed_head_sha": "9a8ff9bfbf523ea61e7ce2c2142ec40c5a192c09", + "current_observed_head_sha": "dbfd4a195ce00cb4e4b96494f114f7f487bb029a", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "d1d899ea7366fff5380712893f16fcba8938b4275563507e5fd2a12c9b832514" + "reproducibility_sha256": "42896ab095426dc5705b16de02764db166187e1975a52eb5c765ebc4e662de7a" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index e0c4a48..e67a154 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T01:35:00Z` +Generated: `2026-07-24T02:52:42Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` -Reproducibility SHA-256: `d1d899ea7366fff5380712893f16fcba8938b4275563507e5fd2a12c9b832514` +Reproducibility SHA-256: `42896ab095426dc5705b16de02764db166187e1975a52eb5c765ebc4e662de7a` ## Summary From 1cb97efc45ffe753389105645c25ed7fe57cf9e5 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 22:04:56 -0500 Subject: [PATCH 63/73] fix(hoxline): isolate Git authority from ambient state --- src/hoxline/case_growth/collector.py | 3 ++ src/hoxline/case_growth/discovery.py | 17 ++++++++++ src/hoxline/review_engine.py | 5 ++- tests/test_case_growth_index_v0.py | 51 ++++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/hoxline/case_growth/collector.py b/src/hoxline/case_growth/collector.py index b4d8f87..90c809e 100644 --- a/src/hoxline/case_growth/collector.py +++ b/src/hoxline/case_growth/collector.py @@ -26,6 +26,7 @@ repo_origin, repo_relative, resolve_repo_paths, + sanitized_git_env, semantic_fingerprint, ) @@ -241,6 +242,7 @@ def _git_output(repo: Path, *args: str) -> str | None: check=False, capture_output=True, text=True, + env=sanitized_git_env(), ) except OSError: return None @@ -254,6 +256,7 @@ def _is_ancestor(repo: Path, ancestor: str, descendant: str) -> bool: check=False, capture_output=True, text=True, + env=sanitized_git_env(), ) except OSError: return False diff --git a/src/hoxline/case_growth/discovery.py b/src/hoxline/case_growth/discovery.py index 5359f7e..92c47f5 100644 --- a/src/hoxline/case_growth/discovery.py +++ b/src/hoxline/case_growth/discovery.py @@ -2,6 +2,7 @@ import json import hashlib +import os import re import subprocess from pathlib import Path @@ -35,6 +36,17 @@ class _UniqueKeyLoader(yaml.SafeLoader): pass +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 + + def _construct_unique_mapping(loader: _UniqueKeyLoader, node: yaml.MappingNode, deep: bool = False) -> dict[Any, Any]: pairs = loader.construct_pairs(node, deep=deep) result: dict[Any, Any] = {} @@ -87,6 +99,7 @@ def git_lines(repo_path: Path, args: list[str]) -> list[str]: text=True, encoding="utf-8", errors="replace", + env=sanitized_git_env(), ) except (OSError, subprocess.CalledProcessError): return [] @@ -135,6 +148,7 @@ def git_commit_exists(repo_path: Path, sha: str) -> bool: check=False, capture_output=True, text=True, + env=sanitized_git_env(), ) except OSError: return False @@ -152,6 +166,7 @@ def git_blob_sha256(repo_path: Path, sha: str, repo_relative_path: str) -> str | ["git", "-C", str(repo_path), "show", f"{sha}:{normalized_path}"], check=False, capture_output=True, + env=sanitized_git_env(), ) except OSError: return None @@ -179,6 +194,7 @@ def git_blob_identity(repo_path: Path, sha: str, repo_relative_path: str) -> tup ["git", "-C", str(repo_path), "cat-file", "blob", blob[0]], check=False, capture_output=True, + env=sanitized_git_env(), ) except OSError: return None @@ -221,6 +237,7 @@ def repo_origin(repo_path: Path) -> str: text=True, encoding="utf-8", errors="replace", + env=sanitized_git_env(), ) except (OSError, subprocess.CalledProcessError): return "UNKNOWN" diff --git a/src/hoxline/review_engine.py b/src/hoxline/review_engine.py index a864c92..66debef 100644 --- a/src/hoxline/review_engine.py +++ b/src/hoxline/review_engine.py @@ -17,7 +17,7 @@ import yaml from .case_growth.collector import verify_selected_source_checkout -from .case_growth.discovery import repo_origin +from .case_growth.discovery import repo_origin, sanitized_git_env from .demo import ( BLOCKED_CLAIM_FAMILIES, PRODUCT, @@ -978,6 +978,7 @@ def _git(repo: Path, *args: str) -> str: check=False, capture_output=True, text=True, + env=sanitized_git_env(), ) if result.returncode != 0: raise ReviewBlocked("authority repository Git identity could not be verified") @@ -3187,6 +3188,7 @@ def verify_tracked_vocabulary(repo_root: Path) -> list[str]: ], check=False, capture_output=True, + env=sanitized_git_env(), ) except OSError as exc: return [f"tracked vocabulary inventory failed: {exc}"] @@ -3197,6 +3199,7 @@ def verify_tracked_vocabulary(repo_root: Path) -> list[str]: ["git", "-C", str(root), "ls-files", "--deleted", "-z"], check=False, capture_output=True, + env=sanitized_git_env(), ) if deleted_result.returncode != 0: detail = deleted_result.stderr.decode("utf-8", errors="replace").strip() diff --git a/tests/test_case_growth_index_v0.py b/tests/test_case_growth_index_v0.py index ee52c9a..25951fb 100644 --- a/tests/test_case_growth_index_v0.py +++ b/tests/test_case_growth_index_v0.py @@ -28,7 +28,7 @@ verify_selected_source_checkout, verify_case_growth_snapshot, ) -from hoxline.case_growth.discovery import REPO_NAMES, repo_origin +from hoxline.case_growth.discovery import REPO_NAMES, repo_dirty, repo_head_sha, repo_origin from hoxline.case_growth.render import render_case_growth_markdown from hoxline.cli import main @@ -240,12 +240,59 @@ def test_repo_origin_rejects_empty_duplicate_in_both_orders(self) -> None: _git(repo, "config", "--add", "remote.origin.url", canonical) _git(repo, "config", "--add", "remote.origin.url", "") self.assertEqual(repo_origin(repo), "UNKNOWN") - _git(repo, "config", "--unset-all", "remote.origin.url") _git(repo, "config", "--add", "remote.origin.url", "") _git(repo, "config", "--add", "remote.origin.url", canonical) self.assertEqual(repo_origin(repo), "UNKNOWN") + def test_git_identity_ignores_ambient_repository_and_index_redirection(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + base = Path(temp_dir) + target = base / "target" + decoy = base / "decoy" + for repo, origin, content in ( + (target, "C:/hostile/target", "target\n"), + (decoy, "https://github.com/HawkinsOperations/hoxline.git", "decoy\n"), + ): + repo.mkdir() + _git(repo, "init") + _git(repo, "config", "user.name", "Hoxline Test") + _git(repo, "config", "user.email", "hoxline-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") + decoy_head = _git(decoy, "rev-parse", "HEAD") + self.assertNotEqual(target_head, decoy_head) + with mock.patch.dict( + 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, + ): + self.assertEqual(repo_origin(target), "C:/hostile/target") + self.assertEqual(repo_head_sha(target), target_head) + + clean_index = base / "clean-index" + shutil.copy2(target / ".git" / "index", clean_index) + (target / "tracked.txt").write_text("changed\n", encoding="utf-8") + _git(target, "add", "tracked.txt") + (target / "tracked.txt").write_text("target\n", encoding="utf-8") + with mock.patch.dict( + os.environ, + {"GIT_INDEX_FILE": str(clean_index)}, + clear=False, + ): + self.assertTrue(repo_dirty(target)) + @classmethod def setUpClass(cls) -> None: cls.index = build_case_growth_index(FIXTURE_ROOT, generated_at="2026-06-27T00:00:00Z") From b2e0b4602a0348901914221b7547e0732ccc750b Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 22:18:29 -0500 Subject: [PATCH 64/73] ci(hoxline): select hardened seven-source revisions --- .github/workflows/ci.yml | 22 +++++++++++----------- tests/test_action_contract.py | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd5c1dd..8ada8d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: b87acd4487ff779bc0cb8b96c783ada0d712a0ed + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: b93fb586fccf80dd40c714281086fe2a940506a7 HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: b87acd4487ff779bc0cb8b96c783ada0d712a0ed + ref: b93fb586fccf80dd40c714281086fe2a940506a7 path: org/.github fetch-depth: 0 persist-credentials: false @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-detections - ref: 015a9dc359b3fa4c4853cd1b18372c3e44eacec9 + ref: 48de1240a8437432f35aee77654b568704627a68 path: org/hawkinsoperations-detections fetch-depth: 0 persist-credentials: false @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation - ref: 03d8b909b2304a9a570129a559f9cd44e98dc2b1 + ref: 5840c517bbacf845acdbbcca10c5c09953dbb595 path: org/hawkinsoperations-validation fetch-depth: 0 persist-credentials: false @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: 26ae0fd0112d2123f90197b9aa5078db0d35db65 + ref: bc489b6c1549288909a6d19cd96f3fa731059ce9 path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -76,7 +76,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 5ee3fe553e78ca35b3a2422b2231c627c1ee1f99 + ref: 0c16f2a97ae73910633daf6193a51918f26d1798 path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -101,12 +101,12 @@ jobs: "hoxline", ] immutable = { - ".github": "b87acd4487ff779bc0cb8b96c783ada0d712a0ed", - "hawkinsoperations-detections": "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", - "hawkinsoperations-validation": "03d8b909b2304a9a570129a559f9cd44e98dc2b1", - "hawkinsoperations-platform": "26ae0fd0112d2123f90197b9aa5078db0d35db65", + ".github": "b93fb586fccf80dd40c714281086fe2a940506a7", + "hawkinsoperations-detections": "48de1240a8437432f35aee77654b568704627a68", + "hawkinsoperations-validation": "5840c517bbacf845acdbbcca10c5c09953dbb595", + "hawkinsoperations-platform": "bc489b6c1549288909a6d19cd96f3fa731059ce9", "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "hawkinsoperations-website": "5ee3fe553e78ca35b3a2422b2231c627c1ee1f99", + "hawkinsoperations-website": "0c16f2a97ae73910633daf6193a51918f26d1798", "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 3959d82..a357df1 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -43,18 +43,18 @@ def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) reviewed_refs = { - "b87acd4487ff779bc0cb8b96c783ada0d712a0ed", - "015a9dc359b3fa4c4853cd1b18372c3e44eacec9", - "03d8b909b2304a9a570129a559f9cd44e98dc2b1", - "26ae0fd0112d2123f90197b9aa5078db0d35db65", + "b93fb586fccf80dd40c714281086fe2a940506a7", + "48de1240a8437432f35aee77654b568704627a68", + "5840c517bbacf845acdbbcca10c5c09953dbb595", + "bc489b6c1549288909a6d19cd96f3fa731059ce9", "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "5ee3fe553e78ca35b3a2422b2231c627c1ee1f99", + "0c16f2a97ae73910633daf6193a51918f26d1798", } assert len(refs) == 6 assert set(refs) == reviewed_refs assert ( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: " - "b87acd4487ff779bc0cb8b96c783ada0d712a0ed" + "b93fb586fccf80dd40c714281086fe2a940506a7" ) in workflow assert "ref: main" not in workflow assert "ref: feature/" not in workflow From 55cf0f5afa38bb40e7480a61725196c5d6603cc2 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 22:20:36 -0500 Subject: [PATCH 65/73] chore(case-growth): seal hardened source pair --- .../current-case-growth-index.json | 38 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 5316c90..89f7745 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T02:52:42Z", + "generated_at": "2026-07-24T03:18:49Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "404b667f364db666cdadf40edd5a32829e3d1499", + "current_observed_head_sha": "b93fb586fccf80dd40c714281086fe2a940506a7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -51,7 +51,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", "source_observed_head_sha": "c2790ab007279faff6130a60dc68818343c255ee", - "current_observed_head_sha": "e0360e6a5011e28417855677fbd4b331ae156966", + "current_observed_head_sha": "48de1240a8437432f35aee77654b568704627a68", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "073e8c0899d306b6d845d48bc47cbd7f5f78c3c7", + "current_observed_head_sha": "5840c517bbacf845acdbbcca10c5c09953dbb595", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "77b2bbde6c0678e69c16ee91d5b2614cd5fd167b", + "current_observed_head_sha": "bc489b6c1549288909a6d19cd96f3fa731059ce9", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "71dcdabb0cab9bc8a08ec838a91482c5b50aaea7", + "current_observed_head_sha": "d963195a9a73a0d3c355489472ea8a9a3c001a65", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -193,7 +193,7 @@ "source_semantic_fingerprint_sha256": "5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-website", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-website", - "repository_dirty_observed": false, + "repository_dirty_observed": true, "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", @@ -209,20 +209,20 @@ "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "source_observed_head_sha": "b0945c4c3db7312f7d50a51d4abab9e65ac454da", - "current_observed_head_sha": "dbfd4a195ce00cb4e4b96494f114f7f487bb029a", + "source_commit_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", + "source_observed_head_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", + "current_observed_head_sha": "b2e0b4602a0348901914221b7547e0732ccc750b", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "src/hoxline/case_growth/collector.py", "authoritative_path": "src/hoxline/case_growth/collector.py", - "authoritative_git_blob_sha": "b4d8f87864c4039fa756fe13da1b6a2371ca6e14", - "source_git_blob_sha": "b4d8f87864c4039fa756fe13da1b6a2371ca6e14", - "source_file_sha256": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", - "authoritative_content_fingerprint": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", - "source_semantic_fingerprint_sha256": "e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428", + "authoritative_git_blob_sha": "90c809e9ccd3764d14903f647980c81341bb3c42", + "source_git_blob_sha": "90c809e9ccd3764d14903f647980c81341bb3c42", + "source_file_sha256": "98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c", + "authoritative_content_fingerprint": "98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c", + "source_semantic_fingerprint_sha256": "98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c", "canonical_origin": "github.com/hawkinsoperations/hoxline", "observed_origin": "github.com/hawkinsoperations/hoxline", "repository_dirty_observed": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5", + "source_manifest_digest": "5b7604e896e479e38ab3667c4c21ed1ab720788d40a65136edf375f96135c917", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -293,9 +293,9 @@ "path": "hawkinsoperations-website", "exists": true, "branch": "feature/hoxline-case-growth-convergence-v1", - "dirty": false, + "dirty": true, "authority_boundary": "route/rendering surface only; not proof authority", - "files_scanned": 273 + "files_scanned": 274 }, { "repo": "hoxline", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "42896ab095426dc5705b16de02764db166187e1975a52eb5c765ebc4e662de7a" + "reproducibility_sha256": "1713167b4863ae8cde338852af5c056a5b4b3023d9a474f6c2a14fe7a6e9e1f4" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index e67a154..c90e623 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T02:52:42Z` +Generated: `2026-07-24T03:18:49Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `b2f9c2d2d886fcf3be3c0c29d5da32231652a893dcd06d5d9f8f088f08bb4fb5` -Reproducibility SHA-256: `42896ab095426dc5705b16de02764db166187e1975a52eb5c765ebc4e662de7a` +Source manifest digest: `5b7604e896e479e38ab3667c4c21ed1ab720788d40a65136edf375f96135c917` +Reproducibility SHA-256: `1713167b4863ae8cde338852af5c056a5b4b3023d9a474f6c2a14fe7a6e9e1f4` ## Summary @@ -41,7 +41,7 @@ Reproducibility SHA-256: `42896ab095426dc5705b16de02764db166187e1975a52eb5c765eb | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `651a43a4dfe0776605d5bd7b85ef4f8381c42b64` | `577dd64c65324f45248ce36e4550cba04b9ff056` | `8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | -| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `b0945c4c3db7312f7d50a51d4abab9e65ac454da` | `b4d8f87864c4039fa756fe13da1b6a2371ca6e14` | `e5f564a3a6e57ae1e35b0818def99093f8398684d5eca51ec13d7a9a34ec5428` | `CURRENT` | +| `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `1cb97efc45ffe753389105645c25ed7fe57cf9e5` | `90c809e9ccd3764d14903f647980c81341bb3c42` | `98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c` | `CURRENT` | ## Convergence Findings From 63ad111b62cca2d9e779534421e6737e6cc36732 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 22:41:17 -0500 Subject: [PATCH 66/73] ci(hoxline): select pair-containing convergence sources --- .github/workflows/ci.yml | 14 +++++++------- tests/test_action_contract.py | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ada8d4..adc4dcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: b93fb586fccf80dd40c714281086fe2a940506a7 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: cf2769104bfe585b9115eb16bef799f2ee31ff21 HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: b93fb586fccf80dd40c714281086fe2a940506a7 + ref: cf2769104bfe585b9115eb16bef799f2ee31ff21 path: org/.github fetch-depth: 0 persist-credentials: false @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: bc489b6c1549288909a6d19cd96f3fa731059ce9 + ref: eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11 path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -76,7 +76,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: 0c16f2a97ae73910633daf6193a51918f26d1798 + ref: a4c91273da4770b79b1e71a844b71a9e981bc606 path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -101,12 +101,12 @@ jobs: "hoxline", ] immutable = { - ".github": "b93fb586fccf80dd40c714281086fe2a940506a7", + ".github": "cf2769104bfe585b9115eb16bef799f2ee31ff21", "hawkinsoperations-detections": "48de1240a8437432f35aee77654b568704627a68", "hawkinsoperations-validation": "5840c517bbacf845acdbbcca10c5c09953dbb595", - "hawkinsoperations-platform": "bc489b6c1549288909a6d19cd96f3fa731059ce9", + "hawkinsoperations-platform": "eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11", "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "hawkinsoperations-website": "0c16f2a97ae73910633daf6193a51918f26d1798", + "hawkinsoperations-website": "a4c91273da4770b79b1e71a844b71a9e981bc606", "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index a357df1..791864c 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -43,18 +43,18 @@ def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) reviewed_refs = { - "b93fb586fccf80dd40c714281086fe2a940506a7", + "cf2769104bfe585b9115eb16bef799f2ee31ff21", "48de1240a8437432f35aee77654b568704627a68", "5840c517bbacf845acdbbcca10c5c09953dbb595", - "bc489b6c1549288909a6d19cd96f3fa731059ce9", + "eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11", "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "0c16f2a97ae73910633daf6193a51918f26d1798", + "a4c91273da4770b79b1e71a844b71a9e981bc606", } assert len(refs) == 6 assert set(refs) == reviewed_refs assert ( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: " - "b93fb586fccf80dd40c714281086fe2a940506a7" + "cf2769104bfe585b9115eb16bef799f2ee31ff21" ) in workflow assert "ref: main" not in workflow assert "ref: feature/" not in workflow From 0dcade7d11ede78e07f707805ad3034458ff948e Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 23 Jul 2026 23:05:47 -0500 Subject: [PATCH 67/73] ci(hoxline): exercise rewritten-observation hardening --- .github/workflows/ci.yml | 4 ++-- tests/test_action_contract.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adc4dcf..95aac7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11 + ref: 6ab0e6029fb76168f05150819a44e537397ca631 path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -104,7 +104,7 @@ jobs: ".github": "cf2769104bfe585b9115eb16bef799f2ee31ff21", "hawkinsoperations-detections": "48de1240a8437432f35aee77654b568704627a68", "hawkinsoperations-validation": "5840c517bbacf845acdbbcca10c5c09953dbb595", - "hawkinsoperations-platform": "eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11", + "hawkinsoperations-platform": "6ab0e6029fb76168f05150819a44e537397ca631", "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", "hawkinsoperations-website": "a4c91273da4770b79b1e71a844b71a9e981bc606", "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 791864c..14c3033 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -46,7 +46,7 @@ def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> "cf2769104bfe585b9115eb16bef799f2ee31ff21", "48de1240a8437432f35aee77654b568704627a68", "5840c517bbacf845acdbbcca10c5c09953dbb595", - "eaf9f45c7c4fc3f79cf952eecfb813de7a6cce11", + "6ab0e6029fb76168f05150819a44e537397ca631", "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", "a4c91273da4770b79b1e71a844b71a9e981bc606", } From dd13842f4e59851f6ddaf0c45fe470570dc322ed Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 09:17:58 -0500 Subject: [PATCH 68/73] chore(hoxline): regenerate repaired case-growth pair --- .../current-case-growth-index.json | 88 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 +- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 89f7745..10228a0 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T03:18:49Z", + "generated_at": "2026-07-24T14:15:03Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "b93fb586fccf80dd40c714281086fe2a940506a7", + "current_observed_head_sha": "eee90989aade24090bf7ad309fc41467347ad5e7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -49,20 +49,20 @@ "repository": "hawkinsoperations-detections", "authority_role": "detection source truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "c2790ab007279faff6130a60dc68818343c255ee", - "source_observed_head_sha": "c2790ab007279faff6130a60dc68818343c255ee", - "current_observed_head_sha": "48de1240a8437432f35aee77654b568704627a68", + "source_commit_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", + "source_observed_head_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", + "current_observed_head_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "detections/DETECTION_PROMOTION_MATRIX.yml", "authoritative_path": "detections/DETECTION_PROMOTION_MATRIX.yml", - "authoritative_git_blob_sha": "e1d040134f139d2598c527c3584e1859e9c9553d", - "source_git_blob_sha": "e1d040134f139d2598c527c3584e1859e9c9553d", - "source_file_sha256": "b73825c8655d7df62cac7737b08b3c95ec72b12c42bb6e3d265f7e74d0068e67", - "authoritative_content_fingerprint": "7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3", - "source_semantic_fingerprint_sha256": "7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3", + "authoritative_git_blob_sha": "3123d4f2a9dabdaa31d7e1d20698a17e7854491c", + "source_git_blob_sha": "3123d4f2a9dabdaa31d7e1d20698a17e7854491c", + "source_file_sha256": "c25219cfa2d097ab7aa8ed22164d6ebcf807ecd6aea94c858de09b5c91ebbcfe", + "authoritative_content_fingerprint": "d5214ccc882ac68eb56b3018b3c37799b7c5851e37c7b76e34cda570f0e061f1", + "source_semantic_fingerprint_sha256": "d5214ccc882ac68eb56b3018b3c37799b7c5851e37c7b76e34cda570f0e061f1", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-detections", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-detections", "repository_dirty_observed": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "5840c517bbacf845acdbbcca10c5c09953dbb595", + "current_observed_head_sha": "646e0d1b7e9f03ca84e7d4e71406659230828b06", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -115,7 +115,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "bc489b6c1549288909a6d19cd96f3fa731059ce9", + "current_observed_head_sha": "3fd0dba09ccc46be9d3aab3fff006771304821dd", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -147,7 +147,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "042a918ad4a8473cd5abcfd575072fc094639682", "source_observed_head_sha": "042a918ad4a8473cd5abcfd575072fc094639682", - "current_observed_head_sha": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", + "current_observed_head_sha": "77b7874dd753369792330508fa3438cf397cd050", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "d963195a9a73a0d3c355489472ea8a9a3c001a65", + "current_observed_head_sha": "ca1c4adb7591a0d780934a70604613ae173fbb27", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -193,7 +193,7 @@ "source_semantic_fingerprint_sha256": "5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-website", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-website", - "repository_dirty_observed": true, + "repository_dirty_observed": false, "authority_source_dirty": false, "source_freshness_state": "CURRENT", "snapshot_freshness_state": "CURRENT", @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", "source_observed_head_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", - "current_observed_head_sha": "b2e0b4602a0348901914221b7547e0732ccc750b", + "current_observed_head_sha": "0dcade7d11ede78e07f707805ad3034458ff948e", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "5b7604e896e479e38ab3667c4c21ed1ab720788d40a65136edf375f96135c917", + "source_manifest_digest": "3ca3a84be0f35e80260a48e92181bbcad88b3726b74afc24e134900e0ff0b858", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -293,7 +293,7 @@ "path": "hawkinsoperations-website", "exists": true, "branch": "feature/hoxline-case-growth-convergence-v1", - "dirty": true, + "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", "files_scanned": 274 }, @@ -418,7 +418,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "separate proof scope before any live-cloud or public route approval", "evidence_confidence": "HIGH", "notes": [ @@ -499,7 +499,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T17:49:24-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -599,7 +599,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -642,7 +642,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -685,7 +685,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -728,7 +728,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -771,7 +771,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -814,7 +814,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -857,7 +857,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -933,7 +933,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T06:37:51-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording", "evidence_confidence": "HIGH", "notes": [ @@ -1013,7 +1013,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T06:37:51-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1089,7 +1089,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "proof-card or public reviewer route only after separate human-approved proof scope", "evidence_confidence": "HIGH", "notes": [ @@ -1179,7 +1179,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion", "evidence_confidence": "HIGH", "notes": [ @@ -1271,7 +1271,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T06:37:51-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1321,7 +1321,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1364,7 +1364,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1407,7 +1407,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1464,7 +1464,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "captured cross-source corroboration evidence under separate proof scope", "evidence_confidence": "MEDIUM", "notes": [ @@ -1523,7 +1523,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T18:33:32-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "create source package under separate source-authoring approval", "evidence_confidence": "LOW", "notes": [ @@ -1586,7 +1586,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "proof and runtime remain separate; any traffic, delivery, signal, route-proof, public-safe, or production wording requires separate approval", "evidence_confidence": "MEDIUM", "notes": [ @@ -1640,7 +1640,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-22T23:40:46-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "preserve hero baseline while successor HO-DET-001 remains the current reviewed source package", "evidence_confidence": "MEDIUM", "notes": [ @@ -1751,7 +1751,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1835,7 +1835,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1914,7 +1914,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1993,7 +1993,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-23T05:15:50-05:00", + "last_updated": "2026-07-24T08:54:44-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved identity runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "1713167b4863ae8cde338852af5c056a5b4b3023d9a474f6c2a14fe7a6e9e1f4" + "reproducibility_sha256": "0c161a7f04a6b3ec43ec14d18adce47a23f55118319afa15e680cf2bb6660797" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index c90e623..f35ab82 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T03:18:49Z` +Generated: `2026-07-24T14:15:03Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `5b7604e896e479e38ab3667c4c21ed1ab720788d40a65136edf375f96135c917` -Reproducibility SHA-256: `1713167b4863ae8cde338852af5c056a5b4b3023d9a474f6c2a14fe7a6e9e1f4` +Source manifest digest: `3ca3a84be0f35e80260a48e92181bbcad88b3726b74afc24e134900e0ff0b858` +Reproducibility SHA-256: `0c161a7f04a6b3ec43ec14d18adce47a23f55118319afa15e680cf2bb6660797` ## Summary @@ -36,7 +36,7 @@ Reproducibility SHA-256: `1713167b4863ae8cde338852af5c056a5b4b3023d9a474f6c2a14f | Repository | Authority role | Authority path | Observed head | Git blob | Semantic fingerprint | Source freshness | | --- | --- | --- | --- | --- | --- | --- | | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `6e6763a81d6af09c2e4588462b56117ce82c2f88` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | -| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `c2790ab007279faff6130a60dc68818343c255ee` | `e1d040134f139d2598c527c3584e1859e9c9553d` | `7ca1a5d76ed11de08832dbd074d120295d71d5935be3f10ed3d874d0198bf3e3` | `CURRENT` | +| `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `f8bc0a0925113ca815bf5692081b5216162cc918` | `3123d4f2a9dabdaa31d7e1d20698a17e7854491c` | `d5214ccc882ac68eb56b3018b3c37799b7c5851e37c7b76e34cda570f0e061f1` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `ebf52f7c6c9b78de767272cc56fccdc584f5c4e0` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | | `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `651a43a4dfe0776605d5bd7b85ef4f8381c42b64` | `577dd64c65324f45248ce36e4550cba04b9ff056` | `8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | From 52867ee7e332dba3cab4d2c3e308d636ed5bb610 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 10:19:47 -0500 Subject: [PATCH 69/73] ci(hoxline): resolve reviewed siblings from one manifest --- .github/workflows/ci.yml | 71 ++++++++++++++++++++++++++++------- tests/test_action_contract.py | 20 +++++----- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95aac7a..01f5678 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: cf2769104bfe585b9115eb16bef799f2ee31ff21 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: f6d88acac2cbb2ab95482bb4b09dc70bd939a06f HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,16 +31,54 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: cf2769104bfe585b9115eb16bef799f2ee31ff21 + ref: f6d88acac2cbb2ab95482bb4b09dc70bd939a06f path: org/.github fetch-depth: 0 persist-credentials: false + - name: Resolve reviewed sibling revisions + id: reviewed + shell: bash + run: | + python - <<'PY' + from pathlib import Path + import json + import os + + manifest_path = Path("org/.github/governance/CONVERGENCE_SOURCE_MANIFEST.json") + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + entries = { + item["repository"]: item + for item in manifest.get("repositories", []) + if isinstance(item, dict) and isinstance(item.get("repository"), str) + } + required = { + "hawkinsoperations-detections": "detections", + "hawkinsoperations-validation": "validation", + "hawkinsoperations-platform": "platform", + "hawkinsoperations-proof": "proof", + "hawkinsoperations-website": "website", + } + if set(entries) != { + ".github", + *required, + "hoxline", + }: + raise SystemExit(f"reviewed manifest must contain exactly seven repositories; got {sorted(entries)}") + with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output: + for repository, output_name in required.items(): + revision = entries[repository].get("revision") + if not isinstance(revision, str) or len(revision) != 40: + raise SystemExit(f"{repository} lacks an immutable reviewed revision") + int(revision, 16) + output.write(f"{output_name}={revision}\n") + PY + - name: Check out detection authority uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-detections - ref: 48de1240a8437432f35aee77654b568704627a68 + ref: ${{ steps.reviewed.outputs.detections }} path: org/hawkinsoperations-detections fetch-depth: 0 persist-credentials: false @@ -49,7 +87,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-validation - ref: 5840c517bbacf845acdbbcca10c5c09953dbb595 + ref: ${{ steps.reviewed.outputs.validation }} path: org/hawkinsoperations-validation fetch-depth: 0 persist-credentials: false @@ -58,7 +96,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-platform - ref: 6ab0e6029fb76168f05150819a44e537397ca631 + ref: ${{ steps.reviewed.outputs.platform }} path: org/hawkinsoperations-platform fetch-depth: 0 persist-credentials: false @@ -67,7 +105,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-proof - ref: 79d4bff2dccb24c2b29a68f4a3be7a1e916414b4 + ref: ${{ steps.reviewed.outputs.proof }} path: org/hawkinsoperations-proof fetch-depth: 0 persist-credentials: false @@ -76,7 +114,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/hawkinsoperations-website - ref: a4c91273da4770b79b1e71a844b71a9e981bc606 + ref: ${{ steps.reviewed.outputs.website }} path: org/hawkinsoperations-website fetch-depth: 0 persist-credentials: false @@ -87,10 +125,21 @@ jobs: run: | python - <<'PY' from pathlib import Path + import json import os import subprocess root = Path("..").resolve() + manifest = json.loads( + (root / ".github" / "governance" / "CONVERGENCE_SOURCE_MANIFEST.json").read_text( + encoding="utf-8" + ) + ) + reviewed = { + item["repository"]: item.get("revision") + for item in manifest["repositories"] + if item["repository"] not in {".github", "hoxline"} + } expected = [ ".github", "hawkinsoperations-detections", @@ -101,12 +150,8 @@ jobs: "hoxline", ] immutable = { - ".github": "cf2769104bfe585b9115eb16bef799f2ee31ff21", - "hawkinsoperations-detections": "48de1240a8437432f35aee77654b568704627a68", - "hawkinsoperations-validation": "5840c517bbacf845acdbbcca10c5c09953dbb595", - "hawkinsoperations-platform": "6ab0e6029fb76168f05150819a44e537397ca631", - "hawkinsoperations-proof": "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "hawkinsoperations-website": "a4c91273da4770b79b1e71a844b71a9e981bc606", + ".github": os.environ["HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA"], + **reviewed, "hoxline": os.environ["HAWKINS_HOXLINE_EVENT_SHA"], } actual = sorted(path.name for path in root.iterdir() if (path / ".git").exists()) diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 14c3033..4803447 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -42,20 +42,18 @@ def test_docs_scan_cleanly() -> None: def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> None: workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) - reviewed_refs = { - "cf2769104bfe585b9115eb16bef799f2ee31ff21", - "48de1240a8437432f35aee77654b568704627a68", - "5840c517bbacf845acdbbcca10c5c09953dbb595", - "6ab0e6029fb76168f05150819a44e537397ca631", - "79d4bff2dccb24c2b29a68f4a3be7a1e916414b4", - "a4c91273da4770b79b1e71a844b71a9e981bc606", - } - assert len(refs) == 6 - assert set(refs) == reviewed_refs + reviewed_manifest = "governance/CONVERGENCE_SOURCE_MANIFEST.json" + command_center_ref = "f6d88acac2cbb2ab95482bb4b09dc70bd939a06f" + assert refs == [command_center_ref] assert ( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: " - "cf2769104bfe585b9115eb16bef799f2ee31ff21" + f"{command_center_ref}" ) in workflow + assert reviewed_manifest in workflow + assert "Resolve reviewed sibling revisions" in workflow + for output_name in ("detections", "validation", "platform", "proof", "website"): + assert f"steps.reviewed.outputs.{output_name}" in workflow + assert '**reviewed' in workflow assert "ref: main" not in workflow assert "ref: feature/" not in workflow assert "persist-credentials: false" in workflow From 6f56e7c49a866aa6ba3df02cfeff130e00a8af13 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 10:43:55 -0500 Subject: [PATCH 70/73] chore(hoxline): refresh converged case growth pair --- .../current-case-growth-index.json | 32 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 ++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index 10228a0..c17be19 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T14:15:03Z", + "generated_at": "2026-07-24T15:39:07Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -19,7 +19,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "eee90989aade24090bf7ad309fc41467347ad5e7", + "current_observed_head_sha": "5c6127f5acc1031bae2528df3ce1f197da882100", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -51,7 +51,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", "source_observed_head_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", - "current_observed_head_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", + "current_observed_head_sha": "9e01f43fb350de3370f8c01a323dcdcdf2e33147", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -83,7 +83,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "646e0d1b7e9f03ca84e7d4e71406659230828b06", + "current_observed_head_sha": "677b704150b0f5f333c27913dd481b4be6a78ab7", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -113,20 +113,20 @@ "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "source_observed_head_sha": "651a43a4dfe0776605d5bd7b85ef4f8381c42b64", - "current_observed_head_sha": "3fd0dba09ccc46be9d3aab3fff006771304821dd", + "source_commit_sha": "a667c4de8b478fe165c3ec612e642bbd5d879492", + "source_observed_head_sha": "a667c4de8b478fe165c3ec612e642bbd5d879492", + "current_observed_head_sha": "4716d7e65525425be4f70127cdc7f7d3de3a7b9e", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", "authoritative_path": "contracts/public-status-source-contract-v1.json", - "authoritative_git_blob_sha": "577dd64c65324f45248ce36e4550cba04b9ff056", - "source_git_blob_sha": "577dd64c65324f45248ce36e4550cba04b9ff056", - "source_file_sha256": "6825af757ac6ae3a8980200a70a93cb60ae9dc41058468c7d7601d1a976c0c52", - "authoritative_content_fingerprint": "8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95", - "source_semantic_fingerprint_sha256": "8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95", + "authoritative_git_blob_sha": "5bc7b79b77150413893f3d8147ae211b98d50e5b", + "source_git_blob_sha": "5bc7b79b77150413893f3d8147ae211b98d50e5b", + "source_file_sha256": "3a1cdc74a86230fe6567ddaed396ed45662e9b4efef7e398ae522a3e018317e4", + "authoritative_content_fingerprint": "f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01", + "source_semantic_fingerprint_sha256": "f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "repository_dirty_observed": false, @@ -179,7 +179,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "ca1c4adb7591a0d780934a70604613ae173fbb27", + "current_observed_head_sha": "0e7cb554ee3fd5519142006246201e7b15f0c9b5", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -211,7 +211,7 @@ "resolved_ref": "feature/hoxline-case-growth-convergence-v1", "source_commit_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", "source_observed_head_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", - "current_observed_head_sha": "0dcade7d11ede78e07f707805ad3034458ff948e", + "current_observed_head_sha": "52867ee7e332dba3cab4d2c3e308d636ed5bb610", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "3ca3a84be0f35e80260a48e92181bbcad88b3726b74afc24e134900e0ff0b858", + "source_manifest_digest": "f295e5268283eeed408bd42c80c38a70b438a166d7882a75d3fd2b9bd79f957e", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "0c161a7f04a6b3ec43ec14d18adce47a23f55118319afa15e680cf2bb6660797" + "reproducibility_sha256": "a8b5ec50636e7b62f9a0c11e0316d62b8009b3d18e4a52faf9b72578e059f8a1" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index f35ab82..a778316 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T14:15:03Z` +Generated: `2026-07-24T15:39:07Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `3ca3a84be0f35e80260a48e92181bbcad88b3726b74afc24e134900e0ff0b858` -Reproducibility SHA-256: `0c161a7f04a6b3ec43ec14d18adce47a23f55118319afa15e680cf2bb6660797` +Source manifest digest: `f295e5268283eeed408bd42c80c38a70b438a166d7882a75d3fd2b9bd79f957e` +Reproducibility SHA-256: `a8b5ec50636e7b62f9a0c11e0316d62b8009b3d18e4a52faf9b72578e059f8a1` ## Summary @@ -38,7 +38,7 @@ Reproducibility SHA-256: `0c161a7f04a6b3ec43ec14d18adce47a23f55118319afa15e680cf | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `6e6763a81d6af09c2e4588462b56117ce82c2f88` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `f8bc0a0925113ca815bf5692081b5216162cc918` | `3123d4f2a9dabdaa31d7e1d20698a17e7854491c` | `d5214ccc882ac68eb56b3018b3c37799b7c5851e37c7b76e34cda570f0e061f1` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `ebf52f7c6c9b78de767272cc56fccdc584f5c4e0` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `651a43a4dfe0776605d5bd7b85ef4f8381c42b64` | `577dd64c65324f45248ce36e4550cba04b9ff056` | `8381676118717c267039bb783a4540544a82e20c3e5df943bfc3eeae05cc5d95` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `a667c4de8b478fe165c3ec612e642bbd5d879492` | `5bc7b79b77150413893f3d8147ae211b98d50e5b` | `f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | | `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `1cb97efc45ffe753389105645c25ed7fe57cf9e5` | `90c809e9ccd3764d14903f647980c81341bb3c42` | `98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c` | `CURRENT` | From cd797da491f07b9a0130278d7245f51962dd82c0 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 10:44:19 -0500 Subject: [PATCH 71/73] ci(hoxline): consume reviewed stabilization manifest --- .github/workflows/ci.yml | 4 ++-- tests/test_action_contract.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01f5678..5b9421f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: f6d88acac2cbb2ab95482bb4b09dc70bd939a06f + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 5c6127f5acc1031bae2528df3ce1f197da882100 HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: f6d88acac2cbb2ab95482bb4b09dc70bd939a06f + ref: 5c6127f5acc1031bae2528df3ce1f197da882100 path: org/.github fetch-depth: 0 persist-credentials: false diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 4803447..150f5ab 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -43,7 +43,7 @@ def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) reviewed_manifest = "governance/CONVERGENCE_SOURCE_MANIFEST.json" - command_center_ref = "f6d88acac2cbb2ab95482bb4b09dc70bd939a06f" + command_center_ref = "5c6127f5acc1031bae2528df3ce1f197da882100" assert refs == [command_center_ref] assert ( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: " From 6bdf263144f2f5465ec232c996666c3c00d16945 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 6 Sep 2026 09:13:14 -0500 Subject: [PATCH 72/73] Add source-executed quality review and owner replay checks --- README.md | 22 +++++++ src/hoxline/cli.py | 20 ++++++ src/hoxline/detection_quality.py | 109 +++++++++++++++++++++++++++++++ tests/test_detection_quality.py | 84 ++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 src/hoxline/detection_quality.py create mode 100644 tests/test_detection_quality.py diff --git a/README.md b/README.md index 2552612..08dad06 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,28 @@ See `docs/demo/HOXLINE_ONE_COMMAND_REVIEWER_DEMO_V0.md` for the design contract ## Reusable Review Engine +### Source-executed detection quality + +```powershell +python -B -m hoxline detection-quality --repo-root .. --detections-ref --validation-ref --platform-ref +``` + +This review executes six canonical detection predicates through platform's +validation-owned handoff. It renders observed confusion matrices and real rule +mutation outcomes from the existing controlled corpus. Add `--format json` for +the owner report, or `--verify ` to reexecute and compare a +saved report. Exact clean source, validation, and platform heads are required. +Altered metrics, nested authority additions, source hashes, and AI approval or +closure fields cannot pass replay merely by changing a report checksum. + +Validation owns behavior and metrics; platform delegates; Hoxline renders. +Mutants remain in memory, survivors remain visible, and parser errors never count +as kills. AI authors candidate engineering labor and tests. +AI does not approve disposition, case closure, public-safe status, or proof promotion. This offline +predicate path does not establish backend parity, endpoint execution, runtime +signal, or production detection quality. The owning validation workflow runs the +same path on GitHub-hosted Linux and Windows. + Use the reusable manifest-driven engine when you want the same deterministic ProofOps loop behind a machine-checkable artifact manifest: ```powershell diff --git a/src/hoxline/cli.py b/src/hoxline/cli.py index 159661a..037abb2 100644 --- a/src/hoxline/cli.py +++ b/src/hoxline/cli.py @@ -34,6 +34,18 @@ def main(argv: list[str] | None = None) -> int: parser = _build_parser() args = parser.parse_args(argv) + if args.command == "detection-quality": + from .detection_quality import QualityReviewError, compare_replay, read_report, render_review, run_review + try: + report = run_review(Path(args.repo_root), args.detections_ref, args.validation_ref, args.platform_ref) + if args.verify: + compare_replay(read_report(Path(args.verify).read_text(encoding="utf-8")), report) + print(json.dumps(report, indent=2, sort_keys=True) if args.format == "json" else render_review(report), end="\n") + return 0 + except (QualityReviewError, OSError) as exc: + print(json.dumps({"status": "BLOCKED", "error": str(exc), "human_review_required": True, "ai_disposition_authority": False})) + return 2 + if args.command == "gauntlet" and args.gauntlet_command == "run": return _run_gauntlet(args) if args.command == "gauntlet" and args.gauntlet_command == "metrics": @@ -77,6 +89,14 @@ def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(prog="hoxline") subparsers = parser.add_subparsers(dest="command") + quality_parser = subparsers.add_parser("detection-quality", help="replay validation-owned source mutation quality through platform") + quality_parser.add_argument("--repo-root", required=True) + quality_parser.add_argument("--detections-ref", required=True) + quality_parser.add_argument("--validation-ref", required=True) + quality_parser.add_argument("--platform-ref", required=True) + quality_parser.add_argument("--format", choices=("json", "markdown"), default="markdown") + quality_parser.add_argument("--verify", help="compare a saved owner report with freshly executed source/corpus") + gauntlet_parser = subparsers.add_parser("gauntlet", help="run Hoxline Gauntlet workflows") gauntlet_subparsers = gauntlet_parser.add_subparsers(dest="gauntlet_command") diff --git a/src/hoxline/detection_quality.py b/src/hoxline/detection_quality.py new file mode 100644 index 0000000..c13fee9 --- /dev/null +++ b/src/hoxline/detection_quality.py @@ -0,0 +1,109 @@ +"""Review validation-owned source execution through the platform handoff.""" +from __future__ import annotations + +import json +import os +from pathlib import Path +import re +import subprocess +import sys +import unicodedata +from typing import Any + + +class QualityReviewError(ValueError): + pass + + +def canonical(value: Any) -> str: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True, allow_nan=False) + + +def read_report(text: str) -> dict: + def unique(pairs): + result, seen = {}, set() + for key, value in pairs: + normalized = unicodedata.normalize("NFKC", key).casefold() + if normalized in seen: + raise QualityReviewError("ambiguous duplicate report keys") + seen.add(normalized); result[key] = value + return result + def invalid(value): + raise QualityReviewError("non-finite report number") + try: + report = json.loads(text, object_pairs_hook=unique, parse_constant=invalid) + except (ValueError, RecursionError) as exc: + raise QualityReviewError("malformed or ambiguous quality report") from exc + if not isinstance(report, dict): + raise QualityReviewError("quality report must be an object") + return report + + +def compare_replay(supplied: dict, observed: dict) -> None: + # Compare the whole reexecuted owner result, not supplied checksums/metrics. + if canonical(supplied) != canonical(observed): + raise QualityReviewError("report differs from fresh validation-owned replay") + + +def _environment() -> dict: + result = {key: value for key, value in os.environ.items() if not key.casefold().startswith("git_")} + result.update(GIT_NO_REPLACE_OBJECTS="1", GIT_TERMINAL_PROMPT="0") + return result + + +def _platform_identity(root: Path, revision: str) -> None: + if not re.fullmatch(r"[0-9a-f]{40}", revision): + raise QualityReviewError("platform requires an exact commit SHA") + def git(*args): + result = subprocess.run(["git", "-C", str(root), *args], capture_output=True, text=True, env=_environment()) + if result.returncode: + raise QualityReviewError("platform source identity unavailable") + return result.stdout.strip() + if Path(git("rev-parse", "--show-toplevel")).resolve() != root.resolve(): + raise QualityReviewError("platform root is not its Git top level") + if git("config", "--local", "--get-all", "remote.origin.url") not in ( + "https://github.com/HawkinsOperations/hawkinsoperations-platform.git", + "https://github.com/HawkinsOperations/hawkinsoperations-platform", + "git@github.com:HawkinsOperations/hawkinsoperations-platform.git", + ): + raise QualityReviewError("platform repository identity mismatch") + if git("rev-parse", "HEAD") != revision or git("status", "--porcelain", "--untracked-files=no"): + raise QualityReviewError("platform must be clean at the requested head") + + +def run_review(org_root: Path, detections_ref: str, validation_ref: str, platform_ref: str) -> dict: + org_root = org_root.resolve() + platform = org_root / "hawkinsoperations-platform" + _platform_identity(platform, platform_ref) + result = subprocess.run([ + sys.executable, "-E", "-B", str(platform / "scripts/ho_factory.py"), + "detection-quality-run", "--repo-root", str(org_root), + "--detections-ref", detections_ref, "--validation-ref", validation_ref, + ], cwd=platform, capture_output=True, text=True, env=_environment()) + if result.returncode: + raise QualityReviewError("platform rejected the validation-owned quality run") + report = read_report(result.stdout) + _platform_identity(platform, platform_ref) + if report.get("schema") != "hawkinsoperations-detection-quality-v1" or report.get("owner_repository") != "HawkinsOperations/hawkinsoperations-validation" or report.get("status") != "PASS": + raise QualityReviewError("unexpected validation owner, schema, or execution status") + return report + + +def render_review(report: dict) -> str: + metrics, mutations = report["quality"], report["mutation_metrics"] + lines = ["# Detection source quality review", "", + f"Executed: {report['detections_evaluated']} source rules; {metrics['events_evaluated']} controlled events.", + f"Observed: TP={metrics['true_positive']} TN={metrics['true_negative']} FP={metrics['false_positive']} FN={metrics['false_negative']}; precision={metrics['precision']} recall={metrics['recall']} F1={metrics['f1']}.", + f"Mutations: {mutations['generated']} generated; {mutations['killed']} killed; {mutations['survived']} survived; {mutations['errors']} errors; score={mutations['mutation_score']}.", "", + "| Detection | Events | FP / FN | Killed / generated | Survived |", + "|---|---:|---:|---:|---:|"] + for package in report["packages"]: + q, m = package["quality"], package["mutation_metrics"] + lines.append(f"| {package['detection_id']} | {q['events_evaluated']} | {q['false_positive']} / {q['false_negative']} | {m['killed']} / {m['generated']} | {m['survived']} |") + lines.extend(["", "Validation owns the results; platform delegates execution; Hoxline renders this review.", + "AI engineering labor authored candidate changes and tests. AI cannot set expected outcomes, approve disposition, close cases, or promote proof.", + "Survivors need corpus review; they are not silently counted as kills. Parser errors are not kills.", + f"Ceiling: {report['boundary']['proof_ceiling']}; NOT_PUBLIC_SAFE; human review required.", + "Does not prove SIEM backend parity, endpoint execution, runtime signal, production quality, or public-safe proof.", + f"Replay SHA-256: `{report['replay_sha256']}`."]) + return "\n".join(lines) + "\n" diff --git a/tests/test_detection_quality.py b/tests/test_detection_quality.py new file mode 100644 index 0000000..a91e90e --- /dev/null +++ b/tests/test_detection_quality.py @@ -0,0 +1,84 @@ +"""Reject report/AI authority tampering against fresh owner replay.""" +from __future__ import annotations + +import copy +import hashlib +import json +from pathlib import Path +import unittest +from unittest.mock import patch + +from hoxline.detection_quality import QualityReviewError, canonical, compare_replay, read_report, run_review, _platform_identity + + +class QualityReviewTests(unittest.TestCase): + def observed(self): + return {"schema": "hawkinsoperations-detection-quality-v1", "owner_repository": "HawkinsOperations/hawkinsoperations-validation", + "status": "PASS", "quality": {"true_positive": 1, "false_negative": 1}, + "mutation_metrics": {"generated": 2, "killed": 1, "survived": 1, "errors": 0}, + "boundary": {"runtime_active": False, "signal_observed": False, "public_safe_status": "NOT_PUBLIC_SAFE", "human_review_required": True, + "ai_disposition_authority": False, "case_closure_authority": False, "proof_promotion_authority": False}, + "sources": [{"repository": "HawkinsOperations/hawkinsoperations-validation", "head": "a" * 40}], + "inputs": [{"rule_sha256": "b" * 64, "corpus_sha256": "c" * 64}], "replay_sha256": "d" * 64} + + def test_exact_replay_and_json_order_are_deterministic(self): + observed = self.observed() + compare_replay(read_report(json.dumps(observed)), observed) + compare_replay(dict(reversed(list(observed.items()))), observed) + + def test_authority_metrics_identity_and_nested_laundering_rejected(self): + attacks = [ + ("runtime_active", True), ("signal_observed", True), ("public_safe_status", "PUBLIC_SAFE"), + ("human_review_required", False), ("ai_disposition_authority", True), + ("case_closure_authority", True), ("proof_promotion_authority", True), + ("ai_disposition_authority", 0), + ] + observed = self.observed() + for field, value in attacks: + candidate = copy.deepcopy(observed); candidate["boundary"][field] = value + with self.subTest(field=field, value=value), self.assertRaises(QualityReviewError): compare_replay(candidate, observed) + mutations = [ + lambda c: c.update(owner_repository="HawkinsOperations/hoxline"), + lambda c: c.update(status="APPROVED"), + lambda c: c["sources"][0].update(head="e" * 40), + lambda c: c["inputs"][0].update(rule_sha256="e" * 64), + lambda c: c["inputs"][0].update(corpus_sha256="e" * 64), + lambda c: c["quality"].update(false_negative=0), + lambda c: c["mutation_metrics"].update(killed=2, survived=0), + lambda c: c.update(ai_notes={"nested": [{"approval": True}]}), + lambda c: c.update(private_evidence={"path": "../private"}), + lambda c: c.update(closed_cases=1), + lambda c: c.update(human_approval=True), + lambda c: c.update(proof_ceiling="PUBLIC_PROOF_SAFE"), + ] + for mutation in mutations: + candidate = copy.deepcopy(observed); mutation(candidate) + candidate["replay_sha256"] = hashlib.sha256(canonical(candidate).encode()).hexdigest() + with self.subTest(candidate=candidate), self.assertRaises(QualityReviewError): compare_replay(candidate, observed) + + def test_duplicate_unicode_and_nonfinite_json_fail_closed(self): + for text in ('{"a": 1, "a": 2}', '{"A": 1, "a": 2}', '{"n": NaN}', '{"n": Infinity}', '[]', '{broken'): + with self.subTest(text=text), self.assertRaises(QualityReviewError): read_report(text) + + def test_mutable_ref_never_executes_platform(self): + for ref in ("main", "HEAD", "../main", "a" * 39, "a" * 41, "a" * 40 + ":path"): + with self.subTest(ref=ref), patch("hoxline.detection_quality.subprocess.run") as run: + with self.assertRaises(QualityReviewError): _platform_identity(Path("platform"), ref) + run.assert_not_called() + + def test_failed_platform_execution_cannot_produce_success(self): + with patch("hoxline.detection_quality._platform_identity"), patch("hoxline.detection_quality.subprocess.run") as run: + run.return_value.returncode = 1 + run.return_value.stdout = json.dumps(self.observed()) + with self.assertRaises(QualityReviewError): run_review(Path("org"), "a" * 40, "b" * 40, "c" * 40) + + def test_wrong_owner_rejected_even_when_subprocess_returns_zero(self): + with patch("hoxline.detection_quality._platform_identity"), patch("hoxline.detection_quality.subprocess.run") as run: + run.return_value.returncode = 0 + report = self.observed(); report["owner_repository"] = "HawkinsOperations/hawkinsoperations-website" + run.return_value.stdout = json.dumps(report) + with self.assertRaises(QualityReviewError): run_review(Path("org"), "a" * 40, "b" * 40, "c" * 40) + + +if __name__ == "__main__": + unittest.main() From be2cd4f08aa5fe04fff89435de47e20abef9f96e Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 6 Sep 2026 09:19:22 -0500 Subject: [PATCH 73/73] Refresh case-growth authority observations and immutable CI source set --- .github/workflows/ci.yml | 4 +- .../current-case-growth-index.json | 70 +++++++++---------- .../case-growth/current-case-growth-index.md | 8 +-- tests/test_action_contract.py | 2 +- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b9421f..2f1abc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: hoxline-trust-boundaries: runs-on: ubuntu-latest env: - HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 5c6127f5acc1031bae2528df3ce1f197da882100 + HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: 18a2e00505f4df4e895bf8d9ecc97052d4d03a61 HAWKINS_HOXLINE_EVENT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} strategy: fail-fast: false @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: HawkinsOperations/.github - ref: 5c6127f5acc1031bae2528df3ce1f197da882100 + ref: 18a2e00505f4df4e895bf8d9ecc97052d4d03a61 path: org/.github fetch-depth: 0 persist-credentials: false diff --git a/examples/case-growth/current-case-growth-index.json b/examples/case-growth/current-case-growth-index.json index c17be19..2966e11 100644 --- a/examples/case-growth/current-case-growth-index.json +++ b/examples/case-growth/current-case-growth-index.json @@ -1,6 +1,6 @@ { "schema_version": "case-growth-index-v1", - "generated_at": "2026-07-24T15:39:07Z", + "generated_at": "2026-09-06T14:17:30Z", "repo_root": "HawkinsOperations", "proof_ceiling": "CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY", "historical_snapshot": false, @@ -16,10 +16,10 @@ { "repository": ".github", "authority_role": "org command-center routing", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "resolved_ref": "agent/source-executed-mutation-factory", "source_commit_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", "source_observed_head_sha": "6e6763a81d6af09c2e4588462b56117ce82c2f88", - "current_observed_head_sha": "5c6127f5acc1031bae2528df3ce1f197da882100", + "current_observed_head_sha": "18a2e00505f4df4e895bf8d9ecc97052d4d03a61", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -48,10 +48,10 @@ { "repository": "hawkinsoperations-detections", "authority_role": "detection source truth", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "resolved_ref": "agent/source-executed-mutation-factory", "source_commit_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", "source_observed_head_sha": "f8bc0a0925113ca815bf5692081b5216162cc918", - "current_observed_head_sha": "9e01f43fb350de3370f8c01a323dcdcdf2e33147", + "current_observed_head_sha": "56cad4f726c0d3988c9464693ce7c127c8f63cad", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -80,10 +80,10 @@ { "repository": "hawkinsoperations-validation", "authority_role": "controlled validation truth", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "resolved_ref": "agent/source-executed-mutation-factory", "source_commit_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", "source_observed_head_sha": "ebf52f7c6c9b78de767272cc56fccdc584f5c4e0", - "current_observed_head_sha": "677b704150b0f5f333c27913dd481b4be6a78ab7", + "current_observed_head_sha": "c8f3f08995d78fa103115610e5c6a2b85ee61701", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -112,21 +112,21 @@ { "repository": "hawkinsoperations-platform", "authority_role": "platform contract truth", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", - "source_commit_sha": "a667c4de8b478fe165c3ec612e642bbd5d879492", - "source_observed_head_sha": "a667c4de8b478fe165c3ec612e642bbd5d879492", - "current_observed_head_sha": "4716d7e65525425be4f70127cdc7f7d3de3a7b9e", + "resolved_ref": "agent/source-executed-mutation-factory", + "source_commit_sha": "13fd8d88b179572bc53759f371d9b968ba3c94d4", + "source_observed_head_sha": "13fd8d88b179572bc53759f371d9b968ba3c94d4", + "current_observed_head_sha": "13fd8d88b179572bc53759f371d9b968ba3c94d4", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, "revision_scope": "content_addressed_authority", "source_path": "contracts/public-status-source-contract-v1.json", "authoritative_path": "contracts/public-status-source-contract-v1.json", - "authoritative_git_blob_sha": "5bc7b79b77150413893f3d8147ae211b98d50e5b", - "source_git_blob_sha": "5bc7b79b77150413893f3d8147ae211b98d50e5b", - "source_file_sha256": "3a1cdc74a86230fe6567ddaed396ed45662e9b4efef7e398ae522a3e018317e4", - "authoritative_content_fingerprint": "f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01", - "source_semantic_fingerprint_sha256": "f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01", + "authoritative_git_blob_sha": "70507d53fcc5ce7c89be401d38fd3e43776bc55d", + "source_git_blob_sha": "70507d53fcc5ce7c89be401d38fd3e43776bc55d", + "source_file_sha256": "34b2801c10de7c269220010797a0a1a7c8eae8e26213243940a9b0e2ca3d8cea", + "authoritative_content_fingerprint": "a4e8183b128cefaed1252168136a64b919dddee50f256d3aa6a681bac77ca841", + "source_semantic_fingerprint_sha256": "a4e8183b128cefaed1252168136a64b919dddee50f256d3aa6a681bac77ca841", "canonical_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "observed_origin": "github.com/hawkinsoperations/hawkinsoperations-platform", "repository_dirty_observed": false, @@ -176,10 +176,10 @@ { "repository": "hawkinsoperations-website", "authority_role": "rendering-only public status contract", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "resolved_ref": "agent/source-executed-mutation-factory", "source_commit_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", "source_observed_head_sha": "5856f8e69527b5e61c3953b88a2ad4c088268655", - "current_observed_head_sha": "0e7cb554ee3fd5519142006246201e7b15f0c9b5", + "current_observed_head_sha": "a18a196d4336e088f94c0cec0305c8ae8ebbe0cf", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -208,10 +208,10 @@ { "repository": "hoxline", "authority_role": "case-growth and fixture-review product truth", - "resolved_ref": "feature/hoxline-case-growth-convergence-v1", + "resolved_ref": "agent/source-executed-mutation-factory", "source_commit_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", "source_observed_head_sha": "1cb97efc45ffe753389105645c25ed7fe57cf9e5", - "current_observed_head_sha": "52867ee7e332dba3cab4d2c3e308d636ed5bb610", + "current_observed_head_sha": "6bdf263144f2f5465ec232c996666c3c00d16945", "source_observation_kind": "reviewed_immutable_commit", "source_parent_sha": null, "self_referential": false, @@ -238,7 +238,7 @@ "next_legal_action": "none; preserve source ownership" } ], - "source_manifest_digest": "f295e5268283eeed408bd42c80c38a70b438a166d7882a75d3fd2b9bd79f957e", + "source_manifest_digest": "e19616241e7b762e1fa8462a5b3d8cbf086e603154ea50c21f371df3e46a7167", "contradictions": [], "drift": [], "next_legal_action": "none; current source-controlled inputs converge", @@ -247,7 +247,7 @@ "repo": ".github", "path": ".github", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "org metadata and reviewer routing only; not proof authority", "files_scanned": 37 @@ -256,7 +256,7 @@ "repo": "hawkinsoperations-detections", "path": "hawkinsoperations-detections", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "source package and source status authority only", "files_scanned": 93 @@ -265,16 +265,16 @@ "repo": "hawkinsoperations-validation", "path": "hawkinsoperations-validation", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "controlled validation authority only", - "files_scanned": 202 + "files_scanned": 204 }, { "repo": "hawkinsoperations-platform", "path": "hawkinsoperations-platform", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "platform runtime-candidate, collector, receipt, and ledger contract authority only", "files_scanned": 121 @@ -292,7 +292,7 @@ "repo": "hawkinsoperations-website", "path": "hawkinsoperations-website", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "route/rendering surface only; not proof authority", "files_scanned": 274 @@ -301,10 +301,10 @@ "repo": "hoxline", "path": "hoxline", "exists": true, - "branch": "feature/hoxline-case-growth-convergence-v1", + "branch": "agent/source-executed-mutation-factory", "dirty": false, "authority_boundary": "product metrics and Hoxline Gauntlet artifact authority only", - "files_scanned": 181 + "files_scanned": 182 } ], "repo_slot_accuracy": { @@ -499,7 +499,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-24T08:54:44-05:00", + "last_updated": "2026-09-06T08:56:27-05:00", "next_gate": "proof-record-specific human review before any public-safe, runtime, or signal promotion", "evidence_confidence": "HIGH", "notes": [ @@ -933,7 +933,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-24T08:54:44-05:00", + "last_updated": "2026-09-06T08:46:02-05:00", "next_gate": "separate runtime receipt and proof review before any runtime, signal, public-safe, production, or approval wording", "evidence_confidence": "HIGH", "notes": [ @@ -1013,7 +1013,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-24T08:54:44-05:00", + "last_updated": "2026-09-06T08:46:02-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -1179,7 +1179,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-24T08:54:44-05:00", + "last_updated": "2026-09-06T08:46:02-05:00", "next_gate": "blocked until separate runtime or signal evidence review supports any runtime, routed-telemetry, public-safe, production, autonomous SOC, or disposition-authority promotion", "evidence_confidence": "HIGH", "notes": [ @@ -1271,7 +1271,7 @@ "case_state": "BLOCKED_WAITING_NEXT_GATE", "metrics_available": false, "metrics_refs": [], - "last_updated": "2026-07-24T08:54:44-05:00", + "last_updated": "2026-09-06T08:46:02-05:00", "next_gate": "reviewer validates source and controlled-test validation before any separately approved private runtime gate", "evidence_confidence": "HIGH", "notes": [ @@ -2024,5 +2024,5 @@ "website_rendering_treated_as_proof": false, "green_ci_treated_as_approval": false }, - "reproducibility_sha256": "a8b5ec50636e7b62f9a0c11e0316d62b8009b3d18e4a52faf9b72578e059f8a1" + "reproducibility_sha256": "324aeb3bf7e5f9c091408db65a053a0a22567c32ffec30f828cede509672428b" } diff --git a/examples/case-growth/current-case-growth-index.md b/examples/case-growth/current-case-growth-index.md index a778316..4a0d9f6 100644 --- a/examples/case-growth/current-case-growth-index.md +++ b/examples/case-growth/current-case-growth-index.md @@ -1,12 +1,12 @@ # Hoxline Case Growth Index v1 -Generated: `2026-07-24T15:39:07Z` +Generated: `2026-09-06T14:17:30Z` Proof ceiling: `CASE_GROWTH_INDEX_CONTROLLED_REPO_AGGREGATION_ONLY` Repo-slot accuracy: `seven expected repo slots evaluated; seven present local repos scanned` Historical snapshot: `false` Current authority: `true` -Source manifest digest: `f295e5268283eeed408bd42c80c38a70b438a166d7882a75d3fd2b9bd79f957e` -Reproducibility SHA-256: `a8b5ec50636e7b62f9a0c11e0316d62b8009b3d18e4a52faf9b72578e059f8a1` +Source manifest digest: `e19616241e7b762e1fa8462a5b3d8cbf086e603154ea50c21f371df3e46a7167` +Reproducibility SHA-256: `324aeb3bf7e5f9c091408db65a053a0a22567c32ffec30f828cede509672428b` ## Summary @@ -38,7 +38,7 @@ Reproducibility SHA-256: `a8b5ec50636e7b62f9a0c11e0316d62b8009b3d18e4a52faf9b725 | `.github` | `org command-center routing` | `governance/COMMAND_CENTER_INVARIANTS.json` | `6e6763a81d6af09c2e4588462b56117ce82c2f88` | `623e3f9e813b0599618a7df41dee1c9a40fb7a18` | `45cfa989c3f742b546f7f8a497632b43c928029bcaa09e80e04c7c894dba660c` | `CURRENT` | | `hawkinsoperations-detections` | `detection source truth` | `detections/DETECTION_PROMOTION_MATRIX.yml` | `f8bc0a0925113ca815bf5692081b5216162cc918` | `3123d4f2a9dabdaa31d7e1d20698a17e7854491c` | `d5214ccc882ac68eb56b3018b3c37799b7c5851e37c7b76e34cda570f0e061f1` | `CURRENT` | | `hawkinsoperations-validation` | `controlled validation truth` | `validation/VALIDATION_REGISTRY.yml` | `ebf52f7c6c9b78de767272cc56fccdc584f5c4e0` | `6fac3ac3d048c3ef687faaf5ebef1b04e846aad1` | `de88ff4a621256cae51ec597a2cbb73f172d16c9d6e5109bcc42ff9a3b461cc3` | `CURRENT` | -| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `a667c4de8b478fe165c3ec612e642bbd5d879492` | `5bc7b79b77150413893f3d8147ae211b98d50e5b` | `f0f16d909e06b4cf67f347c675b999de3f7f10ff7032fbd350dc5a1f54266a01` | `CURRENT` | +| `hawkinsoperations-platform` | `platform contract truth` | `contracts/public-status-source-contract-v1.json` | `13fd8d88b179572bc53759f371d9b968ba3c94d4` | `70507d53fcc5ce7c89be401d38fd3e43776bc55d` | `a4e8183b128cefaed1252168136a64b919dddee50f256d3aa6a681bac77ca841` | `CURRENT` | | `hawkinsoperations-proof` | `proof and claim-boundary truth` | `proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` | `042a918ad4a8473cd5abcfd575072fc094639682` | `623b93e6e5ac141684978ff4dcdc6ed1dec55678` | `68e5de4749bfe34a6677331f6116fab82987c88e999ae14eaf706e9b33536170` | `CURRENT` | | `hawkinsoperations-website` | `rendering-only public status contract` | `schemas/public-status-v0.schema.json` | `5856f8e69527b5e61c3953b88a2ad4c088268655` | `1f10e8c0948635eda720905c1f1e7476ef63bf3c` | `5d04c8fbce269352e341798f28afdc29720fd2ea97b80d63884cfdb32e893a11` | `CURRENT` | | `hoxline` | `case-growth and fixture-review product truth` | `src/hoxline/case_growth/collector.py` | `1cb97efc45ffe753389105645c25ed7fe57cf9e5` | `90c809e9ccd3764d14903f647980c81341bb3c42` | `98277ac2a8bb2b85b7a7cd93863cd9dc3f8aaa9d89cf31038afa6310bc98992c` | `CURRENT` | diff --git a/tests/test_action_contract.py b/tests/test_action_contract.py index 150f5ab..f68d64a 100644 --- a/tests/test_action_contract.py +++ b/tests/test_action_contract.py @@ -43,7 +43,7 @@ def test_ci_uses_immutable_sibling_revisions_and_all_required_trust_checks() -> workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") refs = re.findall(r"^\s+ref:\s+([0-9a-f]{40})\s*$", workflow, flags=re.MULTILINE) reviewed_manifest = "governance/CONVERGENCE_SOURCE_MANIFEST.json" - command_center_ref = "5c6127f5acc1031bae2528df3ce1f197da882100" + command_center_ref = "18a2e00505f4df4e895bf8d9ecc97052d4d03a61" assert refs == [command_center_ref] assert ( "HAWKINS_COMMAND_CENTER_IMMUTABLE_OBSERVED_SHA: "