Skip to content

fix: make the bundled pace pause zero minutes #1722

fix: make the bundled pace pause zero minutes

fix: make the bundled pace pause zero minutes #1722

Workflow file for this run

name: attribution
# Fails a pull request whose commits or body break abcd's AI-attribution
# convention: the kernel trailer `Assisted-by: Claude:<model-version>`, never
# `Co-Authored-By:` for an AI, never a tool's own "Generated with <tool>" footer
# (AGENTS.md § Attribution and acknowledgements, .github/CONTRIBUTING.md).
#
# It also refuses a LIVE AGENT-SESSION URL in any commit message in the range or
# in the pull-request body. That half was gated NOWHERE until it was added — not
# here, not in a hook, not in Go — and one reached three commit messages and two
# pull-request bodies of a managed public repo (iss-2609061438431625). It is the
# only half that is not a regex: `abcd lint outbound` judges it, because the
# opacity classifier that separates a real session id from a documentation slug is
# a conjunction POSIX ERE cannot express.
#
# WHY A GATE AND NOT PROSE: the convention has been written down since the
# beginning and drifted anyway — itd-91 records a reconciliation sweep across 78
# pull requests after PR bodies picked up a tool's default footer, and it drifted
# again on PR #212/#215. The commit half rarely slips, because a contributor
# writes it from AGENTS.md habit; the BODY half slips constantly, because it comes
# from a tool default that nothing was looking at. itd-91 owns the general version
# (a per-repo declared preference every surface reads); this enforces abcd's own
# answer meanwhile.
#
# It also hosts RS004's pull-request half — a title or body that names an iss-N
# must declare its relation to it — for the trigger reason below, not because the
# rule belongs to attribution. The rule itself stays in
# scripts/check-issue-resolution.sh alongside RS001-RS003.
#
# `edited` is in the trigger list deliberately: without it, a body that passed at
# open can be edited afterwards and the gate never re-runs. It is a separate
# workflow rather than a job in ci.yml precisely so a body edit re-runs THIS and
# not the two-platform build matrix. That trigger is also why RS004's form half
# is hosted here: ci.yml takes the default pull_request type list, so an edited
# title would never be re-judged there.
on:
pull_request:
types: [opened, edited, reopened, synchronize]
# Merge-queue entries re-run the required checks on the would-be merge result,
# so this workflow must trigger there for the check to report at all. The
# commit half runs there over the queue's own base..head — the commits the
# merge would land, including the one the merge creates — while the PR-FORM
# steps exempt themselves loudly below: entry to the queue already required
# the pull_request-level run green, and a merge_group event carries no PR body
# or title to validate.
merge_group:
# A push to main runs the commit half over what just landed (before..sha).
# A squash or web-UI merge creates a commit no pull-request run ever saw, and
# `make check-attribution` walks origin/main..HEAD, whose base is main itself,
# so without this nothing judged a commit once it was on main (iss-280).
push:
branches: [main]
concurrency:
# A merge_group event has no PR number; run_id keeps each queue entry in its
# own group rather than all of them sharing (and cancelling) one empty key.
group: attribution-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
attribution:
# Exemptions live INSIDE the steps, deliberately, and not as a job-level `if:`.
# A required status check that never reports leaves a pull request stuck on
# "Expected — waiting for status" forever, and whether a job skipped by an `if:`
# emits a check run with conclusion "skipped" is not a property worth betting a
# pull request's mergeability on. Running the job unconditionally means the check
# ALWAYS reports, so this workflow is safe to add to the required checks
# (iss-212).
#
# The COMMIT half no longer exempts a bot, and that is the point of the change
# rather than a side effect (iss-2609082001204831). The old reasoning — a bot
# cannot write a trailer, so red would train a maintainer to discount red — is
# about the MESSAGE, which the body half still exempts. It says nothing about
# AUTHORSHIP, and authorship is what the gate is now enforcing: a commit is
# authored by a human, machine assistance is disclosed by the trailer alone. So
# a dependabot pull request is red as the bot authored it, and the gate is
# not made to lie in order to keep a bot's pull request green: a bump inside
# the declared bound is re-authored as the owner by dependency-reauthor.yml,
# whose push re-runs this on the person's commit, and any other is landed by
# a human.
# 10 rather than 5: the job now compiles the outbound checker from source, and
# the corpus compiles it again to prove a bare checkout can. A required check
# that goes red on a cold module cache is a false red on every pull request.
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
# fetch-depth: 0 — the commit half walks base..head, and the default shallow
# checkout would not contain the base.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
# Go is here because the gate's SESSION-URL half is not a regex: it is
# `abcd lint outbound` reading the scanner's canonical pattern set and its
# opacity classifier, which POSIX ERE cannot express (see
# scripts/check-attribution.sh). The footer half stays pure shell.
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
# go.mod is the only place the toolchain is written, here as everywhere
# else in this repository (iss-2609090951291799).
go-version-file: go.mod
# Built ONCE, from THIS checkout, and handed to the script — which otherwise
# falls back to `go run` and pays ~0.3s per artefact across the commit range
# and the 94-case corpus.
#
# From the checkout and never from PATH or a plugin root: in this repository
# a released binary is by construction older than the thing under test, so a
# verb or refusal added since the last cut is unknown to it and the failure
# is a plausible wrong answer rather than an error (AGENTS.md § Build, test,
# and checks). RUNNER_TEMP is not on PATH, and nothing else resolves it.
- name: Build the outbound checker from this checkout
run: |
set -euo pipefail
go build -o "$RUNNER_TEMP/abcd" ./cmd/abcd
# The body is attacker-controlled text, so it reaches the shell ONLY as an
# environment variable. Interpolating ${{ github.event.pull_request.body }}
# directly into a run: block would splice that text into the script the runner
# executes — the classic template-injection hole. `printf '%s' "$PR_BODY"` is
# an ordinary variable expansion and is never re-scanned for substitutions.
# The exemption is announced rather than silent: a step that no-ops must say
# so, or a green tick reads as "the body was checked and passed" when nothing
# was checked at all.
- name: Check the pull-request body
env:
PR_BODY: ${{ github.event.pull_request.body }}
PR_AUTHOR_TYPE: ${{ github.event.pull_request.user.type }}
EVENT_NAME: ${{ github.event_name }}
ABCD_OUTBOUND_BIN: ${{ runner.temp }}/abcd
run: |
set -euo pipefail
if [ "$EVENT_NAME" != "pull_request" ]; then
echo "attribution: ${EVENT_NAME} run — no pull-request body to check (validated on the pull request before it merged), nothing was checked."
exit 0
fi
if [ "$PR_AUTHOR_TYPE" = "Bot" ]; then
echo "attribution: pull request authored by a bot — body check exempt, nothing was checked."
exit 0
fi
printf '%s' "$PR_BODY" > "$RUNNER_TEMP/pr-body.md"
bash scripts/check-attribution.sh body "$RUNNER_TEMP/pr-body.md"
# RS004 on the pull-request FORM: a title or body that names an iss-N must
# declare its relation to it (`Resolves:` or `Refs:`).
#
# THE RULE IS NOT HERE. It lives in scripts/check-issue-resolution.sh with
# RS001-RS003, which already own the record-id shapes, the `Resolves:`
# vocabulary and the ledger's folders; a second spelling of any of that in a
# second script is how two gates end up disagreeing about what an id is.
# Only the TRIGGER lives here, and that is the whole reason this step is in
# this workflow rather than beside its siblings in ci.yml: this is the one
# workflow that re-runs on `edited`. ci.yml's `pull_request:` takes the
# default type list (opened, synchronize, reopened), so a title edited after
# the pull request opened — the cheapest way to add or remove a mention —
# would never be judged there. The commit half of RS004 stays in ci.yml's
# issue-resolution job, where the base/head range already is.
#
# Same env-var handling as the body step above: attacker-controlled text
# reaches the shell only through the environment, never spliced into the
# script the runner executes.
- name: Record mentions in the pull-request form declare themselves (RS004)
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_AUTHOR_TYPE: ${{ github.event.pull_request.user.type }}
EVENT_NAME: ${{ github.event_name }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" != "pull_request" ]; then
echo "attribution: ${EVENT_NAME} run — no pull-request form to check (validated on the pull request before it merged), nothing was checked."
exit 0
fi
if [ "$PR_AUTHOR_TYPE" = "Bot" ]; then
echo "attribution: pull request authored by a bot — form check exempt, nothing was checked."
exit 0
fi
printf '%s' "$PR_TITLE" > "$RUNNER_TEMP/pr-title.txt"
printf '%s' "$PR_BODY" > "$RUNNER_TEMP/pr-body-rs004.md"
bash scripts/check-issue-resolution.sh pr "$RUNNER_TEMP/pr-title.txt" "$RUNNER_TEMP/pr-body-rs004.md"
# The gate's own rules are proven in CI, not only asserted. iss-214 asked for
# this: the trailer regex must be checked against the forms actually in this
# repository's history, because its first cut rejected the bracketed model
# identifiers 38 commits already use.
# The corpus builds its own checker (it must prove the build works from a bare
# checkout), so it takes no ABCD_OUTBOUND_BIN from here.
- name: Check the gate's own test corpus
run: bash scripts/check-attribution-cases.sh
# The range is the one each event carries: a pull request's base..head, a
# queue entry's base_sha..head_sha, a push's before..sha. The queue and the
# push are where the commit a merge creates is judged — a squash commit
# composed from the pull request's title and body, a web-UI merge — which
# no pull-request run ever sees (iss-280). The script exempts merge
# MESSAGES from the trailer and still reads their authorship. A base that
# is absent, the all-zeroes placeholder a force-push leaves, or not in this
# checkout is refused loudly rather than guessed around.
- name: Check the commit trailers
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }}
EVENT_NAME: ${{ github.event_name }}
ABCD_OUTBOUND_BIN: ${{ runner.temp }}/abcd
run: |
set -euo pipefail
if [ -z "${BASE_SHA:-}" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ] ||
! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
echo "::error::attribution: no usable base for this ${EVENT_NAME} run (BASE_SHA='${BASE_SHA:-}'); the commit range cannot be bounded, so nothing is passed unchecked"
exit 1
fi
# No bot exemption here: the commit half reads AUTHORSHIP, and a machine
# author is exactly what it refuses (iss-2609082001204831). A bump a
# bot proposed reaches this re-authored as a person, or not at all.
bash scripts/check-attribution.sh commits "$BASE_SHA" "$HEAD_SHA"