Skip to content

Latest commit

 

History

History
423 lines (391 loc) · 17.6 KB

File metadata and controls

423 lines (391 loc) · 17.6 KB

Approval manifest schema v1

scripts/evaluate_state.py accepts only the exact top-level fields below. Hash the complete object as UTF-8 JSON with sorted object keys, compact separators, and no ASCII escaping. Store the lowercase SHA-256 beside the object. An omitted, additional, reordered-list, or changed value invalidates the batch.

These are write-plan schemas. report-only supplies null remediation and finalization manifests, null plan hashes, and a null execution boundary; it classifies fresh GitHub response/thread/gate state without constructing a hypothetical write batch. Its recurring remote_name is also null because that mode never inspects a local checkout or Git remote. Its recurring owned_automation_id is null and automations is empty because it also never reads the Automation registry.

Initial snapshot and manifest

Before the first side effect, including fetch-base, construct an initial_snapshot with exactly these fields. Hash it with the canonical JSON rules above and use that digest as initial_manifest.snapshot_sha256:

{
  "authenticated_user_id": 1234,
  "base_branch": "<base-branch>",
  "base_sha": "<40-hex-base-sha>",
  "commands": [
    {
      "argv": ["<absolute-executable>", "<literal-argument>"],
      "definition_sha256": "<64-hex>"
    }
  ],
  "commit_required": true,
  "create_branch_required": true,
  "current_head_sha": "<40-hex-head-sha>",
  "diff_sha256": "<64-hex>",
  "execution_boundary": {
    "filesystem_read_scope_sha256": "<64-hex>",
    "filesystem_write_scope_sha256": "<64-hex>",
    "git": {
      "active_clean_filters": [],
      "attributes_snapshot_sha256": "<64-hex>",
      "config_snapshot_sha256": "<64-hex>",
      "credential_strategy": "gh-cli-https",
      "external_diff_disabled": true,
      "fsmonitor_disabled": true,
      "git_metadata_snapshot_sha256": "<64-hex>",
      "hooks_disabled": true,
      "invocation_sha256": "<64-hex>",
      "profile": "sanitized-gh-https-v1",
      "protect_hfs_enabled": true,
      "protect_ntfs_enabled": true,
      "protocol_restricted": true,
      "signing_disabled": true,
      "textconv_disabled": true,
      "transport_url_sha256": "<64-hex>"
    },
    "isolation": "no-network-minimal-env-checkout-only",
    "risk_acceptance_sha256": null
  },
  "existing_pr_base_branch": null,
  "existing_pr_head_branch": null,
  "existing_pr_head_sha": null,
  "existing_pr_number": null,
  "existing_pr_same_repository": null,
  "head_branch": "<final-head-branch>",
  "heartbeat_rrule": "FREQ=MINUTELY;INTERVAL=2",
  "host": "github.com",
  "open_prs_sha256": "<64-hex>",
  "paths": ["<sorted/repository-relative/path>"],
  "path_records": [
    {
      "inside_worktree": true,
      "lstat_chain_sha256": "<64-hex>",
      "outside_git_metadata": true,
      "path": "<matching-repository-relative-path>",
      "resolved_target_sha256": "<64-hex>"
    }
  ],
  "policy_schema_version": 1,
  "push_required": true,
  "remote_name": "<validated-remote-name>",
  "remote_refs_sha256": "<64-hex>",
  "repository": "<lowercase-owner/lowercase-repo>",
  "worktree_status_sha256": "<64-hex>"
}

existing_pr_number, existing_pr_base_branch, existing_pr_head_branch, existing_pr_head_sha, and existing_pr_same_repository must either all be null or all describe the one reused pull request. In the latter case existing_pr_same_repository must be true, the branch values must exactly equal base_branch and head_branch, and the PR head SHA must equal current_head_sha. heartbeat_rrule must exactly equal policy. paths are sorted, unique canonical paths; path_records are sorted by path, have exactly the same paths, and require both containment booleans to be true. Each lowercase digest binds the complete fresh lstat chain and the resolved target/candidate location. commands use the command schema below and preserve execution order. The four state digests cover complete normalized observations, not selected entries. The execution boundary uses the exact schema and containment rules below.

The corresponding initial_manifest has exactly these fields:

{
  "automation_prompt_template": "<exact-template-string>",
  "automation_prompt_template_sha256": "<64-hex>",
  "kind": "initial",
  "mode": "<autonomous-or-approval-gated>",
  "operations": [
    "fetch-base",
    "create-branch",
    "run-validation",
    "stage-files",
    "commit",
    "push",
    "create-pr",
    "request-codex-review",
    "bootstrap-automation"
  ],
  "pr_body_sha256": "<64-hex>",
  "pr_title_sha256": "<64-hex>",
  "review_request_body_template": "@codex please review this pull request.\n<!-- github-codex-pr-loop-request run=<same-run-id> round=0 sha={{FINAL_HEAD_SHA}} -->",
  "review_request_body_template_sha256": "<64-hex>",
  "run_id": "<32-lowercase-hex>",
  "snapshot_sha256": "<64-hex>",
  "version": 1
}

The operation order is fixed. Always start with fetch-base. Include create-branch only when create_branch_required is true. Include run-validation only when commands is non-empty. Include stage-files and then commit together only when commit_required is true. Include push only when push_required is true. Include exactly one of reuse-pr or create-pr according to existing_pr_number, then always include request-codex-review and bootstrap-automation. The example above is the maximal new-PR form.

All five digest fields are non-null lowercase SHA-256 values. When reusing a pull request, hash its current unchanged title and body; do not use null. review_request_body_template must contain @codex, exactly one {{FINAL_HEAD_SHA}}, no other placeholder, and the exact canonical marker for the manifest run ID and round zero. Its SHA-256 binds the template before the post-commit head is knowable. Render it only by replacing that one placeholder with the verified final 40-character head SHA. automation_prompt_template_sha256 binds the exact canonical prompt template stored in automation_prompt_template. It must byte-for-byte equal the evaluator-owned v1 template:

Continue $github-codex-pr-loop from the exact checkpoint below. Re-fetch all external state and follow only the installed evaluator's emitted action.
Loop key: {{AUTOMATION_KEY}}
Bootstrap checkpoint JSON: {{BOOTSTRAP_CHECKPOINT_JSON}}

Each supported placeholder appears exactly once and no other placeholder is allowed. After creating the PAUSED inert stub and obtaining the PR/request state, construct this exact prompt document and run scripts/evaluate_state.py --prompt:

{
  "initial_document": {
    "initial_manifest": {"<all-fields-above>": "<unchanged>"},
    "initial_plan_hash": "<64-hex>",
    "initial_snapshot": {"<all-fields-above>": "<fresh-values>"}
  },
  "bootstrap_snapshot": {
    "initial_plan_hash": "<same-64-hex-in-initial-document>",
    "post_write_receipt": {
      "base_sha": "<approved-40-hex-base-sha>",
      "final_head_sha": "<verified-post-write-40-hex-sha>",
      "head_descends_from_initial": true,
      "initial_head_sha": "<approved-initial-40-hex-head-sha>",
      "paths": ["<exact-approved-paths>"],
      "pr_head_sha": "<same-final-head-sha>",
      "pushed_diff_sha256": "<same-approved-diff-digest>",
      "remote_head_sha": "<same-final-head-sha>",
      "review_request_body": "<exact-rendered-round-0-template>",
      "review_request_body_sha256": "<64-hex-of-rendered-body>",
      "worktree_clean": true
    },
    "pr_url": "https://github.com/<owner>/<repo>/pull/42",
    "request_deadline": "<policy-deadline>",
    "state": {"<every-recurring-evaluator-field>": "<fresh-bootstrap-value>"}
  },
  "bootstrap_snapshot_sha256": "<64-hex>",
  "rendered_prompt": "<exact-deterministic-rendering>",
  "rendered_prompt_sha256": "<64-hex>"
}

bootstrap_snapshot has exactly initial_plan_hash, post_write_receipt, pr_url, request_deadline, and state. The plan hash must equal its value in initial_document; the deadline is exactly the policy timeout after state.request_created_at.

The post-write receipt has exactly the eleven fields shown. base_sha, pushed_diff_sha256, and paths equal the approved initial snapshot. The initial head also equals the approved snapshot. When commit_required is true, the final head must differ from and descend from the initial head; otherwise it must equal the initial head. remote_head_sha, pr_head_sha, state.request_head_sha, and state.current_head_sha all equal that final head. The worktree is clean. The review body is the approved template with only {{FINAL_HEAD_SHA}} replaced by the final head, and its digest matches the rendered UTF-8 body.

