feat: implement Deferral signal detector#2
Conversation
The README's Signal anatomy section calls out Pattern and Deferral as reserved enum variants 'no detector produces them yet.' This wires up the Deferral detector and renders deferrals through the digest pipeline. - detect_deferrals mirrors detect_workarounds: OnceLock<RegexSet> with a parallel label table, first-match-wins per assistant message. Patterns cover come-back-to, deferring, defer, punt, leave-for-later, skipping-for-now, park-for-now, next-session, circle-back. - detect_workarounds and detect_deferrals are independent. A message like 'skipping for now' fires both and the tracker files them under separate titles. - DigestReport gains a deferrals: Vec<DeferralSignal> field; run_review populates it. render_digest and write_digest gain a deferrals param inserted between workarounds and p0_alerts; existing tests pass []. - Digest YAML frontmatter always carries deferrals: N (zero acceptable). ## Deferrals section is unconditional with _No deferrals detected._ placeholder, mirroring the existing Corrections/Errors/Workarounds pattern. - The CLI summary println in commands/review.rs and the JSON output (--json from feat/review-nightly-json-since) both include the count. - README Signal anatomy table gains a Deferral row between Workaround and P0 Alert; the prose drops Deferral from the reserved list. - 8 deferrals_* tests in detectors.rs plus a digest render test.
|
Thanks again for the first PR! Merging this after #1. One quick design question for the code itself, not the behavior: The workaround/deferral overlap on "skipping for now" — I'm happy with both signals firing (the precise categorization is genuinely useful), but right now the only place the "this is intentional" reasoning lives is in the test name. Could you add a sentence to the Heads up: I landed a change to Really appreciate the careful work. — Joi |
Summary
Implements the Deferral signal detector that the README's Signal anatomy section reserves but says "no detector produces them yet."
Why this matters
The
Signalenum atcrates/jilog-review/src/signal.rs:11-22already declaresDeferral(DeferralSignal), the title formatter intracker.rs:81-83already handles it, andSignal::session_id()/Signal::kind()already cover the arm. The only thing missing was the detector and its hook inrun_review. This PR fills the gap and updates the README so the documented count matches the implementation.Changes
detect_deferrals(&[Message], &str) -> Vec<DeferralSignal>mirrorsdetect_workarounds: aOnceLock<RegexSet>over assistant text with a parallel label table, first-match-wins per message. Patterns covercome back to,defer/deferring,punt on,leave for later,skipping for now,park for now,next session,circle back.detect_workaroundsanddetect_deferralsare independent. A message like"skipping for now"fires both - the tracker files them under separate titles via the existingsignal_titleformatter. The "skipping for now" overlap test asserts this is intentional rather than double-counting.DigestReportgainspub deferrals: Vec<DeferralSignal>;run_reviewpopulates it.render_digestandwrite_digestgain adeferrals: &[DeferralSignal]parameter inserted betweenworkaroundsandp0_alerts. The existing fourrender_digesttests pass&[]for the new arg.deferrals: N(zero acceptable). The## Deferralssection is unconditional with_No deferrals detected._placeholder when empty, matching the existing pattern for Corrections/Errors/Workarounds.commands/review.rssummaryprintln!and the--jsonoutput (from the previous PR) both include the deferrals count. The JSON shape stays atschema_version: 1; this is an additive key.Deferralrow between Workaround and P0 Alert. Prose atREADME.md:128updates from "Four signal types ... Two more (Pattern, Deferral)" to "Five signal types ... One more (Pattern)".Depends on #1
This branch is built on top of #1 (
--json/--sinceflags), so the diff GitHub renders here includes both commits: #1's 246-line review.rs/query.rs change plus the 215-line deferral work. The two commits are independent in scope; the deferral work just happens to extend the JSON shape #1 introduced.Easiest review path: merge #1 first, then GitHub will auto-rebase this PR to just the deferral commit. If you prefer the reverse order, the deferral commit can be cherry-picked to main and #1 rebased on top - the file overlap is small.
Testing
cargo build --workspacepassescargo test --workspacepasses (105 tests total, was ~70 pre-PR)deferrals_*tests indetectors.rscover: basic match, first-match-wins, user-role skipped, tool-blocks excluded, no-match returns empty, label emission, the workaround-vs-deferral overlap on "skipping for now", and the digest render path.