
Notification Router
Build an event-to-channel notification router: a small module that takes incoming events and routes each to the right destination (Slack channel, email, PagerDuty, webhook) based on type, severity, and quiet-hours rules — with dedupe, throttling, and a fallback channel. Scopes the routing matrix and policy FIRST (event taxonomy, channel map, severity→destination rules, dedupe key, rate limits, fallback), then a developer implements the router module to that matrix, then writes tests covering each routing branch. Scoping the matrix before coding is what guarantees every event type has a defined destination and that critical events can never be silently dropped. Use for notification routing, alert fan-out, event dispatch, webhook routing, and on-call/Slack notification systems.
Steps
Entry step: scope. Each step names the specialist role it wants; the full working prompt is expandable.
- Scope the routerplannerentry
lock the routing matrix, dedupe, throttle, and fallback
Show working prompt
Define the routing contract before coding. (1) Enumerate the event taxonomy (the event types/shapes the router accepts) and the available destination channels. (2) Build the routing matrix: for each (event type × severity) pair, the destination channel(s) — make it total so no input is undefined. (3) Define the dedupe key (which fields identify a duplicate within a window) and the suppression window. (4) Set per-channel rate limits/throttling and a quiet-hours rule (and which severities override quiet hours). (5) Define a fallback channel for events that match no rule, so nothing is dropped silently, and the behavior on a destination send failure (retry/fallback). (6) Pick the language and the function signature, e.g. route(event) -> Destination[]. Write an acceptance-criteria checklist ('every (type,severity) maps to a destination', 'unmatched events hit the fallback, never dropped', 'duplicates within the window are suppressed', 'rate limits enforced', 'critical severity overrides quiet hours', 'pure routing logic is unit-testable without live channels') plus the matrix to notes/scope.md via write_task_note AND the produces path. - Build the routerdeveloper
implement the router module to the matrix
Show working prompt
Implement the router as a single module (e.g. router.ts/router.py) realizing the locked matrix. Separate PURE routing logic — route(event) returning the destination list — from any side-effecting send, so routing is testable in isolation. Implement: matrix lookup with the fallback for unmatched events, dedupe via the locked key + window (an in-memory recent-key set is fine), per-channel throttling, and the quiet-hours rule with the critical-severity override. Validate/normalize inbound events and reject malformed ones explicitly. Never let an event with no matching rule vanish — it goes to fallback. Add brief doc comments mapping code back to matrix rows. On a loop-back fix only the flagged behavior. Write the module path and the implemented signature to notes via write_task_note.
- Test the routerdeveloper
cover every routing branch with assertions
Show working prompt
Write router.test.ts exercising the router's pure logic. Cover, with explicit assertions: (1) a representative event for each (type,severity) row resolves to the matrix's destination; (2) an unmatched event routes to the fallback and is NOT dropped; (3) a duplicate within the window is suppressed and the same key after the window is allowed; (4) throttling blocks the N+1th send to a channel in the window; (5) a critical event during quiet hours still routes, a non-critical one is held; (6) a malformed event is rejected. Use the test runner's describe/it/expect (or equivalent assert). Tests must run without live channels. On a loop-back add the missing cases. Write the test path and the branch-coverage list to notes via write_task_note.
- Evaluatereviewer
Grade the deliverable against every acceptance criterion. All pass → finish; any fail → loop back and fix the gap.
Show working prompt
QA the router against the locked criteria. (1) Run the test suite (or trace it) — every test must pass; a failing/empty suite is FAIL. (2) Confirm tests exist for each matrix row, the fallback path, dedupe (hit and post-window allow), throttling, the quiet-hours + critical-override behavior, and malformed-event rejection. (3) Read router.ts: is routing pure and side-effect-free, is the matrix total, can an unmatched event ever be silently dropped (must be NO)? (4) Spot-check 2 matrix rows in code against notes/scope.md. Write PASS/FAIL per criterion with the gaps. Then route — this is the whole point of the loop: - **Every criterion PASSES →** call `advance_task_step({ ref, stepId: "evaluate", next: "finish" })`. - **Any criterion FAILS →** write the specific gaps to notes, then call `advance_task_step({ ref, stepId: "evaluate", next: "build" })` to loop back. The builder fixes exactly those gaps. Never route to `finish` while any criterion is unmet. The build phase's completion gate already blocked a grossly-incomplete deliverable; your job is the judgment an automated check cannot make (does it actually work, read well, look right). After ~3 unproductive loops, stop and report DONE_WITH_CONCERNS so the user can step in. - Finishdeveloper
All acceptance criteria met. Stamp a short summary and report DONE.
Show working prompt
Every acceptance criterion passed. Write a one-paragraph DONE summary to task notes via `write_task_note`: what was built, the deliverable path(s), and a one-line confirmation that each criterion is met. Then report DONE.
Triggers
Phrases that suggest this craftbook to a crew.
- notification router
- route events to channels
- alert fan-out
- event dispatch
- send notifications to slack
Source
View this craftbook on GitHub · MIT license