watchdog: fix limit-probe self-match + scrollback dedupe wedge; plan(lvl5): badge shows level only
Night-watch findings (monthly-spend-limit window, ~01:49-04:45): - probe text said 'usage limit' which matches LIMIT_RE, so a submitted probe kept limited_now true forever -> reworded to 'quota window' with a CAUTION note (nudge text must never match LIMIT_RE) - dedupe scanned all 40 captured lines, so once a probe scrolled into the conversation no further probe ever fired (builder/adv frozen at nudges=1, orchestrator probes degraded to hourly riding the wake scroll) -> dedupe now only checks the bottom 8 lines (input area) Core invariant HELD: zero kill+reboots during the limit window. plan(lvl5): operator addition - the top-corner level badge (card, dashboard pill, badge SVG) shows only the level number+color, zero capping info; the inline per-rung table keeps intentional-skip/unverified detail.
This commit is contained in:
+10
-5
@@ -376,11 +376,14 @@ def _next_limit_until(pane, now):
|
||||
return now + LIMIT_PROBE_FALLBACK, False
|
||||
|
||||
def _limit_nudge_msg(role):
|
||||
# CAUTION: this text lands in the pane that limit_tick later greps. It must NEVER
|
||||
# match LIMIT_RE (found 2026-06-11: "usage limit has reset" matched, making the
|
||||
# window self-sustaining once the probe scrolled into the conversation).
|
||||
if role == "orchestrator":
|
||||
return ("watchdog limit-probe: if the usage limit has reset, RESUME now — "
|
||||
return ("watchdog probe: if the quota window has reset, RESUME now — "
|
||||
"you are the cc-ci orchestrator; re-check loop status and continue "
|
||||
"supervising from where you stopped.")
|
||||
return ("watchdog limit-probe: if the usage limit has reset, RESUME your loop now — "
|
||||
return ("watchdog probe: if the quota window has reset, RESUME your loop now — "
|
||||
"pull latest, re-read your phase STATUS/REVIEW files, and continue from "
|
||||
"where you stopped; re-arm your loop pacing.")
|
||||
|
||||
@@ -415,10 +418,12 @@ def limit_tick(role, session, pane):
|
||||
if now < state.get("until", 0):
|
||||
return True # quietly inside the window
|
||||
|
||||
# Window elapsed, banner still showing → probe. Dedupe: never stack a second
|
||||
# probe while our own text is still visible (typed-but-unsubmitted, or echoed).
|
||||
# Window elapsed, banner still showing → probe. Dedupe: never stack a second probe
|
||||
# while our own text sits typed-but-unsubmitted in the INPUT area (bottom lines only —
|
||||
# checking the whole pane wedged the machine once a submitted probe scrolled into the
|
||||
# conversation above, suppressing all further probes; found 2026-06-11).
|
||||
msg = _limit_nudge_msg(role)
|
||||
if msg[:28] in pane:
|
||||
if msg[:28] in "\n".join(pane.splitlines()[-8:]):
|
||||
return True
|
||||
nudges = state.get("nudges", 0) + 1
|
||||
log(f"limit probe #{nudges} on {role} ({session}) — nudging to resume")
|
||||
|
||||
Reference in New Issue
Block a user