Skip to content

docs(examples): load committed manifest and Rego in the Foundry example #3160

docs(examples): load committed manifest and Rego in the Foundry example

docs(examples): load committed manifest and Rego in the Foundry example #3160

Workflow file for this run

# Consolidated AI PR review pipeline for agent-governance-toolkit.
# Runs 5 AI analysis agents in parallel on every PR:
# code-review, security-scan, breaking-change, docs-sync, test-coverage
# Results are posted as individual PR comments, then summarized by the
# "Post unified summary" job into a single verdict table.
#
# Replaces: ai-code-review.yml, ai-security-scan.yml (PR job only),
# ai-breaking-change-detector.yml, ai-docs-sync.yml, ai-test-generator.yml,
# ai-pr-summary.yml
name: AI PR Review
# SECURITY: Uses pull_request_target for write access to post PR comments.
# All checkouts pin to BASE ref (never HEAD) to prevent RCE via modified
# composite actions in fork PRs. See workflow security audit.
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: {}
# SECURITY: pull_request_target runs in BASE context. Never checkout PR head ref.
jobs:
ai-agents:
name: ${{ matrix.label }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
models: read
if: >-
github.event.pull_request.draft != true &&
github.actor != 'dependabot[bot]'
continue-on-error: true
strategy:
fail-fast: false
matrix:
include:
- agent-type: code-reviewer
label: "Code Review"
icon: "🔍"
max-tokens: "2000"
paths-changed: "any"
custom-instructions: |
You are reviewing microsoft/agent-governance-toolkit, a security-focused Python monorepo (8 packages, pytest, ruff).
CRITICAL OUTPUT RULES -- your review MUST follow this exact format:
1. Start with: **TL;DR**: N blockers, M warnings. [one-sentence verdict].
2. Summary table (one row per finding, one-line descriptions only):
| # | Sev | Issue | Where |
|---|-----|-------|-------|
3. Action items: one sentence per blocker only. No code blocks in the summary.
4. Warnings: list in table, mark as "fine as follow-up PRs."
5. Nits: do NOT include. Omit entirely.
DO NOT write multi-paragraph explanations, conclusions, or suggestions sections.
DO NOT use headers like "CRITICAL", "WARNING", "SUGGESTIONS", "Conclusion".
DO NOT exceed 200 words total. The contributor knows how to code.
If no issues found, reply only: No issues found. Clean change.
Focus areas (what to flag, not what to write about):
- Policy engine correctness (false negatives = security bypass)
- Trust/identity: crypto, credentials, SPIFFE
- Sandbox escape, thread safety, OWASP Agentic Top 10
- Backward compatibility (public API surface changes)
- Pydantic model validation gaps
- agent-type: security-scanner
label: "Security Scan"
icon: "🛡️"
max-tokens: "1500"
paths-changed: "code"
custom-instructions: |
You are a security analyst reviewing changes to microsoft/agent-governance-toolkit.
BE CONCISE. Only report actual findings, not a methodology walkthrough.
Skip categories with no issues. No boilerplate.
Scan for:
1. Prompt injection defense bypass
2. Policy engine circumvention
3. Trust chain weaknesses
4. Credential exposure
5. Sandbox escape
6. Deserialization attacks
7. Race conditions
8. Supply chain risks
Format (compact):
### Security Review
| Severity | Finding | Fix |
|----------|---------|-----|
If NO security issues found, reply only: No security issues found.
- agent-type: breaking-change-detector
label: "Breaking Changes"
icon: "🔄"
max-tokens: "1000"
paths-changed: "src"
custom-instructions: |
You are an API compatibility analyzer for microsoft/agent-governance-toolkit.
These packages are published to PyPI/NuGet/crates.io.
BE CONCISE. Only list actual breaking or potentially breaking changes.
Do NOT list additive (non-breaking) changes.
Format:
### API Compatibility
| Severity | Change | Impact |
|----------|--------|--------|
If NO breaking changes: No breaking changes detected.
- agent-type: docs-sync-checker
label: "Docs Sync"
icon: "📝"
max-tokens: "1000"
paths-changed: "src"
custom-instructions: |
You are a documentation freshness checker for microsoft/agent-governance-toolkit.
BE CONCISE. Only flag actual issues, no suggestions or nice-to-haves.
Check for:
1. New public APIs without docstrings
2. README sections outdated by this change
3. CHANGELOG missing entry for behavioral changes
Format:
### Docs Sync
- `func()` in `module.py` -- missing docstring
- `README.md` -- section X needs update
If everything is in sync: Documentation is in sync.
- agent-type: test-generator
label: "Test Coverage"
icon: "🧪"
max-tokens: "2000"
paths-changed: "src"
custom-instructions: |
You are a test coverage advisor for microsoft/agent-governance-toolkit.
CRITICAL OUTPUT RULES -- be concise:
1. Only list files with ACTUAL coverage gaps (skip files with adequate coverage).
2. For each gap, list the test name and a one-line description. No paragraphs.
3. Group by file. Skip files where changes are already well-tested.
4. DO NOT list every possible edge case. Focus on the 3-5 highest-value missing tests.
5. DO NOT write "General Recommendations" or "Summary" sections.
6. DO NOT exceed 300 words total.
Format:
### `filename.py`
- `test_name` -- one-line description of what it validates
- `test_name` -- one-line description
If coverage is adequate: Test coverage looks good. No gaps identified.
Priority for gap detection:
- Untested new public methods/classes
- Security-critical paths without negative tests
- Missing boundary/error cases on policy evaluation
steps:
- name: Fork safety check
if: github.event.pull_request.head.repo.full_name != github.repository
run: echo "::notice::Running on fork PR -- composite action resolved from base branch (safe)"
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# SECURITY: pull_request_target defaults to base branch checkout (safe).
persist-credentials: false
fetch-depth: 0
- name: Run ${{ matrix.label }}
uses: ./.github/actions/ai-agent-runner
with:
agent-type: ${{ matrix.agent-type }}
github-token: ${{ secrets.GITHUB_TOKEN }}
model: gpt-4o
fallback-model: gpt-4o
max-tokens: ${{ matrix.max-tokens }}
context-mode: pr-diff
output-mode: pr-comment
custom-instructions: ${{ matrix.custom-instructions }}
# ── Unified summary ────────────────────────────────────────────────────
# Waits for all agents, then posts/updates a single summary table.
summary:
name: Post unified summary
runs-on: ubuntu-latest
needs: [ai-agents]
if: always()
permissions:
issues: write
pull-requests: write
steps:
- name: Collect agent comments and post summary
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;
const SUMMARY_MARKER = '<!-- ai-pr-summary -->';
// RUN_MARKER intentionally uses GITHUB_RUN_ID only — not
// GITHUB_RUN_ATTEMPT. The aggregator filters PR comments to ones
// posted by *this* workflow run; including RUN_ATTEMPT would skip
// comments from a previous attempt that may still be valid (e.g.
// a flaky job that succeeded on its first attempt but failed an
// unrelated job, triggering a re-run). RUN_ID is unique per run
// and stable across attempts.
const RUN_MARKER = `<!-- ai-agent-run:${process.env.GITHUB_RUN_ID} -->`;
const AGENTS = [
{ marker: '<!-- ai-agent:code-reviewer -->', icon: '🔍', label: 'Code Review' },
{ marker: '<!-- ai-agent:security-scanner -->', icon: '🛡️', label: 'Security Scan' },
{ marker: '<!-- ai-agent:breaking-change-detector -->', icon: '🔄', label: 'Breaking Changes' },
{ marker: '<!-- ai-agent:docs-sync-checker -->', icon: '📝', label: 'Docs Sync' },
{ marker: '<!-- ai-agent:test-generator -->', icon: '🧪', label: 'Test Coverage' },
];
let allComments = [];
let page = 1;
while (true) {
const { data: batch } = await github.rest.issues.listComments({
owner, repo, issue_number: prNumber, per_page: 100, page,
});
if (!batch.length) break;
allComments = allComments.concat(batch);
if (batch.length < 100) break;
page++;
}
function isWorkflowComment(comment) {
return comment?.user?.login === 'github-actions[bot]';
}
function parseAgentStatus(body) {
if (!body || !body.includes('<!-- ai-agent-status:completed -->') || !body.includes(RUN_MARKER)) {
return { status: '⚠️', statusLabel: 'Missing', detail: 'No current-run comment' };
}
return { status: '✅', statusLabel: 'Completed', detail: 'Review comment posted; verify AI output' };
}
const rows = AGENTS.map(agent => {
const comment = allComments.find(c => isWorkflowComment(c) && c.body && c.body.includes(agent.marker) && c.body.includes(RUN_MARKER));
const verdict = comment ? parseAgentStatus(comment.body) : { status: '⚠️', statusLabel: 'Missing', detail: 'No current-run comment' };
return `| ${agent.icon} ${agent.label} | ${verdict.status} ${verdict.statusLabel} | ${verdict.detail} |`;
});
const overallVerdict = rows.some(r => r.includes('⚠️'))
? '**Verdict: ⚠️ AI review incomplete; ready for human review**'
: '**Verdict: ✅ Ready for human review**';
const summaryBody = [
SUMMARY_MARKER,
'## PR Review Summary',
'',
'| Check | Status | Details |',
'|-------|--------|---------|',
...rows,
'',
overallVerdict,
'',
'_AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims._',
].join('\n');
const existing = allComments.find(c => isWorkflowComment(c) && c.body && c.body.includes(SUMMARY_MARKER));
try {
if (existing) {
await github.rest.issues.updateComment({
owner, repo, comment_id: existing.id, body: summaryBody,
});
core.info(`Updated summary comment ${existing.id}`);
} else {
await github.rest.issues.createComment({
owner, repo, issue_number: prNumber, body: summaryBody,
});
core.info('Created summary comment');
}
} catch (error) {
core.warning(`Unable to post PR summary comment: ${error.message}`);
if (error?.status === 403 && String(error?.message || '').includes('Resource not accessible')) {
core.warning('Unable to post advisory AI review summary because this run token cannot write PR comments.');
} else {
throw error;
}
}
await core.summary
.addHeading('PR Review Summary')
.addRaw(summaryBody, true)
.write();