Repository navigation
262 lines (254 loc) · 12.2 KB
/
Copy pathagents-md-integrity.yml
File metadata and controls
262 lines (254 loc) · 12.2 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
name: AGENTS.md Integrity (reusable)
# Reusable workflow that checks a repo's AGENTS.md against the Comfy AGENTS.md
# standard (Comfy Engineering Guide, "AGENTS.md, done right", §10): one thin
# top-level AGENTS.md as the single source of truth, a one-line `@AGENTS.md`
# CLAUDE.md shim, no divergent `.cursorrules`, a hard under-200-line ceiling
# (Anthropic guidance) with an aspirational 150-line target, character limits
# (a paragraph is one line, so lines alone do not bound size), per-subtree shims
# in monorepos, and a CODEOWNERS DRI. It runs in the CALLER's context, so
# `actions/checkout` checks out the calling repo and the checks operate on that
# repo's files. Fails with a non-zero exit (and GitHub annotations) so it can
# be wired as a required status check.
#
# The check script is loaded from THIS repo (public, pinned via `workflows_ref`)
# — never from the caller's checkout — so a PR can't rewrite the checker.
#
# Caller pattern (place in the consumer repo at
# .github/workflows/agents-md-integrity.yml):
#
# name: AGENTS.md Integrity
# on:
# pull_request:
# push:
# branches: [main, master]
# jobs:
# agents-md:
# permissions:
# contents: read
# uses: Comfy-Org/github-workflows/.github/workflows/agents-md-integrity.yml@<sha> # v1
# with:
# workflows_ref: <same-sha> # REQUIRED — same SHA as `uses:` above
#
# INPUTS (`workflows_ref` is REQUIRED; all others optional):
# max_lines Hard line ceiling for AGENTS.md; over it FAILS.
# Default 200 (official Anthropic guidance).
# warn_lines Aspirational target; over it emits a non-fatal WARNING
# (but at/under max_lines still passes). Default 150.
# max_chars Hard ceiling on each agents file's total CHARACTERS
# (root and every nested one); over it FAILS. Default 0
# = OFF, so bumping the pin never turns a caller red;
# set 40000 (Claude Code's oversized-memory-file floor)
# to enforce it. The line ceiling alone does not bound
# size: a paragraph is one line.
# warn_chars Total-character target; over it WARNS (never fails).
# Default 25000. 0 = off.
# max_line_chars Per-line character ceiling; any longer line WARNS
# (never fails), catching a paragraph kept on one line.
# Default 3000. 0 = off.
# Every character finding names the file, its size, its
# longest lines, and the remedy: move rationale into
# `docs/agents/` and leave a one-line pointer.
# forbid_cursorrules When true, a legacy root `.cursorrules` file FAILS the
# check (AGENTS.md is the single source of truth).
# Default true.
# check_nested When true, every nested `AGENTS.md` (monorepo subtree)
# must have a sibling `CLAUDE.md` containing `@AGENTS.md`,
# must itself be <= max_lines, and is held to the same
# character limits as the root file. Default true.
# exclude_paths Newline- or comma-separated path globs (repo-root
# relative) carved out of the NESTED scan — for a repo
# that ships agent-instruction files as distributable
# PAYLOAD (a plugin/skill marketplace), where the
# nested-shim rule does not apply to the payload but
# should still bind everywhere else. Prefer this over
# `check_nested: false`, which drops nested coverage for
# the whole repo. Excluded subtrees are never scanned,
# and each one is reported in the run log as
# `EXCLUDED: <path> (matched <glob>)`. A glob that would
# exclude the ROOT agents file or `CLAUDE.md`, or that
# asks for the whole repo without saying so (`/`, `*`,
# `*/**`), is rejected and the run fails — neither root
# compliance nor the nested scan as a whole is excludable.
# Default empty (nothing excluded). Example:
# exclude_paths: |
# plugins/**
# require_shim When true, a missing root `CLAUDE.md` FAILS whenever the
# agents file exists (Claude Code reads only CLAUDE.md and
# does not fall back, so no shim = invisible instructions).
# Default true.
# require_codeowners Controls the CODEOWNERS/DRI check severity. When false
# (default) an unowned AGENTS.md only WARNS; when true it
# FAILS. Default false (warn only).
# agents_file Path to the agents file to check. Default `AGENTS.md`.
# workflows_ref REQUIRED. Ref of Comfy-Org/github-workflows to load the
# check script from. Must be the SAME commit SHA you pin
# `uses:` to — no default, so a caller can't pin the
# workflow while loading the checker from a mutable branch.
#
# No secrets required.
on:
workflow_call:
inputs:
max_lines:
description: Hard line ceiling for AGENTS.md (over it FAILS). Default 200.
type: number
required: false
default: 200
warn_lines:
description: >-
Aspirational line target (over it WARNS, but at/under max_lines still
passes). Default 150.
type: number
required: false
default: 150
max_chars:
description: >-
Hard ceiling on each agents file's total characters, root and nested
(over it FAILS). Default 0 = off; 40000 matches Claude Code's
oversized-memory-file floor.
type: number
required: false
default: 0
warn_chars:
description: >-
Total-character target for each agents file (over it WARNS, never
fails). Default 25000; 0 = off.
type: number
required: false
default: 25000
max_line_chars:
description: >-
Per-line character ceiling for each agents file (a longer line WARNS,
never fails). Default 3000; 0 = off.
type: number
required: false
default: 3000
forbid_cursorrules:
description: >-
Fail when a legacy root `.cursorrules` file exists. Default true.
type: boolean
required: false
default: true
check_nested:
description: >-
Check nested (monorepo) AGENTS.md files for a sibling `@AGENTS.md`
CLAUDE.md shim, the line ceiling and the character limits. Default
true.
type: boolean
required: false
default: true
exclude_paths:
description: >-
Newline- or comma-separated path globs carved out of the nested
AGENTS.md scan (e.g. `plugins/**` for a repo that ships agent
instructions as distributable payload). Excluded subtrees are never
scanned and are reported in the run log. A glob matching the root
agents file or `CLAUDE.md`, or made only of wildcards, is rejected.
Default empty.
type: string
required: false
default: ''
require_shim:
description: >-
Fail when the root `CLAUDE.md` shim is missing while the agents file
exists (Claude Code reads only CLAUDE.md, no fallback). Default true.
type: boolean
required: false
default: true
require_codeowners:
description: >-
Escalate the CODEOWNERS/DRI check from a warning to a failure. Default
false (an unowned AGENTS.md only warns).
type: boolean
required: false
default: false
agents_file:
description: Path to the agents file to check. Default `AGENTS.md`.
type: string
required: false
default: AGENTS.md
workflows_ref:
description: >-
REQUIRED. Ref of Comfy-Org/github-workflows to load the check script
from. Must be the SAME commit SHA you pin `uses:` to — otherwise the
workflow is pinned but the code it runs is not. There is deliberately
no default: a floating `main` default would silently load a mutable
checker.
type: string
required: true
permissions:
contents: read
jobs:
check:
name: AGENTS.md integrity
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout caller repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Require a pinned workflows_ref
# workflows_ref has no default on purpose. GitHub does NOT enforce
# `required: true` for workflow_call inputs, so an omitted input arrives
# as '' and actions/checkout would silently fall back to this repo's
# default branch — running MUTABLE scripts under a pin that claims
# otherwise. Fail fast instead. (BE-5546)
env:
WORKFLOWS_REF: ${{ inputs.workflows_ref }}
run: |
# actions/checkout reads `ref` through core.getInput, which TRIMS, so a
# whitespace-only value is an empty ref to IT while sailing past a bare
# -z test here. Compare the stripped form, and echo only that: dropping
# newlines also stops a multi-line value from smuggling a ::workflow
# command:: into the log, and from satisfying the line-oriented grep
# below on one 40-hex line among many.
REF="$(printf '%s' "$WORKFLOWS_REF" | tr -d '[:space:]')"
if [ -z "$REF" ]; then
echo "::error::workflows_ref is required; pin it to the same commit SHA as the uses: line (see header example)"
exit 1
fi
if ! printf '%s' "$REF" | grep -Eq '^[0-9a-f]{40}$'; then
echo "::warning::workflows_ref '$REF' is not a full 40-hex commit SHA — branch and tag refs are mutable and can skew between jobs mid-run"
fi
- name: Load integrity check script
# The checker comes from THIS repo (public, pinned via workflows_ref),
# never from the caller's checkout, so a PR can't rewrite the check.
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: Comfy-Org/github-workflows
ref: ${{ inputs.workflows_ref }}
path: _agents_md_integrity
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Check AGENTS.md integrity
env:
AGENTS_CHECK_ROOT: ${{ github.workspace }}
MAX_LINES: ${{ inputs.max_lines }}
WARN_LINES: ${{ inputs.warn_lines }}
MAX_CHARS: ${{ inputs.max_chars }}
WARN_CHARS: ${{ inputs.warn_chars }}
MAX_LINE_CHARS: ${{ inputs.max_line_chars }}
FORBID_CURSORRULES: ${{ inputs.forbid_cursorrules }}
CHECK_NESTED: ${{ inputs.check_nested }}
REQUIRE_SHIM: ${{ inputs.require_shim }}
REQUIRE_CODEOWNERS: ${{ inputs.require_codeowners }}
AGENTS_FILE: ${{ inputs.agents_file }}
EXCLUDE_PATHS: ${{ inputs.exclude_paths }}
run: |
# `--exclude` is added ONLY when the caller set a non-blank value, so
# a caller that never heard of exclude_paths runs the exact command
# it ran before.
args=(--root "$AGENTS_CHECK_ROOT")
if [ -n "$(printf '%s' "$EXCLUDE_PATHS" | tr -d '[:space:]')" ]; then
# Attached form: as a separate token, a legitimate glob starting
# with a hyphen (`-generated/**`) is parsed by argparse as an
# unknown option and the job dies with a usage error.
args+=("--exclude=$EXCLUDE_PATHS")
fi
python3 "$GITHUB_WORKSPACE/_agents_md_integrity/.github/agents-md-integrity/check_agents_md.py" \
"${args[@]}"