TenantAtlas/docs/product/spec-candidates.md

256 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Spec Candidates
> Concrete future specs waiting for prioritization.
> Each entry has enough structure to become a real spec when the time comes.
>
> **Flow**: Inbox → Qualified → Planned → Spec created → removed from this file
**Last reviewed**: 2026-03-15
---
## Inbox
> Ungefiltert. Kurze Notiz reicht. Wöchentlich sichten.
- Dashboard trend visualizations (sparklines, compliance gauge, drift-over-time chart)
- Dashboard "Needs Attention" should be visually louder (alert color, icon, severity weighting)
- Dashboard enterprise polish: severity-weighted drift table, actionable alert buttons, progressive disclosure (demoted from Qualified — needs bounded scope before re-qualifying)
- Operations table should show duration + affected policy count
- Density control / comfortable view toggle for admin tables
- Inventory landing page may be redundant — consider pure navigation section
- Settings change history → explainable change tracking
- Workspace chooser v2: search, sort, favorites, pins, environment badges, last activity
---
## Qualified
> Problem + Nutzen klar. Scope noch offen. Braucht noch Priorisierung.
### Queued Execution Reauthorization and Scope Continuity
- **Type**: hardening
- **Source**: architecture audit 2026-03-15
- **Problem**: Queued work still relies too heavily on dispatch-time actor and tenant state. Execution-time scope continuity and capability revalidation are not yet hardened as a canonical backend contract.
- **Why it matters**: This is a backend trust-gap on the mutation path. It creates the class of failure where a UI action was valid at dispatch time but the queued execution is no longer legitimate when it runs.
- **Proposed direction**: Define execution-time reauthorization, tenant operability rechecks, denial semantics, and audit visibility as a dedicated spec instead of scattering local `authorize()` patches.
- **Dependencies**: Existing operations semantics, audit log foundation, queued job execution paths
- **Priority**: high
### Tenant-Owned Query Canon and Wrong-Tenant Guards
- **Type**: hardening
- **Source**: architecture audit 2026-03-15
- **Problem**: Tenant isolation exists, but many reads still depend on local `tenant_id` filters instead of a reusable canonical query path. Wrong-tenant regression coverage is also uneven.
- **Why it matters**: This is isolation drift. Repeated local filtering increases the chance of future cross-tenant mistakes across resources, widgets, actions, and detail pages.
- **Proposed direction**: Define a canonical query entry pattern for tenant-owned models plus a required wrong-tenant regression matrix for tier-1 surfaces.
- **Dependencies**: Canonical tenant context work in Specs 135 and 136
- **Priority**: high
### Livewire Context Locking and Trusted-State Reduction
- **Type**: hardening
- **Source**: architecture audit 2026-03-15
- **Problem**: Complex Livewire and Filament flows still expose ownership-relevant context in public component state without one explicit repo-wide hardening standard.
- **Why it matters**: This is a trust-boundary problem. Even without a known exploit, mutable client-visible identifiers and workflow context make future authorization and isolation mistakes more likely.
- **Proposed direction**: Define a reusable hardening pattern for locked identifiers, server-derived workflow truth, and forged-state regression tests on tier-1 component families.
- **Dependencies**: Managed tenant onboarding draft identity (Spec 138), onboarding lifecycle checkpoint work (Spec 140)
- **Priority**: medium
### Exception / Risk-Acceptance Workflow for Findings
- **Type**: feature
- **Source**: HANDOVER gap analysis, Spec 111 follow-up
- **Problem**: Finding has a `risk_accepted` status value but no formal exception lifecycle. Today, accepting risk is a status transition — there is no dedicated entity to record who accepted the risk, why, under what conditions, or when the acceptance expires. No approval workflow, no expiry/renewal semantics, no structured justification. Auditors cannot answer "who accepted this risk, what was the justification, and is it still valid?"
- **Why it matters**: Enterprise compliance frameworks (ISO 27001, SOC 2, CIS) require documented, time-bounded risk acceptance with clear ownership. A bare status flag does not meet this bar. Without a formal exception lifecycle, risk acceptance becomes invisible to audit trails and impossible to govern at scale.
- **Proposed direction**: First-class `RiskException` (or `FindingException`) entity linked to Finding, with: justification text, owner (actor), `accepted_at`, `expires_at`, renewal/reminder semantics, optional linkage to verification checks or related findings. Approval flow with capability-gated acceptance. Audit trail for creation, renewal, expiry, and revocation. Findings in `risk_accepted` state without a valid exception should surface as governance warnings.
- **Dependencies**: Findings workflow (Spec 111) complete, audit log foundation (Spec 134)
- **Priority**: high
### Evidence Domain Foundation
- **Type**: feature
- **Source**: HANDOVER gap, R2 theme completion
- **Problem**: Review pack export (Spec 109) and permission posture reports (104/105) exist as separate output artifacts. There is no first-class evidence domain model that curates, bundles, and tracks these artifacts as a coherent compliance deliverable for external audit submission.
- **Why it matters**: Enterprise customers need a single, versioned, auditor-ready package — not a collection of separate exports assembled manually. The gap is not export packaging (Spec 109 handles that); it is the absence of an evidence domain layer that owns curation, completeness tracking, and audit-trail linkage.
- **Proposed direction**: Evidence domain model with curated artifact references (review packs, posture reports, findings summaries, baseline governance snapshots). Completeness metadata. Immutable snapshots with generation timestamp and actor. Not a re-implementation of export — a higher-order assembly layer.
- **Dependencies**: Review pack export (109), permission posture (104/105)
- **Priority**: high
### Policy Lifecycle / Ghost Policies (Spec 900 refresh)
- **Type**: feature
- **Source**: Spec 900 draft (2025-12-22), HANDOVER risk #9
- **Problem**: Policies deleted in Intune remain in TenantAtlas indefinitely. No deletion indicators. Backup items reference "ghost" policies.
- **Why it matters**: Data integrity, user confusion, backup reliability
- **Proposed direction**: Soft delete detection during sync, auto-restore on reappear, "Deleted" badge, restore from backup. Draft in Spec 900.
- **Dependencies**: Inventory sync stable
- **Priority**: medium
### Schema-driven Secret Classification
- **Type**: hardening
- **Source**: Spec 120 deferred follow-up
- **Problem**: Secret redaction currently uses pattern-based detection. A schema-driven approach via `GraphContractRegistry` metadata would be more reliable.
- **Why it matters**: Reduces false negatives in secret redaction
- **Proposed direction**: Central classifier in `GraphContractRegistry`, regression corpus
- **Dependencies**: Secret redaction (120) stable, registry completeness (095)
- **Priority**: medium
### Cross-Tenant Compare & Promotion
- **Type**: feature
- **Source**: Spec 043 draft, 0800-future-features
- **Problem**: No way to compare policies between tenants or promote configurations from staging to production.
- **Why it matters**: Core MSP/enterprise workflow. Identified as top revenue lever in brainstorming.
- **Proposed direction**: Compare/diff UI, group/scope-tag mapping, promotion plan (preview → dry-run → cutover → verify)
- **Dependencies**: Inventory sync, backup/restore mature
- **Priority**: medium (high value, high effort)
### System Console Scope Hardening
- **Type**: hardening
- **Source**: Spec 113/114 follow-up
- **Problem**: The system console (`/system`) needs a clear cross-workspace entitlement model. Current platform capabilities (Spec 114) define per-surface access, but cross-workspace query authorization and scope isolation for platform operators are not yet hardened as a standalone contract.
- **Why it matters**: Platform operators acting across workspaces need tight scope boundaries to prevent accidental cross-workspace data exposure in troubleshooting and monitoring flows.
- **Proposed direction**: Formalize cross-workspace query authorization model, scope isolation rules for platform operator sessions, and regression coverage for wrong-workspace access in system console surfaces.
- **Dependencies**: System console (114) stable, canonical tenant context (Specs 135/136)
- **Priority**: low
### System Console Multi-Workspace Operator UX
- **Type**: feature
- **Source**: Spec 113 deferred
- **Problem**: System console (`/system`) currently can't select/filter across workspaces for platform operators. Triage and monitoring require workspace-by-workspace navigation.
- **Why it matters**: Platform ops need cross-workspace visibility for troubleshooting and monitoring at scale.
- **Proposed direction**: Workspace selector/filter in system console views, cross-workspace run aggregation, unified triage entry point.
- **Dependencies**: System console (114) stable, System Console Scope Hardening
- **Priority**: low
### Operations Naming Harmonization Across Run Types, Catalog, UI, and Audit
- **Type**: hardening
- **Source**: coding discovery, operations UX consistency review
- **Why it matters**: Strategically important for enterprise UX, auditability, and long-term platform consistency. `OperationRun` is becoming a cross-domain execution and monitoring backbone, and the current naming drift will get more expensive as new run types and provider domains are added. This should reduce future naming drift, but it is not a blocker-critical refactor and should not be pulled in as a side quest during small UI changes.
- **Problem**: Naming around operations appears historically grown and not consistent enough across `OperationRunType` values, visible run labels, `OperationCatalog` mappings, notifications, audit events, filters, badges, and related UI copy. Internal type names and operator-facing language are not cleanly separated, domain/object/verb ordering is uneven, and small UX fixes risk reinforcing an already inconsistent scheme. If left as-is, new run types for baseline, review, alerts, and additional provider domains will extend the inconsistency instead of converging it.
- **Desired outcome**: A later spec should define a clear naming standard for `OperationRunType`, establish an explicit distinction between internal type identifiers and operator-facing labels, and align terminology across runs, notifications, audit text, monitoring views, and operations UI. New run types should have documented naming rules so they can be added without re-opening the vocabulary debate.
- **In scope**: Inventory of current operation-related naming surfaces; naming taxonomy for internal identifiers versus visible operator language; conventions for verb/object/domain ordering; alignment rules for `OperationCatalog`, run labels, notifications, audit events, filters, badges, and monitoring UI; forward-looking rules for adding new run types and provider/domain families; a pragmatic migration plan that minimizes churn and preserves audit clarity.
- **Out of scope**: Opportunistic mass-refactors during unrelated feature work; immediate renaming of all historical values without a compatibility plan; using a small UI wording issue such as "Sync from Intune" versus "Sync policies" as justification for broad churn; a full operations-domain rearchitecture unless later analysis proves it necessary.
- **Trigger / Best time to do this**: Best tackled when multiple new run types are about to land, when `OperationCatalog` / monitoring / operations hub work is already active, when new domains such as Entra or Teams are being integrated, or when a broader UI naming constitution is ready to be enforced technically. This is a good candidate for a planned cleanup window, not an ad hoc refactor.
- **Risks if ignored**: Continued terminology drift across UI and audit layers, higher cognitive load for operators, weaker enterprise polish, more brittle label mapping, and more expensive cleanup once additional domains and execution types are established. Audit/event language may diverge further from monitoring language, making cross-surface reasoning harder.
- **Suggested direction**: Define stable internal run-type identifiers separately from visible operator labels. Standardize a single naming grammar for operation concepts, including when to lead with verb, object, or domain, and when provider-specific wording is allowed. Apply changes incrementally with compatibility-minded mapping rather than a brachial rename of every historical string. Prefer a staged migration that first defines rules and mapping layers, then updates high-value operator surfaces, and only later addresses legacy internals where justified.
- **Readiness level**: Qualified and strategically important, but intentionally deferred. This should be specified before substantially more run types and provider domains are introduced, yet it should not become an immediate side-track or be bundled into minor UI wording fixes.
- **Candidate quality**:
- Clearly identified cross-cutting problem with architectural and UX impact
- Strong future-facing trigger conditions instead of vague "sometime later"
- Explicit boundaries to prevent opportunistic churn
- Concrete desired outcome without overdesigning the solution
- Easy to promote into a full spec once operations-domain work is prioritized
### Provider Connection Resolution Normalization
- **Type**: hardening
- **Source**: architecture audit provider connection resolution analysis
- **Problem**: The codebase has a dual-resolution model for provider connections. Gen 2 jobs (`ProviderInventorySyncJob`, `ProviderConnectionHealthCheckJob`, `ProviderComplianceSnapshotJob`) receive an explicit `providerConnectionId` and pass it through the `ProviderOperationStartGate`. Gen 1 jobs (`ExecuteRestoreRunJob`, `EntraGroupSyncJob`, `SyncRoleDefinitionsJob`, policy sync jobs, etc.) do NOT — their called services resolve the default connection at runtime via `MicrosoftGraphOptionsResolver::resolveForTenant()` or internal `resolveProviderConnection()` methods. This creates non-deterministic execution: a job dispatched against one connection may silently execute against a different one if the default changes between dispatch and execution. ~20 services use the Gen 1 implicit resolution pattern.
- **Why it matters**: Non-deterministic credential binding is a correctness and audit gap. Enterprise customers need to know exactly which connection identity was used for every Graph API call. The implicit pattern also prevents connection-scoped rate limiting, error attribution, and consent-scope validation. This is the foundational refactor that unblocks all other provider connection improvements.
- **Proposed direction**:
- Refactor all Gen 1 services to accept an explicit `ProviderConnection` (or `providerConnectionId`) parameter instead of resolving default internally
- Update all Gen 1 jobs to accept `providerConnectionId` at dispatch time (resolved at the UI/controller layer via `ProviderOperationStartGate` or equivalent)
- Deprecate `MicrosoftGraphOptionsResolver` — callers should use `ProviderGateway::graphOptions($connection)` directly
- Ensure `provider_connection_id` is recorded in every `OperationRun` context and audit event
- Standardize error handling: all resolution failures produce `ProviderConnectionResolution::blocked()` with structured `ProviderReasonCodes`, not mixed exceptions (`ProviderConfigurationRequiredException`, `RuntimeException`, `InvalidArgumentException`)
- **Known affected services** (Gen 1 / implicit resolution): `RestoreService` (line 2913 internal `resolveProviderConnection()`), `PolicySyncService` (lines 58, 450), `PolicySnapshotService` (line 752), `RbacHealthService` (line 192), `InventorySyncService` (line 730 internal `resolveProviderConnection()`), `EntraGroupSyncService`, `RoleDefinitionsSyncService`, `EntraAdminRolesReportService`, `AssignmentBackupService`, `AssignmentRestoreService`, `ScopeTagResolver`, `TenantPermissionService`, `VersionService`, `ConfigurationPolicyTemplateResolver`, `FoundationSnapshotService`, `FoundationMappingService`, `RestoreRiskChecker`, `PolicyCaptureOrchestrator`, `AssignmentFilterResolver`, `RbacOnboardingService`, `TenantConfigService`
- **Known affected jobs** (Gen 1 / no explicit connectionId): `ExecuteRestoreRunJob`, `EntraGroupSyncJob`, `SyncRoleDefinitionsJob`, `SyncEntraAdminRolesJob`, plus any job that calls a Gen 1 service
- **Gen 2 reference implementations** (correct pattern): `ProviderInventorySyncJob`, `ProviderConnectionHealthCheckJob`, `ProviderComplianceSnapshotJob` — all receive `providerConnectionId`, pass through `ProviderOperationStartGate`, lock row, create `OperationRun` with connection in context
- **Key architecture components**:
- `ProviderConnectionResolver` — correct, keep as-is. `resolveDefault()` returns `ProviderConnectionResolution` value object
- `ProviderOperationStartGate` — canonical dispatch-time gate, correct Gen 2 pattern. Handles 3 operation types: `provider.connection.check`, `inventory_sync`, `compliance.snapshot`
- `MicrosoftGraphOptionsResolver` — legacy bridge (32 lines), target for deprecation. Calls `resolveDefault()` internally, hides connection identity
- `ProviderGateway` — lower-level primitive, builds graph options from explicit connection. Correct, keep as-is
- `ProviderIdentityResolver` — resolves identity (platform vs dedicated) from connection. Correct, keep as-is
- Partial unique index on `provider_connections`: `(tenant_id, provider) WHERE is_default = true`
- **Out of scope**: UX label changes, UI banners, legacy credential field removal (those are separate candidates below)
- **Dependencies**: None — this is the foundational refactor
- **Related specs**: Spec 081 (Tenant credential migration CI guardrails), Spec 088 (provider connection model), Spec 089 (provider gateway), Spec 137 (data-layer provider prep)
- **Priority**: high
### Provider Connection UX Clarity
- **Type**: polish
- **Source**: architecture audit provider connection resolution analysis
- **Problem**: The operator-facing language and information architecture around provider connections creates confusion about why a "default" connection is required, what happens when it's missing, and when actions are tenant-wide vs connection-scoped. Specific issues: (1) "Set as Default" is misleading — it implies preference, but the connection is actually the canonical operational identity; (2) missing-default errors surface as blocked `OperationRun` records or exceptions, but there is no proactive banner/hint on the tenant or connection pages; (3) action labels don't distinguish tenant-wide operations (verify, sync) from connection-scoped operations (health check, test); (4) the singleton auto-promotion (first connection becomes default automatically) is invisible — operators don't understand why their first connection was special.
- **Why it matters**: Reduces support friction and operator confusion. Enterprise operators managing multiple tenants need clear, predictable language about connection lifecycle. The current UX makes the correct architecture feel like a bug ("why do I need a default?").
- **Proposed direction**:
- Rename "Set as Default" → "Promote to Primary" (or "Set as Primary Connection") across all surfaces
- Add a missing-primary-connection banner on tenant detail / connection list when no default exists — with a direct "Promote" action
- Distinguish action labels: tenant-wide actions ("Sync Tenant", "Verify Tenant") vs connection-scoped actions ("Check Connection Health", "Test Connection")
- Improve blocked-notification copy: instead of generic "provider connection required", show "No primary connection configured for [Provider]. Promote a connection to continue."
- Show a transient success notification when auto-promotion happens on first connection creation ("This connection was automatically set as primary because it's the first for this provider")
- Consider an info tooltip or help text explaining the primary connection concept on the connection resource pages
- **Key surfaces to update**: `ProviderConnectionResource` (row actions, header actions, table empty state), `TenantResource` (verify action, connection tab), onboarding wizard consent step, `ProviderNextStepsRegistry` remediation links, notification templates for blocked operations
- **Auto-default creation locations** (4 places, need UX feedback): `CreateProviderConnection` action, `TenantOnboardingController`, `AdminConsentCallbackController`, `ManagedTenantOnboardingWizard`
- **Out of scope**: Backend resolution refactoring (that's the normalization candidate above), legacy field removal
- **Dependencies**: Soft dependency on "Provider Connection Resolution Normalization" — UX improvements are more coherent when the backend consistently uses explicit connections, but many label/banner changes can proceed independently
- **Related specs**: Spec 061 (provider connection UX), Spec 088 (provider connection model)
- **Priority**: medium
### Provider Connection Legacy Cleanup
- **Type**: hardening
- **Source**: architecture audit provider connection resolution analysis
- **Problem**: After normalization is complete, several legacy artifacts remain: (1) `MicrosoftGraphOptionsResolver` — a 32-line convenience bridge that exists only because ~20 services haven't been updated to use explicit connections; (2) service-internal `resolveProviderConnection()` methods in `RestoreService` (line 2913), `InventorySyncService` (line 730), and similar — these are local resolution logic that should not exist once services receive explicit connections; (3) `Tenant` model legacy credential accessors (`app_client_id`, `app_client_secret` fields) — `graphOptions()` already throws `BadMethodCallException`, but the fields and accessors remain; (4) `migration_review_required` flag on `ProviderConnection` — used during the credential migration from tenant-level to connection-level, should be retired once all tenants are migrated.
- **Why it matters**: Dead code increases cognitive load and creates false affordances. New developers may use `MicrosoftGraphOptionsResolver` or internal resolution methods thinking they're the correct pattern. Legacy credential fields on `Tenant` suggest credentials still live there. Cleaning up after normalization makes the correct architecture self-documenting.
- **Proposed direction**:
- Remove `MicrosoftGraphOptionsResolver` class entirely (after normalization ensures zero callers)
- Remove all service-internal `resolveProviderConnection()` / `resolveDefault()` methods
- Remove legacy credential fields from `Tenant` model (migration to drop columns, update factory, update tests)
- Evaluate `migration_review_required` — if all tenants have migrated, remove the flag and related UI (banner, filter)
- Update CI guardrails: `NoLegacyTenantGraphOptionsTest` and `NoTenantCredentialRuntimeReadsSpec081Test` can be simplified or removed once the code they guard against is gone
- Verify no seeders, factories, or test helpers reference legacy patterns
- **Out of scope**: Any new features — this is pure cleanup
- **Dependencies**: Hard dependency on "Provider Connection Resolution Normalization" — cleanup cannot proceed until all callers are migrated
- **Related specs**: Spec 081 (credential migration guardrails), Spec 088 (provider connection model), Spec 137 (data-layer provider prep)
- **Priority**: medium (deferred until normalization is complete)
### Support Intake with Context (MVP)
- **Type**: feature
- **Source**: Product design, operator feedback
- **Problem**: Nutzer haben keinen strukturierten Weg, Probleme direkt aus dem Produkt zu melden. Bei technischen Fehlern fehlen Run-/Tenant-/Provider-Details; bei Access-/UX-Problemen fehlen Route-/RBAC-Kontext. Folge: ineffiziente Support-Schleifen und Rückfragen. Ein vollwertiges Ticketsystem ist falsch priorisiert.
- **Why it matters**: Reduziert Support-Reibung, erhöht Erfassungsqualität, steigert wahrgenommene Produktreife. Schafft typed intake layer für spätere Webhook-/PSA-/Ticketing-Erweiterungen, ohne jetzt ein Helpdesk einzuführen.
- **Proposed direction**: Neues `SupportRequest`-Modell (kein Ticket/Case) mit `source_type` (operation_run, provider_connection, access_denied, generic) und `issue_kind` (technical_problem, access_problem, ux_feedback, other). Drei Entry Paths: (1) Context-bound aus failed OperationRun, (2) Access-Denied/403-Kontext, (3) generischer Feedback-Einstieg (User-Menü). Automatischer Context-Snapshot per `SupportRequestContextBuilder` je source_type. Persistierung vor Delivery. E-Mail-Delivery an konfigurierte Support-Adresse. Fingerprint-basierter Spam-Guard. Audit-Events. RBAC via `support.request.create` Capability. Scope-Isolation. Secret-Redaction in context_jsonb.
- **Dependencies**: OperationRun-Domain stabil, RBAC/Capability-System (066+), Workspace-/Tenant-Scoping
- **Priority**: medium
---
## Covered / Absorbed
> Candidates that were previously qualified but are now substantially covered by existing specs, or were umbrella labels whose children have been promoted individually.
### Governance Architecture Hardening Wave (umbrella — dissolved)
- **Original source**: architecture audit 2026-03-15
- **Status**: Dissolved into individual candidates. The four children are now tracked separately in Qualified: Queued Execution Reauthorization, Tenant-Owned Query Canon, Livewire Context Locking. The fourth child (Findings Workflow Enforcement) is absorbed below.
- **Reference**: [../audits/tenantpilot-architecture-audit-constitution.md](../audits/tenantpilot-architecture-audit-constitution.md), [../audits/2026-03-15-audit-spec-candidates.md](../audits/2026-03-15-audit-spec-candidates.md)
### Findings Workflow Enforcement and Audit Backstop
- **Original source**: architecture audit 2026-03-15, candidate C
- **Status**: Largely absorbed by Spec 111 (findings workflow v2) which defines transition enforcement, timestamp tracking, reason validation, and audit logging. The remaining architectural enforcement gap (model-level bypass prevention) is a hardening follow-up to Spec 111, not a standalone spec-sized problem. Re-qualify only if enforcement softness surfaces as a concrete regression or audit finding.
### Workspace Chooser v2
- **Original source**: Spec 107 deferred backlog
- **Status**: Workspace chooser v1 is covered by Spec 107 + semantic fix in Spec 121. The v2 polish items (search, sort, favorites, pins, environment badges) remain tracked as an Inbox entry. Not qualified as a standalone spec candidate at current priority.
### Dashboard Polish (Enterprise-grade)
- **Original source**: Product review 2026-03-08
- **Status**: Core tenant dashboard is covered by Spec 058 (drift-first KPIs, needs attention, recent lists). Workspace-level landing is in progress via Spec 129. The remaining polish items (sparklines, compliance gauge, progressive disclosure) are tracked in Inbox. This was demoted because the candidate lacked a bounded spec scope — it read as a wish list rather than a specifiable problem.
---
## Planned
> Ready for spec creation. Waiting for slot in active work.
*(empty — move items here when prioritized for next sprint)*
---
## Template
```md
### Title
- **Type**: feature | polish | hardening | bug | research
- **Source**: chat | audit | coding discovery | customer feedback | spec N follow-up
- **Problem**:
- **Why it matters**:
- **Proposed direction**:
- **Dependencies**:
- **Priority**: low | medium | high
```