This text was generated by an LLM (how cgwalters uses LLMs). cgwalters has not reviewed it yet.
GitHub Actions for cgwalters-forge and bootc-dev workflows, with one action per
directory. Use them as cgwalters-forge/actions/<name>@main. Work is tracked
in this repository's issues.
| Action | Status | Roadmap |
|---|---|---|
| lint | actionlint, zizmor and the runner-label policy; this repository's CI calls it | Called from each forge repository's CI |
| workflows-check | Required workflow over a pull request's workflow files: job shape, new dangerous triggers, new zizmor ignores, linter configuration and .gitignore changes, checker references; CI runs its tests with fixture workflows |
An organization ruleset requires it, evaluate mode first |
| secure-host | Runs agentic-job's attested, digest-pinned image: no later step has root or the metadata service, world-write by owner only; CI runs the two-job example, the inputs and the world-write policy on ubuntu-26.04 | agentic-job's own action forwards here; opt-in egress (agentic-job#995) |
| app-token | Mints and masks one role's GitHub App installation token, scoped to named repositories; remint.mjs mints again near expiry or after a 401; CI runs its unit tests, no real mint (no app here) |
Move each role to its app (tracker#766) |
| admit | Fails unless the calling repository's owner id is in its owners.json; both reusable wrappers run it first; CI runs its unit tests | Brokers and tailnet federation match the same ids |
| deployment | Outputs the deployment's defaults from its deployment.json (the agent configuration repository and model); the wrappers use them after their input and variable; CI runs its unit tests | More defaults that name our resources move there |
| renovate | A Renovate preset that moves GitHub-hosted Ubuntu runner labels and nothing else; this repository extends it; just test-renovate validates it and dry-runs fixtures, not in CI |
Each forge repository extends it, once the Renovate app is installed for it |
| tailscale | Settings by organization; CI runs its unit tests and checks the socket and CLI are closed after a join that fails, no real join (no tailnet credentials here) | Replace devspace.yml's and agent.yml's tailscale/github-action steps |
One reusable workflow is here too:
cgwalters-agentic-job, an
agent run of cgwalters-forge/agentic-job with this deployment's tailnet
join, inference proxy, runner, agent and model. Our repositories call it
at main. Any public repository can call it too, and its run's identity
tokens carry the same job_workflow_ref, so the inference broker and the
tailnet federation must match repository_owner_id against the ids in
admit/owners.json as well as that job_workflow_ref;
both wrappers reject any other owner before assigning the job to the
caller-selected runner (admit). With an
inference-url other than ours it needs an inference-audience too, so
that our broker's audience is not sent elsewhere by default (the review
wrapper's variables still default apart). It refuses runs
from a private repository, so agentic-job's opt-in for private
repositories stays off. That opt-in admits only the calling repository.
A repository gets agent runs with the workflow below, a bounds file at
.github/agentic-job/dispatch.toml (and fix.toml for the fix kind), a
runner configuration at .github/agentic-job/hosted.toml, and an
environment that gates the apply token; cgwalters-forge/tracker has all four
to copy. The file's header says what else a caller may set.
Before adopting v0.6.5, callers must migrate every bounds file (allow,
fix-allow and both review bounds): move [outputs], [trigger],
[memory] and [secondary_pull_request], when present, under [github]
(for example, [github.outputs]). Keep shared limits and repository
selectors at the top level; exactly one forge section is required.
Policy calls also require actions: read in the caller's permissions.
Both wrappers pin agentic-job by commit: 427ce22, main's commit seven
past v0.6.12 that adds the own_author push bound (agentic-job#3164),
which tracker's fix runs need for pull requests the bot authored. Its
comment is the tag of that commit's CI build, build-e9bea05..., since
zizmor resolves the comment. Move the pins to the first release after it
(v0.6.13); a proposal of v0.6.12 would be a downgrade.
Since v0.6.12 the agent is the runner's user, the only agent user
(agentic-job#2989), and the wrappers follow upstream's runner-only
boundary. The agent can read whatever its job holds, so the agent job,
cgwalters-agent.yml, has no
id-token and no repository secret: only read permissions, the run token
and a spent tailnet key. Before it, each wrapper's register job, the one
job with an identity token, runs on a hosted runner where nothing of the
agent's or the target's runs. It stays in the wrapper's own file because
the token's job_workflow_ref names the file that defines the job, and
that is what the broker and the tailnet admit. It joins the tailnet, mints
a single-use ephemeral key for the agent job's join
(tailscale), and
registers the run at the broker with the policy job's binary. Both values
reach the agent job only as secrets of its call, which masks them in its
log; the agent job is called by local path, so at the wrapper's commit.
Re-running only a failed agent job fails at the join, since the first
attempt spent the key: re-run all jobs. The agent job then runs upstream's agent.yml sequence: prepare,
agentic-job's runner-user admission, the join, the reachability check,
agentic-job config --host (with no setup.script), the pinned
secure-host forwarder, the setup script as the runner's user, and run
with --runner-home. The pinned image installs its own, older
agentic-job in /usr/local/bin, so the host configuration puts a copy
of the policy binary first on the agent's PATH: the agent's
agentic-job handback commands must match the binary run starts. The
wrapper-runner CI job in
secure-host.yml runs that sequence on
a fresh runner, with no inference or tailnet, and checks that the host
passes run's own probe, the runner has lost sudo and the agent's PATH
finds the policy binary. It is also a deterministic scripted dispatch: the
policy call selects the pinned helper's scripted agent (fake, comment
profile), run starts it with an empty GitHub token and no run token, the
pinned check.yml checks its handback, and wrapper-scripted-dispatch
passes only for an agent exit of 0 and one checked comment with no patch.
Nothing is applied, and the job holds no secret. This is the
repository's own base configuration; the wrappers still refuse fake from
callers. It covers the shared agent job's steps and check's input, not the
tailnet join, registration or apply, which only real runs exercise.
What needed a second user is refused before anything is provisioned: the
dispatch wrapper's session (and idle-minutes), inference-register: api-key, inference-provider and an INFERENCE_API_KEY. The inputs stay
declared so a caller passing them gets that message. The scripted agent
fake stays unsupported. Upstream's own admission refuses a configuration
with [mcp], api-key or a session listener, and run refuses
[child-agent].
The release train may replace only the local secure-host forwarder in the
agent job and the wrapper smoke job with
cgwalters-forge/actions/secure-host@<full immutable commit>: the verified
image PR's merge commit, independently of the agentic-job release commit.
Mutable refs and other action sources are not accepted by the contract tests.
Policy, check, apply and the policy-source checkout/prepare/run sequence stay
on the agentic-job pin.
Since v0.6.0 the agent's home no longer copies the
runner image's /etc/skel, including toolchains and dotfiles.
Mid-run steering stays off unless the caller's runner configuration enables
[session] watch-item = true. Policy derives the watched item from the
dispatch wrapper's issue (in output-repo) or the review event;
run doesn't need any additional input. Upstream limits delivery to 10 updates of
8000 characters, quoted as untrusted item text, not instructions.
name: dispatch
run-name: ${{ inputs.kind }} ${{ inputs.repo }}#${{ inputs.item }}
on:
workflow_dispatch:
inputs:
task: {type: string, required: true}
repo: {type: string, required: true}
item: {type: string, required: true}
kind: {type: choice, required: true, options: [implement, triage, research, review, fix]}
permissions: {}
concurrency:
group: dispatch-${{ inputs.kind }}-${{ inputs.repo }}-${{ inputs.item }}
jobs:
run:
uses: cgwalters-forge/actions/.github/workflows/cgwalters-agentic-job.yml@main
permissions: {contents: read, issues: write, pull-requests: write, actions: read, id-token: write}
with:
task: ${{ inputs.task }}
repo: ${{ inputs.repo }}
item: ${{ inputs.item }}
kind: ${{ inputs.kind }}
apply-environment: agent-apply
secrets:
SAFE_OUTPUTS_PAT: ${{ secrets.CGWALTERS_BOT_PAT }}The wrappers need no secondary-specific input. At the audited wrapper revision
ba6c569,
policy, check and apply all pin agentic-job 427ce22; its
workflow contract
and safe-output contract
govern this feature, not the older v0.6.11 release plan.
The concrete threat is an agent redirecting a privileged write to another
repository. Secondary publication stays off unless the caller's bounds file
sets [github.secondary_pull_request], with an exact repo, base,
branch_prefix, max = 1, max_patch_bytes and max_patch_files.
Check enforces these bounds independently; apply consumes only checked outputs.
Secondary file exemptions must be explicit in that table's
unprotected_files; primary exemptions are not inherited. See the pinned
contract for the patch/base metadata and run-specific branch requirements.
Dispatch forwards the caller's allow (or fix-allow for fixes) to policy,
with a total max-outputs of 3, including any secondary request. Both
review paths allow only 1 output. The caller's max_outputs must admit
the requested total; per-type limits still apply. A secondary request adds
one to the primary create_pull_request.max collector allowance, not to the
wrapper's total ceiling: two PRs leave room for only one other request.
Do not raise a caller's total above 3 expecting this wrapper to request more.
Tracker's activation child owns its bounds changes and the live
primary-plus-secondary publication test; this audit enables no caller.
Both wrappers explicitly forward SAFE_OUTPUTS_PAT to apply, never to the
agent or check. Secondary publication requires that PAT, with access to fork
the secondary repository, disable Actions on its fork, push there and open a
PR; the job token is insufficient. The bot must not own the secondary target.
Live acceptance of the migrated wrappers is recorded by tracker dispatch
42348718765
(register, agent, check and apply succeeded) and event review
42320676566
(register, agent, check and submit succeeded). Both runs' referenced_workflows
resolve the wrappers and shared agent job to ba6c569, and the helpers to
427ce22. The older misctool review 41920939510 resolves to wrapper
5906e674 and helpers v0.6.1, so it is not acceptance of this migration.
These are real agent runs, not the host smoke test; they do not prove
primary-plus-secondary publication or complete #138's required canary rollout.
Two later runs record the runner-only boundary at each wrapper revision
(actions#103). Tracker dispatch
42320486895
(research on actions#196): its run job resolves the dispatch wrapper and
shared agent job to ba6c569 and policy, check and apply to 427ce22.
The 3f9f642 helpers that referenced_workflows also lists belong to
tracker's skipped review job, pinned to wrapper f05e237. Register, agent,
check and apply all succeeded, and apply posted the checked comment
6699900595,
showing the agent as runner running the policy binary 0.6.12.
Event review
42376903094
of #223 resolves the review wrapper and agent job to abc3dba and the
helpers to 427ce22. Its submit job posted APPROVED review
5812467802
with commit_id 7a3ffdd, the admitted head, and apply was skipped. In
both, the agent job's token had only Actions, Contents, Issues,
PullRequests and Metadata read and no id-token. Both checkouts set
persist-credentials: false, and only the register job minted the tailnet
key, "a single-use, ephemeral key, valid for 60 minutes", which the agent
job's join spent. Between ba6c569 and e084c52 the wrappers changed
only their # main self-pins. Those pins moved from 1e867fc, and across
that range admit, deployment and tailscale changed only in
tailscale/setup.test.mjs. A real dispatch and review at a
later wrapper revision with other changes still need recording again.
cgwalters-review is the reusable
event-review wrapper. It composes agentic-job's policy, check, apply and
review helpers at the pinned commit, with the dispatch wrapper's register
and agent jobs, so that the tailnet and the inference broker admit this
file's job_workflow_ref. agentic-job's review workflow can now run a
caller's own network step instead (its #705), but callers have not switched
to it. It refuses private repositories, as the dispatch wrapper does. It adds
our tailnet join and defaults: opencode, the model from
deployment.json, ubuntu-26.04, and the dispatch
wrapper's inference URL and audience. It has no inputs. Repository/organization variables
AGENT, AGENT_MODEL, AGENT_RUNNER (JSON), INFERENCE_URL,
INFERENCE_AUDIENCE, AGENT_CONFIG, AGENT_CONFIG_REPO and
AGENT_CONFIG_PATH select owner-controlled configuration. Set the last
two for agents needing external configuration; there is no hard-coded
configuration repository. AGENT_CONFIG otherwise uses the pinned
agentic-job hosted configuration. AGENTIC_JOB_TAILSCALE_OAUTH_CLIENT_ID
and AGENTIC_JOB_TAILSCALE_TAGS, read by both wrappers, are the tailnet
login of an organization that tailnets.json
doesn't list. When set, they also replace a listed organization's entry. Either
way the wrappers run only for an owner that
admit/owners.json lists.
Call it from the same pull_request_target and issue_comment events and
job condition as this repository's caller,
replacing the job's uses with:
uses: cgwalters-forge/actions/.github/workflows/cgwalters-review.yml@mainKeep that caller's six permission grants. Optional SAFE_OUTPUTS_PAT is
forwarded explicitly, only to apply; it never reaches the agent or the real
review submission. Keep workflow/review.toml, workflow/review-command.toml
and workflow/review.md on the caller's base branch. Automatic reviews admit
non-draft owner/member/collaborator PRs, preserving upstream's association
rule for all authors. Otherwise-unassociated bots wait unless REVIEW_BOT_IDS
lists their numeric IDs and the bounds admit their logins (including
Dependabot). Use a JSON array of numbers; Actions also coerces string IDs.
/review still requires write access through event policy.
Only admitted agent jobs enter per-PR concurrency; new automatic events
cancel the old agent, comments do not. The head is data inside the sandbox,
never executed by a privileged step. Each review is bounded to 150 requests,
500 AIC and 75 agent minutes.
AGENT_REAL_REVIEWS=true opts into checked APPROVE/REQUEST_CHANGES
submission with the job token, at the admitted head. Leaving it unset keeps this off. Enable
GitHub Actions' permission to create/approve PRs for this feature. A stale
head, cancellation or failed check cannot produce a new approval. The
compatibility review status is not a merge gate or human approval.
For a new repository in this organization, put the review caller and the
three workflow/review* files in its initial commit, and set the
variables above (copy them from an onboarded repository such as tracker)
and Actions' permission to approve pull requests when you create it. The
organization's ruleset requires an approving review on main and has no
bypass actors, but the first push to an empty repository isn't a pull
request, so the review is in place before the first one. If you add it later, the
onboarding pull request itself needs an approval from an organization
owner, because nothing can review it yet.
A repository whose name ends in -sandbox is for agents to iterate in
freely: the organization's approval ruleset excludes it, so its pull
requests merge without a review. When it's ready, renaming it to drop the
suffix brings it under the ruleset; the review above should be in place
before that rename.
Before migrating tracker (a separate repository), admit this wrapper's
job_workflow_ref, with the owner ids of admit/owners.json, in the
tailnet federation and inference broker, then
replace its review caller's uses as above and exercise a real review and
a synchronize cancellation. No ACL, OAuth or broker policy is changed here;
record those deployment changes in the private wiki when made. Removing the
deployment-specific pieces from agentic-job is a separate follow-up.
just test-lint checks the wrapper's admission and publication boundaries;
live tailnet joins and GitHub review writes are not exercised locally.
The review wrapper pins this repository's admit, deployment and
tailscale actions by full commit too, so moving main cannot change
privileged code behind a caller's reviewed wrapper pin. Renovate's scoped
git-refs manager in .github/renovate.json proposes
updates from main, grouped as review actions with automerge off. Unlike
release-tag updates, this works without this repository publishing tags. These
updates must merge through review here; a caller pinned to the wrapper's
commit gets them only by reviewing an update to that pin. The dispatch
wrapper continues to use these actions at main for non-review kinds;
its review kind uses separate literal commit-pinned steps.
This is the wrapper slice of actions#174 / tracker#1171, not a caller
cutover. No pool, caller, admission or ruleset is activated here, and
cgwalters-review.yml remains unchanged. The concrete threats are spending
an unpaced fallback model, reading PR-owned policy with privilege, leaking
a GitHub token to hostile head code, and publishing a stale or unchecked
approval. Review dispatch therefore has a closed contract:
- Run
dispatch.ymlin the public base repository, on its default branch. Passkind: review,repo: OWNER/REPOequal to that repository, positiveitem, exact lowercase 40-hexhead, and nonemptypool. These are agent-board#620's ordinary sender inputs, including its fixedtaskhint; that hint is ignored, not review instructions. The caller names the runreview OWNER/REPO#ITEM HEAD (POOL)for the pass reader. - The base repository variable
REVIEW_POOLSis the only pool map. Unset means every review dispatch fails. It is a JSON object, for example{"openai":{"agent":"opencode","model":"provider/model","config":".github/agentic-job/review-hosted.toml"}}. This is an example, not a deployed pool. Keys are lowercase letters, digits or hyphens, start with a letter, and are at most 32 characters. Each selected value has onlyagent(opencodeorclaude), nonempty single-wordmodel(at most 200 characters, letters/digits and_./:+-), and optionalconfig. Config is a relative TOML path without empty,.or..components; absent uses the pinned helper's.agentic-job-source/.github/agentic-job/hosted.toml. A base-owned custom config can name agent configuration repositories and setup. There is no AGENT/AGENT_MODEL/AGENT_CONFIG fallback, nor dispatched agent, model, runner, config, endpoint, budget or task override for reviews. - The trusted dispatched default-branch SHA owns
workflow/review.toml,workflow/review-command.toml,workflow/review.md, merge guidance and the selected config. Review always uses those fixed bounds/task paths. Admission accepts the shipped TOML subset: one-line JSON string arrays forhosts,repos,basesand trigger lists; a booleanforks; no duplicate admission keys or unknown trigger keys. Other policy/output settings are checked by the pinned policy helper. Unsupported admission syntax fails closed. Human authors still need OWNER/MEMBER/COLLABORATOR association and an admitted role (all, or their current permission). Bots always need both immutableREVIEW_BOT_IDSadmission and their bot login in the bounds, even with a trusted association. Otherwise an exact/reviewcomment must have a currently authorized admin/maintain/write author admitted by command bounds. Since #620 sends no command id, the wrapper checks at most 100 comments (a full page is refused) and the latest ten candidate commands; the pass still owns command consumption, deduplication and pacing. The admitted automatic or command bounds file also governs policy/check; automatic bounds cannot override command-specific fork/output limits. - PRs must be open, not draft, based on the base repository's default
branch and still at
head. Policy receives that expected head and checks it again. The hosted sandbox gets no GitHub token or provider key; the pinned runner fetches the exact head as data inside that sandbox, never as a privileged checkout. Reviews are capped at 150 inference requests, 500 AIC and 75 minutes through policy; the agent job retains its existing outer timeout. Broker aggregate limits still apply.
Publication remains off unless AGENT_REAL_REVIEWS=true. Only a successful
agent and successful check with APPROVE or CHANGES reach review submit;
noop, cancellation and failed checks never publish. Submit downloads only
the policy binary and this run's checked artifact, uses the base repository
GITHUB_TOKEN (not the apply PAT), binds commit_id, and generates the
trusted run marker and agent-job link. A preflight also rechecks draft/base;
the helper rechecks open state/head immediately before submission. Review
never falls through to PAT apply or an unbound stale verdict comment.
The helper's existing 422 fallback may post a marked comment instead of a
review; that is not an approval and the pass must not accept it as one.
Before activation, callers need a separately reviewed full-SHA review call,
the matching broker and tailnet job_workflow_ref admissions retaining
owner-ID restrictions, base-owned bounds/task/config and explicit pools.
Keep the existing event caller and its admission for rollback. Local tests
exercise admission, publication wiring and preflight; they do not prove a
live tailnet join, inference registration, sandbox head fetch, Actions bot
identity/commit/run-marker acceptance by the pass, or GitHub submission.
Those require the coordinated cgwalters-forge/review pilot, including
stale-head, cancellation, failed-check/noop and authorized/unauthorized
command cases. Actions must be allowed to create/approve PRs. No ruleset,
legacy removal or rollout is part of this change.
The review-only draft extension requested in actions#200 is not available.
Ordinary dispatched and event reviews still reject drafts; no opt-in or
agent-board-review-result-v1 writer job is exposed. The concrete threat is
laundering arbitrary agent prose (or an ordinary implementation run) into a
verified quick classification by attaching a trusted writer job name. An
ordinary checked APPROVE is not a checked draft-classifier result.
The binary prerequisite landed in agentic-job#3067 and v0.6.12
(eecc8c434e67a7607264ab3f7d90c88b6d02042a). The wrappers' current
427ce2295925654be9c35e91ca5cee91bbc2c9ea pin carries it, and the
runner-only migration above is complete; neither an older pin nor the
removed sandbox user is the remaining blocker. The pinned binary's
draft-classifier contract
requires REVIEW_DRAFT_CLASSIFIER=true in policy dispatch, check's verdict
step and submit, with add_comment,noop and max-outputs: 1.
The reusable workflows at that pin do not yet carry that setting or the
snapshot between stages. Specifically,
policy.yml
has no classifier input/environment on Resolve a dispatched review and
does not upload its $RUNNER_TEMP/dispatch-review.json with the policy
artifact.
check.yml
has no classifier setting and invokes review verdict without
--pull-request. A wrapper environment cannot be passed through a reusable
workflow call. Setting a repository variable alone therefore enables nothing.
The precise prerequisite is upstream reusable-workflow plumbing for a trusted,
off-by-default classifier setting, a policy-owned snapshot uploaded by ID,
and check invoking the binary with that snapshot and setting. Then pin all
helper calls and the agent sequence together to a compatible commit, before
adding wrapper admission or the reserved writer name. Do not duplicate the
policy/check workflows here or substitute a snapshot from the agent. Keep
ordinary draft rejection, credential-free head fetching, no partial apply,
cancellation/failed-check/noop guards and the base GITHUB_TOKEN writer.
Submit must receive BASE and revalidate repository, PR, head, base and exact
title/description. This mode grants no fix, ready, promotion or merge authority.
The exact consumer contract is agent-board#680's docs/poll.md.
Only an actual approving review may produce APPROVED with commit_id, one
agent-board-draft-review JSON fence and one trusted gh-aw run marker. Check
must reject duplicate fences/JSON fields, unknown fields/versions, forged
markers, wrong targets and oversized results; enforce the 8192-byte final
body limit (including the writer's footer/marker) and nonempty single-line
evidence/reason of at most 1024 bytes each. The schema is
agent-board/draft-review/v1; its exact fields are schema, repository,
pr, reviewed_head, title, description, semantic_diff,
description_assessment, change_category, risk, evidence and reason,
plus optional architecture_findings (an array of nonempty single-line
strings, each at most 1024 bytes). A nonempty architecture finding forces deep.
Only pass / matches / none for semantics / description / risk can
support quick; conservative, unknown or negative findings cannot claim it.
Categories are docs, dependencies, ci, mechanical or
narrow_fix_with_test, not path-derived proof. The consumer still owns its
deterministic file/line/path/CI vetoes and reservation/run provenance checks.
After that workflow plumbing lands, update the wrapper's helper pins together,
retain literal privileged dependency pins, and reserve the exact writer job
name only for independently checked kind=review classifier output. Its
publication must occur within that successful job's latest-attempt interval;
a skipped, failed, cancelled or unchecked job cannot attest a result. Keep
head code out of the writer context. Add offline helper fixtures for forged,
duplicate, oversized and mismatched fields, title/description edits, closed/
retargeted/moved PRs, explicit draft admission and exact writer provenance
before exposing the opt-in. Local regressions currently cover rejection and
the ordinary writer boundaries, not a functioning classifier.
Caller migration is blocked too: do not enable draft intake or rename a general job to satisfy the consumer. The four caller children and tracker#1324 remain separate and disabled until compatibility is proven. No caller configuration, permissions, credentials or live reviews change here; the wrapper does not yet expose the binary's classifier setting.
Runs may edit .github/ here, so pull requests are reviewed by
this repository's caller of the wrapper
above. It uses a local reference so it runs the base branch's version, never
the pull request's version, with this repository's bounds and task in
workflow/. The ruleset on main is to require its
approval, which is dismissed when new commits are pushed. The approval is submitted
only with the repository variable AGENT_REAL_REVIEWS=true, alongside the
variables listed above. Dependabot's pull requests are reviewed only with
its ID in REVIEW_BOT_IDS. Without .github/agentic-job/merge.toml the
reviewer is told that a person will approve after its review.
lock-new-items locks each newly
opened issue, so that only collaborators can comment on
it and agent runs on it don't read outsiders' comments (tracker#733). Its one
step locks the item by API with the caller's job token: no PAT, and
none of the item is read or run. Pull requests are not locked: a locked
PR refuses reviews from the review job token, and the supervisor only
dispatches issues. It does not cover the opener's own
text, comments in the seconds before the lock, items opened by another
workflow's job token or transferred in (no opened event), or items that
predate the caller. A repository opts in with this one
file, .github/workflows/lock.yml, matching
this repository's caller except for its uses:
name: lock
on:
issues:
types: [opened]
permissions: {}
jobs:
lock:
uses: cgwalters-forge/actions/.github/workflows/lock-new-items.yml@main
permissions:
issues: writeThe same pull request exempts the file in
.github/agentic-job/workflows.toml, creating it if needed, as this
repository does: without that, workflows-check's shape check refuses every
later pull request. Its zizmor needs the ref-pin policy for our own
workflows, as set in .github/zizmor.yml.
Licensed under either of Apache-2.0 or MIT, at your option.