promote_next counts only moving cards: one waiting on a person holds no ready slot #183

Merged
teahubxyz merged 3 commits from promote-moving-only into main 2026-10-19 13:47:55 +00:00
Owner

Closes #135.

The factory has been idle since about 03:00 UTC: all three ready cards (#79, #80, #85) are parked on needs-human after reviewer objections (PRs #134, #176 and #79's), TIME_PARALLEL=3, so promote_next() promoted nothing while 22 cards sat queued. The heartbeat log said only cards=3 launches_today=1/10. #135 was ranked first by the router twice, but it couldn't be promoted, since the slots it would free were the ones holding it back.

  • time.py: moving(card): a ready card's last real beat (ignoring blocked / capped) is not needs-human, error or stuck. promote_next() counts only moving cards toward TIME_PARALLEL and prints promote: ready=N moving=M. It runs after tick(), so a card Time just auto-retried, or one a human /time retry'd, has a fresh dispatched beat and counts again. A parked card keeps its ready label, so a retry still finds it.
  • Tests (80): three ready, all parked → the oldest queued card is promoted; two moving (one with no beat yet) plus one parked → a slot is free; the parked one sent back to the worker → three moving, no promotion; blocked/capped beats don't count as the last beat.
  • CHANGELOG under Added.

Spec note: #135 asked for the counts on tick()'s line; they're printed by promote_next() itself, which is where they're computed, and it runs every beat.

test_time.py green locally (23 sections).

🤖 Generated with Claude Code

https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD

Closes #135. The factory has been idle since about 03:00 UTC: all three `ready` cards (#79, #80, #85) are parked on `needs-human` after reviewer objections (PRs #134, #176 and #79's), `TIME_PARALLEL=3`, so `promote_next()` promoted nothing while 22 cards sat `queued`. The heartbeat log said only `cards=3 launches_today=1/10`. #135 was ranked first by the router twice, but it couldn't be promoted, since the slots it would free were the ones holding it back. - `time.py`: `moving(card)`: a `ready` card's last real beat (ignoring `blocked` / `capped`) is not `needs-human`, `error` or `stuck`. `promote_next()` counts only moving cards toward `TIME_PARALLEL` and prints `promote: ready=N moving=M`. It runs after `tick()`, so a card Time just auto-retried, or one a human `/time retry`'d, has a fresh `dispatched` beat and counts again. A parked card keeps its `ready` label, so a retry still finds it. - Tests (80): three ready, all parked → the oldest queued card is promoted; two moving (one with no beat yet) plus one parked → a slot is free; the parked one sent back to the worker → three moving, no promotion; `blocked`/`capped` beats don't count as the last beat. - CHANGELOG under Added. Spec note: #135 asked for the counts on `tick()`'s line; they're printed by `promote_next()` itself, which is where they're computed, and it runs every beat. `test_time.py` green locally (23 sections). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
teahubxyz added 3 commits 2026-10-19 13:47:10 +00:00
teahubxyz merged commit 32943339e1 into main 2026-10-19 13:47:55 +00:00
teahubxyz referenced this issue from a commit 2026-10-19 13:48:28 +00:00
Sign in to join this conversation.