feat: Add notification system for scheduled triggers#167
feat: Add notification system for scheduled triggers#167
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Replace the single-tracking-issue approach with a provider-based notification layer. Scheduled findings now route through configurable providers (GitHub Issues, Slack) with suppression rules for false positive management. - Add GitHub Issues provider with two-tier dedup (hash + semantic via Haiku) - Add Slack provider with Block Kit webhook posting - Add YAML-based suppression system (skill + glob paths + title matching) - Add notification dispatcher to orchestrate suppression and provider execution - Remove createOrUpdateIssue and issue-renderer (replaced by providers) - Remove issueTitle from schedule config schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3f91273 to
187c111
Compare
- Move label creation outside per-finding loop to avoid redundant API calls - Remove duplicate notification logging (dispatcher handles its own logging) - Eliminate redundant `anchor` variable identical to `lineRange` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| providers.push(new SlackProvider({ | ||
| ...config, | ||
| webhookUrl: expandEnvVars(config.webhookUrl), | ||
| })); |
There was a problem hiding this comment.
Bug: If a Slack webhookUrl is an unset environment variable, it expands to an empty string, causing fetch to throw a TypeError and the notification to fail silently.
Severity: MEDIUM
Suggested Fix
Re-validate the webhookUrl after environment variable expansion to ensure it's a valid URL and not an empty string. Alternatively, modify expandEnvVars to throw an error for unset variables. A third option is to check the DispatchResult in schedule.ts and log a clear warning if any provider reports errors.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: src/notifications/index.ts#L41-L44
Potential issue: The configuration schema validates the `webhookUrl` before environment
variables are expanded. A variable like `"$SLACK_WEBHOOK_URL"` passes validation as a
non-empty string. Later, `expandEnvVars` resolves this to an empty string if the
environment variable is not set. When the Slack provider attempts to call `fetch('')`,
it throws a `TypeError`. While this error is caught by the provider and logged, the main
workflow in `schedule.ts` does not inspect the dispatch result, leading to a silent
failure where users are unaware that their notifications are not being sent.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| const text = await response.text(); | ||
| result.errors.push(`Slack webhook returned ${response.status}: ${text}`); | ||
| } else { | ||
| result.sent = context.findings.length; |
There was a problem hiding this comment.
Slack provider reports findings count, not messages sent
Low Severity
The SlackProvider sets result.sent = context.findings.length, but it only posts a single webhook message regardless of how many findings exist. The NotificationResult.sent field is documented as "Number of notifications sent (e.g., issues created, messages posted)." The dispatcher then logs sent ${result.sent} notification(s), so with 10 findings it would misleadingly report "slack: sent 10 notifications" when only 1 Slack message was actually posted. The value here likely needs to be 1 to match the semantics used by the GitHub Issues provider (which increments sent per actual API call).
Additional Locations (1)
| unmatchedFindings.map((f) => f.finding), | ||
| existingIssues, | ||
| this.apiKey | ||
| ); |
There was a problem hiding this comment.
Semantic dedup missing same-file filter from spec
Medium Severity
The comment on Phase 2 says "only for same-file findings" and the spec explicitly states "Semantic match via Haiku for same-file findings with no hash match," but the implementation passes all unmatched findings to findSemanticMatches without filtering by file. This sends more data to the LLM than intended, increasing cost, and risks false-positive semantic matches between unrelated findings in different files.


Replace the single-tracking-issue approach with a provider-based notification
layer. Scheduled findings now route through configurable providers (GitHub Issues,
Slack) with suppression rules for false positive management.
The old
createOrUpdateIssue+issue-rendererapproach was tightly coupled toGitHub Issues and couldn't support additional channels. This introduces a
NotificationProviderinterface so adding new providers (email, PagerDuty, etc.)is straightforward.
Key changes:
NotificationProviderinterface with GitHub Issuesand Slack (Block Kit webhooks) implementations. GitHub Issues provider includes
two-tier dedup (content hash + semantic similarity via Haiku)
.warden/suppressions.yamlthatfilter findings by skill, path globs, and title before they reach providers
execution with error isolation
[[notifications]]table inwarden.tomlwith discriminatedunion schema (
type = "github-issues"ortype = "slack")issueTitlefrom schedule config,createOrUpdateIssue, andissue-renderer.ts(replaced by the provider layer)Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com