state is the complete recurring evaluator state: one exact-key, recorded-ID, exact-schedule PAUSED Automation; an open same-repository PR with freshly fetched matching base/head branches and tips; the same authenticated user/run/mode; one canonical pending request with the policy deadline; zero fix rounds and no completed keys; fresh response/thread/gate observations; and null remediation/finalization manifests, plan hashes, approvals, execution boundary, claim, and post-finalization fields.

The prompt validator revalidates the entire initial document, requires a canonical same-repository GitHub PR URL, checks the bootstrap snapshot digest, cross-binds every initial identity and policy value, including exact remote name equality, and runs the recurring evaluator. It accepts only the expected paused-bootstrap state-inconsistency, then replaces {{AUTOMATION_KEY}} with the canonical key and {{BOOTSTRAP_CHECKPOINT_JSON}} with canonical UTF-8 JSON of the complete bootstrap_snapshot using sorted keys, compact separators, and no ASCII escaping. Only a prompt-ready result permits updating the stub. Then view and verify the exact prompt and schedule before activation; the first wakeup re-fetches all external response, thread, and gate state. mode cannot be report-only because that mode has no initial write plan.

The outer initial document has exactly initial_snapshot, initial_manifest, and initial_plan_hash. The plan hash is the lowercase SHA-256 of the complete canonical initial_manifest, not the snapshot alone.

Run scripts/evaluate_state.py --initial on the complete snapshot, manifest, and initial_plan_hash before presenting an approval request. In approval-gated, the user must name that exact hash. Immediately before fetch-base, rebuild all three values and run --initial again; its hash must equal the approved hash byte-for-byte. autonomous mode performs both validations as well and requires its second hash to equal its first validated hash. Any schema error, drift, or hash change permits no initial side effect.

Normalize every qualifying response into this complete object. Use a decimal string for the positive REST/database id, a numeric GitHub database ID for bot_id, a numeric request ID or null for trigger_request_comment_id, lowercase hex digests, and ISO-8601 timestamps with timezones:

{
  "body_sha256": "<64-hex>",
  "bot_id": 199175422,
  "bot_type": "Bot",
  "commit_sha": "<40-hex-or-null>",
  "content_version_at": "<timestamp>",
  "created_at": "<timestamp>",
  "id": "<positive-decimal-string>",
  "source": "<issue-comment-or-review-or-review-comment>",
  "trigger_request_comment_id": 1234,
  "zero_findings_signal": false
}

Sort response objects by source, then normalized id. The manifest response list must exactly equal all qualifying trusted responses in the evaluator snapshot, not just the response that appears decisive.

Remediation manifest

{
  "actionable_threads": [
    {
      "body_sha256": "<64-hex>",
      "content_version_at": "<timestamp>",
      "id": "<thread-node-id>",
      "position_sha256": "<64-hex-of-canonical-path-and-position>",
      "root_comment_id": "<root-comment-node-id>"
    }
  ],
  "commands": [
    {
      "argv": ["<executable>", "<literal-argument>"],
      "definition_sha256": "<64-hex-of-command-definition-or-hook>"
    }
  ],
  "base_branch": "<base-branch>",
  "base_sha": "<requested-40-hex-base-sha>",
  "current_head_sha": "<requested-40-hex-sha>",
  "execution": {
    "filesystem_read_scope_sha256": "<64-hex>",
    "filesystem_write_scope_sha256": "<64-hex>",
    "git": {
      "active_clean_filters": [],
      "attributes_snapshot_sha256": "<64-hex>",
      "config_snapshot_sha256": "<64-hex>",
      "credential_strategy": "gh-cli-https",
      "external_diff_disabled": true,
      "fsmonitor_disabled": true,
      "git_metadata_snapshot_sha256": "<64-hex>",
      "hooks_disabled": true,
      "invocation_sha256": "<64-hex>",
      "profile": "sanitized-gh-https-v1",
      "protect_hfs_enabled": true,
      "protect_ntfs_enabled": true,
      "protocol_restricted": true,
      "signing_disabled": true,
      "textconv_disabled": true,
      "transport_url_sha256": "<64-hex>"
    },
    "isolation": "no-network-minimal-env-checkout-only",
    "risk_acceptance_sha256": null
  },
  "head_branch": "<head-branch>",
  "kind": "remediation",
  "operations": [
    "edit-files",
    "run-validation",
    "commit",
    "push",
    "reply-to-codex-threads",
    "request-codex-review",
    "update-automation"
  ],
  "paths": ["<sorted/repository-relative/path>"],
  "path_records": [
    {
      "inside_worktree": true,
      "lstat_chain_sha256": "<64-hex>",
      "outside_git_metadata": true,
      "path": "<matching-repository-relative-path>",
      "resolved_target_sha256": "<64-hex>"
    }
  ],
  "policy_schema_version": 1,
  "pr_number": 42,
  "remote_name": "<validated-remote-name>",
  "repository": "<lowercase-owner/lowercase-repo>",
  "request_key": "<comment-id>:<requested-40-hex-sha>",
  "review_request_body_template": "@codex please review this fix.\n<!-- github-codex-pr-loop-request run=<same-run-id> round=<fix-rounds-plus-one> sha={{FINAL_HEAD_SHA}} -->",
  "review_request_body_template_sha256": "<64-hex>",
  "responses": [],
  "run_id": "<32-lowercase-hex>",
  "version": 1
}

