diff --git a/plugins/source-control/.claude-plugin/plugin.json b/plugins/source-control/.claude-plugin/plugin.json index 7b33e4576..63953f492 100644 --- a/plugins/source-control/.claude-plugin/plugin.json +++ b/plugins/source-control/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "source-control", - "version": "0.13.3", + "version": "0.13.4", "description": "Git and GitHub delivery workflow: /commit (Conventional Commits + Co-Authored-By trailer via safe heredoc mechanics), /pull-request (prep, create, CI monitoring, review-comment triage, merge, CI-log fetch), /babysit-prs (self-pacing fleet loop — safe by default; opt-in worker/autopilot tiers add gate-checked merge and thread resolution behind a deterministic Python engine), /worktree (create, status, cleanup, audit for parallel-session isolation), /setup (check the effective commit-subject / PR-title convention and babysit-prs config, or apply — interview the repo and write the tracked convention config), and /resolve-conflicts (intent-first merge/rebase conflict resolution with a semantic-conflict sweep — never --abort). The commit-subject / PR-title convention is configurable per repo via a tracked .claude/source-control.md config written by a re-runnable setup skill; Conventional Commits is the default when no convention is declared.", "author": { "name": "Melodic Software", diff --git a/plugins/source-control/CHANGELOG.md b/plugins/source-control/CHANGELOG.md index 2e68a1049..b6e0f7194 100644 --- a/plugins/source-control/CHANGELOG.md +++ b/plugins/source-control/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to the `source-control` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.13.4] + +### Fixed + +- **`babysit-prs` dynamic `/loop` wakeups now map `recommended_cadence` to a concrete + `ScheduleWakeup.delaySeconds` instead of falling back to the generic `/loop` heuristic.** The + snapshot engine emits `recommended_cadence` (`reference/cadence.md`: active / normal / quiet / + idle) and `reference/loop.md` §5.3 told the orchestrator to "derive the wake interval" from it, + but never gave the string-to-seconds translation — so orchestrators silently fell back to the + generic `/loop` skill's own "lean 1200–1800s" fallback-heartbeat range, overriding the domain + skill's tighter adaptive-cadence contract and leaving PRs with pending CI or blocking feedback + unchecked 4–5x longer than intended. §5.3 now carries a deterministic mapping table + (`active`→300, `normal`→900, `quiet`→3600, `idle`→3600) and states plainly that this signal + ALWAYS wins over the generic heuristic whenever a snapshot supplies it — in babysit dynamic mode + the `ScheduleWakeup` delay is the primary cadence signal, not a fallback heartbeat. The `idle` + row is documented as a ceiling: `ScheduleWakeup` clamps `delaySeconds` to `[60, 3600]`, so + cadence.md's daily `idle` intent truncates to the 3600s hourly ceiling — a genuine daily cadence + needs the durable `/schedule` cron mechanism, not a single-session `/loop` wakeup. + ## [0.13.3] ### Fixed diff --git a/plugins/source-control/skills/babysit-prs/reference/loop.md b/plugins/source-control/skills/babysit-prs/reference/loop.md index 4c4ee3805..f49d8ecff 100644 --- a/plugins/source-control/skills/babysit-prs/reference/loop.md +++ b/plugins/source-control/skills/babysit-prs/reference/loop.md @@ -362,9 +362,30 @@ git checkout "$PARKING_BRANCH" At the end of each iteration, schedule the next wake. Cadence has one owner: the engine recommends, this loop schedules. -**Engine-backed runs (Python present):** derive the wake interval from the snapshot's -`recommended_cadence` — `active` 5 minutes, `normal` 15 minutes, `quiet` hourly, `idle` daily — -per [cadence.md](cadence.md)'s states and thresholds. +**Engine-backed runs (Python present):** the snapshot's `recommended_cadence` is the cadence +signal — map it directly to a concrete `ScheduleWakeup.delaySeconds`. The states behind each value +live in [cadence.md](cadence.md); this table owns the seconds: + +| `recommended_cadence` | `ScheduleWakeup.delaySeconds` | +|-----------------------|-------------------------------| +| `active` | 300 | +| `normal` | 900 | +| `quiet` | 3600 | +| `idle` | 3600 (ceiling — see caveat) | + +**This mapping ALWAYS wins** over the generic `/loop` skill's own delay-picking heuristic whenever +a snapshot supplies `recommended_cadence`. Read the field out of the snapshot/state JSON and +schedule from this table — do not fall back to the generic skill's "lean 1200–1800s" range. In +babysit dynamic mode the `ScheduleWakeup` delay **is** the primary cadence signal, not a fallback +heartbeat sitting behind some other armed wake event, so the generic skill's heartbeat framing does +not apply here: an `active` cycle schedules at 300s, never 1200–1800s. + +**Idle ceiling (a true daily cadence cannot run in single-session `/loop`).** `ScheduleWakeup` +clamps `delaySeconds` to `[60, 3600]`, so cadence.md's `idle` = daily (86400s) truncates to the +3600s ceiling — the same wake interval as `quiet`. This is a documented limitation, not a silent +truncation: within `/loop`, `idle` and `quiet` both wake hourly. A genuine daily babysit cadence +needs the durable `/schedule` cron mechanism (a scheduled routine on a real cron interval), not a +single-session `/loop` wakeup — reach for `/schedule` when that is what is wanted. **Python-free degrade ladder** (no snapshot available this iteration):