@miragon/pulse-analytics-connector 0.0.0-bootstrap.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +86 -3
- package/dist/bpmn-name-lookup.d.ts +72 -0
- package/dist/bpmn-name-lookup.d.ts.map +1 -0
- package/dist/bpmn-name-lookup.js +123 -0
- package/dist/bpmn-name-lookup.js.map +1 -0
- package/dist/bpmn-name-lookup.test.d.ts +2 -0
- package/dist/bpmn-name-lookup.test.d.ts.map +1 -0
- package/dist/bpmn-name-lookup.test.js +287 -0
- package/dist/bpmn-name-lookup.test.js.map +1 -0
- package/dist/bpmn-names.d.ts +38 -0
- package/dist/bpmn-names.d.ts.map +1 -0
- package/dist/bpmn-names.js +22 -0
- package/dist/bpmn-names.js.map +1 -0
- package/dist/brand-gates.test.d.ts +2 -0
- package/dist/brand-gates.test.d.ts.map +1 -0
- package/dist/brand-gates.test.js +58 -0
- package/dist/brand-gates.test.js.map +1 -0
- package/dist/builder-texts.d.ts +4 -0
- package/dist/builder-texts.d.ts.map +1 -0
- package/dist/builder-texts.js +14 -0
- package/dist/builder-texts.js.map +1 -0
- package/dist/builder-texts.test.d.ts +2 -0
- package/dist/builder-texts.test.d.ts.map +1 -0
- package/dist/builder-texts.test.js +32 -0
- package/dist/builder-texts.test.js.map +1 -0
- package/dist/definition.d.ts +3 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +151 -0
- package/dist/definition.js.map +1 -0
- package/dist/engine-ids.d.ts +42 -0
- package/dist/engine-ids.d.ts.map +1 -0
- package/dist/engine-ids.js +50 -0
- package/dist/engine-ids.js.map +1 -0
- package/dist/engine-scope.test.d.ts +2 -0
- package/dist/engine-scope.test.d.ts.map +1 -0
- package/dist/engine-scope.test.js +260 -0
- package/dist/engine-scope.test.js.map +1 -0
- package/dist/heatmap-data.d.ts +15 -0
- package/dist/heatmap-data.d.ts.map +1 -0
- package/dist/heatmap-data.js +2 -0
- package/dist/heatmap-data.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +8 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +33 -0
- package/dist/instructions.js.map +1 -0
- package/dist/messages/de.ask-ai.d.ts +9 -0
- package/dist/messages/de.ask-ai.d.ts.map +1 -0
- package/dist/messages/de.ask-ai.js +18 -0
- package/dist/messages/de.ask-ai.js.map +1 -0
- package/dist/messages/de.builder.d.ts +12 -0
- package/dist/messages/de.builder.d.ts.map +1 -0
- package/dist/messages/de.builder.js +53 -0
- package/dist/messages/de.builder.js.map +1 -0
- package/dist/messages/de.server.d.ts +8 -0
- package/dist/messages/de.server.d.ts.map +1 -0
- package/dist/messages/de.server.js +26 -0
- package/dist/messages/de.server.js.map +1 -0
- package/dist/messages/de.sweep.d.ts +10 -0
- package/dist/messages/de.sweep.d.ts.map +1 -0
- package/dist/messages/de.sweep.js +203 -0
- package/dist/messages/de.sweep.js.map +1 -0
- package/dist/messages/en.ask-ai.d.ts +25 -0
- package/dist/messages/en.ask-ai.d.ts.map +1 -0
- package/dist/messages/en.ask-ai.js +24 -0
- package/dist/messages/en.ask-ai.js.map +1 -0
- package/dist/messages/en.builder.d.ts +12 -0
- package/dist/messages/en.builder.d.ts.map +1 -0
- package/dist/messages/en.builder.js +53 -0
- package/dist/messages/en.builder.js.map +1 -0
- package/dist/messages/en.server.d.ts +8 -0
- package/dist/messages/en.server.d.ts.map +1 -0
- package/dist/messages/en.server.js +26 -0
- package/dist/messages/en.server.js.map +1 -0
- package/dist/messages/en.sweep.d.ts +11 -0
- package/dist/messages/en.sweep.d.ts.map +1 -0
- package/dist/messages/en.sweep.js +204 -0
- package/dist/messages/en.sweep.js.map +1 -0
- package/dist/messages/glossary.d.ts +26 -0
- package/dist/messages/glossary.d.ts.map +1 -0
- package/dist/messages/glossary.js +37 -0
- package/dist/messages/glossary.js.map +1 -0
- package/dist/messages/glossary.test.d.ts +2 -0
- package/dist/messages/glossary.test.d.ts.map +1 -0
- package/dist/messages/glossary.test.js +109 -0
- package/dist/messages/glossary.test.js.map +1 -0
- package/dist/messages/index.d.ts +12 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +23 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/plural.d.ts +10 -0
- package/dist/messages/plural.d.ts.map +1 -0
- package/dist/messages/plural.js +17 -0
- package/dist/messages/plural.js.map +1 -0
- package/dist/messages/use-t.d.ts +10 -0
- package/dist/messages/use-t.d.ts.map +1 -0
- package/dist/messages/use-t.js +10 -0
- package/dist/messages/use-t.js.map +1 -0
- package/dist/module.d.ts +48 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +174 -0
- package/dist/module.js.map +1 -0
- package/dist/module.test.d.ts +2 -0
- package/dist/module.test.d.ts.map +1 -0
- package/dist/module.test.js +230 -0
- package/dist/module.test.js.map +1 -0
- package/dist/named-views.d.ts +27 -0
- package/dist/named-views.d.ts.map +1 -0
- package/dist/named-views.js +73 -0
- package/dist/named-views.js.map +1 -0
- package/dist/named-views.test.d.ts +2 -0
- package/dist/named-views.test.d.ts.map +1 -0
- package/dist/named-views.test.js +110 -0
- package/dist/named-views.test.js.map +1 -0
- package/dist/plugin.d.ts +44 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +52 -0
- package/dist/plugin.js.map +1 -0
- package/dist/prometheus-wiring.test.d.ts +2 -0
- package/dist/prometheus-wiring.test.d.ts.map +1 -0
- package/dist/prometheus-wiring.test.js +142 -0
- package/dist/prometheus-wiring.test.js.map +1 -0
- package/dist/server-locale.d.ts +48 -0
- package/dist/server-locale.d.ts.map +1 -0
- package/dist/server-locale.js +33 -0
- package/dist/server-locale.js.map +1 -0
- package/dist/settings-tools.d.ts +23 -0
- package/dist/settings-tools.d.ts.map +1 -0
- package/dist/settings-tools.js +100 -0
- package/dist/settings-tools.js.map +1 -0
- package/dist/settings.d.ts +77 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +99 -0
- package/dist/settings.js.map +1 -0
- package/dist/settings.test.d.ts +2 -0
- package/dist/settings.test.d.ts.map +1 -0
- package/dist/settings.test.js +299 -0
- package/dist/settings.test.js.map +1 -0
- package/dist/steps/app-config.d.ts +30 -0
- package/dist/steps/app-config.d.ts.map +1 -0
- package/dist/steps/app-config.js +46 -0
- package/dist/steps/app-config.js.map +1 -0
- package/dist/steps/dashboard.d.ts +12 -0
- package/dist/steps/dashboard.d.ts.map +1 -0
- package/dist/steps/dashboard.js +58 -0
- package/dist/steps/dashboard.js.map +1 -0
- package/dist/steps/failure-dashboard.d.ts +10 -0
- package/dist/steps/failure-dashboard.d.ts.map +1 -0
- package/dist/steps/failure-dashboard.js +32 -0
- package/dist/steps/failure-dashboard.js.map +1 -0
- package/dist/steps/index.d.ts +3 -0
- package/dist/steps/index.d.ts.map +1 -0
- package/dist/steps/index.js +3 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/steps/steps.test.d.ts +2 -0
- package/dist/steps/steps.test.d.ts.map +1 -0
- package/dist/steps/steps.test.js +153 -0
- package/dist/steps/steps.test.js.map +1 -0
- package/dist/tool-names.d.ts +13 -0
- package/dist/tool-names.d.ts.map +1 -0
- package/dist/tool-names.js +27 -0
- package/dist/tool-names.js.map +1 -0
- package/dist/tools/cluster-compare.d.ts +8 -0
- package/dist/tools/cluster-compare.d.ts.map +1 -0
- package/dist/tools/cluster-compare.js +17 -0
- package/dist/tools/cluster-compare.js.map +1 -0
- package/dist/tools/element.d.ts +8 -0
- package/dist/tools/element.d.ts.map +1 -0
- package/dist/tools/element.js +25 -0
- package/dist/tools/element.js.map +1 -0
- package/dist/tools/engine-compare.d.ts +8 -0
- package/dist/tools/engine-compare.d.ts.map +1 -0
- package/dist/tools/engine-compare.js +18 -0
- package/dist/tools/engine-compare.js.map +1 -0
- package/dist/tools/engine-compare.test.d.ts +2 -0
- package/dist/tools/engine-compare.test.d.ts.map +1 -0
- package/dist/tools/engine-compare.test.js +166 -0
- package/dist/tools/engine-compare.test.js.map +1 -0
- package/dist/tools/engine-landscape.d.ts +7 -0
- package/dist/tools/engine-landscape.d.ts.map +1 -0
- package/dist/tools/engine-landscape.js +13 -0
- package/dist/tools/engine-landscape.js.map +1 -0
- package/dist/tools/engine-landscape.test.d.ts +2 -0
- package/dist/tools/engine-landscape.test.d.ts.map +1 -0
- package/dist/tools/engine-landscape.test.js +110 -0
- package/dist/tools/engine-landscape.test.js.map +1 -0
- package/dist/tools/failures.d.ts +7 -0
- package/dist/tools/failures.d.ts.map +1 -0
- package/dist/tools/failures.js +13 -0
- package/dist/tools/failures.js.map +1 -0
- package/dist/tools/health.d.ts +7 -0
- package/dist/tools/health.d.ts.map +1 -0
- package/dist/tools/health.js +23 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/health.test.d.ts +2 -0
- package/dist/tools/health.test.d.ts.map +1 -0
- package/dist/tools/health.test.js +119 -0
- package/dist/tools/health.test.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +35 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/performance.d.ts +8 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js +25 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/performance.test.d.ts +2 -0
- package/dist/tools/performance.test.d.ts.map +1 -0
- package/dist/tools/performance.test.js +176 -0
- package/dist/tools/performance.test.js.map +1 -0
- package/dist/tools/version-compare.d.ts +8 -0
- package/dist/tools/version-compare.d.ts.map +1 -0
- package/dist/tools/version-compare.js +17 -0
- package/dist/tools/version-compare.js.map +1 -0
- package/dist/tools/version-compare.test.d.ts +2 -0
- package/dist/tools/version-compare.test.d.ts.map +1 -0
- package/dist/tools/version-compare.test.js +82 -0
- package/dist/tools/version-compare.test.js.map +1 -0
- package/dist/toolsets.d.ts +17 -0
- package/dist/toolsets.d.ts.map +1 -0
- package/dist/toolsets.js +47 -0
- package/dist/toolsets.js.map +1 -0
- package/dist/toolsets.test.d.ts +2 -0
- package/dist/toolsets.test.d.ts.map +1 -0
- package/dist/toolsets.test.js +91 -0
- package/dist/toolsets.test.js.map +1 -0
- package/dist/view-title.test.d.ts +2 -0
- package/dist/view-title.test.d.ts.map +1 -0
- package/dist/view-title.test.js +91 -0
- package/dist/view-title.test.js.map +1 -0
- package/dist/widget-tools/comparisons.d.ts +13 -0
- package/dist/widget-tools/comparisons.d.ts.map +1 -0
- package/dist/widget-tools/comparisons.js +177 -0
- package/dist/widget-tools/comparisons.js.map +1 -0
- package/dist/widget-tools/comparisons.test.d.ts +2 -0
- package/dist/widget-tools/comparisons.test.d.ts.map +1 -0
- package/dist/widget-tools/comparisons.test.js +123 -0
- package/dist/widget-tools/comparisons.test.js.map +1 -0
- package/dist/widget-tools/shared.d.ts +36 -0
- package/dist/widget-tools/shared.d.ts.map +1 -0
- package/dist/widget-tools/shared.js +39 -0
- package/dist/widget-tools/shared.js.map +1 -0
- package/dist/widget-tools.d.ts +25 -0
- package/dist/widget-tools.d.ts.map +1 -0
- package/dist/widget-tools.js +269 -0
- package/dist/widget-tools.js.map +1 -0
- package/dist/widget-tools.test.d.ts +2 -0
- package/dist/widget-tools.test.d.ts.map +1 -0
- package/dist/widget-tools.test.js +272 -0
- package/dist/widget-tools.test.js.map +1 -0
- package/package.json +91 -5
- package/src/bpmn-name-lookup.test.ts +333 -0
- package/src/bpmn-name-lookup.ts +197 -0
- package/src/bpmn-names.ts +51 -0
- package/src/brand-gates.test.ts +69 -0
- package/src/builder-texts.test.ts +40 -0
- package/src/builder-texts.ts +15 -0
- package/src/definition.ts +181 -0
- package/src/engine-ids.ts +85 -0
- package/src/engine-scope.test.ts +324 -0
- package/src/heatmap-data.ts +14 -0
- package/src/index.ts +4 -0
- package/src/instructions.ts +33 -0
- package/src/messages/GLOSSARY.md +88 -0
- package/src/messages/de.ask-ai.ts +28 -0
- package/src/messages/de.builder.ts +77 -0
- package/src/messages/de.server.ts +39 -0
- package/src/messages/de.sweep.ts +244 -0
- package/src/messages/en.ask-ai.ts +34 -0
- package/src/messages/en.builder.ts +76 -0
- package/src/messages/en.server.ts +39 -0
- package/src/messages/en.sweep.ts +240 -0
- package/src/messages/glossary.test.ts +122 -0
- package/src/messages/glossary.ts +62 -0
- package/src/messages/index.ts +24 -0
- package/src/messages/plural.ts +18 -0
- package/src/messages/use-t.ts +12 -0
- package/src/module.test.ts +319 -0
- package/src/module.ts +211 -0
- package/src/named-views.test.ts +141 -0
- package/src/named-views.ts +97 -0
- package/src/plugin.ts +94 -0
- package/src/prometheus-wiring.test.ts +174 -0
- package/src/server-locale.ts +59 -0
- package/src/settings-tools.ts +170 -0
- package/src/settings.test.ts +402 -0
- package/src/settings.ts +114 -0
- package/src/steps/app-config.ts +75 -0
- package/src/steps/dashboard.ts +73 -0
- package/src/steps/failure-dashboard.ts +38 -0
- package/src/steps/index.ts +2 -0
- package/src/steps/steps.test.ts +216 -0
- package/src/tool-names.ts +26 -0
- package/src/tools/cluster-compare.ts +28 -0
- package/src/tools/element.ts +35 -0
- package/src/tools/engine-compare.test.ts +210 -0
- package/src/tools/engine-compare.ts +30 -0
- package/src/tools/engine-landscape.test.ts +138 -0
- package/src/tools/engine-landscape.ts +21 -0
- package/src/tools/failures.ts +19 -0
- package/src/tools/health.test.ts +156 -0
- package/src/tools/health.ts +29 -0
- package/src/tools/index.ts +44 -0
- package/src/tools/performance.test.ts +210 -0
- package/src/tools/performance.ts +38 -0
- package/src/tools/version-compare.test.ts +97 -0
- package/src/tools/version-compare.ts +28 -0
- package/src/toolsets.test.ts +107 -0
- package/src/toolsets.ts +56 -0
- package/src/view-title.test.ts +123 -0
- package/src/widget-tools/comparisons.test.ts +170 -0
- package/src/widget-tools/comparisons.ts +229 -0
- package/src/widget-tools/shared.ts +67 -0
- package/src/widget-tools.test.ts +306 -0
- package/src/widget-tools.ts +375 -0
- package/src/widgets/analytics-dashboard/activity-bottleneck-table.tsx +179 -0
- package/src/widgets/analytics-dashboard/definition-breakdown.tsx +108 -0
- package/src/widgets/analytics-dashboard/execution-performance-kpi.tsx +94 -0
- package/src/widgets/analytics-dashboard/execution-summary-kpi.tsx +122 -0
- package/src/widgets/analytics-dashboard/lib.ts +43 -0
- package/src/widgets/bpmn-heatmap.test.tsx +75 -0
- package/src/widgets/bpmn-heatmap.tsx +127 -0
- package/src/widgets/catalogue-sync.test.ts +16 -0
- package/src/widgets/cluster-compare.tsx +148 -0
- package/src/widgets/comparison-shared.test.ts +237 -0
- package/src/widgets/comparison-shared.tsx +371 -0
- package/src/widgets/comparison-widgets.test.tsx +307 -0
- package/src/widgets/dashboard-widgets.test.tsx +397 -0
- package/src/widgets/engine-compare.tsx +87 -0
- package/src/widgets/engine-landscape.tsx +352 -0
- package/src/widgets/failure-dashboard/error-patterns-table.tsx +122 -0
- package/src/widgets/failure-dashboard/failure-rate-table.test.ts +80 -0
- package/src/widgets/failure-dashboard/failure-rate-table.tsx +170 -0
- package/src/widgets/failure-dashboard/lib.ts +32 -0
- package/src/widgets/failure-dashboard/summary-kpi.tsx +122 -0
- package/src/widgets/failure-landscape-widgets.test.tsx +393 -0
- package/src/widgets/figure.tsx +79 -0
- package/src/widgets/hand-off.test.ts +126 -0
- package/src/widgets/hand-off.ts +102 -0
- package/src/widgets/index.ts +124 -0
- package/src/widgets/model-descriptions.dashboard.test.ts +280 -0
- package/src/widgets/model-descriptions.test.ts +251 -0
- package/src/widgets/model-descriptions.ts +428 -0
- package/src/widgets/names.test.ts +32 -0
- package/src/widgets/names.ts +39 -0
- package/src/widgets/query-gate.test.tsx +57 -0
- package/src/widgets/query-gate.tsx +50 -0
- package/src/widgets/settings-section.test.tsx +174 -0
- package/src/widgets/settings-section.tsx +244 -0
- package/src/widgets/version-compare.test.ts +68 -0
- package/src/widgets/version-compare.tsx +96 -0
- package/src/widgets/view-meta.ts +18 -0
- package/src/widgets/widget-render.test-support.tsx +103 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Badge,
|
|
3
|
+
KpiGrid,
|
|
4
|
+
SectionHeading,
|
|
5
|
+
StatusBadge,
|
|
6
|
+
useToolQuery,
|
|
7
|
+
type KpiCell,
|
|
8
|
+
} from "@miragon/mcp-toolkit-ui"
|
|
9
|
+
import { Info } from "lucide-react"
|
|
10
|
+
import {
|
|
11
|
+
HandOffButton,
|
|
12
|
+
Icon,
|
|
13
|
+
ListTable,
|
|
14
|
+
NamedId,
|
|
15
|
+
TableEmptyState,
|
|
16
|
+
TableSkeleton,
|
|
17
|
+
Td,
|
|
18
|
+
ViewMeta,
|
|
19
|
+
WidgetShell,
|
|
20
|
+
formatNumber,
|
|
21
|
+
type ListTableColumn,
|
|
22
|
+
type ListTableGroup,
|
|
23
|
+
} from "@miragon/pulse-widget-shell/widgets"
|
|
24
|
+
import type { EngineLandscapeEngine, EngineLandscapeResult } from "@miragon/pulse-analytics-client"
|
|
25
|
+
import { ANALYTICS_ENGINE_LANDSCAPE_DATA } from "../tool-names.js"
|
|
26
|
+
import { QueryGate } from "./query-gate.js"
|
|
27
|
+
import { useT, type T } from "../messages/use-t.js"
|
|
28
|
+
import { useHandOff, type HandOff } from "./hand-off.js"
|
|
29
|
+
import { processNameOf, type Named } from "../bpmn-names.js"
|
|
30
|
+
import { Figure, FigureLegend, Legend, StateCount } from "./figure.js"
|
|
31
|
+
|
|
32
|
+
/** The landscape payload: the figures plus the process names behind the matrix's keys. */
|
|
33
|
+
export type EngineLandscapeView = Named<EngineLandscapeResult>
|
|
34
|
+
export type EngineLandscapeData = EngineLandscapeView | null
|
|
35
|
+
|
|
36
|
+
/** Engine ids from the layout cell props (`engine` may be a string or a list). */
|
|
37
|
+
function engineIdsFromProps(engine: string | string[] | undefined): string[] {
|
|
38
|
+
if (typeof engine === "string") return engine.length > 0 ? [engine] : []
|
|
39
|
+
if (Array.isArray(engine)) return engine.filter((id) => typeof id === "string" && id.length > 0)
|
|
40
|
+
return []
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The cross-engine overview: what runs where, how much of it, and which engines
|
|
45
|
+
* carry a backlog.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately NOT an engine scoreboard. Two engines run different processes,
|
|
48
|
+
* so a per-engine failure rate or average duration describes the process mix,
|
|
49
|
+
* not the engine — the widget therefore shows absolute counts plus the
|
|
50
|
+
* process-independent job backlog, and offers the KPI comparison only for the
|
|
51
|
+
* definitions that actually run on more than one engine.
|
|
52
|
+
*
|
|
53
|
+
* "—" means one thing here: not measured (an engine that sends no metrics).
|
|
54
|
+
* A process that is not deployed on an engine is an empty matrix cell.
|
|
55
|
+
*/
|
|
56
|
+
export function EngineLandscapeWidget({
|
|
57
|
+
data: initialData,
|
|
58
|
+
engine,
|
|
59
|
+
}: {
|
|
60
|
+
data: EngineLandscapeData
|
|
61
|
+
/** Engine ids to include; pass all configured ones to surface silent engines. */
|
|
62
|
+
engine?: string | string[]
|
|
63
|
+
}) {
|
|
64
|
+
const t = useT()
|
|
65
|
+
const engineIds = engineIdsFromProps(engine)
|
|
66
|
+
const query = useToolQuery<EngineLandscapeView>(
|
|
67
|
+
// The engine scope belongs in the key: a subset view must not read the
|
|
68
|
+
// full-fleet payload out of the cache.
|
|
69
|
+
["analytics:engine-landscape", engineIds.join(",")],
|
|
70
|
+
ANALYTICS_ENGINE_LANDSCAPE_DATA,
|
|
71
|
+
engineIds.length > 0 ? { engine: engineIds } : {},
|
|
72
|
+
{ enabled: !initialData },
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<QueryGate initialData={initialData} query={query} skeleton={<TableSkeleton />}>
|
|
77
|
+
{(data) => (
|
|
78
|
+
<WidgetShell>
|
|
79
|
+
<LandscapeSummary data={data} t={t} />
|
|
80
|
+
<EngineTable data={data} t={t} />
|
|
81
|
+
<ProcessMatrix data={data} t={t} />
|
|
82
|
+
</WidgetShell>
|
|
83
|
+
)}
|
|
84
|
+
</QueryGate>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function LandscapeSummary({ data, t }: { data: EngineLandscapeView; t: T }) {
|
|
89
|
+
const { totals } = data
|
|
90
|
+
const silent = totals.engineCount - totals.reportingEngineCount
|
|
91
|
+
const cells: KpiCell[] = [
|
|
92
|
+
{
|
|
93
|
+
label: t("aLandscape.kpiEngines"),
|
|
94
|
+
value: formatNumber(totals.engineCount),
|
|
95
|
+
// An engine without metrics may be down: worth a look, not an error.
|
|
96
|
+
trend:
|
|
97
|
+
silent > 0 ? t("aLandscape.kpiEnginesSilent", { count: formatNumber(silent) }) : undefined,
|
|
98
|
+
trendTone: silent > 0 ? "warning" : undefined,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
label: t("aLandscape.kpiProcesses"),
|
|
102
|
+
value: formatNumber(totals.processKeyCount),
|
|
103
|
+
trend: t("aLandscape.kpiProcessesShared", {
|
|
104
|
+
count: formatNumber(totals.sharedProcessKeyCount),
|
|
105
|
+
}),
|
|
106
|
+
},
|
|
107
|
+
{ label: t("aLandscape.kpiRunning"), value: formatNumber(totals.runningInstances) },
|
|
108
|
+
{
|
|
109
|
+
label: t("aLandscape.kpiIncidents"),
|
|
110
|
+
value: formatNumber(totals.openIncidents),
|
|
111
|
+
tone: (totals.openIncidents ?? 0) > 0 ? "danger" : undefined,
|
|
112
|
+
},
|
|
113
|
+
]
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
<SectionHeading title={t("aLandscape.heading")} hint={t("aLandscape.headingHint")} />
|
|
117
|
+
{/* A live snapshot: no period, the engines (and how many send no metrics) and when. */}
|
|
118
|
+
<ViewMeta
|
|
119
|
+
className="-mt-2 mb-3"
|
|
120
|
+
engines={{ count: totals.engineCount, silent }}
|
|
121
|
+
asOf={data.asOf}
|
|
122
|
+
/>
|
|
123
|
+
<KpiGrid cells={cells} />
|
|
124
|
+
</>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The per-engine table in its two column groups, headed as such (U17): the
|
|
130
|
+
* load counts are absolute (comparable only as "where the work sits"), while
|
|
131
|
+
* the backlog gauges carry no process label and therefore genuinely compare
|
|
132
|
+
* across engines. The backlog is shown in FULL — it is the only
|
|
133
|
+
* mix-independent evidence this view offers, and the fleet prompt tells the
|
|
134
|
+
* model to judge engines on exactly it — so it never hides; only the
|
|
135
|
+
* inventory column gives way below 768 px.
|
|
136
|
+
*/
|
|
137
|
+
function engineColumns(t: T): ListTableColumn[] {
|
|
138
|
+
return [
|
|
139
|
+
{ label: t("aLandscape.colEngine") },
|
|
140
|
+
{ label: t("aLandscape.colRunning"), align: "right" },
|
|
141
|
+
{ label: t("aLandscape.colIncidents"), align: "right" },
|
|
142
|
+
{ label: t("aLandscape.colFailedJobs"), align: "right" },
|
|
143
|
+
{ label: t("aLandscape.colExecutable"), align: "right" },
|
|
144
|
+
{ label: t("aLandscape.colSuspended"), align: "right" },
|
|
145
|
+
{ label: t("aLandscape.colDueFuture"), align: "right" },
|
|
146
|
+
{ label: t("aLandscape.colExternalTasks"), align: "right" },
|
|
147
|
+
{ label: t("aLandscape.colProcesses"), align: "right", hideBelow: "md" },
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function engineGroups(t: T): ListTableGroup[] {
|
|
152
|
+
return [
|
|
153
|
+
{},
|
|
154
|
+
{ label: t("aLandscape.groupLoad"), span: 3 },
|
|
155
|
+
{ label: t("aLandscape.groupBacklog"), span: 4 },
|
|
156
|
+
{},
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** One count of an engine: the number, or "—" with its meaning for an engine without metrics. */
|
|
161
|
+
function CountCell({ value, t }: { value: number | null; t: T }) {
|
|
162
|
+
return (
|
|
163
|
+
<Td align="right" className="tabular-nums">
|
|
164
|
+
<Figure value={value} why={t("aLandscape.unknown")} />
|
|
165
|
+
</Td>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* An engine's open incidents or jobs without retries: the states among its
|
|
171
|
+
* counts (decision f), a danger edge while there are any ({@link StateCount},
|
|
172
|
+
* the kit's one ladder), as camunda7's engine tiles show the same counts of
|
|
173
|
+
* the same engines in the cross-engine overview this table sits in.
|
|
174
|
+
*/
|
|
175
|
+
function StateCountCell({ value, t }: { value: number | null; t: T }) {
|
|
176
|
+
return (
|
|
177
|
+
<Td align="right" className="tabular-nums">
|
|
178
|
+
<StateCount value={value} why={t("aLandscape.unknown")} />
|
|
179
|
+
</Td>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function EngineRow({ engine: e, t }: { engine: EngineLandscapeEngine; t: T }) {
|
|
184
|
+
return (
|
|
185
|
+
<tr>
|
|
186
|
+
<Td>
|
|
187
|
+
<span className="flex flex-wrap items-center gap-2">
|
|
188
|
+
{/* An engine id is short and one word ("prod-a"): never wrapped. */}
|
|
189
|
+
<span className="font-mono font-medium whitespace-nowrap">{e.engineId}</span>
|
|
190
|
+
{/* The badge may go under the id in a narrow column, but never
|
|
191
|
+
breaks inside itself ("no / metrics"): a pill is one line. */}
|
|
192
|
+
{!e.reporting && (
|
|
193
|
+
<StatusBadge tone="warning" className="whitespace-nowrap">
|
|
194
|
+
{t("aLandscape.noMetrics")}
|
|
195
|
+
</StatusBadge>
|
|
196
|
+
)}
|
|
197
|
+
</span>
|
|
198
|
+
</Td>
|
|
199
|
+
<CountCell value={e.runningInstances} t={t} />
|
|
200
|
+
<StateCountCell value={e.openIncidents} t={t} />
|
|
201
|
+
<StateCountCell value={e.failedJobs} t={t} />
|
|
202
|
+
<CountCell value={e.executableJobs} t={t} />
|
|
203
|
+
<CountCell value={e.suspendedJobs} t={t} />
|
|
204
|
+
<CountCell value={e.jobsDueFuture} t={t} />
|
|
205
|
+
<CountCell value={e.openExternalTasks} t={t} />
|
|
206
|
+
<Td align="right" className="tabular-nums">
|
|
207
|
+
<Figure value={e.deployedDefinitionKeys} why={t("aLandscape.unknown")} />
|
|
208
|
+
{(e.exclusiveDefinitionKeys ?? 0) > 0 && (
|
|
209
|
+
<span className="text-muted-foreground text-small ml-1 whitespace-nowrap">
|
|
210
|
+
{t("aLandscape.exclusiveSuffix", {
|
|
211
|
+
count: formatNumber(e.exclusiveDefinitionKeys),
|
|
212
|
+
})}
|
|
213
|
+
</span>
|
|
214
|
+
)}
|
|
215
|
+
</Td>
|
|
216
|
+
</tr>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function EngineTable({ data, t }: { data: EngineLandscapeView; t: T }) {
|
|
221
|
+
if (data.engines.length === 0) {
|
|
222
|
+
return <TableEmptyState>{t("aLandscape.emptyEngines")}</TableEmptyState>
|
|
223
|
+
}
|
|
224
|
+
return (
|
|
225
|
+
<section>
|
|
226
|
+
<SectionHeading title={t("aLandscape.engines.title")} hint={t("aLandscape.engines.hint")} />
|
|
227
|
+
<ListTable
|
|
228
|
+
ariaLabel={t("aLandscape.engines.tableLabel")}
|
|
229
|
+
columns={engineColumns(t)}
|
|
230
|
+
groups={engineGroups(t)}
|
|
231
|
+
// Eight figures side by side: the compact cell padding keeps every
|
|
232
|
+
// backlog column in a 760 px inline view, in German too.
|
|
233
|
+
className="[&_td]:px-3 [&_th]:px-3"
|
|
234
|
+
>
|
|
235
|
+
{data.engines.map((e) => (
|
|
236
|
+
<EngineRow key={e.engineId} engine={e} t={t} />
|
|
237
|
+
))}
|
|
238
|
+
</ListTable>
|
|
239
|
+
<FigureLegend reasons={[data.engines.some((e) => !e.reporting) && t("aLandscape.unknown")]} />
|
|
240
|
+
</section>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The process × engine inventory. Shared definitions lead the table and are the
|
|
246
|
+
* only rows offering the KPI comparison — everywhere else the two engines have
|
|
247
|
+
* no common workload to compare. Its columns are the engines that send
|
|
248
|
+
* metrics: a silent engine has nothing to show here, the meta line counts it
|
|
249
|
+
* and a note names it. The comparison is pinned at the right edge, so it
|
|
250
|
+
* stays in reach however many engines there are.
|
|
251
|
+
*/
|
|
252
|
+
function ProcessMatrix({ data, t }: { data: EngineLandscapeView; t: T }) {
|
|
253
|
+
if (data.processes.length === 0) {
|
|
254
|
+
return <TableEmptyState>{t("aLandscape.emptyProcesses")}</TableEmptyState>
|
|
255
|
+
}
|
|
256
|
+
const engineIds = data.engines.filter((e) => e.reporting).map((e) => e.engineId)
|
|
257
|
+
const silent = data.engines.filter((e) => !e.reporting).map((e) => e.engineId)
|
|
258
|
+
const columns: ListTableColumn[] = [
|
|
259
|
+
{ label: t("aLandscape.colProcess") },
|
|
260
|
+
...engineIds.map((id): ListTableColumn => ({
|
|
261
|
+
label: <span className="font-mono">{id}</span>,
|
|
262
|
+
align: "right",
|
|
263
|
+
})),
|
|
264
|
+
{ label: t("aLandscape.colIncidents"), align: "right" },
|
|
265
|
+
{
|
|
266
|
+
label: <span className="sr-only">{t("aLandscape.colCompare")}</span>,
|
|
267
|
+
plain: true,
|
|
268
|
+
sticky: true,
|
|
269
|
+
},
|
|
270
|
+
]
|
|
271
|
+
return (
|
|
272
|
+
<section>
|
|
273
|
+
<SectionHeading title={t("aLandscape.matrix.title")} hint={t("aLandscape.matrix.hint")} />
|
|
274
|
+
<p className="text-muted-foreground text-small mb-3 flex items-start gap-2">
|
|
275
|
+
<Icon icon={Info} dense className="mt-0.5" />
|
|
276
|
+
<span>{t("aLandscape.mixNote")}</span>
|
|
277
|
+
</p>
|
|
278
|
+
<ListTable ariaLabel={t("aLandscape.matrix.tableLabel")} columns={columns}>
|
|
279
|
+
{data.processes.map((p) => (
|
|
280
|
+
<tr key={p.processDefinitionKey}>
|
|
281
|
+
<Td>
|
|
282
|
+
<span className="flex flex-wrap items-center gap-2">
|
|
283
|
+
<NamedId
|
|
284
|
+
name={processNameOf(data.names, p.processDefinitionKey)}
|
|
285
|
+
id={p.processDefinitionKey}
|
|
286
|
+
/>
|
|
287
|
+
{p.shared && (
|
|
288
|
+
<Badge variant="outline">
|
|
289
|
+
{t("aLandscape.sharedBadge", { count: formatNumber(p.engineIds.length) })}
|
|
290
|
+
</Badge>
|
|
291
|
+
)}
|
|
292
|
+
</span>
|
|
293
|
+
</Td>
|
|
294
|
+
{engineIds.map((id) => (
|
|
295
|
+
<Td key={id} align="right" className="tabular-nums">
|
|
296
|
+
{p.engineIds.includes(id) ? (
|
|
297
|
+
formatNumber(p.runningByEngine[id] ?? 0)
|
|
298
|
+
) : (
|
|
299
|
+
// Not deployed: an empty cell (the legend says so), never "—".
|
|
300
|
+
<span className="sr-only">{t("aLandscape.notDeployed")}</span>
|
|
301
|
+
)}
|
|
302
|
+
</Td>
|
|
303
|
+
))}
|
|
304
|
+
<Td align="right" className="tabular-nums">
|
|
305
|
+
<StateCount value={p.openIncidentsTotal} />
|
|
306
|
+
</Td>
|
|
307
|
+
<Td align="right">
|
|
308
|
+
{p.shared && (
|
|
309
|
+
<CompareAction processKey={p.processDefinitionKey} on={p.engineIds} t={t} />
|
|
310
|
+
)}
|
|
311
|
+
</Td>
|
|
312
|
+
</tr>
|
|
313
|
+
))}
|
|
314
|
+
</ListTable>
|
|
315
|
+
<Legend>{t("aLandscape.legendNotDeployed")}</Legend>
|
|
316
|
+
{silent.length > 0 && (
|
|
317
|
+
<Legend>
|
|
318
|
+
{t("aLandscape.silentNote", {
|
|
319
|
+
count: formatNumber(silent.length),
|
|
320
|
+
engines: silent.join(", "),
|
|
321
|
+
})}
|
|
322
|
+
</Legend>
|
|
323
|
+
)}
|
|
324
|
+
</section>
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* The one valid engine-vs-engine hand-off: same process, two engines (the
|
|
330
|
+
* further engines it runs on are named so the model can pair them next).
|
|
331
|
+
*/
|
|
332
|
+
export function landscapeCompareHandOff(processKey: string, on: readonly string[]): HandOff {
|
|
333
|
+
const [engineA, engineB, ...more] = on
|
|
334
|
+
return {
|
|
335
|
+
intent: "askAi.landscapeCompare",
|
|
336
|
+
ids: { processDefinitionKey: processKey, engineA, engineB, windowDays: 14 },
|
|
337
|
+
facts: { alsoRunsOn: more.length > 0 ? more : undefined },
|
|
338
|
+
tools: ["analytics_show_engine_compare"],
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function CompareAction({ processKey, on, t }: { processKey: string; on: string[]; t: T }) {
|
|
343
|
+
const { ask } = useHandOff()
|
|
344
|
+
return (
|
|
345
|
+
<HandOffButton
|
|
346
|
+
action="compareEngines"
|
|
347
|
+
variant="icon"
|
|
348
|
+
title={t("aLandscape.compareLabel", { key: processKey })}
|
|
349
|
+
prompt={ask(landscapeCompareHandOff(processKey, on))}
|
|
350
|
+
/>
|
|
351
|
+
)
|
|
352
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HandOffButton,
|
|
3
|
+
ListTable,
|
|
4
|
+
NamedId,
|
|
5
|
+
Section,
|
|
6
|
+
TableEmptyState,
|
|
7
|
+
TableSkeleton,
|
|
8
|
+
Td,
|
|
9
|
+
WidgetShell,
|
|
10
|
+
type ListTableColumn,
|
|
11
|
+
} from "@miragon/pulse-widget-shell/widgets"
|
|
12
|
+
import type { ErrorPatternItem, FailureDashboardData } from "@miragon/pulse-analytics-client"
|
|
13
|
+
import { useFailureDashboardSelfFetch, type FailureScopeProps, type FailureView } from "./lib.js"
|
|
14
|
+
import { QueryGate } from "../query-gate.js"
|
|
15
|
+
import { useT, type T } from "../../messages/use-t.js"
|
|
16
|
+
import { engineIdsOf, useHandOff, type HandOff } from "../hand-off.js"
|
|
17
|
+
import { processNameOf } from "../../bpmn-names.js"
|
|
18
|
+
import { StateCount } from "../figure.js"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Root cause of ONE group of open incidents. The open-incident gauge carries
|
|
22
|
+
* only the incident type and the process (#336) — the message and the
|
|
23
|
+
* failing activity come from the live incidents, so the hand-off passes no
|
|
24
|
+
* field the data cannot fill. A custom incident type is any string: unless
|
|
25
|
+
* id-shaped it is quoted as untrusted data, never inlined.
|
|
26
|
+
*/
|
|
27
|
+
export function errorPatternHandOff(
|
|
28
|
+
pattern: ErrorPatternItem,
|
|
29
|
+
data: Pick<FailureDashboardData, "engines">,
|
|
30
|
+
): HandOff {
|
|
31
|
+
return {
|
|
32
|
+
intent: "askAi.errorPattern",
|
|
33
|
+
ids: {
|
|
34
|
+
engine: engineIdsOf(data.engines),
|
|
35
|
+
processDefinitionKey: pattern.processDefinitionKey,
|
|
36
|
+
incidentType: pattern.incidentType,
|
|
37
|
+
},
|
|
38
|
+
facts: { openIncidentsNow: pattern.incidentCount },
|
|
39
|
+
tools: [
|
|
40
|
+
"analytics_find_failed_instances",
|
|
41
|
+
"camunda7_list_incidents",
|
|
42
|
+
"camunda7_query_historic_incidents",
|
|
43
|
+
"camunda7_query_historic_activity_instances",
|
|
44
|
+
],
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function columns(t: T): ListTableColumn[] {
|
|
49
|
+
return [
|
|
50
|
+
{ label: t("aErrorPatterns.columnIncidentType") },
|
|
51
|
+
{ label: t("aErrorPatterns.columnProcess") },
|
|
52
|
+
{ label: t("aErrorPatterns.columnCount"), align: "right" },
|
|
53
|
+
{
|
|
54
|
+
label: <span className="sr-only">{t("aErrorPatterns.columnAi")}</span>,
|
|
55
|
+
plain: true,
|
|
56
|
+
sticky: true,
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function ErrorPatternsTable({
|
|
62
|
+
data: initialData,
|
|
63
|
+
engine,
|
|
64
|
+
}: { data: FailureView | null } & FailureScopeProps) {
|
|
65
|
+
const t = useT()
|
|
66
|
+
const { ask } = useHandOff()
|
|
67
|
+
const fallbackQuery = useFailureDashboardSelfFetch(initialData, { engine })
|
|
68
|
+
return (
|
|
69
|
+
<QueryGate initialData={initialData} query={fallbackQuery} skeleton={<TableSkeleton />}>
|
|
70
|
+
{(data) => {
|
|
71
|
+
if (data.errorPatterns.length === 0) {
|
|
72
|
+
// Nothing open is "all clear", not missing data.
|
|
73
|
+
return (
|
|
74
|
+
<WidgetShell>
|
|
75
|
+
{data.processBreakdown.length === 0 ? (
|
|
76
|
+
<TableEmptyState variant="done" description={t("aErrorPatterns.emptyDescription")}>
|
|
77
|
+
{t("aErrorPatterns.emptyTitle")}
|
|
78
|
+
</TableEmptyState>
|
|
79
|
+
) : (
|
|
80
|
+
<TableEmptyState>{t("aErrorPatterns.emptyNoPatterns")}</TableEmptyState>
|
|
81
|
+
)}
|
|
82
|
+
</WidgetShell>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<WidgetShell>
|
|
88
|
+
<Section
|
|
89
|
+
title={t("aErrorPatterns.heading")}
|
|
90
|
+
count={data.errorPatterns.length}
|
|
91
|
+
defaultOpen
|
|
92
|
+
>
|
|
93
|
+
<ListTable ariaLabel={t("aErrorPatterns.tableAriaLabel")} columns={columns(t)}>
|
|
94
|
+
{data.errorPatterns.map((pattern) => (
|
|
95
|
+
<tr key={`${pattern.processDefinitionKey}:${pattern.incidentType}`}>
|
|
96
|
+
<Td className="font-mono break-all">{pattern.incidentType}</Td>
|
|
97
|
+
<Td>
|
|
98
|
+
<NamedId
|
|
99
|
+
name={processNameOf(data.names, pattern.processDefinitionKey)}
|
|
100
|
+
id={pattern.processDefinitionKey}
|
|
101
|
+
/>
|
|
102
|
+
</Td>
|
|
103
|
+
<Td align="right" className="tabular-nums">
|
|
104
|
+
<StateCount value={pattern.incidentCount} />
|
|
105
|
+
</Td>
|
|
106
|
+
<Td align="right">
|
|
107
|
+
<HandOffButton
|
|
108
|
+
action="findCause"
|
|
109
|
+
variant="icon"
|
|
110
|
+
prompt={ask(errorPatternHandOff(pattern, data))}
|
|
111
|
+
/>
|
|
112
|
+
</Td>
|
|
113
|
+
</tr>
|
|
114
|
+
))}
|
|
115
|
+
</ListTable>
|
|
116
|
+
</Section>
|
|
117
|
+
</WidgetShell>
|
|
118
|
+
)
|
|
119
|
+
}}
|
|
120
|
+
</QueryGate>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import { failureRateHandOff } from "./failure-rate-table.js"
|
|
3
|
+
import { errorPatternHandOff } from "./error-patterns-table.js"
|
|
4
|
+
import { ANALYTICS_ONLY_SURFACE, analyticsSurface, bindHandOff } from "../hand-off.js"
|
|
5
|
+
|
|
6
|
+
const ask = bindHandOff("en", ANALYTICS_ONLY_SURFACE).ask
|
|
7
|
+
|
|
8
|
+
describe("failureRateHandOff", () => {
|
|
9
|
+
const prompt = ask(
|
|
10
|
+
failureRateHandOff(
|
|
11
|
+
{
|
|
12
|
+
processDefinitionKey: "order",
|
|
13
|
+
runningNow: 50,
|
|
14
|
+
deadJobs: 3,
|
|
15
|
+
openIncidents: 6,
|
|
16
|
+
incidentRatePct: 12,
|
|
17
|
+
},
|
|
18
|
+
{ engines: ["prod-a"] },
|
|
19
|
+
),
|
|
20
|
+
)!
|
|
21
|
+
|
|
22
|
+
it("states the live figures it asks about — open incidents, not failed instances (N85)", () => {
|
|
23
|
+
expect(prompt).toContain('Ids: engine="prod-a", processDefinitionKey="order"')
|
|
24
|
+
expect(prompt).toContain(
|
|
25
|
+
"On screen: openIncidentsNow=6, runningNow=50, incidentRatePct=12, deadJobs=3",
|
|
26
|
+
)
|
|
27
|
+
expect(prompt).not.toMatch(/failed|failure/i)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("leaves an unmeasured rate out instead of sending it as 0", () => {
|
|
31
|
+
const idle = ask(
|
|
32
|
+
failureRateHandOff(
|
|
33
|
+
{
|
|
34
|
+
processDefinitionKey: "order",
|
|
35
|
+
runningNow: 0,
|
|
36
|
+
deadJobs: 0,
|
|
37
|
+
openIncidents: 2,
|
|
38
|
+
incidentRatePct: null,
|
|
39
|
+
},
|
|
40
|
+
{ engines: ["prod-a"] },
|
|
41
|
+
),
|
|
42
|
+
)!
|
|
43
|
+
expect(idle).not.toContain("incidentRatePct")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("routes the regression check to tools that measure incident rates (#327)", () => {
|
|
47
|
+
expect(prompt).toContain("analytics_compare_execution_periods")
|
|
48
|
+
expect(prompt).toContain("analytics_cluster_compare")
|
|
49
|
+
// Its incident rates are null per version — a wasted call for this question.
|
|
50
|
+
expect(prompt).not.toContain("analytics_version_compare")
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe("errorPatternHandOff (N117)", () => {
|
|
55
|
+
const pattern = { incidentType: "failedJob", processDefinitionKey: "order", incidentCount: 4 }
|
|
56
|
+
|
|
57
|
+
it("names only what the open-incident metric carries", () => {
|
|
58
|
+
const prompt = ask(errorPatternHandOff(pattern, { engines: ["prod-a"] }))!
|
|
59
|
+
expect(prompt).toContain(
|
|
60
|
+
'Ids: engine="prod-a", processDefinitionKey="order", incidentType="failedJob"',
|
|
61
|
+
)
|
|
62
|
+
expect(prompt).toContain("On screen: openIncidentsNow=4")
|
|
63
|
+
expect(prompt).not.toMatch(/first seen|last seen|sample instance|activityId/i)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it("names the aggregate's engines, never a placeholder", () => {
|
|
67
|
+
const prompt = ask(errorPatternHandOff(pattern, { engines: ["prod-a", "prod-b"] }))!
|
|
68
|
+
expect(prompt).toContain('engine=["prod-a","prod-b"]')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it("names the camunda7 reads only where camunda7 registers them", () => {
|
|
72
|
+
const withCamunda7 = bindHandOff("en", analyticsSurface(["camunda7_list_incidents"])).ask(
|
|
73
|
+
errorPatternHandOff(pattern, { engines: ["prod-a"] }),
|
|
74
|
+
)!
|
|
75
|
+
expect(withCamunda7).toContain(
|
|
76
|
+
"Tools: analytics_find_failed_instances, camunda7_list_incidents",
|
|
77
|
+
)
|
|
78
|
+
expect(ask(errorPatternHandOff(pattern, { engines: ["prod-a"] }))).not.toContain("camunda7_")
|
|
79
|
+
})
|
|
80
|
+
})
|