From 880e5b93be21595fff34c6c5da11a5f74db75906 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 11:56:55 +0000 Subject: [PATCH] ci(ci): Add a candidate clock to the Strength workflow for time odds matches A time odds match plays the same build on both sides with one given more of the clock, so it prices the time rather than a change. The elo per doubling that every speed price leans on was read that way at v0.4.3, with a per side clock that never reached master. The Strength workflow has one time_control, which reaches both engines. candidate_time_control gives the candidate its own clock and leaves the baseline on time_control. Empty, the match is the symmetric one it was. The manifest records both clocks and the summary line names both, so a time odds run cannot be read as an ordinary one. play-shard is pinned to the mache commit that takes the new input. It moves to that change's release before this merges. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013vnTSb5N6JEKfqkQHAgevq --- .github/workflows/strength-batch.yml | 19 ++++++++++++++++--- .github/workflows/strength.yml | 11 +++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/strength-batch.yml b/.github/workflows/strength-batch.yml index 9a4c65c3..c1f03720 100644 --- a/.github/workflows/strength-batch.yml +++ b/.github/workflows/strength-batch.yml @@ -76,6 +76,12 @@ on: description: Passed to fastchess as tc. type: string required: true + candidate_time_control: + description: > + A clock for the candidate alone, which makes this a time odds match. + Empty gives both sides time_control. + type: string + default: "" sprt: description: Judge a sequential test over the pooled pairs. type: boolean @@ -144,6 +150,7 @@ jobs: BATCH: ${{ inputs.batch }} BATCHES: ${{ inputs.batches }} TIME_CONTROL: ${{ inputs.time_control }} + CANDIDATE_TIME_CONTROL: ${{ inputs.candidate_time_control }} BOOK: ${{ inputs.book }} SPRT: ${{ inputs.sprt }} ELO0: ${{ inputs.elo0 }} @@ -184,7 +191,7 @@ jobs: scripts/build_at.sh "$CANDIDATE_SHA" tools/new scripts/build_at.sh "$BASELINE_SHA" tools/old - - uses: aywrite/mache/actions/play-shard@e3a59ce295c9dfabb62ddb077e88606d022cc55d # v0.7.1 + - uses: aywrite/mache/actions/play-shard@10f17c5120bb58dfacf2304532bbc63aef86fabe # unreleased, aywrite/mache#35 id: play with: candidate: ${{ inputs.candidate_name }} @@ -200,6 +207,7 @@ jobs: batch: ${{ inputs.batch }} batches: ${{ inputs.batches }} time_control: ${{ inputs.time_control }} + candidate_time_control: ${{ inputs.candidate_time_control }} hash_mb: ${{ inputs.hash_mb }} concurrency: ${{ inputs.concurrency }} startup_ms: ${{ inputs.startup_ms }} @@ -241,7 +249,11 @@ jobs: echo "shard: ${SHARD}" echo "start: ${START:-not chosen, the shard did not get that far}" echo "pairs: ${PAIRS}" - echo "time_control: ${TIME_CONTROL}" + if [ -n "$CANDIDATE_TIME_CONTROL" ]; then + echo "time_control: ${TIME_CONTROL} for the baseline, ${CANDIDATE_TIME_CONTROL} for the candidate" + else + echo "time_control: ${TIME_CONTROL}" + fi echo "hash_mb: ${HASH}" echo "concurrency: ${CONCURRENCY}" echo "startup_ms: ${STARTUP_MS}" @@ -319,7 +331,8 @@ jobs: baseline: ${{ inputs.baseline_name }} baseline_sha: ${{ inputs.baseline_sha }} shards: ${{ inputs.count }} - time_control: ${{ inputs.time_control }} + # what the summary prints, so a time odds match says so + time_control: ${{ inputs.candidate_time_control && format('{0} against {1}', inputs.candidate_time_control, inputs.time_control) || inputs.time_control }} sprt: ${{ inputs.sprt }} elo0: ${{ inputs.elo0 }} elo1: ${{ inputs.elo1 }} diff --git a/.github/workflows/strength.yml b/.github/workflows/strength.yml index 3f689865..643c9f54 100644 --- a/.github/workflows/strength.yml +++ b/.github/workflows/strength.yml @@ -72,6 +72,10 @@ on: description: The table both sides are given, in MB. 256 is what the engine defaults to. A smaller table puts the replacement policy under the pressure a longer game would, and figures at different sizes are not comparable. type: number default: 256 + candidate_time_control: + description: A clock for the candidate alone, for a time odds match, where the same build plays itself with one side given more time. The baseline plays time_control. Left empty both sides play time_control. + type: string + default: "" workflow_call: inputs: candidate: @@ -112,6 +116,9 @@ on: hash_mb: type: number default: 256 + candidate_time_control: + type: string + default: "" publish: description: Append the result to the release notes for this tag type: boolean @@ -260,6 +267,7 @@ jobs: # each other's openings seed: ${{ inputs.seed || github.run_id }} time_control: ${{ inputs.time_control }} + candidate_time_control: ${{ inputs.candidate_time_control }} sprt: ${{ fromJSON(needs.resolve.outputs.sprt) }} elo0: ${{ fromJSON(needs.resolve.outputs.elo0) }} elo1: ${{ fromJSON(needs.resolve.outputs.elo1) }} @@ -290,6 +298,7 @@ jobs: book: ${{ needs.resolve.outputs.book }} seed: ${{ inputs.seed || github.run_id }} time_control: ${{ inputs.time_control }} + candidate_time_control: ${{ inputs.candidate_time_control }} sprt: ${{ fromJSON(needs.resolve.outputs.sprt) }} elo0: ${{ fromJSON(needs.resolve.outputs.elo0) }} elo1: ${{ fromJSON(needs.resolve.outputs.elo1) }} @@ -320,6 +329,7 @@ jobs: book: ${{ needs.resolve.outputs.book }} seed: ${{ inputs.seed || github.run_id }} time_control: ${{ inputs.time_control }} + candidate_time_control: ${{ inputs.candidate_time_control }} sprt: ${{ fromJSON(needs.resolve.outputs.sprt) }} elo0: ${{ fromJSON(needs.resolve.outputs.elo0) }} elo1: ${{ fromJSON(needs.resolve.outputs.elo1) }} @@ -350,6 +360,7 @@ jobs: book: ${{ needs.resolve.outputs.book }} seed: ${{ inputs.seed || github.run_id }} time_control: ${{ inputs.time_control }} + candidate_time_control: ${{ inputs.candidate_time_control }} sprt: ${{ fromJSON(needs.resolve.outputs.sprt) }} elo0: ${{ fromJSON(needs.resolve.outputs.elo0) }} elo1: ${{ fromJSON(needs.resolve.outputs.elo1) }}