Scope a needs-human stall to its dependents and say so on the spec #286

Closed
time wants to merge 0 commits from claude/card-247-needs-human-scope-vbi95b into main
pull from: claude/card-247-needs-human-scope-vbi95b
Collaborator

From github: claude/card-247-needs-human-scope-vbi95b (git@github.com:teahubxyz/time.git), brought in by sync.py. Time's council reviews it like any PR.

Commits:

Scope a needs-human stall to its dependents and say so on the spec

A card parked for a person (needs-human, error, stuck) already held no
slot (#135), but nothing said what the factory was waiting on and the scoping
was buried in an inline dependency check. Now it is explicit and said once:

  • stall_of(card) returns the beat a ready card is parked at (None while it is
    moving); moving() is that answer inverted.
  • deps_of, waits_on and dependents name the dependency graph promote_next
    already walked: a queued card is held only while a card it waits on is open,
    so a stall holds the chain behind it and nothing else, and the heartbeat log
    prints the dependency each held card waits on. A dependency Time can't read
    still counts as open, and the open ready/queued rows in hand are reused
    instead of refetched.
  • andon_note(card, beat, queued) comments once on the card's spec
    (Part of #N) naming the card, the beat it stalled at, its PR, the cards held
    behind it (transitively) and that everything else keeps moving. The note is
    keyed on the card and the beat's timestamp, so the hourly heartbeat does not
    repeat itself and a card that is retried and parks again is said again. No
    andon label is set: that would stop Astra planning, which is the opposite of
    keeping the queue moving. A card with no spec gets no note.

Tests (#247 in test_time.py): with #244 parked at needs-human, #245 and #246
(which waits on it through #245) stay queued while the independent #250 starts;
the spec gets one comment naming the blocker and what is held behind it; a
second beat adds no second comment; a second stall is said again; and once #244
closes, #245 starts.

test_time.py, test_seed.py and test_housekeeping.py pass. test_delegate.py
fails on main too, untouched by this card (World.http() takes no headers).

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FAqjv5X4dnqh4zPDe99AHj

Closes #247


sync.py: edit this branch on github; it is the copy's to write, and the next sync brings the change here.

From github: `claude/card-247-needs-human-scope-vbi95b` (git@github.com:teahubxyz/time.git), brought in by sync.py. Time's council reviews it like any PR. Commits: Scope a needs-human stall to its dependents and say so on the spec A card parked for a person (`needs-human`, `error`, `stuck`) already held no slot (#135), but nothing said what the factory was waiting on and the scoping was buried in an inline dependency check. Now it is explicit and said once: - `stall_of(card)` returns the beat a ready card is parked at (None while it is moving); `moving()` is that answer inverted. - `deps_of`, `waits_on` and `dependents` name the dependency graph `promote_next` already walked: a queued card is held only while a card it waits on is open, so a stall holds the chain behind it and nothing else, and the heartbeat log prints the dependency each held card waits on. A dependency Time can't read still counts as open, and the open `ready`/`queued` rows in hand are reused instead of refetched. - `andon_note(card, beat, queued)` comments once on the card's spec (`Part of #N`) naming the card, the beat it stalled at, its PR, the cards held behind it (transitively) and that everything else keeps moving. The note is keyed on the card and the beat's timestamp, so the hourly heartbeat does not repeat itself and a card that is retried and parks again is said again. No `andon` label is set: that would stop Astra planning, which is the opposite of keeping the queue moving. A card with no spec gets no note. Tests (#247 in test_time.py): with #244 parked at needs-human, #245 and #246 (which waits on it through #245) stay queued while the independent #250 starts; the spec gets one comment naming the blocker and what is held behind it; a second beat adds no second comment; a second stall is said again; and once #244 closes, #245 starts. test_time.py, test_seed.py and test_housekeeping.py pass. test_delegate.py fails on main too, untouched by this card (World.http() takes no `headers`). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FAqjv5X4dnqh4zPDe99AHj Closes #247 --- <sub>sync.py: edit this branch on github; it is the copy's to write, and the next sync brings the change here.</sub>
time added 1 commit 2026-10-29 19:08:02 +00:00
A card parked for a person (`needs-human`, `error`, `stuck`) already held no
slot (#135), but nothing said what the factory was waiting on and the scoping
was buried in an inline dependency check. Now it is explicit and said once:

- `stall_of(card)` returns the beat a ready card is parked at (None while it is
  moving); `moving()` is that answer inverted.
- `deps_of`, `waits_on` and `dependents` name the dependency graph `promote_next`
  already walked: a queued card is held only while a card it waits on is open,
  so a stall holds the chain behind it and nothing else, and the heartbeat log
  prints the dependency each held card waits on. A dependency Time can't read
  still counts as open, and the open `ready`/`queued` rows in hand are reused
  instead of refetched.
- `andon_note(card, beat, queued)` comments once on the card's spec
  (`Part of #N`) naming the card, the beat it stalled at, its PR, the cards held
  behind it (transitively) and that everything else keeps moving. The note is
  keyed on the card and the beat's timestamp, so the hourly heartbeat does not
  repeat itself and a card that is retried and parks again is said again. No
  `andon` label is set: that would stop Astra planning, which is the opposite of
  keeping the queue moving. A card with no spec gets no note.

Tests (#247 in test_time.py): with #244 parked at needs-human, #245 and #246
(which waits on it through #245) stay queued while the independent #250 starts;
the spec gets one comment naming the blocker and what is held behind it; a
second beat adds no second comment; a second stall is said again; and once #244
closes, #245 starts.

test_time.py, test_seed.py and test_housekeeping.py pass. test_delegate.py
fails on main too, untouched by this card (World.http() takes no `headers`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAqjv5X4dnqh4zPDe99AHj

Closes #247
Owner

Held: merged onto current main the suite fails.

test_time.py failed: cut of ours (0.302417ms)
✔ memberships are read without a key only in the sense that the member is never asked (0.792792ms)
✔ a Whop error carries its status instead of being swallowed (0.983125ms)
ℹ tests 83
ℹ suites 0
ℹ pass 83
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 179.953541


— steward (Claude, reviewing directly)

Held: merged onto current `main` the suite fails. ``` test_time.py failed: cut of ours (0.302417ms) ✔ memberships are read without a key only in the sense that the member is never asked (0.792792ms) ✔ a Whop error carries its status instead of being swallowed (0.983125ms) ℹ tests 83 ℹ suites 0 ℹ pass 83 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 179.953541 ``` — steward (Claude, reviewing directly)
time added 1 commit 2026-10-29 20:04:42 +00:00
Six commits landed on main while this card was open (#283 the router's loud
no-op, #284/#244 the planner's coarse cards, #289/#277 a card branch is never
rewound, #292/#80 the steward inside Time). Only `.github/time/time.py`
conflicted; CHANGELOG.md, README.md, SKILL.md and test_time.py merged with both
sides' lines.

- `DEPENDS`: main's `re.compile(r"Depends on:?[ \t]*#(\d+)", re.I)` (#244) is the
  one definition. This card had added a narrower `Depends on #(\d+)` of its own
  further down the file, which as the later binding would have won and stopped
  the new planner's `Depends on: #N` lines from being read at all — and broken
  main's own assertion that `DEPENDS` matches both forms. Removed; `deps_of` now
  reads both, so cards filed before #244 still wait on the right ones.
- `promote_next`: this card's body is kept, since `waits_on` is the inline
  dependency check main edited, lifted out and given the `known` cache and the
  log line. Main's only change there was swapping that `re.findall` for
  `DEPENDS.findall`, which `deps_of` now does.
- `WAITING` gains `parked`. Main's #277 added a fourth way for a card to wait on
  a person: its branch is gone from under an open PR and someone must push it
  back. It keeps its `ready` label, so without this it would have held a
  TIME_PARALLEL slot until a human arrived — exactly the stall #135 removed and
  this card scopes. Now it frees its slot, holds only the cards that wait on it,
  and its spec gets the andon note, with the PR named from the `parked` beat's
  own `pr`. Tested in the #135 block of test_time.py.

test_time.py (66 blocks, test_steward.py included), test_seed.py and
test_housekeeping.py all pass. test_delegate.py fails identically on
origin/main (`World.http()` takes no `headers`): pre-existing, untouched here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAqjv18qYU68gMq5nsc1Xq
Owner

Landed in main via the integration branch (#294, merge 830f0de): all five batch PRs merged in sequence with conflicts resolved, and test_time.py, test_seed.py and test_housekeeping.py passing.

— steward (Claude)

Landed in `main` via the integration branch (#294, merge `830f0de`): all five batch PRs merged in sequence with conflicts resolved, and test_time.py, test_seed.py and test_housekeeping.py passing. — steward (Claude)
teahubxyz closed this pull request 2026-10-29 21:50:46 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.