1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Remote builder for the bl-24e7 speculative merge queue (bl-6312, design
# docs/design/bl-24e7-speculative-merge-queue.md).
#
# The contract is the verdict store's own file format — no workflow-specific
# protocol exists. Push a candidate commit to a `speculation/**` branch and
# this job runs the STOCK gate on it; the gate's hook records the verdict into
# the runner's local store exactly as it would on a laptop, and the store dir
# ships home as an artifact. Retrieval is symmetric and manual by design (a
# remote builder must never become a close dependency):
#
# gh run download -n verdicts -D /tmp/verdicts
# bl-speculate import /tmp/verdicts/*.toml # an empty artifact = no verdict: rebuild
# git push origin --delete speculation/<name> # the TTL sweep is you
#
# Two honesty notes. (1) The gate fingerprint hashes the toolchain the gate
# itself exports (`BALLS_TOOLCHAIN="$(rustc -V)"` in scripts/pre-commit; ops
# bl-6124): a verdict recorded here HITS locally only when the runner's
# toolchain matches yours — a mismatch is not a bug, it is the fingerprint
# refusing to vouch across toolchains. Pin versions on both sides when you
# want remote verdicts to land. (2) Public-repo standard runners are free with no minutes quota; the
# real caps are 20 concurrent jobs (Free plan) and 6 h per job.
name: speculate
on:
push:
branches:
- 'speculation/**'
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: the gate fingerprint scripts/pre-commit will export (compare with local rustc -V)
run: rustc -V
- name: the gate's own tooling
run: cargo install cargo-tarpaulin --locked
- name: build the recorder the hook consults
run: cargo build --release --bin bl-speculate
# A FAIL is recorded ONLY when the gate judged the tree, and its exit
# code says so (bl-988d, from yog bl-673a). 75 is check-coverage.sh
# reporting tarpaulin signaled on both attempts: an infrastructure death,
# not a verdict — and a stored FAIL is permanent (keyed by tree, the
# speculator stops the chain at it without rebuilding). So 75 records
# nothing, and a job killed outright writes no output: also nothing.
- name: run the stock gate (its hook records the verdict on pass)
id: gate
run: |
set +e
PATH="$PWD/target/release:$PATH" BALLS_IDENTITY=github-actions scripts/pre-commit
code=$?
if [ "$code" -eq 75 ]; then
echo "the gate produced NO verdict about this tree; nothing is recorded (bl-988d)" >&2
elif [ "$code" -ne 0 ]; then
echo "verdict=fail" >>"$GITHUB_OUTPUT"
fi
exit "$code"
- name: record the failure the aborting hook cannot
if: failure() && steps.gate.outputs.verdict == 'fail'
run: PATH="$PWD/target/release:$PATH" BALLS_IDENTITY=github-actions BALLS_TOOLCHAIN="$(rustc -V)" bl-speculate record fail
- uses: actions/upload-artifact@v4
if: always()
with:
name: verdicts
path: ~/.local/state/balls/plugins/bl-speculate/verdicts/