Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
677 lines
17 KiB
JSON
677 lines
17 KiB
JSON
[
|
||
{
|
||
"id": "F001",
|
||
"description": "Corrective migration adds activity_type, acknowledged_at, acknowledged_by columns to rmm_alerts",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F002",
|
||
"description": "Migration adds dedup_key to rmm_alerts with index on (tenant, integration_id, dedup_key)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F003",
|
||
"description": "Migration adds occurrence_count (default 1) and last_occurrence_at to rmm_alerts",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F004",
|
||
"description": "Migration adds nullable matched_rule_id to rmm_alerts",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F005",
|
||
"description": "Migration adds auto_ticket_created (default false) to rmm_alerts",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F006",
|
||
"description": "Migration adds conditions and actions jsonb (default {}) to rmm_alert_rules",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F007",
|
||
"description": "Migration drops the eleven flat filter/action columns from rmm_alert_rules",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F008",
|
||
"description": "Migration down() restores the prior schema",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1"
|
||
]
|
||
},
|
||
{
|
||
"id": "F009",
|
||
"description": "NormalizedRmmAlertEvent contract defined in shared/rmm/alerts/contracts.ts with kind triggered|reset|acknowledged",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2"
|
||
]
|
||
},
|
||
{
|
||
"id": "F010",
|
||
"description": "RmmAlertOutboundAdapter interface with per-provider resolution; providers without an adapter are skipped",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2",
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F011",
|
||
"description": "Shared Zod schemas for rule conditions and actions JSONB shapes",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2",
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F012",
|
||
"description": "mapNinjaOneWebhookToAlertEvent normalizer: field mapping, severity normalization, condition identity from statusCode falling back to activityType",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2"
|
||
]
|
||
},
|
||
{
|
||
"id": "F013",
|
||
"description": "TacticalRMM payload normalizer producing NormalizedRmmAlertEvent",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2"
|
||
]
|
||
},
|
||
{
|
||
"id": "F014",
|
||
"description": "NinjaOne webhook routes CONDITION TRIGGERED/RESET/ACKNOWLEDGED through processRmmAlertEvent, replacing inline rmm_alerts writes; auth/tenant-resolution/tier gating unchanged",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2",
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F015",
|
||
"description": "Tactical webhook routes alert payloads through processRmmAlertEvent, replacing its direct rmm_alerts writes",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-2",
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F016",
|
||
"description": "processRmmAlertEvent entry point dispatches by event kind",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F017",
|
||
"description": "Triggered events upsert rmm_alerts on (tenant, integration_id, external_alert_id) with raw payload stored in metadata jsonb",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F018",
|
||
"description": "dedup_key computed from device + condition identity and stored on the alert row",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F019",
|
||
"description": "Rule evaluation selects active rules for the integration ordered by priority_order; first match wins",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F020",
|
||
"description": "All present condition fields must match: severities, activityTypes, alertClasses, sourceTypes, organizationIds, keywords",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F021",
|
||
"description": "messagePattern regex condition evaluated; a rule that fails to evaluate is logged and skipped without aborting the pipeline",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F022",
|
||
"description": "A rule with an empty conditions object matches every alert (catch-all)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F023",
|
||
"description": "matched_rule_id persisted on the alert row for later lifecycle decisions",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3"
|
||
]
|
||
},
|
||
{
|
||
"id": "F024",
|
||
"description": "No matching rule, or matched rule with createTicket false, stores the alert without creating a ticket",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-3",
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F025",
|
||
"description": "Dedup: alert whose dedup_key matches an alert with a still-open linked ticket joins that ticket and increments occurrence_count / last_occurrence_at",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F026",
|
||
"description": "Dedup repeat adds an internal 're-triggered — Nth occurrence' comment to the existing ticket",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F027",
|
||
"description": "No open dedup ticket: a new ticket is created via the shared alert ticket creator",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F028",
|
||
"description": "Ticket creation honors rule actions boardId, priorityOverride, and assignToUserId",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F029",
|
||
"description": "Severity-to-priority fallback mapping applies when no priorityOverride is set",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F030",
|
||
"description": "Ticket templates render {{device}}, {{message}}, {{severity}}, {{organization}} placeholders, with sensible defaults when no template is set",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F031",
|
||
"description": "Created tickets get source + source_reference, an asset association, and an initial internal comment with alert details",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F032",
|
||
"description": "Client resolution for the ticket: the asset's client, else the organization mapping's client",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F033",
|
||
"description": "auto_ticket_created set and ticket_id stored on the alert row after ticket creation",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-4"
|
||
]
|
||
},
|
||
{
|
||
"id": "F034",
|
||
"description": "Reset events mark the alert resolved and set resolved_at",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F035",
|
||
"description": "Reset with a linked ticket and rule autoResolveTicket always adds a resolution comment; without the flag the ticket is untouched",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F036",
|
||
"description": "Untouched determination: no human-authored comments, no time entries, no manual status change; rule auto-assignment does not count",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F037",
|
||
"description": "Untouched tickets close via actions.autoResolveStatusId, else the tenant's first is_closed status",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F038",
|
||
"description": "Touched tickets stay open after alert reset (comment only)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F039",
|
||
"description": "Acknowledged events set alert status acknowledged with acknowledged_at/acknowledged_by",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F040",
|
||
"description": "Ticket-closed event-bus subscriber finds unresolved linked alerts by ticket_id and honors the matched rule's resetAlertOnTicketClose (default true)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F041",
|
||
"description": "NinjaOne outbound adapter resets the alert via NinjaOneClient.resetAlert and updates the local alert status",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F042",
|
||
"description": "Outbound reset failures are logged and stamped into alert metadata and never block the ticket close",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F043",
|
||
"description": "Tactical outbound adapter implemented if its API supports alert resolution; otherwise the provider ships without an adapter and the pipeline skips outbound reset",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-5"
|
||
]
|
||
},
|
||
{
|
||
"id": "F044",
|
||
"description": "listRmmAlertRules server action returns rules for an integration ordered by priority_order",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F045",
|
||
"description": "createRmmAlertRule server action validates conditions/actions against the shared Zod schemas",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F046",
|
||
"description": "updateRmmAlertRule server action",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F047",
|
||
"description": "deleteRmmAlertRule server action",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F048",
|
||
"description": "reorderRmmAlertRules server action updates priority_order",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F049",
|
||
"description": "All rule CRUD actions are admin-gated and tenant-scoped",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F050",
|
||
"description": "Save-time validation rejects an invalid messagePattern regex",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-6"
|
||
]
|
||
},
|
||
{
|
||
"id": "F051",
|
||
"description": "Alert Rules section renders in RMM integration settings for both NinjaOne and TacticalRMM",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F052",
|
||
"description": "Rules list is priority-ordered with active toggle, reorder controls, edit and delete",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F053",
|
||
"description": "Rule editor Match group: severities, activity types, alert classes, source types, keywords, message regex",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F054",
|
||
"description": "Rule editor organization picker fed from rmm_organization_mappings",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F055",
|
||
"description": "Rule editor Actions group: create-ticket toggle, board picker, priority override, assignee picker",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F056",
|
||
"description": "Rule editor title/description template inputs with placeholder hints",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F057",
|
||
"description": "Rule editor autoResolveTicket and resetAlertOnTicketClose toggles and notify-users picker",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F058",
|
||
"description": "Rule editor surfaces validation errors inline; delete requires confirmation",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7"
|
||
]
|
||
},
|
||
{
|
||
"id": "F059",
|
||
"description": "RMM_ALERT_TRIGGERED registered in the workflow v2 event catalog with a provider-generic payload schema",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F060",
|
||
"description": "RMM_ALERT_RESOLVED registered in the workflow v2 event catalog",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F061",
|
||
"description": "Pipeline publishes the workflow v2 alert events, replacing the orphaned legacy-bus publishes",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F062",
|
||
"description": "rmm.alerts.create_ticket workflow action invokes the shared ticket creator by alert ID",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F063",
|
||
"description": "rmm-alert notification category registered and honoring per-user notification preferences",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F064",
|
||
"description": "Matched rule notifyUserIds produce in-app notifications",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F065",
|
||
"description": "Matched rule notifyUserIds produce email notifications per user preference",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-8"
|
||
]
|
||
},
|
||
{
|
||
"id": "F066",
|
||
"description": "CSRF validation implemented in the NinjaOne OAuth callback using the state payload's csrf and timestamp",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-9"
|
||
]
|
||
},
|
||
{
|
||
"id": "F067",
|
||
"description": "ninjaone/alerts modules moved into shared/rmm/alerts with imports updated and the superseded resetInNinjaOne TODO removed",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-9"
|
||
]
|
||
},
|
||
{
|
||
"id": "F068",
|
||
"description": "rmm_organization_mappings.auto_create_tickets deprecated: no read paths remain; rules are the sole gate for ticket creation",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-9"
|
||
]
|
||
},
|
||
{
|
||
"id": "F069",
|
||
"description": "Migration creates rmm_maintenance_windows (optional integration/client/asset scopes, one-off starts_at/ends_at, weekly recurrence jsonb, name, is_active)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1",
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F070",
|
||
"description": "Migration adds suppressed_by_window_id to rmm_alerts; alert status supports 'suppressed'",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-1",
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F071",
|
||
"description": "Pipeline checks maintenance windows before rule matching: an active window matching all its non-null scopes at occurredAt suppresses the alert",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F072",
|
||
"description": "Weekly recurring windows evaluate day and time range in the window's timezone; one-off windows by starts_at/ends_at",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F073",
|
||
"description": "Suppressed alerts are stored with status suppressed and suppressed_by_window_id: no ticket, no notifications, no workflow events",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F074",
|
||
"description": "A reset for a suppressed alert resolves it quietly with no ticket actions",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F075",
|
||
"description": "Maintenance window CRUD server actions (list/create/update/delete), admin-gated, Zod-validated",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F076",
|
||
"description": "Maintenance Windows settings subsection beside Alert Rules: list plus editor with client/asset scope pickers and one-off or weekly recurring schedule with timezone",
|
||
"implemented": false,
|
||
"prdRefs": [
|
||
"FR-7",
|
||
"FR-10"
|
||
]
|
||
},
|
||
{
|
||
"id": "F077",
|
||
"description": "Per-integration Temporal schedule runs alert reconciliation every N minutes (default 15, configurable 5–60, default on for connected integrations)",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F078",
|
||
"description": "Poll cycle fetches RMM-active alerts and upserts ones missing locally as triggered events through the shared pipeline",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F079",
|
||
"description": "Poll cycle synthesizes reset events for local active alerts no longer active in the RMM",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F080",
|
||
"description": "Poll cycle processes still-active suppressed alerts whose window has ended through the normal rules path",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-10",
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F081",
|
||
"description": "Polling enable/disable and interval setting exposed in integration settings UI with 5–60 minute bounds",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-7",
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F082",
|
||
"description": "TacticalRMM reconciliation uses its alerts API through the same scheduled workflow",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-11"
|
||
]
|
||
},
|
||
{
|
||
"id": "F083",
|
||
"description": "Reconciliation schedule is created when an integration connects and removed when it disconnects",
|
||
"implemented": true,
|
||
"prdRefs": [
|
||
"FR-11"
|
||
]
|
||
}
|
||
]
|