{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://dev_workspace/schemas/policy_profiles/test_only_hardening_pr_merge_v1.schema.json",
  "title": "test_only_hardening_pr_merge_v1",
  "description": "Profile-as-code contract for test-only hardening PR merge lifecycle (task-2553+16). Defines the merge_ready_predicate (AND-gate), hold_conditions (engine auto-computed), deterministic merge_method resolution (9-R.6), single-token auth invariant (9-R.5), bounded Gemini-thread resolve (9-R.1), review-state fail-closed allowlist (9-R.2), liveness budget (9-R.3), and the pre/post-merge irreversibility split (9-R / task-2553+12).",
  "type": "object",
  "required": [
    "profile_id",
    "version",
    "target",
    "merge_ready_predicate",
    "hold_conditions",
    "merge_method",
    "auth",
    "gemini_thread_resolve",
    "liveness_budget",
    "post_merge_steps",
    "irreversibility_policy",
    "scope_invariants"
  ],
  "additionalProperties": false,
  "properties": {
    "profile_id": { "const": "test_only_hardening_pr_merge_v1" },
    "version": { "type": "string", "pattern": "^v[0-9]+$" },
    "task_id": { "const": "task-2553+16" },
    "target": {
      "type": "object",
      "required": ["repo", "pr_number", "base_ref", "sanctioned_head_sha"],
      "additionalProperties": false,
      "properties": {
        "repo": { "type": "string" },
        "pr_number": { "type": "integer" },
        "base_ref": { "type": "string" },
        "sanctioned_head_sha": { "type": "string", "pattern": "^[0-9a-f]{40}$" }
      }
    },
    "merge_ready_predicate": {
      "type": "object",
      "description": "AND-gate. ALL conditions must evaluate true for BOT merge to be permitted.",
      "required": [
        "mergeable_eq",
        "merge_state_status_in",
        "review_decision_allowlist",
        "ci_all_success",
        "unresolved_review_threads_eq",
        "effective_diff_test_only",
        "production_byte0",
        "head_sha_eq_sanctioned"
      ],
      "additionalProperties": false,
      "properties": {
        "mergeable_eq": { "const": "MERGEABLE" },
        "merge_state_status_in": {
          "type": "array",
          "description": "9-R.2: only CLEAN is affirmative-safe. HAS_HOOKS retained as schema-allowable but profile narrows to CLEAN.",
          "items": { "enum": ["CLEAN", "HAS_HOOKS"] },
          "minItems": 1
        },
        "review_decision_allowlist": {
          "type": "array",
          "description": "9-R.2 fail-closed allowlist. Only APPROVED or empty-string permitted; empty only affirmative-safe with CLEAN + CI all SUCCESS + 0 unresolved threads.",
          "items": { "enum": ["APPROVED", ""] }
        },
        "ci_all_success": { "const": true },
        "unresolved_review_threads_eq": { "const": 0 },
        "effective_diff_test_only": { "const": true },
        "production_byte0": {
          "type": "object",
          "required": ["path", "baseline_sha256"],
          "additionalProperties": false,
          "properties": {
            "path": { "type": "string" },
            "baseline_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
          }
        },
        "head_sha_eq_sanctioned": { "const": true }
      }
    },
    "hold_conditions": {
      "type": "object",
      "description": "Engine auto-computed. Any true => HOLD, merge call 0.",
      "required": [
        "predicate_any_fail",
        "production_diff",
        "forbidden_touch",
        "bot_merge_unavailable",
        "post_merge_fail",
        "codex_high_or_critical",
        "credential_or_permission_expansion",
        "critical7"
      ],
      "additionalProperties": false,
      "properties": {
        "predicate_any_fail": { "const": true },
        "production_diff": { "const": true },
        "forbidden_touch": { "const": true },
        "bot_merge_unavailable": { "const": true },
        "post_merge_fail": { "const": true },
        "codex_high_or_critical": { "const": true },
        "credential_or_permission_expansion": { "const": true },
        "critical7": { "const": true }
      }
    },
    "merge_method": {
      "type": "object",
      "description": "9-R.6 deterministic resolution against repo allow_* flags.",
      "required": ["resolution_priority", "rebase_only_policy", "default_preference"],
      "additionalProperties": false,
      "properties": {
        "resolution_priority": {
          "type": "array",
          "items": { "enum": ["squash", "merge"] },
          "minItems": 2,
          "maxItems": 2
        },
        "rebase_only_policy": { "const": "HOLD" },
        "default_preference": { "const": "squash" }
      }
    },
    "auth": {
      "type": "object",
      "description": "9-R.5 single-token fail-closed invariant.",
      "required": ["credential_kind", "env_key", "token_prefix_expected", "owner_pat_forbidden", "on_absent_or_expired"],
      "additionalProperties": false,
      "properties": {
        "credential_kind": { "const": "github_app_installation_token" },
        "env_key": { "const": "BOT_GITHUB_TOKEN" },
        "token_prefix_expected": { "const": "ghs_" },
        "owner_pat_forbidden": { "const": true },
        "on_absent_or_expired": { "const": "HOLD" }
      }
    },
    "gemini_thread_resolve": {
      "type": "object",
      "description": "9-R.1 hard-bound single resolve.",
      "required": ["max_resolve_calls", "require_exact_unresolved_count", "require_unique_blocking_identification", "only_when_sole_remaining_blocker", "no_other_mutations"],
      "additionalProperties": false,
      "properties": {
        "max_resolve_calls": { "const": 1 },
        "require_exact_unresolved_count": { "const": 1 },
        "require_unique_blocking_identification": { "const": true },
        "only_when_sole_remaining_blocker": { "const": true },
        "no_other_mutations": { "const": true }
      }
    },
    "liveness_budget": {
      "type": "object",
      "description": "9-R.3 anti-spin budget for persistent BLOCKED / CI-pending.",
      "required": ["max_wait_minutes", "poll_interval_seconds", "max_polls", "on_budget_exceeded"],
      "additionalProperties": false,
      "properties": {
        "max_wait_minutes": { "type": "integer", "minimum": 0 },
        "poll_interval_seconds": { "type": "integer", "minimum": 1 },
        "max_polls": { "type": "integer", "minimum": 0 },
        "on_budget_exceeded": { "const": "PRE_MERGE_HOLD" }
      }
    },
    "post_merge_steps": {
      "type": "array",
      "items": { "enum": ["smoke", "reconcile", "closeout"] },
      "minItems": 1
    },
    "irreversibility_policy": {
      "type": "object",
      "description": "9-R / task-2553+12 pre/post-merge split.",
      "required": ["pre_merge_hold", "post_merge_hold", "idempotency_merged_noop", "rollback_revert_force"],
      "additionalProperties": false,
      "properties": {
        "pre_merge_hold": { "const": "merge_call_0" },
        "post_merge_hold": { "const": "irreversible_kept_no_rollback_hold_for_chair" },
        "idempotency_merged_noop": { "const": true },
        "rollback_revert_force": { "const": "forbidden" }
      }
    },
    "scope_invariants": {
      "type": "object",
      "required": ["forbidden_paths_regex", "live_ws_path", "live_ws_sanctioned_commit", "live_ws_branch", "merge_ops_isolation"],
      "additionalProperties": false,
      "properties": {
        "forbidden_paths_regex": { "type": "array", "items": { "type": "string" } },
        "live_ws_path": { "type": "string" },
        "live_ws_sanctioned_commit": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
        "live_ws_branch": { "type": "string" },
        "merge_ops_isolation": { "const": "isolated_worktree_gh_api_only" }
      }
    }
  }
}
