Skip to content

Commit 9b8feda

Browse files
docs: reconcile zizmor.md and AGENTS.md gate/fork-upload wording
Tighten the developer-facing and maintainer-facing descriptions of the zizmor PR gate: consolidate where findings surface (annotations always, Security-tab upload on GHAS including fork PRs), and condense the job-status-vs-code-scanning-results rationale. Also broaden the commit-convention line to cover any human directing an agent to author. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Mark Matyas <mmatyas@qti.qualcomm.com>
1 parent f25b282 commit 9b8feda

2 files changed

Lines changed: 34 additions & 72 deletions

File tree

‎.github/zizmor.md‎

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,14 @@ inline ignore). See *For maintainers* for the ruleset details.
1616
- A **GitHub Actions Security Scan (zizmor)** check appears on PRs automatically.
1717
- If there are **high-severity** findings, the check **fails** and blocks the
1818
merge. Lower-severity findings are advisory (surfaced, non-blocking).
19-
- Findings appear as **inline annotations** on the check. On repositories with
20-
GitHub Advanced Security, full results (all severities) are also uploaded to the
21-
**Security → Code scanning** tab.
22-
- The gate works the same way on **pull requests from forks** — the scan runs and
23-
blocks on high-severity findings. On GHAS-enabled repos, fork PRs also populate
24-
the Security tab: GitHub's code-scanning upload endpoint accepts SARIF from a
25-
fork PR's read-only token on `pull_request` runs, so no write token is required.
26-
27-
### Where findings show up
28-
29-
The check always renders blocking findings as inline annotations (GitHub shows at
30-
most 10 per step; the step log has the full list). On repos with code scanning
31-
available, all findings (all severities) are additionally uploaded to the Security
32-
tab for history and lower-severity visibility — this works on fork PRs too. Only
33-
repos without code scanning fall back to annotations-only. The run's job summary
34-
explains what happened for that specific run.
19+
- Findings always appear as **inline annotations** on the check (GitHub shows at
20+
most 10 per step; the step log has the full list). On repos with GitHub Advanced
21+
Security, all findings (all severities) are *also* uploaded to the **Security →
22+
Code scanning** tab; repos without it get annotations only. The run's job summary
23+
explains what happened for that run.
24+
- This all works the same on **pull requests from forks**, Security-tab upload
25+
included: GitHub's code-scanning endpoint accepts SARIF from a fork PR's
26+
read-only token on `pull_request` runs, so no write token is required.
3527

3628
## Fixing a finding
3729

‎AGENTS.md‎

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ rulesets; they are not meant to run against this repo.
3636

3737
## Commit conventions
3838

39-
- Commit as the repo owner (Mark Matyas). Use a `Signed-off-by:` trailer
39+
- Commit as the repo owner (Mark Matyas) or other human directing an agent to author contributions. Use a `Signed-off-by:` trailer
4040
(`git commit --signoff`).
4141
- Credit AI assistance with an `Assisted-by: <tool>:<model>` trailer, matching
4242
existing history.
@@ -126,66 +126,36 @@ Push-surface design (in `qcom-reusable-workflows`, planned):
126126
workflow input that any repo dev could flip off.
127127
- Coverage boundary: this surface only reaches repos that CALL the reusable
128128
workflow. Repos that don't get PR-gate coverage only (no push scan) until the
129-
GitHub App lands.
130-
131-
**Long-term:** a GitHub App replaces the push surface entirely (server-side scan on
132-
push webhooks with its own credentials), covering fork PRs and repos that don't
133-
call the reusable workflow — full control regardless of local workflow files.
129+
**GitHub App** long-term replacement lands (server-side scan on push webhooks
130+
with its own credentials — covers fork PRs and non-adopting repos regardless of
131+
local workflow files).
134132

135133
## PR gate: ONE ruleset rule, gate on job status
136134

