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
502 lines
14 KiB
JSON
502 lines
14 KiB
JSON
[
|
|
{
|
|
"id": "F001",
|
|
"description": "Migration: create inbound_email_rules table with (tenant, id) composite PK, position/provider_ids/conditions/action_type/action_config/on_no_match/fallback_inbound_ticket_defaults_id columns, tenant+position index, Citus distribution, transaction:false",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #1"
|
|
]
|
|
},
|
|
{
|
|
"id": "F002",
|
|
"description": "Migration: create client_name_aliases table with (tenant, id) PK, client_id, alias, unique index on (tenant, lower(alias)), tenant+client index, Citus distribution",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #1"
|
|
]
|
|
},
|
|
{
|
|
"id": "F003",
|
|
"description": "Migration: extend email_processed_messages processing_status check constraint to include 'skipped'",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #1"
|
|
]
|
|
},
|
|
{
|
|
"id": "F004",
|
|
"description": "Shared TypeScript types for InboundEmailRule, RuleCondition, per-action-type action_config shapes, and evaluation outcome",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #2"
|
|
]
|
|
},
|
|
{
|
|
"id": "F005",
|
|
"description": "Validation schemas for rule create/update payloads: condition field/operator enums, action_config shape per action_type, on_no_match enum, regex pattern length cap",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F006",
|
|
"description": "Evaluator: case-insensitive equals/contains/starts_with/ends_with operators over condition fields",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #2"
|
|
]
|
|
},
|
|
{
|
|
"id": "F007",
|
|
"description": "Evaluator: matches_regex operator with try/catch compile (invalid = condition false, logged once per rule per process) and pattern length cap",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #2"
|
|
]
|
|
},
|
|
{
|
|
"id": "F008",
|
|
"description": "Evaluator: field accessors from parsed email \u2014 from_address, from_domain, to_address (any recipient), subject, body_text sliced to ~100KB",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #2"
|
|
]
|
|
},
|
|
{
|
|
"id": "F009",
|
|
"description": "Extractor: between/after/before templates with occurrence first/last, compiled to the same internal extractor as raw regex",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F010",
|
|
"description": "Extractor: raw regex extraction using capture group 1",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F011",
|
|
"description": "Extracted-value normalization: trim, collapse internal whitespace, lowercase; empty result = non-match",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F012",
|
|
"description": "Client matcher: normalized exact match against clients.client_name, excluding inactive clients",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F013",
|
|
"description": "Client matcher: fallback to client_name_aliases lookup (tenant-scoped, case-insensitive)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F014",
|
|
"description": "Rule loader: single indexed query for active tenant rules ordered by position, filtered in-process by provider_ids (NULL = all)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #3",
|
|
"Non-functional Requirements"
|
|
]
|
|
},
|
|
{
|
|
"id": "F015",
|
|
"description": "Evaluation loop: first conditions-match executes action; resolved action stops evaluation; on_no_match=proceed continues to subsequent rules",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #3"
|
|
]
|
|
},
|
|
{
|
|
"id": "F016",
|
|
"description": "on_no_match=skip outcome: email skipped when the matched rule's extraction/classification fails",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #3"
|
|
]
|
|
},
|
|
{
|
|
"id": "F017",
|
|
"description": "on_no_match=fallback_destination outcome: ticket created using fallback_inbound_ticket_defaults_id",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #3"
|
|
]
|
|
},
|
|
{
|
|
"id": "F018",
|
|
"description": "Engine error isolation: any unexpected evaluator error logs a warning with rule id and falls through to the unmodified pipeline",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Non-functional Requirements"
|
|
]
|
|
},
|
|
{
|
|
"id": "F019",
|
|
"description": "Pipeline hook in processInboundEmailInApp: rules evaluated on new-ticket path only, after thread matching fails, before resolveInboundTicketDefaults and sender matching",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #3"
|
|
]
|
|
},
|
|
{
|
|
"id": "F020",
|
|
"description": "Skip action: no ticket/comment/attachment processing; email_processed_messages row with processing_status='skipped' and metadata { ruleId, ruleName }",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #4"
|
|
]
|
|
},
|
|
{
|
|
"id": "F021",
|
|
"description": "Skip action works for tenants with no inbound_ticket_defaults configured (evaluated before defaults requirement)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #4"
|
|
]
|
|
},
|
|
{
|
|
"id": "F022",
|
|
"description": "Rule-assigned client takes precedence over exact sender-contact match and domain match",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F023",
|
|
"description": "Contact attribution for rule-assigned client: sender contact within that client if present, else the client's primary contact",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F024",
|
|
"description": "Rule-assigned client flows through resolveEffectiveInboundTicketDefaults so client-level defaults overrides apply",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #5"
|
|
]
|
|
},
|
|
{
|
|
"id": "F025",
|
|
"description": "set_destination action: referenced inbound_ticket_defaults applied at the top of the destination cascade; sender matching still attributes client/contact",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #6"
|
|
]
|
|
},
|
|
{
|
|
"id": "F026",
|
|
"description": "Created tickets record appliedRuleId and clientMatchSource in tickets.email_metadata",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #8"
|
|
]
|
|
},
|
|
{
|
|
"id": "F027",
|
|
"description": "INBOUND_EMAIL_RECEIVED activity-log entry includes the applied rule name",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #8"
|
|
]
|
|
},
|
|
{
|
|
"id": "F028",
|
|
"description": "One structured log line per evaluated email: rules considered, rule matched, outcome",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #8"
|
|
]
|
|
},
|
|
{
|
|
"id": "F029",
|
|
"description": "Dangling reference handling: deleted fallback/destination defaults set or deleted/inactive aliased client degrades to non-match/proceed with warning",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Non-functional Requirements"
|
|
]
|
|
},
|
|
{
|
|
"id": "F030",
|
|
"description": "AI classify OSS stub: dynamic loader returns no_decision (inboundReplyAcknowledgementDecider pattern)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F031",
|
|
"description": "AI classify EE module: builds prompt from instruction + subject/from/body excerpt; no tenant client list in prompt",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7",
|
|
"Security / Permissions"
|
|
]
|
|
},
|
|
{
|
|
"id": "F032",
|
|
"description": "AI classify EE module: constrained JSON response parsing { decision: skip|assign_client|no_decision, extracted_client_name }",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F033",
|
|
"description": "AI assign_client decision resolves extracted_client_name through the same exact+alias client matcher",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F034",
|
|
"description": "allowed_outcomes enforcement: AI decisions outside the rule's allowed_outcomes are treated as no_decision",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F035",
|
|
"description": "AI failure handling: timeout/error/missing add-on/malformed response = non-match routed to on_no_match; ticket creation never blocks on AI",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F036",
|
|
"description": "AI token usage logged through the AI module's existing usage tracking",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #7"
|
|
]
|
|
},
|
|
{
|
|
"id": "F037",
|
|
"description": "Server action: list inbound email rules for tenant",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F038",
|
|
"description": "Server action: create rule with full payload validation",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F039",
|
|
"description": "Server action: update rule with full payload validation",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F040",
|
|
"description": "Server action: delete rule",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F041",
|
|
"description": "Server action: reorder rules (persist positions atomically)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F042",
|
|
"description": "Server action: test rule \u2014 runs the shared evaluator against a draft rule + sample From/Subject/Body without persisting; returns per-condition pass/fail, extracted value, resolved client, final outcome",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9",
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F043",
|
|
"description": "Server actions: client name alias CRUD (list per client, create, delete) with uniqueness error surfaced",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F044",
|
|
"description": "RBAC: all rule/alias/test actions require the email-provider-admin permission",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Security / Permissions"
|
|
]
|
|
},
|
|
{
|
|
"id": "F045",
|
|
"description": "UI: Inbound Rules section in email settings alongside provider list and InboundTicketDefaultsManager",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F046",
|
|
"description": "UI: rules table with name, human-readable summary, mailbox-filter chips, active toggle, edit/delete",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F047",
|
|
"description": "UI: reorder rules via up/down controls persisting position through the reorder action (drag-and-drop deferred: no dnd dependency in the repo)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F048",
|
|
"description": "UI: human-readable rule summary generator (e.g. \"From contains @huntress.com \u2192 assign client from subject\")",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F049",
|
|
"description": "UI: inline active/inactive toggle on rule rows",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F050",
|
|
"description": "UI: rule editor drawer with name, active toggle, mailbox multi-select (empty = all mailboxes)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F051",
|
|
"description": "UI: condition builder \u2014 add/remove rows of field + operator + value; matches-regex available as a normal operator",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F052",
|
|
"description": "UI: action picker for the four action types",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F053",
|
|
"description": "UI: AI action visible but disabled with upsell hint when EE + AI add-on is not active",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F054",
|
|
"description": "UI: extraction config \u2014 source select, template select (between/after/before/regex), delimiter/pattern inputs, occurrence select",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F055",
|
|
"description": "UI: destination picker backed by active inbound_ticket_defaults sets (for set_destination and fallback)",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F056",
|
|
"description": "UI: AI action config \u2014 instruction textarea and allowed-outcomes checkboxes",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F057",
|
|
"description": "UI: non-match behavior select; fallback destination picker shown only when fallback_destination selected",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F058",
|
|
"description": "UI: validation errors from server schemas surfaced inline in the editor",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"Functional Requirements #9"
|
|
]
|
|
},
|
|
{
|
|
"id": "F059",
|
|
"description": "UI: live tester panel \u2014 sample From/Subject/Body inputs invoking the test-rule action",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F060",
|
|
"description": "UI: tester results \u2014 per-condition pass/fail, extracted value, resolved client or no-match, final outcome",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F061",
|
|
"description": "UI: tester alias quick-add \u2014 when extraction succeeds but no client resolves, offer adding the value as an alias with a client picker",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
},
|
|
{
|
|
"id": "F062",
|
|
"description": "UI: \"Matching aliases\" section on the client record next to inbound email domains, with add/remove",
|
|
"implemented": true,
|
|
"prdRefs": [
|
|
"UX / UI Notes"
|
|
]
|
|
}
|
|
]
|