Repository navigation
fix: make the bundled pace pause zero minutes #1722
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |