Skip to content

Keeper

Keeper #551

Workflow file for this run

name: Keeper
# Keeps the Turing Arena on-chain rounds alive: every 5 minutes it pushes a real
# price, settles due rounds, reveals committed AI personas, and opens a fresh
# round with commits, so the live site always has something to play.
#
# Setup: add a repository secret KEEPER_PRIVATE_KEY = the 0x... operator key
# (wallet 0xBAE3...3b1E, which is isOperator on ProofOfAlpha + owner/reporter on
# the oracle + owner of the persona agent NFTs). Funded with Mantle Sepolia MNT.
# Until that secret is set, the scheduled run gates out cleanly (green, skipped)
# rather than failing.
on:
schedule:
- cron: "*/5 * * * *" # every 5 minutes (UTC). GitHub may delay under load.
workflow_dispatch: {} # manual "Run workflow" button
# One tick at a time: never let two overlapping runs collide on chain state or on
# the keeper-state commit. Newer runs wait; we don't cancel a tick mid-flight.
concurrency:
group: keeper
cancel-in-progress: false
permissions:
contents: write # to push keeper-state/ back to the repo
# Run the JS-based actions on Node 24, not the deprecated Node 20 runtime.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
tick:
name: Keeper tick
runs-on: ubuntu-latest
steps:
# Gate the whole tick on the secret being present. When it isn't (e.g. a
# fork, or before the maintainer adds it), every later step skips and the
# job stays green instead of erroring every 5 minutes.
- name: Check operator key is configured
id: gate
env:
KEY: ${{ secrets.KEEPER_PRIVATE_KEY }}
run: |
if [ -n "$KEY" ]; then
echo "ok=true" >> "$GITHUB_OUTPUT"
else
echo "ok=false" >> "$GITHUB_OUTPUT"
echo "KEEPER_PRIVATE_KEY is not set; skipping this tick. Add the repo secret to go live."
fi
- uses: actions/checkout@v5
if: steps.gate.outputs.ok == 'true'
- uses: pnpm/action-setup@v6 # version comes from package.json "packageManager"
if: steps.gate.outputs.ok == 'true'
- uses: actions/setup-node@v5
if: steps.gate.outputs.ok == 'true'
with:
node-version: 20
cache: pnpm
- name: Install
if: steps.gate.outputs.ok == 'true'
run: pnpm install --frozen-lockfile
- name: Run keeper tick
id: tick
if: steps.gate.outputs.ok == 'true'
continue-on-error: true # still commit any state written (e.g. new agent ids) on a partial failure
env:
PRIVATE_KEY: ${{ secrets.KEEPER_PRIVATE_KEY }}
# Public config, safe to hardcode. Mirrors contracts/deployments/5003*.json.
CHAIN_ID: "5003"
# Official RPC for reliable tx submission. The frontend's heavy polling
# was moved to alternative endpoints (see web rpcUrls), so this endpoint
# is no longer saturated and leaves headroom for users' wallet txs.
MANTLE_SEPOLIA_RPC_URL: "https://rpc.sepolia.mantle.xyz"
PROOF_OF_ALPHA_ADDRESS: "0x4f5AFD41BDb602C824e5a86F19E95314180144cf"
IDENTITY_REGISTRY_ADDRESS: "0xbB174b6D9a8ca439d5B3735b6570AAD3FEE8405F"
PRICE_ORACLE_ADDRESS: "0x31510d8a6Bbe5eEF2a315099AD2F94B504a4EEe3"
MANTLE_DEX_ORACLE_ADDRESS: "0x53cf4b4E989dBbDd7009c5108C21AE765f82480b"
# Generous windows: GitHub's */5 cron is best-effort and often runs
# late, so each phase must outlast a delayed or skipped tick. A reveal
# window wider than the cron interval guarantees a tick lands inside it
# to reveal the personas before settlement.
KEEPER_COMMIT_SECONDS: "900" # 15 min commit
KEEPER_REVEAL_SECONDS: "600" # 10 min reveal (>> 5 min cron)
KEEPER_SETTLE_SECONDS: "240" # 4 min before settle
run: pnpm --filter @turing-arena/agent keeper
- name: Commit & push keeper-state
if: steps.gate.outputs.ok == 'true'
env:
GIT_AUTHOR_NAME: turing-arena-keeper
GIT_AUTHOR_EMAIL: keeper@users.noreply.github.com
GIT_COMMITTER_NAME: turing-arena-keeper
GIT_COMMITTER_EMAIL: keeper@users.noreply.github.com
run: |
git add keeper-state
if git diff --cached --quiet; then
echo "No keeper-state changes to commit."
exit 0
fi
git commit -m "keeper: tick $(date -u +%FT%TZ)"
# Land cleanly even if the branch moved since checkout.
git pull --rebase --autostash origin "${GITHUB_REF_NAME}" || true
git push origin "HEAD:${GITHUB_REF_NAME}"
- name: Surface tick failure
if: steps.gate.outputs.ok == 'true' && steps.tick.outcome == 'failure'
run: |
echo "::error::keeper tick reported a failure (state, if any, was still committed)."
exit 1