fix(babysit): refuse a non-boolean wake_on_green instead of coercing it - #7665
Conversation
The gh-pr watch read wake_on_green as bool(params.get(...)). The cron
message is JSON, so a caller can write {"wake_on_green": "false"} -- a
string. bool("false") is True, so the switch read as ENABLED and woke
the operator the moment the PR went all-green, the exact opposite of
what they asked for. Any non-empty string ("no", "0", "off") did this.
Validate against bool explicitly (bool is a subclass of int) and raise
ValueError for anything else, so a nonsense flag becomes a terminal Done
like every other malformed field in identity(), rather than running
forever with inverted behaviour. The absent-key default stays True.
Add a unit test per rejected spelling plus one asserting the terminal
Done rather than a coerced wake, alongside the existing malformed-parameter
cases.
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Real silent-inversion harm, fixed at the parse site with the same terminal-refusal discipline its sibling fields already use — proportionate and complete. [DESIGN-REVIEWED] 31b7774 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of All checks complete. The fix deletes a coercion rather than adding surface, matches the file's existing per-field terminal-validation discipline, the only real sibling (the byte-identical copy in unmerged PR #7634's relocation) is declared in the scope note, and the repo-wide First-Principles-Verdict: PASS A silently inverted explicit disable becomes a loud terminal stop; the change removes a coercion instead of adding surface, and every behavior shift is declared. What this change shipsIntent: stop the PR watch from waking an operator who explicitly turned green-wakes off — a FIX (reported defect #7644).
Checks run: no shared strict-bool helper is importable from this standalone skill script (grep [FIRST-PRINCIPLES-REVIEWED] 31b7774 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
NicholasRBowers
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (2 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: fix with clear root cause -- bool coercion of a JSON-string wake_on_green inverted an explicit disable; now refused as a terminal invalid config, with red-before regression tests.
Fixes #7644.
Problem
The gh-pr watch probe read its
wake_on_greenswitch as:The cron message is JSON, so a caller can write
{"wake_on_green": "false"}(a string).bool("false")isTrue, so an explicit disable silently inverts to ENABLED and the watch wakes the operator on all-green anyway. Any non-empty string ("no","0","off") has the same effect.Fix
In
PrWatchProbe.identity()(src/kiro_crew/builtin_skills/kirocrew-dev/babysit/scripts/pr_watch.py), replaced the coercion with explicit validation that mirrors the existing malformed-parameter discipline already used forrepo,pr, andcoalesce_secs:A non-boolean now raises
ValueError, which the wrapper converts into a terminalDone(the watch says so once and stops) rather than running forever with the opposite of the requested behaviour. Becauseboolis a subclass ofint, the guard uses an explicitisinstance(..., bool)check. The absent-key default remainsTrue.Tests
Added to
test/test_babysit_pr_watch.pyalongside the existing malformed-parameter cases:test_string_wake_on_green_is_refused_not_coerced— parametrized over"false","no","0","off"; each asserts terminalDone.test_string_wake_on_green_does_not_coerce_to_a_wake— on an all-green rollup, assertsDonefires instead of the review-readyReporta truthy coercion would have produced.test_real_boolean_true_wake_on_green_still_wakes,test_real_boolean_false_wake_on_green_stays_quiet,test_absent_wake_on_green_defaults_to_waking— confirm real booleans and the default still behave.Verification
Ran the target module offline against the sandbox Python: 70 passed. Falsification check: with the source fix reverted (test change only), the 5 new string-rejection tests fail against the old
bool(...)coercion; with the fix restored they pass.The documented full-suite entry (
make backend && make test) requires building the dev venv from PyPI, which is unavailable under the sandbox's repository-access-only network mode, so full-suite CI verification should run in the pipeline.Scope note
Targets
pr_watch.pyonmainas directed. The relocation intokiro_crew.probes.gh_pr(PR #7634) is not yet merged; when it lands, the byte-identical expression atprobes/gh_pr.py:329will need the same guard carried across, or this change rebased onto the relocated path.Pattern harvest
Rule candidate: semgrep
Pattern:
bool(params.get("flag", default))over a value parsed from JSON. Everynon-empty string coerces to True, so
"false","no","0"and"off"all invertan explicit disable into enabled. Prefer an
isinstance(x, bool)guard that refusesthe value over a coercion that silently means the opposite of what was sent.