feat(notifications): output standard slice 1 — config-driven alert rendering#131
Merged
Conversation
…ndering Automation alerts reached Discord as raw JSON: the whole alert path converged on _render_alert_content()'s json.dumps. Per the approved design (docs/superpowers/specs/2026-07-10-output-standard-design.md, spec_v3.md §25): - New notifications/output_renderer.py: OutputRenderer resolves a surface key (automation_alert.<capability> → automation_alert.default → generic key/value) to a Jinja2 template + Discord embed spec from config/output_formats.yaml. Deterministic template facts first; optional local-LLM voice pass (new task type format_user_output, local-only routing) rewrites the description sentence and falls back to template text on any failure with event_type="fallback_activated". No surface ever gets JSON. - Formats shipped: product_watch, news_check, default; semantic embed colours (good_news/action_needed/failure/info). - AutomationDispatcher takes an optional output_renderer; rendered text is persisted as alert_content and the embed rides discord_dm/discord_channel dispatches. Legacy plain-text path retained as the failure fallback. - NotificationService.dispatch_dm and DonnaBot.send_dm accept an embed (embed replaces content, matching _send's existing contract), including the blackout/quiet-hours queue path. - Wiring: _try_build_output_renderer in cli_wiring (non-fatal on missing config); voice pass routes through ctx.router. Voice pass verified live against qwen2.5:32b (facts preserved, 9s, ~$0.00003). - Tests: 12 renderer unit tests incl. real-config golden + drift checks; 2 dispatcher tests (renderer path, renderer-failure fallback); DM embed assertion updated. Full unit suite: 2255 passed. Follow-ups logged for slices 2–3 (reminders/proactive prompts, digest). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty
- BotProtocol.send_dm gains the optional embed param to match DonnaBot and NotificationService.dispatch_dm. - _Tolerant subclasses dict[str, Any] (mypy type-arg). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty
Resolve conflict in docs/superpowers/specs/followups.md by keeping both the calendar-OAuth (PR #130, now on main) and output-standard follow-up entries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163xNY4AKcd3pHF8CYVGPH5
nfeuer
pushed a commit
that referenced
this pull request
Jul 10, 2026
Resolve conflict in docs/superpowers/specs/followups.md by keeping both the output-standard (PR #131, now on main) and local-LLM-reliability follow-up entries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163xNY4AKcd3pHF8CYVGPH5
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.
Problem
Automation alerts (e.g. the shirt price watch) reach Discord as raw JSON —
_render_alert_content()was ajson.dumpsinto a plain message.Changes (spec_v3.md §25; design doc committed in this PR)
notifications/output_renderer.py— resolvesautomation_alert.<capability>→automation_alert.default→ generic key/value (never JSON) againstconfig/output_formats.yaml(Jinja2 template + embed spec, semantic colours). Deterministic facts first; optional voice pass via new local-only task typeformat_user_outputrewrites the description sentence, falling back to template text on any failure (fallback_activated).alert_content, embed ridesdiscord_dm/discord_channel. Legacy text path kept as the failure floor.dispatch_dm/send_dmgain embed support (embed replaces content, matching_send), including the blackout queue._try_build_output_renderer(non-fatal if config missing), voice pass throughctx.router.Example (live-verified against qwen2.5:32b)
Before:
Automation 'shirt on sale' alert:\nOutput: {"price_usd": 29.99, ...}After (embed, green): 🛍️ Seraphina Gown — $29.99 — "The Seraphina Gown is now available at $29.99, below your set threshold of $34, and your size is in stock. Should you act now to secure it?" +
price usd: 29.99 · in stock: ✓ · size available: ✓Testing
Follow-ups (logged in followups.md)
Slice 2: reminders + proactive prompts onto the renderer. Slice 3: digest. SMS length budget.
🤖 Generated with Claude Code
https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty