The cloud lane: cloud sessions build cards on the cloud-session credit #197

Merged
teahubxyz merged 6 commits from cloud-lane into main 2026-10-19 16:46:15 +00:00
Owner

The owner's $250 cloud-session credit (Max, expires 2026-11-04 11:59 PM PT) covers Claude Code cloud sessions started "from web, desktop, CLI, or mobile" (terms; Routines, Remote Control and Chat are excluded). This makes it a second build lane beside the API worker.

How a card moves

  1. Astra's live run labels the best-ranked actionable cards of the lane's sizes (M) cloud, until per_day (3) are in the lane. This happens before the API worker's picks. A cloud card counts as lined up, so it's never queued for the API worker.
  2. A launcher on hb-5 starts one claude --cloud session per cloud card, on the GitHub copy, with the card as the prompt.
    • It runs as a user agent in the desktop session, which is where the claude.ai sign-in lives; SSH sessions can't read that keychain.
    • It reads Teahub's public issue list and holds no Teahub credentials.
    • The launcher lives on the Mac, not in this repo.
  3. The session pushes claude/card-N-*. Here, sync.py brings the branch home as a PR that closes the card, adding Closes #N when the commits don't. It then moves the card from cloud to ready, leaving a note without a beat.
  4. The heartbeat adopts the PR (the card is ready with no beats and an open PR closes it). The council reviews and time lands it.
  5. A card the commits close leaves the lane too, whatever the session called its branch.

Changes

  • router.py: cloud config (per_day, sizes ⊆ S/M; default per_day: 0, no lane); pick_cloud(); cloud added to LINED_UP; the table shows "to the cloud today" and "cloud lane".
  • astra.py: cloud picks first, then the API picks from the rest. It labels cloud in live mode and says "To the cloud lane:" in the log; the header shows the lane's fill; the beat carries cloud / would_cloud.
  • sync.py: card_of(), hand_back(), Closes #N in the PR body, and hand-back for every card the branch or commits name.
  • time/router.json: "cloud": {"per_day": 3, "sizes": ["M"]}.
  • Tests: 80 covers config, pick_cloud, the lane being full, the table, and live/dry Astra with the lane. 83 covers card_of, PR bodies, hand-back (and a card not in the lane staying put), and a branch the session named itself.

test_time.py is green locally on current main (24 sections; the havre step is skipped without npm here, and CI runs it).

Pacing: the lane holds 3 cards at a time, and the launcher starts at most 3 sessions a day and stops before the credit lapses. Per-session cost isn't visible to Time; the owner's balance after the first day sets the pace.

🤖 Generated with Claude Code

https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD

The owner's $250 cloud-session credit (Max, expires 2026-11-04 11:59 PM PT) covers Claude Code cloud sessions started "from web, desktop, CLI, or mobile" ([terms](https://support.claude.com/en/articles/17152539-cloud-sessions-bonus-credit-promotion); Routines, Remote Control and Chat are excluded). This makes it a second build lane beside the API worker. **How a card moves** 1. Astra's live run labels the best-ranked actionable cards of the lane's sizes (`M`) `cloud`, until `per_day` (3) are in the lane. This happens before the API worker's picks. A `cloud` card counts as lined up, so it's never queued for the API worker. 2. A launcher on hb-5 starts one `claude --cloud` session per `cloud` card, on the GitHub copy, with the card as the prompt. - It runs as a user agent in the desktop session, which is where the claude.ai sign-in lives; SSH sessions can't read that keychain. - It reads Teahub's public issue list and holds no Teahub credentials. - The launcher lives on the Mac, not in this repo. 3. The session pushes `claude/card-N-*`. Here, `sync.py` brings the branch home as a PR that closes the card, adding `Closes #N` when the commits don't. It then moves the card from `cloud` to `ready`, leaving a note without a beat. 4. The heartbeat adopts the PR (the card is `ready` with no beats and an open PR closes it). The council reviews and `time` lands it. 5. A card the commits close leaves the lane too, whatever the session called its branch. **Changes** - `router.py`: `cloud` config (`per_day`, `sizes` ⊆ S/M; default `per_day: 0`, no lane); `pick_cloud()`; `cloud` added to `LINED_UP`; the table shows "to the cloud today" and "cloud lane". - `astra.py`: cloud picks first, then the API picks from the rest. It labels `cloud` in live mode and says "To the cloud lane:" in the log; the header shows the lane's fill; the beat carries `cloud` / `would_cloud`. - `sync.py`: `card_of()`, `hand_back()`, `Closes #N` in the PR body, and hand-back for every card the branch or commits name. - `time/router.json`: `"cloud": {"per_day": 3, "sizes": ["M"]}`. - Tests: 80 covers config, `pick_cloud`, the lane being full, the table, and live/dry Astra with the lane. 83 covers `card_of`, PR bodies, hand-back (and a card not in the lane staying put), and a branch the session named itself. `test_time.py` is green locally on current main (24 sections; the havre step is skipped without npm here, and CI runs it). Pacing: the lane holds 3 cards at a time, and the launcher starts at most 3 sessions a day and stops before the credit lapses. Per-session cost isn't visible to Time; the owner's balance after the first day sets the pace. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
teahubxyz added 6 commits 2026-10-19 16:43:14 +00:00
teahubxyz merged commit 1a2ae6bc7e into main 2026-10-19 16:46:15 +00:00
Sign in to join this conversation.