137-
**Current approach (interim).** Enforce with a SINGLE ruleset rule:
138-
**"Require workflows to pass before merging"**, source repo
139-
`qualcomm/qcom-enterprise-workflows`, workflow `.github/workflows/zizmor-scan.yml`.
140-
The merge gate is the **job's exit status**: the `Scan and enforce (gate)` step
141-
runs zizmor in `--format=github` (annotations) mode, which PRESERVES zizmor's
142-
severity exit codes (11–14), so the job fails when the highest finding is at or
143-
above `ZIZMOR_FAIL_SEVERITY` (default `high`).
144-
145-
**Rollout without blocking teams: Evaluate mode.** Set the ruleset to **Evaluate**
146-
(not Active) during rollout. For the "require workflows to pass" rule specifically,
147-
Evaluate mode **still runs the workflow** — so PR annotations and job summaries are
148-
posted and developers see findings — but **nothing is blocked** (neither merges nor
149-
direct pushes). Flip to **Active** to start blocking. This is the interim that
150-
avoids the direct-push block impacting teams while findings are still surfaced.
151-
`zizmor-scan.yml` lists ONLY ruleset-injected events (`pull_request`, `merge_group`);
152-
it has no `push`/`workflow_dispatch` triggers (they would be no-ops in targets).
153-
Verified: the enterprise ruleset triggers the workflow with only "Require workflows
154-
to pass before merging" enabled.
135+
Enforce with a SINGLE ruleset rule: **"Require workflows to pass before merging"**,
136+
source repo `qualcomm/qcom-enterprise-workflows`, workflow
137+
`.github/workflows/zizmor-scan.yml`. (Verified: the ruleset triggers the workflow
138+
with only this rule enabled.) The workflow has two steps:
139+
140+
- **`Scan and enforce (gate)`** — the gate; always runs. `annotations:true`,
141+
`advanced-security:false`, `min-severity:<fail-severity>`. Preserves zizmor's
142+
severity exit codes, so the job fails when the highest finding is ≥
143+
`ZIZMOR_FAIL_SEVERITY` (default `high`). Identical on fork PRs, no-GHAS repos,
144+
and normal PRs — needs no analysis, GHAS license, or write token.
145+
- **`Upload results to code scanning (best-effort)`** — GHAS repos only (fork PRs
146+
INCLUDED). `advanced-security:true` (SARIF, exits 0) + `continue-on-error:true`,
147+
so it is purely COSMETIC (populates the Security tab, never affects the gate).
148+
Skipped, not failed, on no-GHAS repos.
155149

156150
**Why NOT "Require code scanning results".** That rule can only require a tool that
157-
has **already produced an analysis** for the repo — and nothing in the central
158-
ruleset produces one (a ruleset can only inject on PR events, never `on: push`). A
159-
repo with no local zizmor workflow, not yet reached by the push-scan surface, has
160-
no analysis, so the rule **fails closed** and blocks every PR (observed: *"Waiting
161-
for Code Scanning results — Code Scanning may not be configured for the target
162-
branch"*). Gating on job status needs no pre-existing analysis, no GHAS license,
163-
and no write token, so it works identically on every repo. This mirrors **Grafana's**
164-
at-scale zizmor rollout, which also gates on the job exit code. (Note: fork PRs
165-
CAN upload SARIF — GitHub's code-scanning endpoint accepts SARIF from the read-only
166-
fork token on `pull_request` runs, verified empirically — so the fork token is NOT
167-
a reason to avoid code-scanning results; the analysis-provenance problem above is.)
168-
169-
**What each part does in `zizmor-scan.yml`:**
170-
171-
- **`Scan and enforce (gate)`** — always runs; the gate. Uses
172-
`advanced-security:false`, `annotations:true`, `min-severity:<fail-severity>`.
173-
Fails the job on findings ≥ threshold. Identical behavior on fork PRs, no-GHAS
174-
repos, and normal PRs.
175-
- **`Upload results to code scanning (best-effort)`** — GHAS-enabled repos only
176-
(fork PRs INCLUDED). Uses `advanced-security:true` (SARIF, exits 0 on findings)
177-
with `continue-on-error:true`, so it is purely COSMETIC: populates the Security
178-
tab / full-severity history and can NEVER affect the gate. Skipped (not failed)
179-
on no-GHAS repos.
180-
- **`ZIZMOR_FAIL_SEVERITY`** — rollout lever. `never` = advisory (scan + annotate,
181-
never block); ratchet `high → medium → low` to tighten. Onboard new fleets at
182-
`never` first.
183-
184-
**Long-term direction:** a GitHub App + backend service to scan on push webhooks
185-
with its own credentials and govern dismissals, covering repos that don't call the
186-
reusable workflow. Once analysis is guaranteed for every repo, "require code
187-
scanning results" becomes viable as the gate. The single-rule/job-status model
188-
above is the interim bridge until that exists.
151+
has **already produced an analysis** for the repo — and nothing central produces
152+
one (a ruleset injects only on PR events, never `on: push`). A repo not yet reached
153+
by the push surface has no analysis, so the rule **fails closed** and blocks every
154+
PR (*"Waiting for Code Scanning results…"*). Job-status gating has no such
155+
dependency. This mirrors **Grafana's** at-scale rollout. Note: this is NOT a
156+
fork-token problem — fork PRs CAN upload SARIF (GitHub's endpoint accepts the
157+
read-only fork token on `pull_request` runs, verified empirically). The blocker is
158+
analysis provenance, which only the push surface / GitHub App resolves.
189159

190160
## Exceptions / governance direction
191161

0 commit comments

Comments
 (0)