Skip to content

ci: seed cross-family review status - #27

Merged
MichaelSaucier merged 2 commits into
mainfrom
codex/seed-cross-family-review-status
Jun 23, 2026
Merged

ci: seed cross-family review status#27
MichaelSaucier merged 2 commits into
mainfrom
codex/seed-cross-family-review-status

Conversation

@MichaelSaucier

@MichaelSaucier MichaelSaucier commented Jun 23, 2026

Copy link
Copy Markdown

Summary

  • add a shared pull_request_target workflow that seeds the required external cross-family-adversarial-review commit status as pending
  • trigger it when a PR opens, reopens, receives a new head commit, or transitions from draft to ready for review
  • keep pass/fail authority in the existing cross-family review gate: this workflow only seeds the required status and never marks review as passed

Security Model

  • runs from the base-branch workflow copy, not the PR head workflow copy
  • does not check out code and does not execute PR-controlled files
  • only requests statuses: write
  • posts only state=pending to context cross-family-adversarial-review
  • uses the exact PR head SHA from github.event.pull_request.head.sha

Validation

  • git diff --check
  • Python YAML parse of the workflow file
  • identical workflow hash across all 8 Civilization repos: 6b2426b378578d629b2466270ee507485af517278f23b0945445b484e96b8ad8
  • earlier same-branch rollout verified the status-posting command shape live; this hardened version moves the trigger to pull_request_target, which takes effect for future PRs once this workflow exists on main

@MichaelSaucier

Copy link
Copy Markdown
Author

Cross-Family Adversarial Review

  • Reviewer family: claude
  • Reviewed head: cc343a86085dccedc8641b37a688019142d70b9d
  • Artifact path: /Transpara/transpara-ai/repos/agent/.adversarial-review/20260623T095825Z-pr27/claude.result.md

Adversarial Review — PR #27 ci: seed cross-family review status

Verdict

No blockers. The workflow correctly implements the hardened pull_request_target seeding pattern described in the authorized scope: it triggers on the four required actions, runs with least-privilege statuses: write, performs no checkout of PR code, and posts only state=pending — never success. The scope-critical injection surface is also handled correctly (all event-derived values flow through env:, never via inline ${{ }} interpolation into the shell). Residual items below are non-blocking.

Scope conformance (verified against authorized scope)

  • Trigger set (.github/workflows/cross-family-review-status.yml:4-5): types: [opened, reopened, synchronize, ready_for_review] — exact match to the required four actions.
  • Runs from base branch / privileged trigger: uses pull_request_target. By design GitHub executes the base-branch copy of this file with a write-capable token, so a PR author cannot tamper with the seeding logic from within their own PR. Correct security model.
  • No PR code execution (:18-39): no actions/checkout, no fork ref, no build/test step — only a single gh api call. The inline comment (:28-30) documents the intent and the code matches it.
  • Does not mark passed (:36): posts -f state=pending only. Pass/fail authority is not touched.
  • Least privilege (:7-8): permissions: statuses: write only — no contents, pull-requests, etc.

Findings (ordered by severity)

1. (Low / by-design) Effectiveness depends on out-of-scope branch protection config

