* feat(notifications): move Notification Routing to Skipper tier
Notification routing is automation (route alerts to channels by rules),
not enterprise compliance. Aligning the gate with Skipper makes the tier
boundary read consistently with the rest of the automation surface
(webhooks, auto-update, auto-heal, scheduled tasks).
Backend: requireAdmiral -> requirePaid on the five /api/notification-routes
endpoints. Dashboard configuration-status now exposes the routing-rules
row to any paid tier.
Frontend: settings registry tier flipped to skipper; the Admiral wrapper
around NotificationRoutingSection is removed (the inner CapabilityGate
stays, preserving forward-compat with older remote nodes).
Tests: added a tier-enforcement describe block covering Skipper (200) and
Community (403 PAID_REQUIRED on all five endpoints).
Docs: refreshed alerts-notifications, licensing, overview, dashboard,
troubleshooting, and reference/settings; cleaned one fence-spec line per
Directive 31.
* fix(notifications): address audit findings on tier-move PR
Docs: rewrite three lines that survived the initial sweep. The dashboard
"you do not see a locked placeholder" clause and the settings.mdx
"hidden on Community and Skipper" phrase were Directive 31 fence-spec.
The alerts-notifications troubleshooting note still said "an Admiral
routing rule" and contradicted the tier move.
Tests: the Community-negative cases on POST/PUT/DELETE/POST :id/test
could not distinguish requirePaid from a stray requireAdmiral, because
Community fails on the tier check before variant is read. Adding
Skipper-positive coverage per endpoint locks the gate identity in.
Replace the leaky mockReturnValueOnce with a per-test mockReturnValue
plus an afterEach restore so spies cannot bleed across tests.
- Add label_ids and categories columns to notification_routes via idempotent migration
- Matcher logic always evaluates routes (AND semantics across all non-empty matchers)
- getStackLabelIds skips DB call when no enabled route uses label filtering
- Extract ALL_NOTIFICATION_CATEGORIES array from NotificationService as single source of truth
- Derive VALID_CATEGORIES set from the array in the route handler
- Extract validateLabelIds and validateCategories helpers to remove POST/PUT duplication
- Extract tryAddColumn as a private DatabaseService class method (removes 5 local re-declarations)
- Extract CATEGORY_LABELS to frontend/src/lib/notificationCategories.ts (shared by NotificationPanel and NotificationRoutingSection)
- Frontend form adds label and category multiselects with AND-filter hint
- Route cards show label and category badges; empty-matcher routes show 'Matches all alerts'
- Add tests for category-only, label-only, and combined AND-semantics routing
- Add authMiddleware to GET/POST /api/agents (previously unauthenticated)
- Add input validation to POST /api/agents (type, URL, enabled)
- Add URL host validation via new URL() to notification-routes POST/PUT
- Add priority type validation and enabled boolean check to routes
- Add name length limit (100 chars) to routes POST/PUT
- Add stack pattern dedup and whitespace filtering
- Add NaN guard to DELETE /api/notifications/:id
- Add dispatch_error to NotificationHistory TypeScript interface
- Extract cleanStackPatterns() and validateHttpsUrl() helpers
- Add standard and diagnostic logging to agents and routes endpoints
- Add comprehensive integration tests for notification-routes CRUD
- Add agents auth and validation tests
- Add dispatch error recording unit tests