-
Notifications
You must be signed in to change notification settings - Fork 147
138 lines (125 loc) · 5.26 KB
/
agent-docs-audit.yml
File metadata and controls
138 lines (125 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
name: Agent docs audit
# Audit agent-context docs (CLAUDE.md, AGENTS.md, .claude/rules/) against
# agent-docs-policy.md.
#
# Two layers:
# L1 (deterministic): line budgets, symlink/pair integrity, broken @imports,
# broken path refs, unresolved commands. No API calls.
# L2 + L3 (semantic): Haiku triage filters L1's flagged set; Sonnet verifies
# concrete claims via Read/Glob/Grep and produces
# KEEP/TRIM/MOVE/UPDATE/INVESTIGATE findings.
#
# Warning-only for now. Uploads artifacts and writes a Step Summary; does not
# post PR comments, does not fail the workflow on findings.
#
# AI layers are skipped automatically if ANTHROPIC_API_KEY is unavailable
# (fork PRs, secret not set). In that case the L1 report still uploads.
on:
workflow_dispatch:
schedule:
# Daily 06:00 UTC — L1 structural check only, free. Catches new broken
# refs/symlink drift within a day without burning API budget.
- cron: '0 6 * * *'
# Weekly Monday 14:00 UTC — full L1+L2+L3 with semantic audit. Slower
# cadence is fine for semantic drift; the API cost (~$1-2/run) only
# makes sense weekly.
- cron: '0 14 * * 1'
pull_request:
paths:
- '**/AGENTS.md'
- '**/CLAUDE.md'
- '**/CLAUDE.local.md'
- '.claude/rules/**'
- 'agent-docs-policy.md'
- '.github/scripts/agent-docs-audit*'
- '.github/scripts/agent-docs-l1*'
- '.github/scripts/agent-docs-pr-comment*'
- '.github/workflows/agent-docs-audit.yml'
permissions:
contents: read
# Needed for the pull_request sticky comment. This matches existing
# PR-comment workflows such as visual-test.yml.
pull-requests: write
concurrency:
group: agent-docs-audit-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: 20
# Detect whether AI layers can run.
#
# SECURITY: PR-triggered runs always fall back to L1-only.
# The audited input is markdown (CLAUDE.md, AGENTS.md) which is
# itself prompt text and can be modified in the PR. Running a
# tool-using model (Read/Glob/Grep/Bash) over PR-authored prompt
# files while ANTHROPIC_API_KEY is in env would be a prompt-
# injection pathway. risk-assess.yml is safer because its input
# is a code diff and it only allows Read/Glob/Grep. Until our
# threat model matches that, L2/L3 only run on scheduled and
# workflow_dispatch events (input is main, trusted by review).
- name: Detect AI eligibility
id: ai
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping AI layers on pull_request - running L1 (deterministic drift) only"
elif [ "${{ github.event.schedule }}" = "0 6 * * *" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Daily schedule - running L1 only (structural drift). Full L1+L2+L3 runs Mondays."
elif [ -z "${{ secrets.ANTHROPIC_API_KEY }}" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping AI layers - no ANTHROPIC_API_KEY in repo secrets"
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Install script deps
if: steps.ai.outputs.skip != 'true'
run: npm install --prefix .github/scripts @anthropic-ai/claude-agent-sdk @anthropic-ai/sdk
- name: Run audit (L1 + L2 + L3)
if: steps.ai.outputs.skip != 'true'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
REPO_ROOT: ${{ github.workspace }}
run: |
node .github/scripts/agent-docs-audit.mjs > /tmp/agent-docs-audit-summary.md
cat /tmp/agent-docs-audit-summary.md
- name: Run audit (L1 only)
if: steps.ai.outputs.skip == 'true'
env:
REPO_ROOT: ${{ github.workspace }}
run: |
node .github/scripts/agent-docs-audit.mjs --skip-ai > /tmp/agent-docs-audit-summary.md
cat /tmp/agent-docs-audit-summary.md
- name: Write step summary
if: always()
run: |
if [ -f /tmp/agent-docs-audit-summary.md ]; then
cat /tmp/agent-docs-audit-summary.md >> $GITHUB_STEP_SUMMARY
fi
- name: Upload audit artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: agent-docs-audit
path: |
/tmp/agent-docs-audit-summary.md
/tmp/agent-docs-audit.json
/tmp/agent-docs-audit-l1.md
if-no-files-found: warn
retention-days: 30
# Diff-scoped sticky PR comment. Pull_request runs are L1-only; this
# surfaces deterministic findings only for agent-doc files touched by the
# PR. No AI, no Bash, no secrets.
- name: Post sticky PR comment with L1 findings
if: github.event_name == 'pull_request'
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
REPO_ROOT: ${{ github.workspace }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node .github/scripts/agent-docs-pr-comment.mjs