Skip to content

koth-ledger

koth-ledger #218

Workflow file for this run

# appends dethrone rows to competition/LEADERBOARD.md for merged ladder
# PRs. the scoring gates stay read-only by design (the engine gate holds
# no write token at all); this workflow never runs on a PR event, so an
# untrusted PR can never reach its write token.
#
# it is a SWEEP, not a per-push hook: the gate arms auto-merge with the
# workflow GITHUB_TOKEN, and github's recursion guard suppresses workflow
# triggers for pushes caused by that token — so an auto-merged dethrone
# never fires a push event here (round 1, PR #566, proved it live). the
# push trigger still catches human-merged rows immediately; the schedule
# and manual dispatch pick up auto-merged ones. update_leaderboard.py is
# idempotent per PR, so overlapping sweeps converge instead of duplicating.
name: koth-ledger
on:
push:
schedule:
- cron: "17 */6 * * *"
workflow_dispatch:
permissions: {}
concurrency:
group: koth-ledger
cancel-in-progress: false
jobs:
ledger:
if: >-
vars.KOTH_LADDER_BASE != '' &&
(github.event_name != 'push' ||
(github.ref_name == vars.KOTH_LADDER_BASE &&
!startsWith(github.event.head_commit.message, 'docs(competition): ledger row')))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
# the push token: the ladder ruleset requires the gate check on every
# push, and the workflow GITHUB_TOKEN holds no bypass, so rows are
# pushed with an owner token (repo secret). safe because this workflow
# never runs on PR events and never executes untrusted code — the only
# inputs are gate-authored comments parsed as json.
- uses: actions/checkout@v4
with:
ref: ${{ vars.KOTH_LADDER_BASE }}
token: ${{ secrets.KOTH_LEDGER_TOKEN || github.token }}
- name: append rows for recently merged ladder prs
env:
GH_TOKEN: ${{ github.token }}
LADDER: ${{ vars.KOTH_LADDER_BASE }}
run: |
gh pr list --repo "$GITHUB_REPOSITORY" --base "$LADDER" \
--state merged --limit 15 --json number,author \
--jq '.[] | "\(.number) \(.author.login)"' > /tmp/merged.txt
appended=0
while read -r pr author; do
[ -n "$pr" ] || continue
# only the gate's own comments are trusted: a scorecard-shaped
# comment from anyone else must never reach the ledger
gh api "repos/${GITHUB_REPOSITORY}/issues/${pr}/comments" \
--paginate \
--jq '.[] | select(.user.login == "github-actions[bot]") | .body' \
> /tmp/comments.txt || continue
rm -f /tmp/report.json
python3 - <<'PYEOF'
import re
body = open("/tmp/comments.txt", encoding="utf-8").read()
blocks = re.findall(r"```json\n(.*?)\n```", body, flags=re.S)
reports = [b for b in blocks if '"dethroned": true' in b]
if reports:
with open("/tmp/report.json", "w", encoding="utf-8") as fh:
fh.write(reports[-1])
PYEOF
if [ -f /tmp/report.json ]; then
python3 .github/scripts/update_leaderboard.py \
--report /tmp/report.json --pr "$pr" --author "$author" \
&& appended=1
# the ratchet: a merged engine-lane dethrone becomes the new
# champion, so the next challenger's merge threshold rises
# automatically. the winner is the single strategy file the
# pr added.
lane=$(jq -r '.lane // "kit"' /tmp/report.json)
if [ "$lane" = "engine" ]; then
mean=$(jq -r '.challenger.mean' /tmp/report.json)
winner=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${pr}/files" \
--paginate \
--jq '.[] | select(.status != "removed") | .filename' \
| grep -E '^contrib/strategies/[A-Za-z0-9_]+\.py$' \
| grep -v 'baseline\.py' | head -1 || true)
if [ -n "$winner" ] && [ -f "$winner" ]; then
python3 .github/scripts/promote_champion.py \
--strategy "$winner" --pr "$pr" --mean "$mean"
fi
fi
fi
done < /tmp/merged.txt
echo "sweep done (appended=$appended)"
- name: commit the rows
run: |
if git diff --quiet -- competition/LEADERBOARD.md; then
echo "ledger unchanged"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add competition/LEADERBOARD.md
git commit -m "docs(competition): ledger rows from sweep"
git push