v1.3.0 Phase B: summarizer hygiene, quiet-hours mode, guarded follow-ups #105

Merged
claude-bot merged 1 commit from feat/v1.3.0-phaseB-summarizer into main 2026-07-19 01:07:18 +00:00
Contributor

Completes the v1.3.0 summarizer work. With all new flags at their defaults, runtime behavior is unchanged except two intended always-on fixes (the token-formula fix and the #16 hygiene/validator, which only neutralize delimiters and reject fabricated URLs).

#16 / F-33 — prompt hygiene + validators (always-on, low-risk)

  • Token formula fix: _output_tokens_for_chars was a no-op — max(4096, min(max_chars*6, 4096)) always returned 4096. Now max(4096, min(max_chars*6, 8192)): 4096 floor for reasoning-model headroom, scales with the char budget, 8192 cap. The extra_config override branch is unchanged.
  • Delimiter neutralization: feed-controlled source_text/source_title are sanitized (zero-width space after < in delimiter tags) before interpolation into the <official_source>/<metadata> prompt blocks, so malformed or hostile feed content can't break out of its block. Stored DB text is untouched.
  • Output-URL validator: rejects a URL in the summary that isn't present in the source text, feeding the existing validation-retry loop.

The real-world Qwen quality-tracking part of #16 is an ongoing operational activity (needs a running model + real events), not a code deliverable — see the closing note on the issue.

#19 — quiet-hours AI mode (QUIET_HOURS_AI_MODE, default replace)

During quiet hours, non-critical (watch/advisory) alerts either have the verbose raw product replaced by the concise AI summary (default) or supplemented by it. With no AI summary configured, replace degrades to today's full suppression — raw verbose text is still never sent in quiet hours. Warning-class alerts bypass quiet hours entirely and are unaffected. The AI follow-up opens its own DB session and never touches SentAlert / delivery-outbox / public-page bookkeeping, so the (nws_alert_id, location_id, channel_id) dedup invariant is intact.

#21 — guardrailed timing/impact follow-ups (AI_TIMING_IMPACT_FOLLOWUPS_ENABLED, default false)

Ships dark. When enabled, a bounded prompt clause permits approximate onset/duration/impact synthesized strictly from the official text + structured context, preserving uncertainty and inventing no towns/roads/distances/exact times; existing validators still guard over-reach. With the flag off the prompt is byte-for-byte unchanged.

Testing

Full suite green on the dev server: 658 passed (14 new tests covering the token formula, delimiter neutralization vs. stored-text preservation, the URL validator, the #21 clause on/off, and the #19 mode decision).

Closes #16, #19, #21

🤖 Generated with Claude Code

Completes the v1.3.0 summarizer work. With all new flags at their defaults, runtime behavior is unchanged **except** two intended always-on fixes (the token-formula fix and the #16 hygiene/validator, which only neutralize delimiters and reject fabricated URLs). ## #16 / F-33 — prompt hygiene + validators (always-on, low-risk) - **Token formula fix**: `_output_tokens_for_chars` was a no-op — `max(4096, min(max_chars*6, 4096))` always returned 4096. Now `max(4096, min(max_chars*6, 8192))`: 4096 floor for reasoning-model headroom, scales with the char budget, 8192 cap. The `extra_config` override branch is unchanged. - **Delimiter neutralization**: feed-controlled `source_text`/`source_title` are sanitized (zero-width space after `<` in delimiter tags) before interpolation into the `<official_source>`/`<metadata>` prompt blocks, so malformed or hostile feed content can't break out of its block. Stored DB text is untouched. - **Output-URL validator**: rejects a URL in the summary that isn't present in the source text, feeding the existing validation-retry loop. The real-world Qwen quality-tracking part of #16 is an ongoing operational activity (needs a running model + real events), not a code deliverable — see the closing note on the issue. ## #19 — quiet-hours AI mode (`QUIET_HOURS_AI_MODE`, default `replace`) During quiet hours, non-critical (watch/advisory) alerts either have the verbose raw product **replaced** by the concise AI summary (default) or **supplemented** by it. With no AI summary configured, `replace` degrades to today's full suppression — raw verbose text is still never sent in quiet hours. **Warning-class alerts bypass quiet hours entirely and are unaffected.** The AI follow-up opens its own DB session and never touches `SentAlert` / delivery-outbox / public-page bookkeeping, so the `(nws_alert_id, location_id, channel_id)` dedup invariant is intact. ## #21 — guardrailed timing/impact follow-ups (`AI_TIMING_IMPACT_FOLLOWUPS_ENABLED`, default `false`) Ships dark. When enabled, a bounded prompt clause permits approximate onset/duration/impact synthesized **strictly** from the official text + structured context, preserving uncertainty and inventing no towns/roads/distances/exact times; existing validators still guard over-reach. With the flag off the prompt is byte-for-byte unchanged. ## Testing Full suite green on the dev server: **658 passed** (14 new tests covering the token formula, delimiter neutralization vs. stored-text preservation, the URL validator, the #21 clause on/off, and the #19 mode decision). Closes #16, #19, #21 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summarizer: prompt hygiene, output-URL validator, quiet-hours REPLACE, guarded follow-ups
All checks were successful
CI / test (pull_request) Successful in 4m3s
ea81a69e95
#16 / F-33 — prompt hygiene + validators (always-on, low-risk):
- Fix _output_tokens_for_chars, which was a no-op: max(4096, min(max_chars*6, 4096))
  always returned 4096, so max_chars had no effect. Now max(4096, min(max_chars*6,
  8192)) — 4096 floor for reasoning-model headroom, scales with the char budget,
  8192 cap. The extra_config override is unchanged.
- Neutralize prompt delimiters: feed-controlled source_text/source_title are
  sanitized (zero-width space after '<' in delimiter tags) before interpolation
  into the <official_source>/<metadata> prompt blocks, so malformed or hostile feed
  content can't break out of its block. Stored DB text is untouched.
- Output-URL validator: summary_validation_errors now rejects a URL in the summary
  that does not appear in the source text, feeding the existing retry loop.

#19 — quiet-hours AI mode (QUIET_HOURS_AI_MODE, default "replace"):
  During quiet hours, non-critical (watch/advisory) alerts either have the verbose
  raw product REPLACED by the concise AI summary (default) or SUPPLEMENTED by it.
  With no AI summary configured, replace degrades to today's full suppression — raw
  verbose text is still never sent in quiet hours. Warning-class alerts bypass quiet
  hours entirely and are unaffected. The AI followup opens its own session and never
  touches SentAlert/outbox/public-page bookkeeping, so dedup invariants are intact.

#21 — guardrailed timing/impact follow-ups (AI_TIMING_IMPACT_FOLLOWUPS_ENABLED,
  default false): ships dark. When enabled, a bounded prompt clause permits
  approximate onset/duration/impact synthesized strictly from official text +
  structured context, preserving uncertainty; existing validators still guard
  over-reach. With the flag off the prompt is byte-for-byte unchanged.

Full suite green on the dev server: 658 passed (14 new).

Closes #16, #19, #21

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
claude-bot deleted branch feat/v1.3.0-phaseB-summarizer 2026-07-19 01:07:18 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot!105
No description provided.