Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 25 additions & 33 deletions .github/workflows/agent_governance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ on:
pull_request:
types: [opened, synchronize, reopened, edited, ready_for_review]

# This workflow runs with the pull request head in scope, so it stays read-only.
# Publishing the warning comment needs write access that forked pull requests
# never get here; that half lives in agent_governance_comment.yml, which is
# triggered by workflow_run and never checks out pull request code.
permissions:
contents: read
pull-requests: write
issues: write

jobs:
governance:
Expand Down Expand Up @@ -55,39 +57,29 @@ jobs:
run: |
cat agent_governance_summary.md >> "$GITHUB_STEP_SUMMARY"

- name: Comment governance warnings
if: >-
always() &&
github.event.pull_request.head.repo.full_name == github.repository
- name: Collect comment payload
if: always()
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if ! grep -qi '^| warning |' agent_governance_summary.md; then
exit 0
fi

marker='<!-- agent-governance-warning -->'
body_file="$(mktemp)"
json_file="$(mktemp)"
{
echo "$marker"
echo
cat agent_governance_summary.md
} > "$body_file"
jq -Rs '{body: .}' < "$body_file" > "$json_file"

existing_comment_id="$(gh api --paginate \
"repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
--jq ".[] | select(.body | contains(\"${marker}\")) | .id" \
| head -n 1)"

if [ -n "$existing_comment_id" ]; then
gh api --method PATCH \
"repos/${GITHUB_REPOSITORY}/issues/comments/${existing_comment_id}" \
--input "$json_file"
set -eu
mkdir -p agent_governance_payload
if [ -s agent_governance_summary.md ]; then
cp agent_governance_summary.md agent_governance_payload/
else
gh api --method POST \
"repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
--input "$json_file"
{
echo "## Agent Governance Check"
echo
echo "Checker did not run."
} > agent_governance_payload/agent_governance_summary.md
fi
printf '%s\n' "$PR_NUMBER" > agent_governance_payload/pr_number.txt

- name: Upload comment payload
if: always()
uses: actions/upload-artifact@v7
with:
name: agent-governance-payload
path: agent_governance_payload/
retention-days: 1
if-no-files-found: error
100 changes: 100 additions & 0 deletions .github/workflows/agent_governance_comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Agent Governance Comment

# Privileged half of the governance check. `Agent Governance` runs on
# `pull_request`, where forked pull requests only ever get a read-only token, so
# it cannot comment on the very pull requests that need the feedback most. This
# workflow is triggered by `workflow_run`, which always runs from the base
# branch with the base repository's permissions.
#
# Safety contract for this file:
# - It never checks out pull request code and never runs anything from the head.
# - Its only inputs are the text artifact uploaded by the analysis run.
# - Pull request supplied text reaches the API through files and `jq -Rs`, never
# through shell interpolation.
on:
workflow_run:
workflows: ["Agent Governance"]
types: [completed]

permissions:
actions: read
pull-requests: write
issues: write

jobs:
comment:
name: Publish governance warnings
if: github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Download comment payload
id: payload
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
set -eu
mkdir -p agent_governance_payload
if ! gh run download "$RUN_ID" \
--repo "$GITHUB_REPOSITORY" \
--name agent-governance-payload \
--dir agent_governance_payload; then
echo "No governance payload for run ${RUN_ID}; nothing to publish."
echo "found=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "found=true" >> "$GITHUB_OUTPUT"

- name: Comment governance warnings
if: steps.payload.outputs.found == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -eu
summary_file=agent_governance_payload/agent_governance_summary.md
pr_file=agent_governance_payload/pr_number.txt
if [ ! -s "$summary_file" ] || [ ! -s "$pr_file" ]; then
echo "Incomplete governance payload; nothing to publish."
exit 0
fi

pr_number="$(tr -dc '0-9' < "$pr_file")"
if [ -z "$pr_number" ]; then
echo "Payload does not carry a pull request number; nothing to publish."
exit 0
fi

if ! grep -qi '^| warning |' "$summary_file"; then
exit 0
fi

# GitHub rejects comment bodies over 65536 characters.
body_limit=60000
marker='<!-- agent-governance-warning -->'
body_file="$(mktemp)"
json_file="$(mktemp)"
{
echo "$marker"
echo
head -c "$body_limit" "$summary_file"
if [ "$(wc -c < "$summary_file")" -gt "$body_limit" ]; then
echo
echo "_Summary truncated. See the \`Agent Governance\` run summary for the full report._"
fi
} > "$body_file"
jq -Rs '{body: .}' < "$body_file" > "$json_file"

existing_comment_id="$(gh api --paginate \
"repos/${GITHUB_REPOSITORY}/issues/${pr_number}/comments" \
--jq ".[] | select(.body | contains(\"${marker}\")) | .id" \
| head -n 1)"

if [ -n "$existing_comment_id" ]; then
gh api --method PATCH \
"repos/${GITHUB_REPOSITORY}/issues/comments/${existing_comment_id}" \
--input "$json_file"
else
gh api --method POST \
"repos/${GITHUB_REPOSITORY}/issues/${pr_number}/comments" \
--input "$json_file"
fi
9 changes: 9 additions & 0 deletions docs/developers_guide/agent_governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ ABACUS uses a layered review model:
- `Agent Governance` is the deterministic GitHub Actions check for low-noise
diff rules. Repository maintainers may make this workflow a required check in
branch protection.
- `Agent Governance` is split across two workflow files. The `pull_request`
half runs the checker with a read-only token and uploads the summary as an
artifact. The `Agent Governance Comment` half is triggered by `workflow_run`,
so it runs from the base branch with the base repository's permissions and
can publish the warning comment on pull requests from forks, which the
`pull_request` half cannot do. The comment half never checks out pull request
code and reads only the uploaded text artifact. `workflow_run` workflows are
always taken from the default branch, so changes to the comment half take
effect only after they are merged.
- CodeRabbit is a PR-triggered AI reviewer for semantic review hints. Its
repository configuration lives in `.coderabbit.yaml` and uses this document
plus `AGENTS.md` as review guidelines.
Expand Down
Loading