Hands-off Time: spec planner, auto-merge on Approve + green checks, next card, update workflow

- spec issues (label spec) become queued cards, parsed deterministically, seat fallback
- TIME_AUTOMERGE=on: squash-merge when the latest trusted verdict is Approve and all checks pass
- Object, red or missing checks, or protected paths go to needs-human
- merged card frees the slot; the next unblocked queued card gets ready and dispatches in the same run
- update.yml pulls the engine from teahubxyz/time into project repos via PR
- ci.yml runs Time's own tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpT7eWzeLhRwuYmsSQnbRb
This commit is contained in:
claude committed 2026-10-11 11:46:43 -07:00
1 parent 9108adf4c6
commit 889e93799e
9 files changed
+379 -14

No files matched your search

+19 -3
View File
@@ -1,21 +1,32 @@
name: heartbeat
# Time's clock: hourly, and right away when a card gets the `ready` label (dry-run until TIME_MODE=live).
# 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]
permissions:
contents: read
contents: write # squash-merge approved, green PRs
issues: write
pull-requests: write
checks: read
concurrency:
group: heartbeat
cancel-in-progress: false
jobs:
tick:
if: github.event_name != 'issues' || github.event.label.name == 'ready'
# issues: only `ready`/`spec` labels. comments: only on PRs, only a reviewer verdict (Approve/Object).
if: >-
(github.event_name != 'issues' || contains(fromJSON('["ready","spec"]'), github.event.label.name)) &&
(github.event_name != 'issue_comment' || (github.event.issue.pull_request &&
(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: 5
steps:
@@ -32,3 +43,8 @@ jobs:
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 }}