The merge-blocking guarantee is not provided by this workflow alone. Seeding a pending status on context cross-family-adversarial-review (:37) only blocks merge if that context is configured as a required status check in branch protection on the base branch. If it is not required, a pending status does not prevent merge.

  • Failure mode: if the context is not marked required, the seeding is cosmetic and the gate is open.
  • Mitigating fact: if it is required, GitHub already blocks on the absent ("Expected") check even when seeding fails — so the failure mode here is fail-safe, not fail-open.
  • Evidence needed: confirm cross-family-adversarial-review is a required check in transpara-ai/agent base-branch protection. (Out of this PR's diff, but it is the load-bearing assumption.)

2. (Low / by-design) Context-name / SHA contract with the real review gate is implicit

This workflow seeds context cross-family-adversarial-review against github.event.pull_request.head.sha (:24, :36-37). For the gate to ever clear, the existing cross-family review must post success/failure to the identical context string against the exact same head SHA.

  • Failure mode: any drift (different context name, or the reviewer posting against a stale/merge SHA) leaves the pending permanently unresolved and blocks merge indefinitely. This is the intended fail-closed direction, but a silent context mismatch would be hard to diagnose.
  • Evidence needed: confirm the real cross-family review gate publishes to the same cross-family-adversarial-review context against the PR head SHA. The shared 8-repo hash match (per Codex validation) is reassuring that the context string is standardized across repos.

3. (Informational) cancel-in-progress: true is safe here, but worth noting

concurrency with cancel-in-progress: true keyed on PR number (:10-13) means rapid pushes can cancel an in-flight seeding run.

  • Analysis: the newest run for a group is never cancelled (nothing newer exists to cancel it), and it always posts for the newest head SHA — which is the only SHA that matters for merge. Intermediate SHAs may not get a pending status, but they are never merge candidates. No correctness or security impact.

4. (Informational) This exact workflow is not exercised live on this PR

Because pull_request_target runs the base-branch copy, and this file does not yet exist on main, the workflow as written cannot be validated by this PR's own checks (the green "Build & Test" run is ordinary CI, not this workflow). Codex's note acknowledges this: the command shape was verified in an earlier same-branch rollout, but the hardened definition only takes effect for future PRs after merge to main.

  • Recommendation: after merge, confirm on the next inbound PR that the status actually posts (correct context, correct head SHA, pending) and that branch protection blocks accordingly. This is the only true end-to-end validation point.

Notes on what was checked and found clean

  • Script injection: no untrusted ${{ }} expansion inside run:; HEAD_SHA, PR_URL, EVENT_ACTION all pass through env: (:20-27) and are referenced as quoted shell vars (:36-39). Correct.
  • Token exposure: GH_TOKEN: ${{ github.token }} is only available to the static gh api step; no untrusted code runs in the job, so the privileged token is not reachable by PR code.
  • Idempotency / synchronize semantics: re-seeding pending on synchronize correctly invalidates any prior pass on an old SHA, satisfying the "exact-head" requirement.
  • Draft handling: seeding fires for draft PRs (current PR is a draft) and again on ready_for_review; desirable, not a problem.
  • YAML / diff hygiene: consistent with Codex's git diff --check and YAML-parse validation; single-file additive change.

@MichaelSaucier
MichaelSaucier marked this pull request as ready for review June 23, 2026 10:09
@MichaelSaucier

Copy link
Copy Markdown
Author

Ready-State Cross-Family Adversarial Review

  • Reviewer family: claude
  • Reviewed head: cc343a86085dccedc8641b37a688019142d70b9d
  • PR state at review: ready_for_review
  • Artifact path: /tmp/civ-ready-agent-27-WEPjPC/.adversarial-review/20260623T101515Z-pr27/claude.result.md

Cross-Family Adversarial Review — PR #27 ci: seed cross-family review status

Verdict

No blockers. The workflow matches every property in the authorized scope: it is a pull_request_target workflow that runs from the base copy, never checks out or executes PR code, holds a single minimal permission, seeds only state=pending, and grants itself no success/failure authority. The change is exactly one file and is in scope for the PR title. Details and residual (non-blocking) risks below.

Scope-claim verification

Claim Evidence Status
pull_request_target from base copy cross-family-review-status.yml:4 pull_request_target workflows always execute the base-branch copy of the file, so the PR's own version of this file is not active for PR #27 — it only takes effect once merged.
No checkout / no PR-code execution No actions/checkout; run: block (:27–39) only computes a string and calls gh api ✅ Nothing untrusted is fetched or run.
Pending-only seeding :36 -f state=pending (both branches of the if only change description, :30/:32) ✅ State is hard-coded pending; the ready_for_review branch alters only the description text.
No success/failure authority Workflow never posts success/failure/error ✅ It cannot self-approve.

Additional hardening confirmed:

  • Minimal token blast radiuspermissions: statuses: write (:7–8) is the only grant; GitHub sets every other permission to none. Even with the base-context token that pull_request_target provides, this token can touch nothing but commit statuses.
  • No script-injection surface — all attacker-reachable values are passed through env: (:21–26) and referenced as quoted shell variables (${HEAD_SHA}, ${PR_URL}, ${EVENT_ACTION}), not interpolated as ${{ }} inside run:. Moreover every interpolated field (head.sha, html_url, action, repository) is GitHub-generated, not free-text, so even the env values are not injectable.
  • Correct target commit — it posts to github.event.pull_request.head.sha (:24), the exact PR head that branch protection evaluates, not github.sha (which under pull_request_target is the base merge commit). This is the "exact-head" property the scope requires.
  • Fails closedcancel-in-progress (:12) can cancel a seed job, and re-seeding pending on reopened/ready_for_review/synchronize only ever moves the gate toward pending. Pending (or a missing required check) blocks merge, so every reachable transition tightens rather than loosens the gate.

Live-state consistency

The cross-family-adversarial-review context currently reads SUCCESS (targeting issue comment #issuecomment-4778081865) on head cc343a8. This is consistent with the design: this workflow only seeds pending, so the SUCCESS was necessarily posted by the external review authority, not by this workflow. The Codex validation ("status was posted successfully for head cc343a8") is therefore not contradicted by the diff. Head SHA cc343a8 and draft=false both match the live metadata.

Residual (non-blocking) risks

  1. Gate effectiveness is out of band. This workflow only seeds the status; it does nothing unless branch protection on main marks cross-family-adversarial-review as a required status check. That config is not in this packet. Evidence needed: confirm branch-protection requires this context (and ideally requires it on the exact head), otherwise the workflow is cosmetic.
  2. Status identity is not constrained. Any actor/workflow with statuses: write on the repo can post success for this context and satisfy the gate; GitHub's status model has no notion of "trusted poster." This is inherent, not a defect of this file, but worth recording as the gate's trust assumption.
  3. The current SUCCESS cannot be authenticated from this packet. I can confirm this workflow did not and cannot post it; I cannot prove from the packet that the SUCCESS on cc343a8 came from a legitimate exact-head review versus a manual/forced status. The issue-comment targetUrl is consistent with a real review but not verifiable here.
  4. No re-seed on converted_to_draft/edited. Not security-relevant (those don't change head SHA), noted only for completeness — seeding logic correctly keys off head-SHA-changing events plus the explicit ready_for_review reset.

Recommendation

Safe to merge from a workflow-hardening standpoint. Before relying on it as a gate, confirm residual item #1 (branch protection requires the cross-family-adversarial-review context).

@MichaelSaucier
MichaelSaucier merged commit e2e3765 into main Jun 23, 2026
2 checks passed
@MichaelSaucier
MichaelSaucier deleted the codex/seed-cross-family-review-status branch June 23, 2026 10:48
@MichaelSaucier
MichaelSaucier restored the codex/seed-cross-family-review-status branch June 26, 2026 16:47
@MichaelSaucier
MichaelSaucier deleted the codex/seed-cross-family-review-status branch June 26, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant