MCP Server
Active
1
0

Import UAPF package: incident-triage.uapf

This commit is contained in:
2026-06-01 18:25:37 +00:00
commit 7fe0fda7a5
26 changed files with 2509 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.uapf.tmp
.DS_Store
__pycache__/
*.pyc

69
README.md Normal file
View File

@@ -0,0 +1,69 @@
# lv.itsm.incident.triage
UAPF v2.5.0 package — LVRTC Incident Triage.
## Quick install (uapf-engine)
The engine reads `.uapf` ZIP archives from its `/packages` mount. Build:
```bash
cd uapf-packages/incident-triage
zip -rq ../incident-triage.uapf .
mv ../incident-triage.uapf /path/to/engine/packages/
docker compose restart uapf-engine
```
Or use the admin install-from-url endpoint:
```bash
curl -X POST http://uapf-engine:4000/uapf/admin/install-from-url \
-H "Authorization: Bearer $UAPF_ENGINE_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sourceUrl": "https://processgit.org/AI_Sandbox/incident-triage/archive/main.zip"}'
```
## Layout
```
incident-triage/
manifest.json UAPF v2.5.0 manifest
uapf.yaml YAML mirror of manifest (easier diff)
algorithms/ 7 Algorithm Cards with embedded v2.5.0 tests
bpmn/ incident-triage.bpmn — 9 service tasks, linear
dmn/ 3 DMN 1.3 tables: priority, ownership, routing
resources/ guardrails.yaml, mappings.yaml
metadata/ lifecycle.yaml, ownership.yaml
docs/ overview.md (start here)
fixtures/ 3 sample signals + expected post-triage state
tests/bpmn/ Sidecar BPMN scenario tests
```
## Algorithm Cards
| Card | Capability | Determinism |
|---|---|---|
| `algo.incident_triage.normalize_signal` | `intake.normalize@1` | deterministic |
| `algo.incident_triage.classify_incident` | `ai.classify@1` | stochastic |
| `algo.incident_triage.suggest_priority` | `ai.suggest_priority@1` | stochastic |
| `algo.incident_triage.evaluate_dmn` | `dmn.evaluate@1` | deterministic |
| `algo.incident_triage.draft_response` | `ai.draft_response@1` | stochastic |
| `algo.incident_triage.update_incident` | `incident.update@1` | deterministic, stateful |
| `algo.incident_triage.emit_event` | `event.emit@1` | deterministic, stateful |
All seven carry the v2.5.0 mandatory embedded `tests` array (≥ 2 cases each;
17 cases total across the package).
## Spec version
Tracks **`main`** of `github.com/UAPFormat/UAPF-specification`. Current
locked spec at package release: **v2.5.0** (2026-05-21).
## Source
- Workspace (OpenITSM repo): `uapf-packages/incident-triage/`
- ProcessGit (canonical): `https://processgit.org/AI_Sandbox/incident-triage`
- Engine: any uapf-engine ≥ commit aligned with UAPF v2.5.0 schemas
## License
MIT — same as the OpenITSM repository.

View File

@@ -0,0 +1,133 @@
kind: uapf.algorithm.card
id: algo.incident_triage.classify_incident
version: 1.0.0
name: Incident classifier
intent: |
Reads the normalised payload and picks one taxonomy code from a fixed
closed list. The classifier is LLM-backed at runtime (Claude via the
LLM gateway) and falls back to a deterministic keyword matcher when
the gateway is unreachable. The taxonomy code is the primary driver
for the priority and routing DMN decisions; downstream rules treat
this output as authoritative.
algorithm_kind: classifier
io:
inputs:
- id: payload
type: object
cardinality: single
documentation: |
The normalized_payload from the upstream intake.normalize step.
At minimum {title, description?, host?, severity?}.
- id: text
type: string
cardinality: single
documentation: |
Optional pre-flattened text. If absent, the host derives it from
payload.title + payload.description + payload.host.
outputs:
- id: taxonomy_code
type: string
constraints:
enum:
- network.outage.link_down
- network.degradation
- network.routing
- network.dns
- security.incident
- facility.power
- storage.capacity
- service.customer_request
- unknown.uncategorized
documentation: The chosen taxonomy code from the closed list above.
- id: confidence
type: probability
constraints:
minimum: 0
maximum: 1
documentation: Model-reported confidence; the stub fallback returns 0.75 for matched / 0.20 for unmatched.
- id: reasoning
type: string
documentation: One-sentence justification (English). Persisted with the AI decision; not shown to operator by default.
- id: label_hint
type: string
documentation: Human-friendly short label derived from the taxonomy code (e.g. "link_down").
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.classify@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.classify@1
note: |
Host-fulfilled UAPF-IP capability backed by the LLM gateway
(default Anthropic). When LLM_PROVIDER is unavailable, the host
falls back to a regex-driven keyword matcher that produces the
same output shape.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 800
max_latency_ms: 30000
failure_mode: |
Returns taxonomy_code='unknown.uncategorized' with confidence<=0.25.
Triage continues; the DMN priority table treats unknown as P4 default.
reference:
legal: |
Latvijas Republikas Datu valsts inspekcijas vadlīnijas par
automatizētu lēmumu pieņemšanu — operators may override at any time.
standard: |
ITIL 4 — Incident Management practice; ISO/IEC 20000-1 — service
management taxonomy alignment.
limitations:
- Closed taxonomy of 9 codes — broader incident types fall to unknown.uncategorized.
- Latvian and English input supported; mixed-locale text may degrade confidence.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: bgp-flap-network-routing
description: |
Edge router BGP session flapping — the classifier should pick
network.routing, not the broader network.outage.link_down.
inputs:
payload:
title: "BGP session flapping rtr-core-02 → AS6939"
host: "rtr-core-02.lvrtc.lv"
description: "BGP peer 198.51.100.1 toggled UP/DOWN 7 times in 12 minutes."
severity: "high"
expected_outputs:
taxonomy_code: "network.routing"
- name: customer-bandwidth-request
description: |
Latvian customer email asking for a bandwidth uplift — a
service.customer_request, not a network outage.
inputs:
payload:
title: "Klients SIA Latvija Tev: lūgums palielināt joslas platumu"
description: "Mūsu uzņēmumam nepieciešams palielināt internet pieslēguma joslas platumu no 100 Mbps uz 500 Mbps."
severity: "average"
expected_outputs:
taxonomy_code: "service.customer_request"
- name: ddos-volumetric
description: |
Volumetric UDP flood pattern — security.incident takes precedence
over generic network classifications even when the symptom is
network-shaped.
inputs:
payload:
title: "DDoS attack pattern detected on edge"
description: "Volumetric UDP flood, 4.2 Gbps inbound to 192.0.2.0/24."
severity: "critical"
expected_outputs:
taxonomy_code: "security.incident"

View File

@@ -0,0 +1,123 @@
kind: uapf.algorithm.card
id: algo.incident_triage.draft_response
version: 1.0.0
name: Customer response drafter
intent: |
Drafts a customer-facing incident notification in parallel Latvian
and English on behalf of LVRTC. Output is a PROPOSED AIDecision —
never auto-sent. Operator approval in the GUI is required before any
message leaves the system (guardrail approval.human_required_for
enforces this at runtime).
Tone: professional, calm, factual. Acknowledges the problem, states
that the team is investigating, gives an ETA only if known. Does NOT
promise specific resolutions. Uses proper Latvian diacritics. Bodies
capped at ~90 words each.
algorithm_kind: transformer
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: locale
type: string
constraints:
enum: [lv, en, auto]
documentation: |
'lv' or 'en' forces a single primary locale; 'auto' produces
both bodies and lets the operator choose. Default 'lv' for LVRTC.
- id: what_happened
type: string
documentation: One-line summary of the incident (used in subject + opening).
- id: eta_minutes
type: integer
cardinality: single
constraints:
minimum: 0
documentation: 'Optional. When provided, surfaced in body as \"Aptuvenais risināšanas laiks: X min\".'
outputs:
- id: subject_lv
type: string
- id: subject_en
type: string
- id: body_lv
type: string
documentation: Latvian body, proper diacritics, <=~90 words.
- id: body_en
type: string
documentation: English body, <=~90 words.
- id: locale
type: string
documentation: The locale code echoed back; informational.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.draft_response@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.draft_response@1
note: |
Host-fulfilled UAPF-IP capability backed by the LLM gateway with
Anthropic as the default provider. The host enforces that the
resulting AIDecision row is PROPOSED (never AUTO_APPLIED) for
this capability. Operator approval moves it to APPROVED before
any outbound transport adapter is invoked.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 1500
max_latency_ms: 60000
failure_mode: |
Returns deterministic stub drafts ("Mūsu komanda ir saņēmusi
paziņojumu...") with locale='lv' and a flag indicating LLM unavailability.
Operator can edit before approving.
reference:
legal: |
GDPR 2016/679 Article 13 — information to data subjects; LVRTC
customer-communication standards.
standard: |
ITIL 4 — Communication and Awareness practice during major incidents.
limitations:
- Cap of ~90 words per body — long incident narratives are truncated.
- Does not yet support Russian or other locales beyond lv/en.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: link-down-with-eta
description: |
Edge link down with an ETA of 30 minutes. Both bodies should
acknowledge the outage and surface the ETA.
inputs:
case_id: "33333333-3333-3333-3333-333333333333"
locale: "auto"
what_happened: "Tīkla pārtraukums rtr-r1"
eta_minutes: 30
expected_outputs:
locale: "lv"
subject_lv: "[LVRTC] Informējam par incidentu"
subject_en: "[LVRTC] Incident notification"
- name: customer-request-no-eta
description: |
Customer-initiated request acknowledgement without an ETA.
inputs:
case_id: "44444444-4444-4444-4444-444444444444"
locale: "auto"
what_happened: "Klienta pieprasījums par joslas platumu"
expected_outputs:
locale: "lv"
subject_lv: "[LVRTC] Informējam par incidentu"
subject_en: "[LVRTC] Incident notification"

View File

@@ -0,0 +1,120 @@
kind: uapf.algorithm.card
id: algo.incident_triage.emit_event
version: 1.0.0
name: Case event emitter
intent: |
Appends a CaseEvent row to the case timeline. This is the canonical
way the BPMN signals "I completed a step" to the operator UI and to
the audit pipeline. Each event ends up as one row in case_events and
is also a candidate for VC signing (the VeriDocs SDK wraps a subset
of event types as Verifiable Credentials in Phase 1).
algorithm_kind: emitter
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: type
type: string
constraints:
enum:
- signal_attached
- status_changed
- triaged
- classified
- prioritized
- routed
- assigned
- ai_decision_recorded
- dmn_evaluated
- comment_added
- escalated
- resolved
- closed
documentation: Canonical event type. Used by the timeline filter pills in the operator UI.
- id: payload
type: object
documentation: |
Type-specific payload. For 'routed': {classification, priority,
ownership, group_slug}. For 'classified': {taxonomy_code,
confidence}. The schema per type is documented in the OpenITSM
case-events module.
- id: actor_label
type: string
documentation: |
Free-form actor identifier. For UAPF-driven events this is
typically 'uapf:lv.itsm.incident.triage'. For operator actions
it's 'operator:<user-slug>'.
outputs:
- id: event_id
type: string
documentation: UUID of the new CaseEvent row.
- id: recorded_at
type: string
documentation: ISO-8601 timestamp of the row insert (server clock).
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/event.emit@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: event.emit@1
note: |
Host-fulfilled UAPF-IP capability. Append-only — the host's
CaseEvent table has no UPDATE or DELETE paths exposed to UAPF
callers. The same row may later have a VC reference attached
by the VeriDocs SDK pipeline.
determinism: deterministic
side_effects: writes_state
complexity:
typical_latency_ms: 8
max_latency_ms: 2000
failure_mode: |
Throws on unknown event type or missing case_id. Caller in the
triage BPMN treats failure as soft — the case still ends in its
decided state, just without the closing 'routed' marker.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: routed-event
description: |
Standard 'routed' event at the end of triage. Payload echoes the
classification, priority, ownership, group_slug decided upstream.
inputs:
case_id: "99999999-9999-9999-9999-999999999999"
type: "routed"
payload:
classification: "security.incident"
priority: "P1"
ownership: "lvrtc"
group_slug: "soc-l2"
actor_label: "uapf:lv.itsm.incident.triage"
expected_outputs:
recorded_at: "any-iso-timestamp"
- name: ai-decision-recorded
description: |
AI decision recording — payload carries the AIDecision row id so
operators can click through to the proposal.
inputs:
case_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
type: "ai_decision_recorded"
payload:
capability: "ai.draft_response"
decision_id: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
confidence: 0.7
requires_human_approval: true
actor_label: "uapf:lv.itsm.incident.triage"
expected_outputs:
recorded_at: "any-iso-timestamp"

View File

@@ -0,0 +1,111 @@
kind: uapf.algorithm.card
id: algo.incident_triage.evaluate_dmn
version: 1.0.0
name: DMN decision evaluator
intent: |
Wraps a DMN 1.3 decision-table evaluation as a callable UAPF-IP
capability. The triage BPMN invokes the three DMN tables in this
package (priority, ownership, routing) natively via businessRuleTask;
this card describes the dmn.evaluate@1 capability for cases where the
same evaluation is needed outside the BPMN runtime (operator manual
re-evaluation, batch backfill, regression testing).
Engine-internal DMN evaluation and this capability call return the
same output for the same input — that property is the only thing
this card asserts.
algorithm_kind: rule_table
io:
inputs:
- id: package_id
type: string
documentation: UAPF package id whose dmn/ cornerstone holds the table.
- id: decision_id
type: string
constraints:
enum: [priority, ownership, routing]
documentation: Which DMN decision to evaluate in this package.
- id: inputs
type: object
documentation: |
Decision-input columns as a flat object. For priority:
{severity, service_tier, ai_suggested_priority, classification}.
For ownership: {classification, host_domain, source}.
For routing: {classification, priority, ownership}.
outputs:
- id: output
type: object
documentation: |
Object with the decision-output columns. priority -> {priority}.
ownership -> {ownership}. routing -> {group_slug}.
- id: hit_rule_ids
type: array
documentation: Rule ids that matched (FIRST hit-policy produces 1; ANY may produce N).
- id: hit_policy
type: string
constraints:
enum: [FIRST, UNIQUE, ANY, PRIORITY, OUTPUT_ORDER, COLLECT, RULE_ORDER]
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/dmn.evaluate@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: dmn.evaluate@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host reads the
DMN file from the package's dmn/ cornerstone and applies the
DMN 1.3 hit-policy semantics. Same decision artifact, same
output as the in-process engine evaluation.
determinism: deterministic
side_effects: pure
complexity:
typical_latency_ms: 5
max_latency_ms: 5000
failure_mode: |
Throws if the requested package_id is not loaded by the runtime, or
if the named decision_id is not present in that package's dmn/
directory. Triage callers fall back to safe defaults (P4, lvrtc,
helpdesk-l1) so the case still completes.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: priority-critical-tier1
description: |
The priority table should rule P1 for a critical+tier_1 input
regardless of AI suggestion.
inputs:
package_id: "lv.itsm.incident.triage"
decision_id: "priority"
inputs:
severity: "critical"
service_tier: "tier_1"
ai_suggested_priority: "P3"
classification: "network.outage.link_down"
expected_outputs:
output:
priority: "P1"
hit_policy: "FIRST"
- name: routing-security-to-soc
description: |
Routing of a security.incident at P1 should land on soc-l2.
inputs:
package_id: "lv.itsm.incident.triage"
decision_id: "routing"
inputs:
classification: "security.incident"
priority: "P1"
ownership: "lvrtc"
expected_outputs:
output:
group_slug: "soc-l2"
hit_policy: "FIRST"

View File

@@ -0,0 +1,91 @@
kind: uapf.algorithm.card
id: algo.incident_triage.normalize_signal
version: 1.0.0
name: Signal normalizer
intent: |
Reads a freshly-received Signal row by id and folds the
source-specific payload (Zabbix event, IMAP email, Jira DC issue,
manual entry) into a single normalised shape with the fields
downstream cards expect: title, description, host, severity, source
hint, optional contact metadata, and a content-hash for dedupe.
Idempotent; safe to re-run.
algorithm_kind: transformer
io:
inputs:
- id: signal_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
documentation: UUID of the Signal row to normalise.
outputs:
- id: normalized_payload
type: object
documentation: |
Adapter-agnostic dictionary. Stable keys: title, description,
host, severity (one of disaster|high|critical|average|warning|low|info),
source, source_event_id, optional received_at, optional contact_email.
- id: dedupe_hash
type: string
documentation: SHA-256 hex over (source, source_event_id, host) used to suppress repeat signals.
- id: source_kind
type: string
documentation: One of zabbix|email|jira_dc|jira_cloud|manual|cti.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/intake.normalize@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: intake.normalize@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host's
intake.normalize handler implements one normaliser per source.
Hash is a placeholder until the runtime publishes the
implementation hash.
determinism: deterministic
side_effects: pure
complexity:
typical_latency_ms: 30
max_latency_ms: 2000
failure_mode: |
Returns source_kind='unknown' with whatever raw_payload was available
on the signal row. Triage continues with best-effort classification.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: zabbix-link-down
description: |
Zabbix event for a transport-link outage on an edge router.
Title, host and severity are normalised from the raw event shape.
inputs:
signal_id: "11111111-1111-1111-1111-111111111111"
expected_outputs:
source_kind: "zabbix"
normalized_payload:
title: "Link down on edge router rtr-r1"
host: "rtr-r1.lvrtc.lv"
severity: "high"
source: "zabbix"
- name: email-customer-lv
description: |
Customer-facing Latvian-language email about a bandwidth uplift
request. Subject becomes title; body becomes description.
inputs:
signal_id: "22222222-2222-2222-2222-222222222222"
expected_outputs:
source_kind: "email"
normalized_payload:
title: "Klients SIA Latvija Tev: lūgums palielināt joslas platumu"
severity: "average"
source: "email"

View File

@@ -0,0 +1,98 @@
kind: uapf.algorithm.card
id: algo.incident_triage.suggest_priority
version: 1.0.0
name: Priority suggester
intent: |
Reads the classified incident plus its service-tier and reported
severity and proposes a priority on the P1..P4 scale. This output is
a SOFT suggestion only — the priority DMN table makes the binding
decision, with this suggestion as one of its four input columns.
Separating "AI suggestion" from "binding decision" keeps the AI
contestable and the auditor's job tractable.
algorithm_kind: classifier
io:
inputs:
- id: severity
type: string
constraints:
enum: [disaster, critical, high, average, warning, low, info]
documentation: Source-reported severity (Zabbix verbatim; normalised for other adapters).
- id: service_tier
type: string
constraints:
enum: [tier_1, tier_2, best_effort]
documentation: Service tier of the affected service (from the connection or host catalog).
- id: classification
type: string
documentation: |
Taxonomy code from ai.classify@1. The suggester applies a fixed
elevation for security.incident regardless of severity.
outputs:
- id: priority
type: string
constraints:
enum: [P1, P2, P3, P4]
- id: reason
type: string
documentation: One-sentence justification (English). Visible to operator.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/ai.suggest_priority@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: ai.suggest_priority@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host can answer
via either the LLM gateway or a deterministic rule. The DMN
priority table downstream applies a binding decision on top.
determinism: stochastic
side_effects: pure
complexity:
typical_latency_ms: 600
max_latency_ms: 30000
failure_mode: |
Returns priority='P4' with reason='LLM unavailable, defaulted'. The
binding DMN can still raise the priority based on severity x tier.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: disaster-on-tier1-is-p1
description: |
Disaster severity on a tier_1 service is always P1 regardless of
classification.
inputs:
severity: "disaster"
service_tier: "tier_1"
classification: "network.outage.link_down"
expected_outputs:
priority: "P1"
- name: security-incident-elevates
description: |
Security incidents elevate to P1 regardless of severity field.
inputs:
severity: "average"
service_tier: "tier_2"
classification: "security.incident"
expected_outputs:
priority: "P1"
- name: customer-request-low
description: |
A customer service request on best_effort tier is P4 unless
escalated by an operator.
inputs:
severity: "info"
service_tier: "best_effort"
classification: "service.customer_request"
expected_outputs:
priority: "P4"

View File

@@ -0,0 +1,122 @@
kind: uapf.algorithm.card
id: algo.incident_triage.update_incident
version: 1.0.0
name: Incident updater
intent: |
Applies a patch to the Case row (priority, ownership, assigned_group_id,
taxonomy_code) and optionally transitions the FSM (open -> triaged,
triaged -> in_progress, etc). Every transition emits a CaseEvent so
the timeline reflects the change. Refuses illegal transitions per the
state machine in OpenITSM's services/incident.py module — the engine
treats the resulting error as a hard failure.
algorithm_kind: emitter
io:
inputs:
- id: case_id
type: string
cardinality: single
constraints:
pattern: "^[0-9a-fA-F-]{36}$"
- id: patch
type: object
documentation: |
Subset of writable case fields. Allowed keys: priority,
ownership, assigned_group_id, taxonomy_code, sla_breached,
operator_notes. Unknown keys are rejected.
- id: status
type: string
constraints:
enum: [open, triaged, in_progress, waiting_customer, waiting_third_party, resolved, closed, cancelled]
documentation: |
Optional target status. If absent, only field updates are applied.
If present, an FSM transition is attempted; illegal transitions
raise.
- id: reason
type: string
documentation: Human-readable reason recorded on the CaseEvent.
outputs:
- id: case_id
type: string
- id: new_status
type: string
documentation: The case status after the call (unchanged if status input was absent).
- id: success
type: boolean
- id: event_ids
type: array
documentation: CaseEvent row ids emitted by this call.
implementation:
type: external
medium: mcp_tool
uri: uapf-ip://capability/incident.update@1
hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
runtime:
capability: incident.update@1
note: |
Host-fulfilled UAPF-IP capability. The OpenITSM host validates
the patch against the writable-field allowlist and runs FSM
transitions through services/incident.transition_case. All
mutations happen in a single DB transaction with the emitted
CaseEvent rows.
determinism: deterministic
side_effects: writes_state
complexity:
typical_latency_ms: 25
max_latency_ms: 5000
failure_mode: |
Illegal FSM transition or unknown patch key throws and the entire
call is rolled back. Triage callers do NOT proceed past this step on
failure — the case stays at its prior status.
reference:
legal: |
Latvian National Standard for ITSM (LVS EN ISO/IEC 20000-1).
standard: |
ITIL 4 — Incident Management state machine.
owners:
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle:
status: draft
tests:
- name: open-to-triaged-with-priority
description: |
Open case gets a priority + assigned group and transitions to triaged.
inputs:
case_id: "55555555-5555-5555-5555-555555555555"
patch:
priority: "P1"
ownership: "lvrtc"
assigned_group_id: "66666666-6666-6666-6666-666666666666"
status: "triaged"
reason: "Auto-triaged by lv.itsm.incident.triage v1.0.0"
expected_outputs:
success: true
new_status: "triaged"
- name: patch-only-no-transition
description: |
Patch without a status input updates fields but leaves the FSM
state alone.
inputs:
case_id: "77777777-7777-7777-7777-777777777777"
patch:
operator_notes: "Customer called to follow up"
reason: "Operator note added"
expected_outputs:
success: true
- name: illegal-transition-throws
description: |
Closed cases cannot return to in_progress; the capability fails.
inputs:
case_id: "88888888-8888-8888-8888-888888888888"
status: "in_progress"
reason: "Attempting to reopen a closed case"
expected_outputs:
success: false

274
bpmn/incident-triage.bpmn Normal file
View File

@@ -0,0 +1,274 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:uapf="https://uapf.dev/bpmn-ext/v1"
xmlns:uapf24="https://uapf.dev/bpmn/v2.4"
id="Definitions_IncidentTriage"
targetNamespace="https://uapf.dev/processes/lv.itsm.incident.triage"
exporter="openitsm-uapf-generator" exporterVersion="1.0.0">
<bpmn:process id="Process_IncidentTriage" name="Incident Triage" isExecutable="true">
<bpmn:documentation>
Linear UAPF process invoked by OpenITSM intake. Each step is a host-fulfilled
UAPF-IP capability call governed by an Algorithm Card under algorithms/.
The three dmn.evaluate@1 invocations read priority.dmn / ownership.dmn /
routing.dmn from the package's dmn/ cornerstone.
</bpmn:documentation>
<bpmn:startEvent id="Start" name="Signal received">
<bpmn:outgoing>Flow_01</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:serviceTask id="Task_NormalizeSignal"
name="Normalize signal"
uapf:capability="intake.normalize@1"
uapf24:algorithmCardRef="algo.incident_triage.normalize_signal">
<bpmn:documentation>Reads the freshly-received Signal row and folds the source-specific payload (Zabbix / IMAP / Jira / manual) into a uniform shape downstream cards expect.</bpmn:documentation>
<bpmn:incoming>Flow_01</bpmn:incoming>
<bpmn:outgoing>Flow_02</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_NormalizeSignal_in_signal_id" name="signal_id : string"/>
<bpmn:dataOutput id="Task_NormalizeSignal_out_normalized_payload" name="normalized_payload : object"/>
<bpmn:dataOutput id="Task_NormalizeSignal_out_dedupe_hash" name="dedupe_hash : string"/>
<bpmn:dataOutput id="Task_NormalizeSignal_out_source_kind" name="source_kind : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_NormalizeSignal_in_signal_id</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_NormalizeSignal_out_normalized_payload</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_NormalizeSignal_out_dedupe_hash</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_NormalizeSignal_out_source_kind</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_ClassifyIncident"
name="Classify incident"
uapf:capability="ai.classify@1"
uapf24:algorithmCardRef="algo.incident_triage.classify_incident">
<bpmn:documentation>Picks one taxonomy code from a closed list. LLM-backed at runtime with deterministic regex fallback.</bpmn:documentation>
<bpmn:incoming>Flow_02</bpmn:incoming>
<bpmn:outgoing>Flow_03</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_ClassifyIncident_in_payload" name="payload : object"/>
<bpmn:dataInput id="Task_ClassifyIncident_in_text" name="text : string"/>
<bpmn:dataOutput id="Task_ClassifyIncident_out_taxonomy_code" name="taxonomy_code : string"/>
<bpmn:dataOutput id="Task_ClassifyIncident_out_confidence" name="confidence : number"/>
<bpmn:dataOutput id="Task_ClassifyIncident_out_reasoning" name="reasoning : string"/>
<bpmn:dataOutput id="Task_ClassifyIncident_out_label_hint" name="label_hint : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_ClassifyIncident_in_payload</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_ClassifyIncident_in_text</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_ClassifyIncident_out_taxonomy_code</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_ClassifyIncident_out_confidence</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_ClassifyIncident_out_reasoning</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_ClassifyIncident_out_label_hint</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_SuggestPriority"
name="Suggest priority"
uapf:capability="ai.suggest_priority@1"
uapf24:algorithmCardRef="algo.incident_triage.suggest_priority">
<bpmn:documentation>Soft P1..P4 suggestion from severity x service_tier x classification. The downstream DMN makes the binding decision.</bpmn:documentation>
<bpmn:incoming>Flow_03</bpmn:incoming>
<bpmn:outgoing>Flow_04</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_SuggestPriority_in_severity" name="severity : string"/>
<bpmn:dataInput id="Task_SuggestPriority_in_service_tier" name="service_tier : string"/>
<bpmn:dataInput id="Task_SuggestPriority_in_classification" name="classification : string"/>
<bpmn:dataOutput id="Task_SuggestPriority_out_priority" name="priority : string"/>
<bpmn:dataOutput id="Task_SuggestPriority_out_reason" name="reason : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_SuggestPriority_in_severity</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_SuggestPriority_in_service_tier</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_SuggestPriority_in_classification</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_SuggestPriority_out_priority</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_SuggestPriority_out_reason</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_EvaluatePriorityDmn"
name="Evaluate priority DMN"
uapf:capability="dmn.evaluate@1"
uapf24:algorithmCardRef="algo.incident_triage.evaluate_dmn">
<bpmn:documentation>Binding priority decision: FIRST-hit DMN over severity, service_tier, ai_suggested_priority, classification.</bpmn:documentation>
<bpmn:incoming>Flow_04</bpmn:incoming>
<bpmn:outgoing>Flow_05</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_EvaluatePriorityDmn_in_package_id" name="package_id : string"/>
<bpmn:dataInput id="Task_EvaluatePriorityDmn_in_decision_id" name="decision_id : string"/>
<bpmn:dataInput id="Task_EvaluatePriorityDmn_in_inputs" name="inputs : object"/>
<bpmn:dataOutput id="Task_EvaluatePriorityDmn_out_output" name="output : object"/>
<bpmn:dataOutput id="Task_EvaluatePriorityDmn_out_hit_rule_ids" name="hit_rule_ids : array"/>
<bpmn:dataOutput id="Task_EvaluatePriorityDmn_out_hit_policy" name="hit_policy : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_EvaluatePriorityDmn_in_package_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluatePriorityDmn_in_decision_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluatePriorityDmn_in_inputs</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_EvaluatePriorityDmn_out_output</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluatePriorityDmn_out_hit_rule_ids</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluatePriorityDmn_out_hit_policy</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_EvaluateOwnershipDmn"
name="Evaluate ownership DMN"
uapf:capability="dmn.evaluate@1"
uapf24:algorithmCardRef="algo.incident_triage.evaluate_dmn">
<bpmn:documentation>Binding ownership decision: stay inside LVRTC or hand off externally based on classification + host_domain + source.</bpmn:documentation>
<bpmn:incoming>Flow_05</bpmn:incoming>
<bpmn:outgoing>Flow_06</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_EvaluateOwnershipDmn_in_package_id" name="package_id : string"/>
<bpmn:dataInput id="Task_EvaluateOwnershipDmn_in_decision_id" name="decision_id : string"/>
<bpmn:dataInput id="Task_EvaluateOwnershipDmn_in_inputs" name="inputs : object"/>
<bpmn:dataOutput id="Task_EvaluateOwnershipDmn_out_output" name="output : object"/>
<bpmn:dataOutput id="Task_EvaluateOwnershipDmn_out_hit_rule_ids" name="hit_rule_ids : array"/>
<bpmn:dataOutput id="Task_EvaluateOwnershipDmn_out_hit_policy" name="hit_policy : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_EvaluateOwnershipDmn_in_package_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluateOwnershipDmn_in_decision_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluateOwnershipDmn_in_inputs</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_EvaluateOwnershipDmn_out_output</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluateOwnershipDmn_out_hit_rule_ids</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluateOwnershipDmn_out_hit_policy</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_EvaluateRoutingDmn"
name="Evaluate routing DMN"
uapf:capability="dmn.evaluate@1"
uapf24:algorithmCardRef="algo.incident_triage.evaluate_dmn">
<bpmn:documentation>Picks the expert group (helpdesk-l1, noc-l1/l2, soc-l2, facility-l2, platform-l2, external-handoff) from classification + priority + ownership.</bpmn:documentation>
<bpmn:incoming>Flow_06</bpmn:incoming>
<bpmn:outgoing>Flow_07</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_EvaluateRoutingDmn_in_package_id" name="package_id : string"/>
<bpmn:dataInput id="Task_EvaluateRoutingDmn_in_decision_id" name="decision_id : string"/>
<bpmn:dataInput id="Task_EvaluateRoutingDmn_in_inputs" name="inputs : object"/>
<bpmn:dataOutput id="Task_EvaluateRoutingDmn_out_output" name="output : object"/>
<bpmn:dataOutput id="Task_EvaluateRoutingDmn_out_hit_rule_ids" name="hit_rule_ids : array"/>
<bpmn:dataOutput id="Task_EvaluateRoutingDmn_out_hit_policy" name="hit_policy : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_EvaluateRoutingDmn_in_package_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluateRoutingDmn_in_decision_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EvaluateRoutingDmn_in_inputs</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_EvaluateRoutingDmn_out_output</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluateRoutingDmn_out_hit_rule_ids</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EvaluateRoutingDmn_out_hit_policy</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_UpdateIncident"
name="Update incident state"
uapf:capability="incident.update@1"
uapf24:algorithmCardRef="algo.incident_triage.update_incident">
<bpmn:documentation>Applies the decided priority + ownership + assigned_group_id and transitions open -> triaged. All mutations in one DB transaction.</bpmn:documentation>
<bpmn:incoming>Flow_07</bpmn:incoming>
<bpmn:outgoing>Flow_08</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_UpdateIncident_in_case_id" name="case_id : string"/>
<bpmn:dataInput id="Task_UpdateIncident_in_patch" name="patch : object"/>
<bpmn:dataInput id="Task_UpdateIncident_in_status" name="status : string"/>
<bpmn:dataInput id="Task_UpdateIncident_in_reason" name="reason : string"/>
<bpmn:dataOutput id="Task_UpdateIncident_out_case_id" name="case_id : string"/>
<bpmn:dataOutput id="Task_UpdateIncident_out_new_status" name="new_status : string"/>
<bpmn:dataOutput id="Task_UpdateIncident_out_success" name="success : boolean"/>
<bpmn:dataOutput id="Task_UpdateIncident_out_event_ids" name="event_ids : array"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_UpdateIncident_in_case_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_UpdateIncident_in_patch</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_UpdateIncident_in_status</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_UpdateIncident_in_reason</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_UpdateIncident_out_case_id</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_UpdateIncident_out_new_status</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_UpdateIncident_out_success</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_UpdateIncident_out_event_ids</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_DraftResponse"
name="Draft customer response"
uapf:capability="ai.draft_response@1"
uapf24:algorithmCardRef="algo.incident_triage.draft_response">
<bpmn:documentation>Drafts a parallel Latvian + English customer notification. Produces an AIDecision row with requires_human_approval=true. Operator must approve before any outbound transport adapter runs.</bpmn:documentation>
<bpmn:incoming>Flow_08</bpmn:incoming>
<bpmn:outgoing>Flow_09</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_DraftResponse_in_case_id" name="case_id : string"/>
<bpmn:dataInput id="Task_DraftResponse_in_locale" name="locale : string"/>
<bpmn:dataInput id="Task_DraftResponse_in_what_happened" name="what_happened : string"/>
<bpmn:dataInput id="Task_DraftResponse_in_eta_minutes" name="eta_minutes : integer"/>
<bpmn:dataOutput id="Task_DraftResponse_out_subject_lv" name="subject_lv : string"/>
<bpmn:dataOutput id="Task_DraftResponse_out_subject_en" name="subject_en : string"/>
<bpmn:dataOutput id="Task_DraftResponse_out_body_lv" name="body_lv : string"/>
<bpmn:dataOutput id="Task_DraftResponse_out_body_en" name="body_en : string"/>
<bpmn:dataOutput id="Task_DraftResponse_out_locale" name="locale : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_DraftResponse_in_case_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_DraftResponse_in_locale</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_DraftResponse_in_what_happened</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_DraftResponse_in_eta_minutes</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_DraftResponse_out_subject_lv</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_DraftResponse_out_subject_en</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_DraftResponse_out_body_lv</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_DraftResponse_out_body_en</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_DraftResponse_out_locale</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:serviceTask id="Task_EmitEvent"
name="Emit routed event"
uapf:capability="event.emit@1"
uapf24:algorithmCardRef="algo.incident_triage.emit_event">
<bpmn:documentation>Emits the closing 'routed' CaseEvent. Payload echoes classification, priority, ownership, group_slug for the timeline.</bpmn:documentation>
<bpmn:incoming>Flow_09</bpmn:incoming>
<bpmn:outgoing>Flow_10</bpmn:outgoing>
<bpmn:ioSpecification>
<bpmn:dataInput id="Task_EmitEvent_in_case_id" name="case_id : string"/>
<bpmn:dataInput id="Task_EmitEvent_in_type" name="type : string"/>
<bpmn:dataInput id="Task_EmitEvent_in_payload" name="payload : object"/>
<bpmn:dataInput id="Task_EmitEvent_in_actor_label" name="actor_label : string"/>
<bpmn:dataOutput id="Task_EmitEvent_out_event_id" name="event_id : string"/>
<bpmn:dataOutput id="Task_EmitEvent_out_recorded_at" name="recorded_at : string"/>
<bpmn:inputSet>
<bpmn:dataInputRefs>Task_EmitEvent_in_case_id</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EmitEvent_in_type</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EmitEvent_in_payload</bpmn:dataInputRefs>
<bpmn:dataInputRefs>Task_EmitEvent_in_actor_label</bpmn:dataInputRefs>
</bpmn:inputSet>
<bpmn:outputSet>
<bpmn:dataOutputRefs>Task_EmitEvent_out_event_id</bpmn:dataOutputRefs>
<bpmn:dataOutputRefs>Task_EmitEvent_out_recorded_at</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
</bpmn:serviceTask>
<bpmn:endEvent id="End" name="Triage complete">
<bpmn:incoming>Flow_10</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_01" sourceRef="Start" targetRef="Task_NormalizeSignal"/>
<bpmn:sequenceFlow id="Flow_02" sourceRef="Task_NormalizeSignal" targetRef="Task_ClassifyIncident"/>
<bpmn:sequenceFlow id="Flow_03" sourceRef="Task_ClassifyIncident" targetRef="Task_SuggestPriority"/>
<bpmn:sequenceFlow id="Flow_04" sourceRef="Task_SuggestPriority" targetRef="Task_EvaluatePriorityDmn"/>
<bpmn:sequenceFlow id="Flow_05" sourceRef="Task_EvaluatePriorityDmn" targetRef="Task_EvaluateOwnershipDmn"/>
<bpmn:sequenceFlow id="Flow_06" sourceRef="Task_EvaluateOwnershipDmn" targetRef="Task_EvaluateRoutingDmn"/>
<bpmn:sequenceFlow id="Flow_07" sourceRef="Task_EvaluateRoutingDmn" targetRef="Task_UpdateIncident"/>
<bpmn:sequenceFlow id="Flow_08" sourceRef="Task_UpdateIncident" targetRef="Task_DraftResponse"/>
<bpmn:sequenceFlow id="Flow_09" sourceRef="Task_DraftResponse" targetRef="Task_EmitEvent"/>
<bpmn:sequenceFlow id="Flow_10" sourceRef="Task_EmitEvent" targetRef="End"/>
</bpmn:process>
</bpmn:definitions>

142
dmn/ownership.dmn Normal file
View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<dmn:definitions
xmlns:dmn="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:feel="https://www.omg.org/spec/DMN/20191111/FEEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
id="DMN_Ownership"
name="Incident ownership"
namespace="https://uapf.dev/dmn/lv.itsm.incident.triage/ownership"
exporter="openitsm-uapf-generator"
exporterVersion="1.0.0">
<dmn:description>
Determine whether LVRTC owns this incident or it should be transferred.
</dmn:description>
<dmn:decision id="ownership" name="Incident ownership">
<dmn:variable
id="var_ownership"
name="ownership_result"
typeRef="Any"/>
<dmn:decisionTable id="DecisionTable_ownership" hitPolicy="FIRST">
<dmn:input id="input_ownership_0" label="classification">
<dmn:inputExpression id="inputExpr_ownership_0" typeRef="string">
<dmn:text>classification</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_ownership_1" label="host_domain">
<dmn:inputExpression id="inputExpr_ownership_1" typeRef="string">
<dmn:text>host_domain</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_ownership_2" label="source">
<dmn:inputExpression id="inputExpr_ownership_2" typeRef="string">
<dmn:text>source</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:output id="output_ownership_0" label="ownership" typeRef="string"/>
<dmn:output id="output_ownership_1" label="reason" typeRef="string"/>
<dmn:rule id="rule_ownership_1">
<dmn:inputEntry id="ie_ownership_1_0">
<dmn:text>"security.incident"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_1_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_1_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_1_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_1_1">
<dmn:text>"Security incidents owned by LVRTC SOC"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_ownership_2">
<dmn:inputEntry id="ie_ownership_2_0">
<dmn:text>"facility.power"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_2_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_2_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_2_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_2_1">
<dmn:text>"Facility / power is LVRTC's"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_ownership_3">
<dmn:inputEntry id="ie_ownership_3_0">
<dmn:text>"service.customer_request"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_3_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_3_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_3_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_3_1">
<dmn:text>"Customer requests are LVRTC's intake"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_ownership_4">
<dmn:inputEntry id="ie_ownership_4_0">
<dmn:text>"unknown.uncategorized"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_4_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_4_2">
<dmn:text>"phone"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_4_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_4_1">
<dmn:text>"Phone intake stays with LVRTC L1"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_ownership_5">
<dmn:inputEntry id="ie_ownership_5_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_5_1">
<dmn:text>"lvrtc.lv"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_5_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_5_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_5_1">
<dmn:text>"Anything on lvrtc.lv hosts is ours"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_ownership_6">
<dmn:inputEntry id="ie_ownership_6_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_6_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_ownership_6_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_ownership_6_0">
<dmn:text>"lvrtc"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_ownership_6_1">
<dmn:text>"Default: LVRTC owns until escalation transfers"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
</dmn:decisionTable>
</dmn:decision>
</dmn:definitions>

326
dmn/priority.dmn Normal file
View File

@@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?>
<dmn:definitions
xmlns:dmn="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:feel="https://www.omg.org/spec/DMN/20191111/FEEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
id="DMN_Priority"
name="Incident priority"
namespace="https://uapf.dev/dmn/lv.itsm.incident.triage/priority"
exporter="openitsm-uapf-generator"
exporterVersion="1.0.0">
<dmn:description>
Determine the final P1..P4 priority from severity, service tier, and AI
suggestion. FIRST matching rule wins.
</dmn:description>
<dmn:decision id="priority" name="Incident priority">
<dmn:variable
id="var_priority"
name="priority_result"
typeRef="Any"/>
<dmn:decisionTable id="DecisionTable_priority" hitPolicy="FIRST">
<dmn:input id="input_priority_0" label="severity">
<dmn:inputExpression id="inputExpr_priority_0" typeRef="string">
<dmn:text>severity</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_priority_1" label="service_tier">
<dmn:inputExpression id="inputExpr_priority_1" typeRef="string">
<dmn:text>service_tier</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_priority_2" label="ai_suggested_priority">
<dmn:inputExpression id="inputExpr_priority_2" typeRef="string">
<dmn:text>ai_suggested_priority</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_priority_3" label="classification">
<dmn:inputExpression id="inputExpr_priority_3" typeRef="string">
<dmn:text>classification</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:output id="output_priority_0" label="priority" typeRef="string"/>
<dmn:output id="output_priority_1" label="reason" typeRef="string"/>
<dmn:rule id="rule_priority_1">
<dmn:inputEntry id="ie_priority_1_0">
<dmn:text>"disaster"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_1_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_1_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_1_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_1_0">
<dmn:text>"P1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_1_1">
<dmn:text>"Disaster severity always overrides"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_2">
<dmn:inputEntry id="ie_priority_2_0">
<dmn:text>"critical"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_2_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_2_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_2_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_2_0">
<dmn:text>"P1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_2_1">
<dmn:text>"Critical severity always overrides"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_3">
<dmn:inputEntry id="ie_priority_3_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_3_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_3_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_3_3">
<dmn:text>"security.incident"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_3_0">
<dmn:text>"P1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_3_1">
<dmn:text>"Security incidents are always P1"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_4">
<dmn:inputEntry id="ie_priority_4_0">
<dmn:text>"high"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_4_1">
<dmn:text>"tier_1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_4_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_4_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_4_0">
<dmn:text>"P1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_4_1">
<dmn:text>"High on tier_1 service"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_5">
<dmn:inputEntry id="ie_priority_5_0">
<dmn:text>"high"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_5_1">
<dmn:text>"tier_2"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_5_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_5_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_5_0">
<dmn:text>"P2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_5_1">
<dmn:text>"High on tier_2 service"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_6">
<dmn:inputEntry id="ie_priority_6_0">
<dmn:text>"high"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_6_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_6_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_6_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_6_0">
<dmn:text>"P2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_6_1">
<dmn:text>"High severity, lower tier"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_7">
<dmn:inputEntry id="ie_priority_7_0">
<dmn:text>"average"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_7_1">
<dmn:text>"tier_1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_7_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_7_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_7_0">
<dmn:text>"P2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_7_1">
<dmn:text>"Average on tier_1 service"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_8">
<dmn:inputEntry id="ie_priority_8_0">
<dmn:text>"average"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_8_1">
<dmn:text>"tier_2"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_8_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_8_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_8_0">
<dmn:text>"P3"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_8_1">
<dmn:text>"Average on tier_2 service"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_9">
<dmn:inputEntry id="ie_priority_9_0">
<dmn:text>"warning"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_9_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_9_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_9_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_9_0">
<dmn:text>"P3"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_9_1">
<dmn:text>"Warning severity"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_10">
<dmn:inputEntry id="ie_priority_10_0">
<dmn:text>"information"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_10_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_10_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_10_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_10_0">
<dmn:text>"P4"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_10_1">
<dmn:text>"Informational"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_11">
<dmn:inputEntry id="ie_priority_11_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_11_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_11_2">
<dmn:text>"P1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_11_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_11_0">
<dmn:text>"P1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_11_1">
<dmn:text>"Fallback: trust AI suggestion P1"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_12">
<dmn:inputEntry id="ie_priority_12_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_12_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_12_2">
<dmn:text>"P2"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_12_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_12_0">
<dmn:text>"P2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_12_1">
<dmn:text>"Fallback: trust AI suggestion P2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_13">
<dmn:inputEntry id="ie_priority_13_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_13_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_13_2">
<dmn:text>"P3"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_13_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_13_0">
<dmn:text>"P3"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_13_1">
<dmn:text>"Fallback: trust AI suggestion P3"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_priority_14">
<dmn:inputEntry id="ie_priority_14_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_14_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_14_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_priority_14_3">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_priority_14_0">
<dmn:text>"P4"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_priority_14_1">
<dmn:text>"Default when nothing else matches"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
</dmn:decisionTable>
</dmn:decision>
</dmn:definitions>

278
dmn/routing.dmn Normal file
View File

@@ -0,0 +1,278 @@
<?xml version="1.0" encoding="UTF-8"?>
<dmn:definitions
xmlns:dmn="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:feel="https://www.omg.org/spec/DMN/20191111/FEEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
id="DMN_Routing"
name="Incident routing"
namespace="https://uapf.dev/dmn/lv.itsm.incident.triage/routing"
exporter="openitsm-uapf-generator"
exporterVersion="1.0.0">
<dmn:description>
Route to an expert group slug based on classification + priority. FIRST hit.
</dmn:description>
<dmn:decision id="routing" name="Incident routing">
<dmn:variable
id="var_routing"
name="routing_result"
typeRef="Any"/>
<dmn:decisionTable id="DecisionTable_routing" hitPolicy="FIRST">
<dmn:input id="input_routing_0" label="classification">
<dmn:inputExpression id="inputExpr_routing_0" typeRef="string">
<dmn:text>classification</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_routing_1" label="priority">
<dmn:inputExpression id="inputExpr_routing_1" typeRef="string">
<dmn:text>priority</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:input id="input_routing_2" label="ownership">
<dmn:inputExpression id="inputExpr_routing_2" typeRef="string">
<dmn:text>ownership</dmn:text>
</dmn:inputExpression>
</dmn:input>
<dmn:output id="output_routing_0" label="group_slug" typeRef="string"/>
<dmn:output id="output_routing_1" label="reason" typeRef="string"/>
<dmn:rule id="rule_routing_1">
<dmn:inputEntry id="ie_routing_1_0">
<dmn:text>"security.incident"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_1_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_1_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_1_0">
<dmn:text>"soc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_1_1">
<dmn:text>"Security goes to SOC L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_2">
<dmn:inputEntry id="ie_routing_2_0">
<dmn:text>"network.outage.link_down"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_2_1">
<dmn:text>"P1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_2_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_2_0">
<dmn:text>"noc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_2_1">
<dmn:text>"Network P1 to NOC L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_3">
<dmn:inputEntry id="ie_routing_3_0">
<dmn:text>"network.outage.link_down"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_3_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_3_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_3_0">
<dmn:text>"noc-l1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_3_1">
<dmn:text>"Network non-P1 to NOC L1"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_4">
<dmn:inputEntry id="ie_routing_4_0">
<dmn:text>"network.degradation"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_4_1">
<dmn:text>"P1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_4_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_4_0">
<dmn:text>"noc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_4_1">
<dmn:text>"Network degradation P1 to NOC L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_5">
<dmn:inputEntry id="ie_routing_5_0">
<dmn:text>"network.degradation"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_5_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_5_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_5_0">
<dmn:text>"noc-l1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_5_1">
<dmn:text>"Network degradation lower to NOC L1"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_6">
<dmn:inputEntry id="ie_routing_6_0">
<dmn:text>"network.routing"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_6_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_6_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_6_0">
<dmn:text>"noc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_6_1">
<dmn:text>"Routing issues need L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_7">
<dmn:inputEntry id="ie_routing_7_0">
<dmn:text>"network.dns"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_7_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_7_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_7_0">
<dmn:text>"noc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_7_1">
<dmn:text>"DNS issues to NOC L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_8">
<dmn:inputEntry id="ie_routing_8_0">
<dmn:text>"facility.power"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_8_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_8_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_8_0">
<dmn:text>"facility-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_8_1">
<dmn:text>"Power to facility ops"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_9">
<dmn:inputEntry id="ie_routing_9_0">
<dmn:text>"storage.capacity"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_9_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_9_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_9_0">
<dmn:text>"platform-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_9_1">
<dmn:text>"Storage to platform ops"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_10">
<dmn:inputEntry id="ie_routing_10_0">
<dmn:text>"service.customer_request"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_10_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_10_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_10_0">
<dmn:text>"helpdesk-l1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_10_1">
<dmn:text>"Customer requests to L1 helpdesk"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_11">
<dmn:inputEntry id="ie_routing_11_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_11_1">
<dmn:text>"P1"</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_11_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_11_0">
<dmn:text>"noc-l2"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_11_1">
<dmn:text>"Default P1: NOC L2"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_12">
<dmn:inputEntry id="ie_routing_12_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_12_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_12_2">
<dmn:text>"lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_12_0">
<dmn:text>"helpdesk-l1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_12_1">
<dmn:text>"Default: L1 helpdesk triages"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_13">
<dmn:inputEntry id="ie_routing_13_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_13_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_13_2">
<dmn:text>"not_lvrtc"</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_13_0">
<dmn:text>"external-handoff"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_13_1">
<dmn:text>"Not ours - external handoff"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
<dmn:rule id="rule_routing_14">
<dmn:inputEntry id="ie_routing_14_0">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_14_1">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:inputEntry id="ie_routing_14_2">
<dmn:text>-</dmn:text>
</dmn:inputEntry>
<dmn:outputEntry id="oe_routing_14_0">
<dmn:text>"helpdesk-l1"</dmn:text>
</dmn:outputEntry>
<dmn:outputEntry id="oe_routing_14_1">
<dmn:text>"Final default"</dmn:text>
</dmn:outputEntry>
</dmn:rule>
</dmn:decisionTable>
</dmn:decision>
</dmn:definitions>

View File

@@ -0,0 +1,49 @@
# Host lookup tables
This package expects the OpenITSM host to maintain three deployment-specific
lookup tables. They are NOT part of the package (the package only defines the
abstract triage process); each host deployment populates them.
## Expert groups
The routing DMN resolves to one of these `group_slug` values. The host MUST
have a matching `expert_groups` row for each:
| slug | name |
|-------------------|-------------------|
| `helpdesk-l1` | L1 helpdesk |
| `noc-l1` | Network Ops L1 |
| `noc-l2` | Network Ops L2 |
| `soc-l2` | Security Ops L2 |
| `facility-l2` | Facility & Power L2 |
| `platform-l2` | Platform Ops L2 |
| `external-handoff`| External handoff |
## Service tiers
The priority DMN consumes these tier codes. Hosts MUST have a matching
`service_tiers` row.
| code | name | first_response | resolution |
|---------------|-------------------------------|----------------|------------|
| `tier_1` | Tier 1 (mission-critical) | 15 min | 240 min |
| `tier_2` | Tier 2 (standard) | 60 min | 480 min |
| `best_effort` | Best effort (non-SLA) | — (no SLA) | — |
## Taxonomy
The closed list of taxonomy codes `ai.classify@1` can emit, mirrored in the
classify_incident Algorithm Card's `io.outputs.taxonomy_code.constraints.enum`
and consumed as input to the priority + routing DMN.
| code | LV | EN |
|-------------------------------|--------------------------|---------------------|
| `network.outage.link_down` | Saites pārtraukums | Link down |
| `network.degradation` | Tīkla degradācija | Network degradation |
| `network.routing` | Maršrutēšana | Routing |
| `network.dns` | DNS | DNS |
| `security.incident` | Drošības incidents | Security incident |
| `facility.power` | Elektroapgāde | Power / facility |
| `storage.capacity` | Diska vieta | Storage capacity |
| `service.customer_request` | Klienta pieprasījums | Customer request |
| `unknown.uncategorized` | Neklasificēts | Uncategorized |

60
docs/overview.md Normal file
View File

@@ -0,0 +1,60 @@
# `lv.itsm.incident.triage` — overview
Level-4 UAPF process for triaging IT-infrastructure incidents at LVRTC.
## What it does
A new Signal lands in OpenITSM (Zabbix webhook, IMAP poll, Jira DC webhook,
manual entry). The host opens a Case and starts a session of this package
against the UAPF engine. The engine then drives the linear flow defined in
`bpmn/incident-triage.bpmn`, calling back to OpenITSM at every step:
1. **Normalize** the source-specific payload to a uniform shape.
2. **Classify** into one of nine taxonomy codes (LLM + regex fallback).
3. **Suggest** a soft P1..P4 priority.
4. **Evaluate** the three DMN tables in order: priority (binding),
ownership (LVRTC vs external), routing (which expert group).
5. **Update** the case with the decided priority + group + status=triaged.
6. **Draft** a parallel Latvian + English customer notification — flagged
PROPOSED, requires operator approval before send.
7. **Emit** the closing `routed` CaseEvent.
## Why the split
Classification, priority suggestion, and customer-response drafting are
the AI steps. Everything *binding* — the actual priority, who handles it,
which group — lives in versioned DMN, not in Python. That keeps the AI
contestable and the auditor's job tractable: an evaluator can read
`dmn/priority.dmn` and know exactly what priority an incident *will*
receive, given its severity and tier, without running anything.
## Cornerstones
- **bpmn/** — `incident-triage.bpmn` — 1 process, 9 service tasks, linear.
- **dmn/** — `priority.dmn` (14 rules), `ownership.dmn` (6 rules),
`routing.dmn` (14 rules), all FIRST hit-policy.
- **algorithms/** — 7 algorithm cards, each with embedded v2.5.0 tests.
- **resources/** — guardrails (PII, approval gating, timeouts, retention)
and host mappings (expert groups, service tiers, taxonomy).
- **metadata/** — lifecycle + ownership.
## Versioning
This package targets **UAPF v2.5.0** (track main, refreshed on every
schema release). Breaking changes follow the spec's CHANGELOG.
## Host requirements
OpenITSM must implement and advertise (via `/uapf/host/manifest`) the
seven UAPF-IP capabilities listed in `requires_capabilities`:
- `intake.normalize@1`
- `ai.classify@1`
- `ai.suggest_priority@1`
- `ai.draft_response@1`
- `dmn.evaluate@1`
- `incident.update@1`
- `event.emit@1`
The first six are intent-bearing (each governed by its own Algorithm
Card); `event.emit` is an append-only timeline writer.

View File

@@ -0,0 +1,16 @@
{
"source": "email",
"external_id": "MSG-2026-05-25-001",
"raw_payload": {
"subject": "Klients SIA Latvija Tev: lūgums palielināt joslas platumu",
"from": "ivars.berzins@klients-latvija.lv",
"to": "atbalsts@lvrtc.lv",
"body": "Labdien, mūsu uzņēmumam nepieciešams palielināt internet pieslēguma joslas platumu no 100 Mbps uz 500 Mbps. Lūdzu, informējiet par procedūru un izmaksām. Ar cieņu, Ivars Bērziņš"
},
"expected_after_triage": {
"taxonomy_code": "service.customer_request",
"priority": "P3",
"ownership": "lvrtc",
"group_slug": "helpdesk-l1"
}
}

View File

@@ -0,0 +1,17 @@
{
"source": "zabbix",
"external_id": "ZBX-EVT-9374",
"raw_payload": {
"title": "DDoS attack pattern detected on edge",
"host": "rtr-r3.lvrtc.lv",
"severity": "critical",
"body": "Volumetric UDP flood, 4.2 Gbps inbound to 192.0.2.0/24. Source: 12 ASNs, predominantly AS197207. Auto-mitigation engaged.",
"tags": ["security", "ddos"]
},
"expected_after_triage": {
"taxonomy_code": "security.incident",
"priority": "P1",
"ownership": "lvrtc",
"group_slug": "soc-l2"
}
}

View File

@@ -0,0 +1,18 @@
{
"source": "zabbix",
"external_id": "ZBX-EVT-9342",
"raw_payload": {
"title": "Link down on edge router rtr-r1",
"host": "rtr-r1.lvrtc.lv",
"severity": "high",
"body": "Interface ge-0/0/3 went DOWN at 09:14:02 UTC. BGP session to upstream provider AS6939 also flapping.",
"event_id": "9342",
"tags": ["network", "edge"]
},
"expected_after_triage": {
"taxonomy_code": "network.routing",
"priority": "P1",
"ownership": "lvrtc",
"group_slug": "noc-l2"
}
}

63
manifest.json Normal file
View File

@@ -0,0 +1,63 @@
{
"kind": "uapf.package",
"id": "lv.itsm.incident.triage",
"name": "LVRTC Incident Triage",
"description": "Level-4 UAPF process for triaging IT-infrastructure incidents at LVRTC.\n\nSix BPMN service tasks invoke the UAPF-IP capabilities intake.normalize@1,\nai.classify@1, ai.suggest_priority@1, ai.draft_response@1,\nincident.update@1 and event.emit@1. Three DMN decision tables encode the\ndeterministic rules previously hidden inside the host: priority maps\nseverity x service-tier x AI-suggestion x classification onto P1-P4;\nownership decides whether the case stays inside LVRTC or hands off to an\nexternal partner; routing picks the expert group (helpdesk-l1, noc-l1/l2,\nsoc-l2, facility-l2, platform-l2, external-handoff).\n\nClassification, priority suggestion and customer response drafting are AI\nsteps; the rules that decide *what* the AI proposes versus *who* handles\nit live in versioned DMN, not Python. Drafted customer responses are\nproduced in both Latvian and English and require human approval before\nsending (governed by Algorithm Card algo.incident_triage.draft_response).\n\nv1.0.0 targets UAPF v2.5.0: algorithm cards carry embedded `tests`\narrays (>=2 per card) per chapter 13.16; BPMN service tasks carry\nuapf24:algorithmCardRef attributes; resource targets are dispatch\nendpoints only.\n",
"level": 4,
"version": "1.0.0",
"requires_capabilities": [
"intake.normalize@1+",
"ai.classify@1+",
"ai.suggest_priority@1+",
"ai.draft_response@1+",
"dmn.evaluate@1+",
"incident.update@1+",
"event.emit@1+"
],
"profiles_supported": [
"uapf-ip-orchestrated"
],
"guardrails": "resources/guardrails.yaml",
"includes": [],
"dependencies": {},
"cornerstones": {
"bpmn": true,
"dmn": true,
"cmmn": false,
"resources": true
},
"paths": {
"bpmn": "bpmn",
"dmn": "dmn",
"resources": "resources",
"metadata": "metadata"
},
"exposure": {
"mcp": {
"enabled": true,
"runnable": true,
"exposedEntrypoints": [
"Process_IncidentTriage"
],
"exposedArtifacts": [
"manifest",
"bpmn",
"dmn",
"docs"
]
}
},
"owners": [
{
"type": "team",
"id": "lvrtc",
"contact": "incident-mgmt@lvrtc.lv"
},
{
"type": "team",
"id": "openitsm-stewards",
"contact": "stewards@openitsm.algomation.io"
}
],
"lifecycle": "draft"
}

16
metadata/lifecycle.yaml Normal file
View File

@@ -0,0 +1,16 @@
kind: uapf.metadata.lifecycle
status: draft
created: "2026-06-01T00:00:00Z"
lastModified: "2026-06-01T00:00:00Z"
changeHistory:
- version: "1.0.0"
date: "2026-06-01"
summary: |
Initial release. Six BPMN service tasks invoke UAPF-IP capabilities
(intake.normalize, ai.classify, ai.suggest_priority, ai.draft_response,
incident.update, event.emit). Three DMN decision tables encode the
priority / ownership / routing rules previously hidden in the
OpenITSM host's inline triage runner. Seven algorithm cards (the
six service-task cards plus dmn.evaluate as a host-fulfilled
capability for governance) carry embedded tests per UAPF v2.5.0.
author: "openitsm-stewards"

19
metadata/ownership.yaml Normal file
View File

@@ -0,0 +1,19 @@
kind: uapf.metadata.ownership
owners:
- type: team
id: lvrtc
name: VAS Latvijas Valsts Radio un Televizijas Centrs
contact: incident-mgmt@lvrtc.lv
role: owner
- type: team
id: openitsm-stewards
name: OpenITSM Package Stewards
contact: stewards@openitsm.algomation.io
role: maintainer
approvers:
- lvrtc-incident-board
- openitsm-stewards
escalation:
level_1:
team: lvrtc-incident-board
contact: incident-board@lvrtc.lv

15
processgit.mcp.yaml Normal file
View File

@@ -0,0 +1,15 @@
# Hints for ProcessGit's MCP indexer about this package.
kind: processgit.mcp.hints
package_id: lv.itsm.incident.triage
description: LVRTC incident triage — UAPF v2.5.0 package
tags:
- itsm
- incident-management
- lvrtc
- openitsm
- critical-infrastructure
- nis2
locale_primary: lv
locales:
- lv
- en

37
resources/guardrails.yaml Normal file
View File

@@ -0,0 +1,37 @@
kind: uapf.resources.guardrails
# Applied by the UAPF runtime to every UAPF-IP capability invocation
# governed by this package. Cross-cutting safety rails — enforced regardless
# of which Algorithm Card the runtime is dispatching.
pii:
redact_in_payloads: true
forbidden_in_drafts:
- personas_kods
- magnetic_stripe
- iban
allowed_in_drafts:
- case_number
- host_domain
- approximate_eta
approval:
human_required_for:
- ai.draft_response # outbound customer text always reviewed
- incident.update # write actions never auto-applied
auto_applied:
- intake.normalize
- ai.classify
- ai.suggest_priority
- dmn.evaluate
- event.emit
timeouts:
capability_default_ms: 30000
llm_default_ms: 45000
dmn_default_ms: 5000
retention:
algorithm_outputs_days: 365
audit_events_days: 2557 # 7 years (Latvian state-archive default)
signed_artifacts: indefinite

194
resources/mappings.yaml Normal file
View File

@@ -0,0 +1,194 @@
kind: uapf.resources.mapping
# Host-readable contract for the seven capability-backed service tasks in
# this package. Algorithm Card references live on the BPMN service tasks
# themselves (uapf24:algorithmCardRef attribute) per UAPF v2.4.0+. The
# targets below are dispatch endpoints only.
targets:
- id: agent.intake_normalizer
type: system_api
name: Signal normalizer
description: Host capability intake.normalize@1, governed by the normalize_signal Algorithm Card.
capabilities:
- capability.intake.normalize
- id: agent.classifier
type: ai_agent
name: Incident taxonomy classifier
description: Host capability ai.classify@1 (LLM-backed), governed by the classify_incident Algorithm Card.
capabilities:
- capability.ai.classify
- id: agent.priority_suggester
type: ai_agent
name: Priority suggester
description: Host capability ai.suggest_priority@1, governed by the suggest_priority Algorithm Card.
capabilities:
- capability.ai.suggest_priority
- id: agent.dmn_evaluator
type: system_api
name: DMN decision evaluator
description: Host capability dmn.evaluate@1, governed by the evaluate_dmn Algorithm Card. Invoked three times in the BPMN with different decision_id input.
capabilities:
- capability.dmn.evaluate
- id: agent.response_drafter
type: ai_agent
name: Customer response drafter
description: Host capability ai.draft_response@1, governed by the draft_response Algorithm Card. Requires human approval before send.
capabilities:
- capability.ai.draft_response
- id: agent.incident_updater
type: system_api
name: Incident state writer
description: Host capability incident.update@1, governed by the update_incident Algorithm Card. Applies field patches and FSM transitions.
capabilities:
- capability.incident.update
- id: agent.event_emitter
type: system_api
name: Case event emitter
description: Host capability event.emit@1, governed by the emit_event Algorithm Card. Append-only timeline writer.
capabilities:
- capability.event.emit
bindings:
- source: { type: bpmn.serviceTask, ref: Task_NormalizeSignal }
targetId: agent.intake_normalizer
mode: autonomous
contract:
input:
- { name: signal_id, type: string, required: true }
output:
- { name: normalized_payload, type: object }
- { name: dedupe_hash, type: string }
- { name: source_kind, type: string }
timeout: "5s"
requiredCapabilities: [capability.intake.normalize]
- source: { type: bpmn.serviceTask, ref: Task_ClassifyIncident }
targetId: agent.classifier
mode: autonomous
contract:
input:
- { name: payload, type: object, required: true }
- { name: text, type: string }
output:
- { name: taxonomy_code, type: string }
- { name: confidence, type: number, description: "0.0-1.0 model confidence" }
- { name: reasoning, type: string }
- { name: label_hint, type: string }
timeout: "30s"
requiredCapabilities: [capability.ai.classify]
- source: { type: bpmn.serviceTask, ref: Task_SuggestPriority }
targetId: agent.priority_suggester
mode: autonomous
contract:
input:
- { name: severity, type: string, required: true }
- { name: service_tier, type: string, required: true }
- { name: classification, type: string, required: true }
output:
- { name: priority, type: string }
- { name: reason, type: string }
timeout: "30s"
requiredCapabilities: [capability.ai.suggest_priority]
- source: { type: bpmn.serviceTask, ref: Task_EvaluatePriorityDmn }
targetId: agent.dmn_evaluator
mode: autonomous
contract:
input:
- { name: package_id, type: string, required: true }
- { name: decision_id, type: string, required: true }
- { name: inputs, type: object, required: true }
output:
- { name: output, type: object }
- { name: hit_rule_ids, type: array }
- { name: hit_policy, type: string }
timeout: "5s"
requiredCapabilities: [capability.dmn.evaluate]
- source: { type: bpmn.serviceTask, ref: Task_EvaluateOwnershipDmn }
targetId: agent.dmn_evaluator
mode: autonomous
contract:
input:
- { name: package_id, type: string, required: true }
- { name: decision_id, type: string, required: true }
- { name: inputs, type: object, required: true }
output:
- { name: output, type: object }
- { name: hit_rule_ids, type: array }
- { name: hit_policy, type: string }
timeout: "5s"
requiredCapabilities: [capability.dmn.evaluate]
- source: { type: bpmn.serviceTask, ref: Task_EvaluateRoutingDmn }
targetId: agent.dmn_evaluator
mode: autonomous
contract:
input:
- { name: package_id, type: string, required: true }
- { name: decision_id, type: string, required: true }
- { name: inputs, type: object, required: true }
output:
- { name: output, type: object }
- { name: hit_rule_ids, type: array }
- { name: hit_policy, type: string }
timeout: "5s"
requiredCapabilities: [capability.dmn.evaluate]
- source: { type: bpmn.serviceTask, ref: Task_UpdateIncident }
targetId: agent.incident_updater
mode: autonomous
contract:
input:
- { name: case_id, type: string, required: true }
- { name: patch, type: object, required: true }
- { name: status, type: string }
- { name: reason, type: string }
output:
- { name: case_id, type: string }
- { name: new_status, type: string }
- { name: success, type: boolean }
- { name: event_ids, type: array }
timeout: "10s"
requiredCapabilities: [capability.incident.update]
- source: { type: bpmn.serviceTask, ref: Task_DraftResponse }
targetId: agent.response_drafter
mode: supervised
contract:
input:
- { name: case_id, type: string, required: true }
- { name: locale, type: string }
- { name: what_happened, type: string, required: true }
- { name: eta_minutes, type: number }
output:
- { name: subject_lv, type: string }
- { name: subject_en, type: string }
- { name: body_lv, type: string }
- { name: body_en, type: string }
- { name: locale, type: string }
timeout: "60s"
requiredCapabilities: [capability.ai.draft_response]
- source: { type: bpmn.serviceTask, ref: Task_EmitEvent }
targetId: agent.event_emitter
mode: autonomous
contract:
input:
- { name: case_id, type: string, required: true }
- { name: type, type: string, required: true }
- { name: payload, type: object }
- { name: actor_label, type: string }
output:
- { name: event_id, type: string }
- { name: recorded_at, type: string }
timeout: "5s"
requiredCapabilities: [capability.event.emit]

View File

@@ -0,0 +1,35 @@
kind: uapf.test.bpmn
target: bpmn/incident-triage.bpmn
process_id: Process_IncidentTriage
name: triage-link-down
description: |
End-to-end happy path: a Zabbix link-down event arrives, classifies as
network.routing, gets prioritised P1 by the priority DMN, owned by LVRTC,
routed to noc-l2, the case transitions to triaged, a Latvian customer
draft is proposed, and a 'routed' event is emitted.
input:
signal_id: "11111111-1111-1111-1111-111111111111"
payload:
title: "Link down on edge router rtr-r1"
host: "rtr-r1.lvrtc.lv"
severity: "high"
description: "BGP session also flapping"
expected_steps_completed:
- Task_NormalizeSignal
- Task_ClassifyIncident
- Task_SuggestPriority
- Task_EvaluatePriorityDmn
- Task_EvaluateOwnershipDmn
- Task_EvaluateRoutingDmn
- Task_UpdateIncident
- Task_DraftResponse
- Task_EmitEvent
expected_outputs:
taxonomy_code: "network.routing"
priority: "P1"
ownership: "lvrtc"
group_slug: "noc-l2"
new_status: "triaged"

79
uapf.yaml Normal file
View File

@@ -0,0 +1,79 @@
kind: uapf.package
id: lv.itsm.incident.triage
name: LVRTC Incident Triage
description: |
Level-4 UAPF process for triaging IT-infrastructure incidents at LVRTC.
Six BPMN service tasks invoke the UAPF-IP capabilities intake.normalize@1,
ai.classify@1, ai.suggest_priority@1, ai.draft_response@1,
incident.update@1 and event.emit@1. Three DMN decision tables encode the
deterministic rules previously hidden inside the host: priority maps
severity x service-tier x AI-suggestion x classification onto P1-P4;
ownership decides whether the case stays inside LVRTC or hands off to an
external partner; routing picks the expert group (helpdesk-l1, noc-l1/l2,
soc-l2, facility-l2, platform-l2, external-handoff).
Classification, priority suggestion and customer response drafting are AI
steps; the rules that decide *what* the AI proposes versus *who* handles
it live in versioned DMN, not Python. Drafted customer responses are
produced in both Latvian and English and require human approval before
sending (governed by Algorithm Card algo.incident_triage.draft_response).
v1.0.0 targets UAPF v2.5.0: algorithm cards carry embedded `tests`
arrays (>=2 per card) per chapter 13.16; BPMN service tasks carry
uapf24:algorithmCardRef attributes; resource targets are dispatch
endpoints only.
level: 4
version: "1.0.0"
requires_capabilities:
- intake.normalize@1+
- ai.classify@1+
- ai.suggest_priority@1+
- ai.draft_response@1+
- dmn.evaluate@1+
- incident.update@1+
- event.emit@1+
profiles_supported:
- uapf-ip-orchestrated
guardrails: resources/guardrails.yaml
includes: []
dependencies: {}
cornerstones:
bpmn: true
dmn: true
cmmn: false
resources: true
paths:
bpmn: bpmn
dmn: dmn
resources: resources
metadata: metadata
exposure:
mcp:
enabled: true
runnable: true
exposedEntrypoints:
- "Process_IncidentTriage"
exposedArtifacts:
- manifest
- bpmn
- dmn
- docs
owners:
- type: team
id: lvrtc
contact: incident-mgmt@lvrtc.lv
- type: team
id: openitsm-stewards
contact: stewards@openitsm.algomation.io
lifecycle: draft