feat: scheduled supervision sweeps + manual command (PRD #17)#21
Merged
Conversation
Extract the verdict-apply path into services/verdict_apply.py so the watcher and the new sweep share ONE observer-authority apply path. run_sweep (deep, injected seams) enumerates live linked sessions, reads each transcript tail, judges once via judge_session, applies the verdict, and returns a per-session SweepReport — with an active-hours window guard (--force bypass) and per-session error isolation so one bad session never aborts the sweep. bach supervise runs it manually (bare invocation or 'run'; --task, --force). New window config knobs (start=8, end=22, validated 0<=start<end<=24). Closes #18 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
render_sweep_agent emits a com.bach.supervise plist with a StartCalendarInterval array of N daytime entries invoking bach supervise. install/uninstall_sweep_agent are idempotent (unload-before-overwrite) via an injected launchctl runner, printing the manual 'bach supervise' fallback on launchctl failure. New supervise_sweeps_per_day=5 knob. Distinct label from the daemon so both can coexist. Closes #19 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(PRD #17) _compute_sweep_hours used floor division (step=span//N), clustering N=5 over [8,22) into [8,10,12,14,16] and leaving 16:00-22:00 unsupervised. Switch to round(start + i*span/N) → [8,11,14,16,19], spanning the window. Add the N=5 spread assertion that previously let the clustering hide (only count + in-window were checked). /review + /backend-taste + /qa otherwise ship-it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ADR-018 records the scheduled-sweep vs always-on-daemon trade-off (~36x cheaper), the keep-both decision, and accepted limitations. New how-to covers bach supervise (manual/--force), supervise install (5x/day daytime, [8,11,14,16,19]), tuning knobs, and when to use sweeps vs the daemon. CLAUDE.md gains the supervise command block. Surgical /code-comments pass adds WHY intent across the sweep modules; corrected the spacing example to the accurate floored-step [8,10,12,14,16] vs round [8,11,14,16,19]. Closes #20 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements PRD #17 — a cheap, scheduled alternative to the always-on observer daemon, plus a manual trigger. Addresses the long-term cost of continuous supervision: the daemon's ~5-min judge tick is ~180 codex
gpt-5.4-minicalls per session per day; 5 sweeps/day ≈ 36× cheaper.What shipped (issues #18–#20)
bach supervise— one-shot supervision sweep, no persistent process: enumerate live task-linked sessions → read each transcript tail → judge once → apply the verdict → per-session summary → exit. Manual via barebach supervise/run, with--task <id>and--force. Extracted the verdict-apply path intoverdict_apply.pyso the sweep and the watcher share one observer-authority apply path. Per-session error isolation; active-hours window guard. New window config knobs (start=8,end=22).bach supervise install/uninstallregisterscom.bach.supervisewith aStartCalendarIntervalarray of N daytime entries (default 5, spread across 08:00–22:00). Idempotent; printedbach supervisefallback on launchctl failure; distinct label from the daemon. Newsupervise_sweeps_per_day=5knob.Cost model
bach supervisedoes exactly one judge call per session per run. Run 5×/day during daylight (08:00–22:00) → flat, predictable cost. The schedule spreads to hours [8, 11, 14, 16, 19] for N=5 (review fixed an initial floor-division bug that clustered them at [8,10,12,14,16], leaving the late afternoon uncovered).Relationship to the daemon (PRD #10)
The always-on daemon is untouched and remains the opt-in real-time option. Scheduled sweeps are the recommended cost-conscious default. Both may be installed (distinct labels) but that's redundant — documented in ADR-018. Supervision stays pull, not push (verdicts land on the artifact/board).
Quality
make gateverified between slices./review+/backend-taste+/qa, no HITL) fixed the sweep-hour spread + added the spread-assertion test that let the bug hide; verdict-apply extraction, window guard, error isolation, idempotent install, and fallback all reviewed "ship it".make gategreen: 1225 passed, 1 skipped (ruff + mypy + pytest).Closes #17, #18, #19, #20
🤖 Generated with Claude Code