Workflows batch: heartbeat knobs, housekeeping.yml, its test in CI (#127)
heartbeat.yml passes TIME_CAP_SLACK, TIME_ANTHROPIC_API (the caching kill switch), TIME_REVIEW_TIEBREAK and TIME_REVIEW_QUORUM; housekeeping.yml runs the daily sweep at 13:17 UTC; ci.yml runs test_housekeeping.py. Owner-approved merge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
checks(): a status from one of Time's own workflows is not a check (#124)
A seat's review wakes heartbeat.yml during a heartbeat; the concurrency group cancels the run and Gitea records it as a failure on the PR head. Those statuses neither block nor earn a merge; CI decides. Owner-approved merge.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
labs/aicharts: fast mode (Clef first, LLM checks) as demo default (#42)
Opened 2026-10-14 by a Claude Code session under the owner's login, which is why it read as the operator's PR for three days. Lab-only and reversible: Clef-flash draws first, the LLM checks in parallel, the chart is redrawn only on disagreement; API callers keep the LLM default. The lab's tests pass on the branch (run 652, "aicharts: all good"); its deploy step cannot run from prod, which holds no Cloudflare secrets, so nothing is deployed by this merge.
Squash-merged by the CEO session on the owner's explicit instruction.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
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
TIME_PROTECTED governs every protected path except the workflow directories (#81)
touches_protected() hard-coded all of .github/ and .gitea/ on top of TIME_PROTECTED, so the engine under .github/time/ could never land without a human. Only the workflow directories stay hard-coded; DEFAULT_PROTECTED is unchanged. With this repo's TIME_PROTECTED narrowed, card PRs under .github/time/ with a trusted Approve and green checks merge via the time bot.
CI green on 3b757fa. Squash-merged by the CEO session on the owner's explicit instruction.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
The job timeout moves from 20 to 55 minutes: workers can overlap inside one run (#75), and at 20 every in-flight worker died together. TIME_SEAT_CLAUDE reaches the heartbeat so the reviewer can post as the `claude` user once #76 lands; until then it is unused.
Both 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
time/worker-run cd .github/time && sed -n 80,150p test_time.py; sed -n 455,480p test_time.py; grep -n "claude-sonnet
Objections go back to the worker, with the review attached (#59)
Revise loop for `/time retry`: an open PR's reviewer comments go to the worker on the same branch, open_pr updates the open PR, the card returns to `dispatched`, and verdicts older than the last revision no longer count. Check 55 covers it.
Reviewed: Approve by Time's reviewer (haiku); the two confirmation points answered by the steward. Merged by the CEO session on the owner's explicit clearance after eleven steward sign-offs were blocked by a session permission guard.
Closes#53
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
Co-authored-by: Time <time@users.noreply.teahub.xyz>
time/worker-run git fetch origin gh-pages 2>&1|tail -1; git show FETCH_HEAD:ventures/dollhouse/app.js | sed -n 140,3
seed: land a venture built elsewhere on yolopreneur.lol (#64)
Actions -> seed -> Run workflow with a slug, the URL(s) of a .tgz whose top folder is the slug, and its sha256
(plus a name and a one-liner for a new venture). seed.py checks the bundle, copies it over ventures/<slug>/ on
gh-pages in one commit, adds or refreshes the venture's line in ventures/ventures.json, and the workflow asks
Teahub pages to pull. Nothing in the bundle runs. test_seed.py runs in CI. Merged at the owner's request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9wtGiGEeq1H5DA3T88PRe
Closes#51
Merged by Claude for the operator (the delegated merger for engine PRs). Reviewer objection answered on the PR: CI was green; the reviewer cannot see checks yet (#52).
Co-authored-by: Time <time@users.noreply.teahub.xyz>
Co-committed-by: Time <time@users.noreply.teahub.xyz>
time/worker-run python3 - <<'EOF'
p=".github/time/test_time.py"
s=open(p).read()
old='import concurrent.futures as c
Gitea 1.24 also refuses the Actions identity label edits (removing a label is a 500), which crashed
promote_next and stalled the queue. gh() now uses TIME_TOKEN for every write except comments (Time's
beats are gitea-actions' comments) and commit statuses; the time user never counts as trusted and
mention replies ignore it; astra.yml gets TIME_TOKEN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017fTCFiw6ohysUDcWoPsTys
- checks(): the worker's own time/worker-run status can block or delay a merge but never counts as green
- dispatch_ci(): with no other check on the head, Time runs TIME_CI_WORKFLOW (ci.yml, workflow_dispatch) on the PR branch and waits; beat ci-dispatched, once
- ci.yml accepts workflow_dispatch
- touches_protected(): .github/ is always protected, whatever TIME_PROTECTED says
- labs-aicharts.yml: deploy URL via env, not ${{ }} in the script
- tests: worker-status-only no longer merges; dispatch once; real context merges
Claude-Session: https://claude.ai/code/session_01CYHXFvWMCs1GUmRmpySqQ6
Co-authored-by: claude <noreply@anthropic.com>
- beats_of/verdict/retry: trust only github-actions[bot] and owner/member/collaborator authors (forged state and stranger retries on a public repo)
- touches_protected: paginate pull files and include previous_filename
- browser_check: drop .git after clone, serve only the venture folder, Chrome with no network but localhost
- Hands.run: execute in a docker container when a daemon exists (TIME_RUN_IMAGE), host shell fallback
- tests: logins on fake comments, stranger retry/forged beat cases, paginated files fake
Claude-Session: https://claude.ai/code/session_01CYHXFvWMCs1GUmRmpySqQ6
Co-authored-by: claude <noreply@anthropic.com>
Live: Clef answered both probes right in ~0.5 s but at 0.21-0.25 confidence, so
the 0.5 floor sent them to the LLM. Expose every confidence and the weakest
question; a request may set its own floor (only changes cost); bench.py takes
clef@0 for calibration and sends a user agent Cloudflare accepts.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>
decide.js asks ~10 choice questions in one call (mark, x, measure, agg, series,
order, top, one filter per text column) and assembles the spec; the validator
still checks it. Weakest needed answer < FLOOR, a failed check or a model error
hands the question to the LLM path, and the response says why. Page: brain
picker, latency/confidence line, 'or as <mark>' from the mark probabilities.
bench.py scores brains on 12 fixed questions.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>
First live run: Workers AI answered specs the validator refused (agg spelling).
normalizeSpec fixes case and common aliases only; on a 422 the response
carries model_said (shown, never run). Smoke now fails on a bad spec.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>
Library (aicharts.js): spec validator, fixed ops (filter/group/sort/bin/limit),
Chart.js config, attach() for existing charts, <ai-chart> element.
Worker: sample orders in D1, schema/sample/aggregates to Workers AI with
structured output + one repair round, saved_charts, per-IP and daily caps.
deploy.py: workers.dev only, no DNS. For teahub matcha/teahub#93.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>
grok 4.7 fix rounds (28-54 steps, three at once) were most of a quick $20 of Venice
spend in the first live batch. Kimi's code model is a different model from round one's
Qwen Coder, so escalation still gets a fresh attempt, at a fraction of the cost.
TIME_VENTURE_ESCALATE still overrides.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>
Two findings from the first live batch (#26):
- its children waited a tick: GitHub doesn't list a card made a moment ago. spawn()
now hands its cards to the same tick's card loop.
- every fix round was a page claiming what its code didn't do: promised drops, and
'builds on' the trending repos the brief asked it to name. The brief now says the
repos are the wave it rides, forbids promised features, and has the worker re-read
the page against app.js; fix rounds remove a failing claim instead of building.
Claude-Session: https://claude.ai/code/session_01VDkN7ukK1kXkqov8FaZxva
Co-authored-by: Claude <noreply@anthropic.com>