From 393651ad7b6b35499d8a9a098b6e7d9071438fea Mon Sep 17 00:00:00 2001 From: AddisonTech Date: Wed, 21 Oct 2026 14:37:21 +0000 Subject: [PATCH 1/3] Pin the triage agent to 0.3.1 with its eval results Pins to 7eb8aff, main just after the 0.3.1 merge, which carries the committed Haiku eval results. The rebuilt outage fixtures differ only in the agent version and run timings. --- backend/requirements.txt | 2 +- backend/tests/test_triage_api.py | 4 ++-- frontend/src/test/triage-grading.json | 14 +++++++------- frontend/src/test/triage-outage-utc.json | 6 +++--- frontend/src/test/triage-outage.json | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index f5a937a..4f3a975 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,4 +1,4 @@ fastapi==0.111.0 uvicorn[standard]==0.29.0 pydantic==2.14.1 -bms-alarm-triage-agent @ git+https://github.com/AddisonTech/bms-alarm-triage-agent@cc5f707f329a20862dbb19318e6d428c8162bcfd +bms-alarm-triage-agent @ git+https://github.com/AddisonTech/bms-alarm-triage-agent@7eb8aff82eb442ba92ece3d15c18bff7dabe904c diff --git a/backend/tests/test_triage_api.py b/backend/tests/test_triage_api.py index a87aeef..4ed5a64 100644 --- a/backend/tests/test_triage_api.py +++ b/backend/tests/test_triage_api.py @@ -21,7 +21,7 @@ def test_export_returns_the_journal_format(client): assert res.headers["content-disposition"].startswith('attachment; filename="alarm-journal-') doc = res.json() assert doc["format"] == "bms-triage-journal" and doc["version"] == 1 - assert doc["source"] == "power-dashboard 1.3.0" + assert doc["source"] == "power-dashboard 1.3.1" parsed = agent_journal.parse(doc) assert parsed.points and parsed.series @@ -129,7 +129,7 @@ def test_the_agent_is_pinned_to_one_release(): def test_the_installed_agent_is_0_3(): import bms_alarm_triage - assert bms_alarm_triage.__version__ == "0.3.0" + assert bms_alarm_triage.__version__ == "0.3.1" @pytest.mark.parametrize("zone_name", ["America/New_York", "UTC"]) diff --git a/frontend/src/test/triage-grading.json b/frontend/src/test/triage-grading.json index c2c986d..941c4ba 100644 --- a/frontend/src/test/triage-grading.json +++ b/frontend/src/test/triage-grading.json @@ -1,10 +1,10 @@ { - "agent_version": "0.3.0", + "agent_version": "0.3.1", "writer": "template", "writer_name": "template, no model", - "date": "2026-10-20", + "date": "2026-10-21", "cases_run": 58, - "cases_passed": 55, + "cases_passed": 58, "graders": [ { "name": "grounding", @@ -16,16 +16,16 @@ { "name": "root_cause", "what": "The write-up names the right cause, says what is wrong with it from the evidence, and never turns a consequence into the cause. With no cause in the evidence, it names none.", - "passed": 38, + "passed": 40, "applicable": 40, - "rate": 0.95 + "rate": 1.0 }, { "name": "escalation", "what": "What should escalate gets a card and nothing else does, and no card talks its own escalation down as nuisance or nothing to act on.", - "passed": 55, + "passed": 58, "applicable": 58, - "rate": 0.9483 + "rate": 1.0 }, { "name": "uncertainty", diff --git a/frontend/src/test/triage-outage-utc.json b/frontend/src/test/triage-outage-utc.json index 4f3c091..64d98cf 100644 --- a/frontend/src/test/triage-outage-utc.json +++ b/frontend/src/test/triage-outage-utc.json @@ -13,7 +13,7 @@ } }, "triage": { - "agent_version": "0.3.0", + "agent_version": "0.3.1", "time_zone": { "name": "UTC", "label": "UTC" @@ -1957,7 +1957,7 @@ ], "nuisance": [], "unresolved": [], - "elapsed_s": 0.054, + "elapsed_s": 0.037, "writer": { "kind": "template", "name": "template, no model", @@ -1965,6 +1965,6 @@ }, "read_only": true }, - "elapsed_ms": 82.4, + "elapsed_ms": 50.4, "cached": false } diff --git a/frontend/src/test/triage-outage.json b/frontend/src/test/triage-outage.json index bcbe31c..3273912 100644 --- a/frontend/src/test/triage-outage.json +++ b/frontend/src/test/triage-outage.json @@ -13,7 +13,7 @@ } }, "triage": { - "agent_version": "0.3.0", + "agent_version": "0.3.1", "time_zone": { "name": "America/New_York", "label": "EDT" @@ -1957,7 +1957,7 @@ ], "nuisance": [], "unresolved": [], - "elapsed_s": 0.053, + "elapsed_s": 0.043, "writer": { "kind": "template", "name": "template, no model", @@ -1965,6 +1965,6 @@ }, "read_only": true }, - "elapsed_ms": 87.7, + "elapsed_ms": 61.9, "cached": false } From 71bf04e85fcdf92525cffbbf88fbe069e5f16e71 Mon Sep 17 00:00:00 2001 From: AddisonTech Date: Wed, 21 Oct 2026 14:37:21 +0000 Subject: [PATCH 2/3] Key Triage cards by point and episode start Since agent 0.3.1 one point can have two escalated episodes, so a point alone no longer makes a unique key for a card, a consequence, or a set aside entry. --- frontend/src/lib/triage.js | 8 +++++ frontend/src/lib/triage.test.js | 17 +++++++-- frontend/src/pages/Triage.jsx | 18 +++++----- frontend/src/pages/Triage.test.jsx | 58 ++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/frontend/src/lib/triage.js b/frontend/src/lib/triage.js index 30194eb..8e707c6 100644 --- a/frontend/src/lib/triage.js +++ b/frontend/src/lib/triage.js @@ -156,3 +156,11 @@ export function passRate(rate) { const pct = rate * 100 return `${Number.isInteger(pct) ? pct : pct.toFixed(1)}%` } + +// A React key for one entry. Since agent 0.3.1 one point can have two +// episodes, each its own card, so a point alone is not unique: an entry with +// a start time is keyed by point and start, and one without (the nuisance +// and unresolved lists carry none) by point and its place in the list. +export function episodeKey(entry, index) { + return entry.start ? `${entry.point}@${entry.start}` : `${entry.point}#${index}` +} diff --git a/frontend/src/lib/triage.test.js b/frontend/src/lib/triage.test.js index f9e53de..598b7cc 100644 --- a/frontend/src/lib/triage.test.js +++ b/frontend/src/lib/triage.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { - chartGeometry, dipText, eventFinished, evidenceText, floodParts, graderTitle, itemWriter, passRate, reduction, ruleLabel, - seconds, valueText, writerLabel, + chartGeometry, dipText, episodeKey, eventFinished, evidenceText, floodParts, graderTitle, itemWriter, passRate, reduction, + ruleLabel, seconds, valueText, writerLabel, } from './triage' import outage from '../test/triage-outage.json' import outageUtc from '../test/triage-outage-utc.json' @@ -111,3 +111,16 @@ describe('grading', () => { }) }) +describe('episodeKey', () => { + it('tells two episodes on one point apart by their start', () => { + const a = episodeKey({ point: 'SIM-DC/UPS-B/ONBATT', start: '2026-09-21T14:18:20+00:00' }, 0) + const b = episodeKey({ point: 'SIM-DC/UPS-B/ONBATT', start: '2026-09-21T14:28:20+00:00' }, 1) + expect(a).not.toBe(b) + expect(a).toBe('SIM-DC/UPS-B/ONBATT@2026-09-21T14:18:20+00:00') + }) + + it('falls back to the place in the list when there is no start', () => { + expect(episodeKey({ point: 'P' }, 0)).not.toBe(episodeKey({ point: 'P' }, 1)) + }) +}) + diff --git a/frontend/src/pages/Triage.jsx b/frontend/src/pages/Triage.jsx index c3d5686..32ce133 100644 --- a/frontend/src/pages/Triage.jsx +++ b/frontend/src/pages/Triage.jsx @@ -5,8 +5,8 @@ import { api, exportJournalUrl } from '../services/api' import { PriorityBadge } from '../components/Badges' import { clockIn, dateTimeIn, viewerZone, zoneLabel } from '../lib/zone' import { - capitalize, chartGeometry, evidenceText, floodParts, graderTitle, itemWriter, passRate, reduction, ruleLabel, - writerLabel, + capitalize, chartGeometry, episodeKey, evidenceText, floodParts, graderTitle, itemWriter, passRate, reduction, + ruleLabel, writerLabel, } from '../lib/triage' const CHART_W = 320 @@ -38,8 +38,8 @@ function Consequences({ items, zone }) { {open && (