feat(tickets): open, comment and resolve tickets from the UI and MCP; known issues on alerts and anomalies (#6351, #6352)
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 1m37s
CI / integration (push) Successful in 1m51s
CI / publish (push) Successful in 1m16s

A ticket records a known problem: title, area, hosts, subject, description,
links to alert rules and anomalies, and a log of comments, edits, resolving
and reopening. Opening, resolving (with the resolution as the note) and
reopening go on the timeline; every step is audited, and the audit rows stay
off the timeline.

An alert or anomaly an open ticket covers (by link, by subject, or a
host-wide ticket on the host itself) shows a Known issue badge, and offers
"Open a ticket" prefilled from its rule or anomaly when none does. Hosts,
monitors and containers get a Known issues card.

MCP: list_tickets, get_ticket (viewer); open_ticket, update_ticket,
comment_ticket, resolve_ticket, reopen_ticket (operator). list_alerts and
list_anomalies carry known_issue; get_overview, get_host, get_monitor and
get_container carry open_tickets. Also update_change and delete_change for
changes recorded by hand (author or admin).

Tickets are left out of backups, like the timeline (2026-10-15 ruling).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 22:06:43 -04:00
co-authored by Claude Opus 5.5
parent 1ea86c0330
commit 2a561210c3
40 changed files with 1786 additions and 35 deletions
+55
View File
@@ -0,0 +1,55 @@
# Tickets
A ticket records a known problem that takes more than a moment to fix: a disk failing slowly, a service that keeps restarting, a backup that times out at night. It says what is wrong, what has been tried and, once resolved, what fixed it. Acknowledging an alert only quiets it. A ticket keeps the story, and while it is open the alerts and anomalies it covers read as a **known issue**.
Open the list from **Tickets** in the sidebar. A host's page, a monitor's page and a container's page each show a **Known issues** card with their open tickets. MCP clients use `list_tickets`, `get_ticket`, `open_ticket`, `update_ticket`, `comment_ticket`, `resolve_ticket` and `reopen_ticket`.
**Source:** `steward/tickets/` (`service.py` for every write, `routes.py` for the pages), `steward/mcp/tools/tickets.py`
**Tables:** `tickets`, `ticket_links`, `ticket_notes` (`steward/models/tickets.py`)
---
## What a ticket holds
| Field | |
|---|---|
| Number | `#12`, in order of opening |
| Title | What is wrong, one line (up to 200 characters) |
| Area | One of the timeline's areas (Settings → Timeline) |
| Hosts | The hosts it is on, by name; none for a problem not on any one host |
| Subject | What it is about when that is not a host as a whole: an alert rule's resource (`web-1:/`), a container (`web-1/nginx`), a monitor's name |
| Description | What is seen, since when, what has been tried |
| Links | Alert rules and metric anomalies it covers |
| Log | Opened, comments, edits (which fields changed, what was linked or unlinked), resolved, reopened, each with who and when |
A ticket is **open** or **resolved**. Resolving one needs a resolution: what fixed it, or why it no longer matters. Reopening clears it, with an optional reason.
## Opening one
- **From an alert:** **Open a ticket** on an active alert (Alerts page) fills in the title, area, hosts and subject from the rule, and links the rule.
- **From an anomaly:** **Open a ticket** on an anomaly to review, or one reviewed as real, does the same and links the anomaly.
- **From a page:** the **Known issues** card on a host, monitor or container opens one about it.
- **From scratch:** **Open a ticket** on the Tickets page.
- **Through MCP:** `open_ticket(title, area, description, hosts, subject, alert_rules, anomalies)`.
Opening, editing, commenting, resolving and reopening need an operator or admin. Viewers read everything.
## Known issues
An alert or anomaly is a known issue while an open ticket:
- links its alert rule (or the covering group rule it came from), or the anomaly;
- names its resource as its subject; or
- is about its host as a whole (no subject), when the resource is the host itself.
The Alerts page and the anomaly lists show a **Known issue** badge with the ticket. `list_alerts` and `list_anomalies` return the same as `known_issue`. `get_overview` lists up to ten open tickets in `open_tickets` and counts them all in `counts.tickets_open`. `get_host`, `get_monitor` and `get_container` carry their own `open_tickets`.
## On the timeline
Opening, resolving and reopening a ticket each write a timeline entry under the ticket's area, on its hosts and subject (`ticket.opened`, `ticket.resolved`, `ticket.reopened`, kind Ticket). The resolution is the resolved entry's note, so the timeline keeps what fixed it. Comments and edits stay in the ticket's own log.
Every step is audited (`ticket.opened`, `ticket.edited`, `ticket.commented`, `ticket.resolved`, `ticket.reopened`). Those audit rows stay off the timeline, which holds the ticket's own entries.
## Backups
Tickets are not in Steward's backups yet, the same as the timeline itself (`steward/backup/contents.py`, `EXCLUDED`).