Files
time/.github/workflows/heartbeat.yml
T
teahubxyzandClaude Fable 5.1 b539fff1b2
ci / test (push) Successful in 35s
heartbeat: per-seat tokens for haiku, sonnet, opus and fable
One Gitea user per seat (model line); the reviewer's verdict becomes a real review by that seat's own user once #76 lands. TIME_SEAT_CLAUDE stays until then as the umbrella.

On the owner's explicit instruction (CEO session).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
2026-10-17 22:54:56 +00:00

62 lines
3.6 KiB
YAML

name: heartbeat
# Time's clock: hourly, right away when a card gets `ready` or a spec gets `spec`, and when a PR is reviewed
# (dry-run until TIME_MODE=live; merges only with TIME_AUTOMERGE=on).
on:
schedule:
- cron: "7 * * * *"
workflow_dispatch:
issues:
types: [labeled]
pull_request_review:
types: [submitted]
issue_comment:
types: [created]
# (GitHub-style `permissions:` are ignored by Gitea; the Actions token has write access to this repo)
concurrency:
group: heartbeat
cancel-in-progress: false
jobs:
tick:
# issues: only `ready`/`spec` labels. comments: only on PRs, only a reviewer verdict (Approve/Object), never Time's own.
if: >-
(github.event_name != 'issues' || contains(fromJSON('["ready","spec"]'), github.event.label.name)) &&
(github.event_name != 'issue_comment' || (github.event.issue.pull_request && github.event.comment.user.login != 'gitea-actions' &&
(startsWith(github.event.comment.body, '**Approve') || startsWith(github.event.comment.body, 'Approve') ||
startsWith(github.event.comment.body, '**Object') || startsWith(github.event.comment.body, 'Object'))))
runs-on: ubuntu-latest
timeout-minutes: 55 # workers can overlap inside one job now (#75); under the hourly cadence, and the concurrency group queues the next run
steps:
- uses: actions/checkout@v5
with:
sparse-checkout: .github/time
persist-credentials: false # the worker's shell must not find the token in .git/config
- run: python3 .github/time/time.py heartbeat
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
VENICE_API_KEY: ${{ secrets.VENICE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # user-level secret on teahubxyz (docs/decisions.md on the spine)
TIME_TOKEN: ${{ secrets.TIME_TOKEN }} # repo secret: the `time` bot opens and merges PRs (Gitea denies that to the Actions identity)
TIME_SEAT_CLAUDE: ${{ secrets.TIME_SEAT_CLAUDE }} # umbrella seat, retired once the per-seat tokens below are in use (#76)
TIME_SEAT_HAIKU: ${{ secrets.TIME_SEAT_HAIKU }} # one Gitea user per seat (model line): a seat's verdict is a real review by its own user (#76)
TIME_SEAT_SONNET: ${{ secrets.TIME_SEAT_SONNET }}
TIME_SEAT_OPUS: ${{ secrets.TIME_SEAT_OPUS }}
TIME_SEAT_FABLE: ${{ secrets.TIME_SEAT_FABLE }}
TIME_TRUSTED: ${{ vars.TIME_TRUSTED }} # org owners etc.; collaborators are trusted without being listed
TIME_RUN_IMAGE: node:22-bookworm # the worker's shell runs in a throwaway container, not next to these secrets
TIME_MODE: ${{ vars.TIME_MODE }}
TIME_DAILY_CAP: ${{ vars.TIME_DAILY_CAP }}
TIME_DAILY_USD: ${{ vars.TIME_DAILY_USD }} # spend governor: dollars per day (default 5) …
TIME_MONTHLY_USD: ${{ vars.TIME_MONTHLY_USD }} # … and per month (default 100); ledger in time/ledger/
TIME_CURSOR_MODEL: ${{ vars.TIME_CURSOR_MODEL }}
TIME_REVIEW: ${{ vars.TIME_REVIEW }}
TIME_STUCK_HOURS: ${{ vars.TIME_STUCK_HOURS }}
TIME_AUTOMERGE: ${{ vars.TIME_AUTOMERGE }}
TIME_REQUIRE_CHECKS: ${{ vars.TIME_REQUIRE_CHECKS }}
TIME_PROTECTED: ${{ vars.TIME_PROTECTED }}
TIME_PARALLEL: ${{ vars.TIME_PARALLEL }}
TIME_PLANNER: ${{ vars.TIME_PLANNER }}
TIME_WORKER: ${{ vars.TIME_WORKER }}
TIME_WORKER_STEPS: ${{ vars.TIME_WORKER_STEPS }}
TIME_WORKER_MAX_TOKENS: ${{ vars.TIME_WORKER_MAX_TOKENS }}