mirror of
https://github.com/stephenleo/bmad-autonomous-development.git
synced 2026-04-25 20:34:55 +02:00
feat(bad): add blocking-sleep timer variant for Codex
Widens TIMER_SUPPORT from boolean to enum (cron | blocking-sleep | prompt) so Codex gets real auto-fire timers via a shell `sleep N` instead of falling back to manual prompt continuation. Legacy true/false still accepted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,7 +96,7 @@ BAD is configured at install time (`/bad setup`) and stores settings in the `bad
|
||||
| `RETRO_TIMER_SECONDS` | `600` | Delay before auto-retrospective |
|
||||
| `CONTEXT_COMPACTION_THRESHOLD` | `80` | Context window % at which to compact context |
|
||||
| `STALE_TIMEOUT_MINUTES` | `60` | Minutes of subagent inactivity before watchdog alerts (0 = disabled) |
|
||||
| `TIMER_SUPPORT` | `true` | Use native platform timers; `false` for prompt-based continuation |
|
||||
| `TIMER_SUPPORT` | `cron` (Claude Code) / `blocking-sleep` (Codex) / `prompt` (others) | `cron` uses `CronCreate`; `blocking-sleep` uses a shell `sleep N` (auto-fires when it returns); `prompt` waits for a user reply. Legacy `true`/`false` still accepted |
|
||||
| `MONITOR_SUPPORT` | `true` | Use the Monitor tool for CI/PR-merge polling; `false` for Bedrock/Vertex/Foundry |
|
||||
| `API_FIVE_HOUR_THRESHOLD` | `80` | (Claude Code) 5-hour usage % at which to pause |
|
||||
| `API_SEVEN_DAY_THRESHOLD` | `95` | (Claude Code) 7-day usage % at which to pause |
|
||||
|
||||
@@ -99,7 +99,7 @@ BAD is configured at install time (`/bad setup`) and stores settings in the `bad
|
||||
| `RETRO_TIMER_SECONDS` | `600` | Seconds before auto-retrospective after epic completion |
|
||||
| `CONTEXT_COMPACTION_THRESHOLD` | `80` | Context window % at which to compact context |
|
||||
| `STALE_TIMEOUT_MINUTES` | `60` | Minutes of subagent inactivity before watchdog alerts (0 = disabled) |
|
||||
| `TIMER_SUPPORT` | `true` | Use native platform timers; `false` for prompt-based continuation |
|
||||
| `TIMER_SUPPORT` | `cron` (Claude Code) / `blocking-sleep` (Codex) / `prompt` (others) | `cron` uses `CronCreate`; `blocking-sleep` uses a shell `sleep N` (auto-fires when it returns); `prompt` waits for a user reply. Legacy `true`/`false` still accepted |
|
||||
| `MONITOR_SUPPORT` | `true` | Use the Monitor tool for CI/PR-merge polling; `false` for Bedrock/Vertex/Foundry |
|
||||
| `API_FIVE_HOUR_THRESHOLD` | `80` | (Claude Code) 5-hour usage % at which to pause |
|
||||
| `API_SEVEN_DAY_THRESHOLD` | `95` | (Claude Code) 7-day usage % at which to pause |
|
||||
|
||||
@@ -56,7 +56,7 @@ Load base values from the `bad` section of `_bmad/config.yaml` at startup. Then
|
||||
| `WAIT_TIMER_SECONDS` | `wait_timer_seconds` | `3600` | Post-batch wait before re-checking PR status (1 hr) |
|
||||
| `CONTEXT_COMPACTION_THRESHOLD` | `context_compaction_threshold` | `80` | Context window % at which to compact/summarise context |
|
||||
| `STALE_TIMEOUT_MINUTES` | `stale_timeout_minutes` | `60` | Minutes of subagent inactivity before watchdog alerts (0 = disabled) |
|
||||
| `TIMER_SUPPORT` | `timer_support` | `true` | When `true`, use native platform timers; when `false`, use prompt-based continuation |
|
||||
| `TIMER_SUPPORT` | `timer_support` | `cron` (Claude Code) / `blocking-sleep` (Codex) / `prompt` (others) | `cron` uses `CronCreate`; `blocking-sleep` uses a shell `sleep N` that auto-fires when it returns; `prompt` waits for a user reply. Legacy `true`→`cron`, `false`→`prompt` |
|
||||
| `MONITOR_SUPPORT` | `monitor_support` | `true` | When `true`, use the Monitor tool for CI and PR-merge polling; when `false`, fall back to manual polling loops (required for Bedrock/Vertex/Foundry) |
|
||||
| `API_FIVE_HOUR_THRESHOLD` | `api_five_hour_threshold` | `80` | (Claude Code) 5-hour rate limit % that triggers a pause |
|
||||
| `API_SEVEN_DAY_THRESHOLD` | `api_seven_day_threshold` | `95` | (Claude Code) 7-day rate limit % that triggers a pause |
|
||||
@@ -68,7 +68,7 @@ Load base values from the `bad` section of `_bmad/config.yaml` at startup. Then
|
||||
|
||||
After resolving all values, print the active configuration so the user can confirm before Phase 0 begins:
|
||||
```
|
||||
⚙️ BAD config: MAX_PARALLEL_STORIES=3, RUN_CI_LOCALLY=false, AUTO_PR_MERGE=false, MODEL_STANDARD=sonnet, MODEL_QUALITY=opus, TIMER_SUPPORT=true, ...
|
||||
⚙️ BAD config: MAX_PARALLEL_STORIES=3, RUN_CI_LOCALLY=false, AUTO_PR_MERGE=false, MODEL_STANDARD=sonnet, MODEL_QUALITY=opus, TIMER_SUPPORT=cron, ...
|
||||
```
|
||||
|
||||
---
|
||||
@@ -536,7 +536,7 @@ Read `references/coordinator/pattern-notify.md` whenever a `📣 Notify:` callou
|
||||
|
||||
## Timer Pattern
|
||||
|
||||
Read `references/coordinator/pattern-timer.md` when instructed to start a timer. It covers both `TIMER_SUPPORT=true` (CronCreate) and `TIMER_SUPPORT=false` (prompt-based) paths.
|
||||
Read `references/coordinator/pattern-timer.md` when instructed to start a timer. It covers all three `TIMER_SUPPORT` paths: `cron` (Claude Code's `CronCreate`), `blocking-sleep` (Codex — shell `sleep N` auto-fires), and `prompt` (manual reply).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -201,9 +201,10 @@ If multiple harnesses are detected, repeat this step once per additional harness
|
||||
section clearly and store model/threshold values with a harness prefix (e.g.
|
||||
`claude_model_standard`).
|
||||
|
||||
Automatically write without prompting:
|
||||
- Claude Code: `timer_support: true`, `monitor_support: true`
|
||||
- All other harnesses: `timer_support: false`, `monitor_support: false`
|
||||
Automatically write without prompting (based on the `current_harness` detected in Step 2):
|
||||
- Claude Code: `timer_support: cron`, `monitor_support: true`
|
||||
- `openai-codex`: `timer_support: blocking-sleep`, `monitor_support: false`
|
||||
- All other harnesses: `timer_support: prompt`, `monitor_support: false`
|
||||
|
||||
## Step 7: Write Files
|
||||
|
||||
@@ -220,7 +221,7 @@ Write a temp JSON file with collected answers structured as:
|
||||
"retro_timer_seconds": "600",
|
||||
"context_compaction_threshold": "80",
|
||||
"stale_timeout_minutes": "60",
|
||||
"timer_support": true,
|
||||
"timer_support": "<cron on Claude Code, blocking-sleep on openai-codex, prompt elsewhere>",
|
||||
"monitor_support": true,
|
||||
"model_standard": "<resolved in Step 6 — sonnet on Claude Code, gpt-5.3-codex default on openai-codex>",
|
||||
"model_quality": "<resolved in Step 6 — opus on Claude Code, gpt-5.4 default on openai-codex>",
|
||||
|
||||
@@ -49,7 +49,7 @@ If `rate_limits.five_hour.used_percentage` is present and **> `API_FIVE_HOUR_THR
|
||||
date -d @{resets_at}
|
||||
```
|
||||
2. Print: `"⏸ 5-hour usage limit at {usage}% — auto-pausing until reset at {reset_time}. BAD will resume automatically."`
|
||||
3. **If `TIMER_SUPPORT=true`:** compute a cron expression from the reset epoch and schedule a resume:
|
||||
3. **If `TIMER_SUPPORT=cron`** (or legacy `true`): compute a cron expression from the reset epoch and schedule a resume:
|
||||
```bash
|
||||
# macOS
|
||||
date -r {resets_at} '+%M %H %d %m *'
|
||||
@@ -63,7 +63,14 @@ If `rate_limits.five_hour.used_percentage` is present and **> `API_FIVE_HOUR_THR
|
||||
|
||||
Save the job ID. Do not ask the user for input — resume automatically when `BAD_RATE_LIMIT_TIMER_FIRED` arrives.
|
||||
|
||||
4. **If `TIMER_SUPPORT=false`:** print the reset time and wait for the user to reply when they're ready to continue. Then re-check the limit before proceeding.
|
||||
4. **If `TIMER_SUPPORT=blocking-sleep`:** compute the remaining seconds and block in the shell until reset:
|
||||
```bash
|
||||
SLEEP_SECS=$(( {resets_at} - $(date +%s) ))
|
||||
[ "$SLEEP_SECS" -gt 0 ] && sleep "$SLEEP_SECS"
|
||||
```
|
||||
When `sleep` returns, re-read session state and re-check `five_hour.used_percentage` — if now below `API_FIVE_HOUR_THRESHOLD`, proceed to Check 3. If still too high (rare — usage sample hasn't refreshed), sleep another 60s and re-check.
|
||||
|
||||
5. **If `TIMER_SUPPORT=prompt`** (or legacy `false`): print the reset time and wait for the user to reply when they're ready to continue. Then re-check the limit before proceeding.
|
||||
|
||||
---
|
||||
|
||||
@@ -79,7 +86,7 @@ If `rate_limits.seven_day.used_percentage` is present and **> `API_SEVEN_DAY_THR
|
||||
date -d @{resets_at}
|
||||
```
|
||||
2. Print: `"⏸ 7-day usage limit at {usage}% — auto-pausing until reset at {reset_time}. BAD will resume automatically."`
|
||||
3. **If `TIMER_SUPPORT=true`:** compute a cron expression from the reset epoch and schedule a resume:
|
||||
3. **If `TIMER_SUPPORT=cron`** (or legacy `true`): compute a cron expression from the reset epoch and schedule a resume:
|
||||
```bash
|
||||
# macOS
|
||||
date -r {resets_at} '+%M %H %d %m *'
|
||||
@@ -93,7 +100,14 @@ If `rate_limits.seven_day.used_percentage` is present and **> `API_SEVEN_DAY_THR
|
||||
|
||||
Save the job ID. Resume automatically when `BAD_RATE_LIMIT_TIMER_FIRED` arrives.
|
||||
|
||||
4. **If `TIMER_SUPPORT=false`:** print the reset time and wait for the user to reply when ready. Then re-check before proceeding.
|
||||
4. **If `TIMER_SUPPORT=blocking-sleep`:** compute the remaining seconds and block in the shell until reset:
|
||||
```bash
|
||||
SLEEP_SECS=$(( {resets_at} - $(date +%s) ))
|
||||
[ "$SLEEP_SECS" -gt 0 ] && sleep "$SLEEP_SECS"
|
||||
```
|
||||
When `sleep` returns, re-read session state and re-check `seven_day.used_percentage` — if now below `API_SEVEN_DAY_THRESHOLD`, proceed to Phase 0. If still too high, sleep another 60s and re-check.
|
||||
|
||||
5. **If `TIMER_SUPPORT=prompt`** (or legacy `false`): print the reset time and wait for the user to reply when ready. Then re-check before proceeding.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
|
||||
Both the retrospective and post-batch wait timers use this pattern. The caller supplies the duration, fire prompt, option labels, and actions.
|
||||
|
||||
Behaviour depends on `TIMER_SUPPORT`:
|
||||
Behaviour depends on `TIMER_SUPPORT`. Accepted values:
|
||||
|
||||
- `cron` — Claude Code's `CronCreate` schedules a one-shot wake-up.
|
||||
- `blocking-sleep` — Codex (and any harness without a scheduler but with a shell): a blocking `sleep N` *is* the timer; it auto-fires when it returns.
|
||||
- `prompt` — no auto-fire; wait for the user to reply.
|
||||
|
||||
Legacy values: `true` is read as `cron`, `false` is read as `prompt`.
|
||||
|
||||
---
|
||||
|
||||
## If `TIMER_SUPPORT=true` (native platform timers)
|
||||
## If `TIMER_SUPPORT=cron` (native platform timers)
|
||||
|
||||
**Step 1 — compute target cron expression** (convert seconds to minutes: `SECONDS ÷ 60`):
|
||||
```bash
|
||||
@@ -64,7 +70,37 @@ echo "⏱ Time elapsed: $((ELAPSED / 60))m $((ELAPSED % 60))s"
|
||||
|
||||
---
|
||||
|
||||
## If `TIMER_SUPPORT=false` (prompt-based continuation)
|
||||
## If `TIMER_SUPPORT=blocking-sleep` (Codex — shell-blocking timer)
|
||||
|
||||
Save `TIMER_START=$(date +%s)`. Codex has no scheduler, but its shell tool blocks on long commands — so `sleep N` *is* the timer. Print the options menu:
|
||||
|
||||
> Timer running — shell sleep for {N} minutes. I'll act automatically when it returns.
|
||||
>
|
||||
> - **[C] Continue** — {C label} (auto-fires when sleep completes)
|
||||
> - **[S] Stop** — {S label}
|
||||
> - **[X] Exit** — {X label} ← omit this line if no [X] label was supplied
|
||||
> - **[M] N** — to modify the countdown, press **Esc** (or ctrl-C) to interrupt the sleep, then reply with `[M] <minutes>`
|
||||
|
||||
📣 **Notify** (see `references/coordinator/pattern-notify.md`) with the same options. Make sure the user knows that typing a reply mid-wait requires pressing Esc first — while the shell tool is running, the agent is blocked on it.
|
||||
|
||||
Then run a single blocking command:
|
||||
```bash
|
||||
sleep {SECONDS}
|
||||
EXIT=$?
|
||||
ELAPSED=$(( $(date +%s) - TIMER_START ))
|
||||
echo "⏱ Time elapsed: $((ELAPSED / 60))m $((ELAPSED % 60))s (exit=$EXIT)"
|
||||
```
|
||||
|
||||
- **`EXIT=0`** (sleep completed normally) → run the [C] action automatically. This is the auto-fire path.
|
||||
- **`EXIT!=0`** (user interrupted with Esc / ctrl-C) → reprint the menu and wait for the user's reply:
|
||||
- **[C]** → run the [C] action
|
||||
- **[S]** → run the [S] action
|
||||
- **[X]** → run the [X] action ← only if [X] label was supplied
|
||||
- **[M] N** → update `TIMER_START`, recompute `SECONDS = N × 60`, print the updated wait message, 📣 **Notify**, then re-run the `sleep $SECONDS` command above
|
||||
|
||||
---
|
||||
|
||||
## If `TIMER_SUPPORT=prompt` (prompt-based continuation)
|
||||
|
||||
Save `TIMER_START=$(date +%s)`. No native timer is created — print the options menu immediately and wait for user reply:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user