Sort thread snapshots by id and paths lexicographically. Paths must be unique, normalized, and repository-relative. path_records use the exact initial record schema, sort by path, and match paths one-for-one. Recompute and require identical records immediately before each edit and staging operation. Preserve command execution order. If there are no commands, omit run-validation from operations; otherwise the operation list must be exactly the order shown. actionable_threads must have exactly the snapshot's actionable_codex_threads entries.

The review-request template uses the same one-placeholder rules as the initial template, but its canonical marker round is fix_rounds + 1. Hash the template inside the remediation plan before editing. After the approved fix is committed and pushed, substitute only the verified new full head SHA, verify the rendered body, post it as the next canonical request, and persist that final SHA as the new request head.

Use explicit-risk-acceptance for execution.isolation only after the user accepts the exact unavailable isolation guarantees and commands. In that case, risk_acceptance_sha256 is the lowercase SHA-256 of the canonical acceptance record. The isolated form requires null.

The execution object must exactly match the independent execution_boundary in the evaluator snapshot. Construct its git object by following git-execution-boundary.md. The safe profile requires every protection boolean to be true, no active clean filters, the gh-cli-https strategy, and exact filesystem read/write scope digests. Use explicit-risk-acceptance-v1 only with explicit-risk-acceptance; enumerate the actual protection booleans and filters and bind the acceptance digest. remote_name must equal the validated initial remote and current recurring state; a changed or ambiguous remote invalidates the plan.

Finalization manifest

{
  "base_branch": "<base-branch>",
  "base_sha": "<requested-40-hex-base-sha>",
  "current_head_sha": "<requested-40-hex-sha>",
  "eligible_threads": [],
  "gates": {
    "approvals": "<pass-fail-pending-or-unknown>",
    "branch_protection": "<pass-fail-pending-or-unknown>",
    "checks": "<pass-fail-pending-or-unknown>",
    "mergeability": "<pass-fail-pending-or-unknown>"
  },
  "head_branch": "<head-branch>",
  "kind": "finalization",
  "non_codex_rooted_threads": 0,
  "operations": ["delete-owned-automation"],
  "policy_schema_version": 1,
  "pr_is_draft": false,
  "pr_number": 42,
  "remote_name": "<validated-remote-name>",
  "repository": "<lowercase-owner/lowercase-repo>",
  "request_key": "<comment-id>:<requested-40-hex-sha>",
  "responses": [],
  "run_id": "<32-lowercase-hex>",
  "version": 1
}

Each eligible_threads entry uses the same exact thread schema and sorting as a remediation thread. Its length must equal eligible_codex_threads. Prepend mark-draft-ready to operations when pr_is_draft is true. Then append resolve-codex-threads when eligible threads exist. Always end with delete-owned-automation. The four gate values, draft state, non-Codex count, qualifying responses, and operation list must match the evaluator snapshot. The remote name, base branch, head branch, and base SHA must match the canonical request checkpoint. Any current remote, base-tip, or PR branch drift invalidates the plan and forbids GitHub mutation or Automation cleanup.

The first finalization plan authorizes the listed GitHub operations but never produces merge readiness or immediate cleanup. After those operations, build a fresh finalization manifest from a new snapshot; it must have a false pr_is_draft, no eligible threads, and only delete-owned-automation. Preserve the original claimed request key/hash separately. Only finish-finalization from that post-mutation snapshot permits deletion and fresh merge-gate reporting.