Split out of #831 / #832, which adds a shell prompt to CLAUDE.md's tag-slide policy and explicitly labels it a prompt rather than a gate.
Why the shell form cannot be a gate
It greps added diff lines, which is not the same question as "did a job gain a permission key":
- a key whose only change is its trailing comment appears as an addition (measured: the first draft flagged
.github/workflows/claude-review.yml's checks: read in #832 itself, where only the comment differed);
- a permission added to a job that previously had no
permissions: block at all looks identical to any other added line, and so does an entire permissions: block added wholesale;
- it cannot tell which job gained the key, so a
permissions: block on a job that is not the workflow_call entry point reads the same as one on a job that is.
#832 narrows it to files containing workflow_call:, which removes the caller false positives, but the remaining imprecision is structural. A stop-gate that cries wolf gets read past, which is worse than no gate.
What the instrument should do
Parse both refs, walk jobs.*.permissions per callee workflow, and compare the key sets per job:
- an added key on any job of a
workflow_call workflow is a stop;
- a removed or unchanged key is fine;
- a job gaining a
permissions: block where it had none is an addition of every key in it;
- a job losing its block inherits the caller's, which is a widening and needs its own verdict.
Fail closed on an unparsable workflow, per the precedent in audit_workflow_token_usage.py / audit_workflow_action_pins.py, and report how many callee jobs were examined so a vacuous run is distinguishable from a clean one.
Wire it into slide-major-tag.yml so the slide itself refuses, rather than relying on the operator running the prompt.
Precedent
.github/workflows/scripts/audit_workflow_token_usage.py and audit_workflow_action_pins.py are the shape to copy, including sharing workflow_discovery.py rather than re-deriving the population, and the run-workflow-audit-tests.py conventions (negative controls, a .yaml fixture, confirmed mutations).
Posted by Claude Code (AI agent) --- not written by a human.
Split out of #831 / #832, which adds a shell prompt to CLAUDE.md's tag-slide policy and explicitly labels it a prompt rather than a gate.
Why the shell form cannot be a gate
It greps added diff lines, which is not the same question as "did a job gain a permission key":
.github/workflows/claude-review.yml'schecks: readin #832 itself, where only the comment differed);permissions:block at all looks identical to any other added line, and so does an entirepermissions:block added wholesale;permissions:block on a job that is not theworkflow_callentry point reads the same as one on a job that is.#832 narrows it to files containing
workflow_call:, which removes the caller false positives, but the remaining imprecision is structural. A stop-gate that cries wolf gets read past, which is worse than no gate.What the instrument should do
Parse both refs, walk
jobs.*.permissionsper callee workflow, and compare the key sets per job:workflow_callworkflow is a stop;permissions:block where it had none is an addition of every key in it;Fail closed on an unparsable workflow, per the precedent in
audit_workflow_token_usage.py/audit_workflow_action_pins.py, and report how many callee jobs were examined so a vacuous run is distinguishable from a clean one.Wire it into
slide-major-tag.ymlso the slide itself refuses, rather than relying on the operator running the prompt.Precedent
.github/workflows/scripts/audit_workflow_token_usage.pyandaudit_workflow_action_pins.pyare the shape to copy, including sharingworkflow_discovery.pyrather than re-deriving the population, and therun-workflow-audit-tests.pyconventions (negative controls, a.yamlfixture, confirmed mutations).Posted by Claude Code (AI agent) --- not written by a human.