Skip to content

feat(watchlist): implement delivery webhook dispatch#86

Open
hnshah wants to merge 1 commit intomvanhorn:mainfrom
hnshah:feat/delivery-webhook-dispatch
Open

feat(watchlist): implement delivery webhook dispatch#86
hnshah wants to merge 1 commit intomvanhorn:mainfrom
hnshah:feat/delivery-webhook-dispatch

Conversation

@hnshah
Copy link
Copy Markdown

@hnshah hnshah commented Mar 25, 2026

Completes the delivery layer that was scaffolded but never implemented.

Problem

watchlist config delivery writes to settings table but _run_topic() never reads the config or sends notifications. Research runs silently, findings accumulate in SQLite, nothing comes out.

Impact

  • Watchlist is designed for always-on AI environments
  • Without delivery, research findings sit in DB with no notification
  • Users must manually check briefings instead of being notified

Changes

Added delivery functions:

  • _deliver_findings() - Reads config, checks conditions, dispatches
  • _format_delivery_message() - Formats announce/silent/default modes
  • _send_slack_webhook() - POST to Slack incoming webhooks
  • _send_generic_webhook() - POST JSON to generic https:// URLs

Delivery conditions:

  • Only sends when delivery_channel is configured
  • Only sends when counts['new'] > 0
  • Errors are logged but don't block research

Message modes:

  • announce (default): "📰 last30days update: {topic} 3 new, 1 updated"
  • silent: "last30days: 3 new findings for 'topic'"
  • default: "last30days: Research complete for 'topic'"

Config validation:

  • Updated cmd_config() to validate delivery URLs (must be https://)
  • Empty string disables delivery
  • Returns status: enabled/disabled

Testing

  • ✅ 8 unit tests covering all delivery paths
  • ✅ Message formatting verified (announce/silent modes)
  • ✅ Slack webhook POST format validated
  • ✅ Generic webhook POST format validated
  • ✅ No-op conditions tested (empty channel, zero new)
  • ✅ Error handling confirmed (failures don't propagate)

Future Enhancements

Webhook dispatch is generic enough to support:

  • Telegram (via bot API endpoint)
  • Discord (via webhook URL)
  • Email (via HTTP gateway)
  • Custom integrations (any https:// endpoint)

Usage

# Configure Slack webhook
last30 config delivery https://hooks.slack.com/services/T/B/xxx

# Configure generic webhook
last30 config delivery https://myserver.com/last30days-hook

# Disable delivery
last30 config delivery ""

Completes Issue #1 from Oatis's fix spec! 🎯

Completes the delivery layer that was scaffolded but never implemented.
Config commands existed but findings were never actually delivered.

## Problem
`watchlist config delivery` writes to settings table but `_run_topic()`
never reads the config or sends notifications. Research runs silently,
findings accumulate in SQLite, nothing comes out.

## Impact
- Watchlist is designed for always-on AI environments
- Without delivery, research findings sit in DB with no notification
- Users must manually check briefings instead of being notified

## Changes

**Added delivery functions:**
- `_deliver_findings()` - Reads config, checks conditions, dispatches
- `_format_delivery_message()` - Formats announce/silent/default modes
- `_send_slack_webhook()` - POST to Slack incoming webhooks
- `_send_generic_webhook()` - POST JSON to generic https:// URLs

**Delivery conditions:**
- Only sends when `delivery_channel` is configured
- Only sends when `counts['new'] > 0`
- Errors are logged but don't block research

**Message modes:**
- `announce` (default): "📰 *last30days update: {topic}* 3 new, 1 updated"
- `silent`: "last30days: 3 new findings for 'topic'"
- `default`: "last30days: Research complete for 'topic'"

**Config validation:**
- Updated `cmd_config()` to validate delivery URLs (must be https://)
- Empty string disables delivery
- Returns status: enabled/disabled

**Integration point:**
- Called in `_run_topic()` after `store.update_run(..., status='completed')`
- Happens AFTER findings are stored (delivery is non-blocking)

## Testing
- 8 unit tests covering all delivery paths
- Message formatting verified (announce/silent modes)
- Slack webhook POST format validated
- Generic webhook POST format validated
- No-op conditions tested (empty channel, zero new)
- Error handling confirmed (failures don't propagate)

## Future Enhancements
Webhook dispatch is generic enough to support:
- Telegram (via bot API endpoint)
- Discord (via webhook URL)
- Email (via HTTP gateway)
- Custom integrations (any https:// endpoint)

Current implementation focuses on webhooks as the universal primitive.

## Usage
```bash
# Configure Slack webhook
last30 config delivery https://hooks.slack.com/services/T/B/xxx

# Configure generic webhook
last30 config delivery https://myserver.com/last30days-hook

# Disable delivery
last30 config delivery ""

# Set delivery mode (optional, default: announce)
# (delivery_mode setting already exists in schema)
```
@DanRWilloughby
Copy link
Copy Markdown

This is the missing piece for always-on research. I'm building autonomous agents that need to surface research findings proactively (morning briefs, pre-outreach intel). Without delivery, watchlist is a write-only database. The webhook dispatch makes it composable with other systems. Big +1.

@mvanhorn
Copy link
Copy Markdown
Owner

Hey Hiten, wanted to loop back on all three PRs. The watchlist improvements - webhook dispatch here, HN/Polymarket storage (#85), and the scanning window extension (#84) - all look promising. As I mentioned on #84, there's a bigger release coming that's going to change a lot of the internals. I can't commit to merging any of these right now, but I promise I'll consider each one once 3.0 lands and ping you to rebase if needed. Thanks for being patient with the process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants