From fcee3b9c78b401196cc022db76b8d12938fbdac0 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Wed, 15 Jul 2026 20:30:53 -0400 Subject: [PATCH] Emit ParseMethod-only EMF set so fallback alarm can fire The fallback-rate alarm queries the ParseOutcome series keyed on ParseMethod alone, but the emitter published only the joint (ParseMethod, TemplateId) dimension set. CloudWatch materializes exactly the listed dimension sets and does not auto-aggregate, so the alarm's series never received data: it evaluated a constant 0 and could never page on template-drift coverage collapse. Publish both ["ParseMethod"] and ["ParseMethod","TemplateId"] and update the EMF regression test to assert both sets are present. --- lambdas/wo/email_processor/handler.py | 11 +++++++++-- .../wo/email_processor/tests/test_bedrock_fallback.py | 9 +++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lambdas/wo/email_processor/handler.py b/lambdas/wo/email_processor/handler.py index 3b7efb8..4da7abd 100644 --- a/lambdas/wo/email_processor/handler.py +++ b/lambdas/wo/email_processor/handler.py @@ -149,13 +149,20 @@ def emit_parse_metric(method, template_id, reason_code, work_order_id): Zero-latency (no PutMetricData API call): the extraction path is async and the role already has logs:PutLogEvents. ParseMethod/TemplateId are the only promoted (dimensioned) fields to keep cardinality low; ReasonCode and - work_order_id ride along as Logs-Insights-queryable properties.""" + work_order_id ride along as Logs-Insights-queryable properties. + + Two dimension sets are published: ["ParseMethod"] (aggregated across all + template ids -- the series the fallback-rate alarm queries) AND + ["ParseMethod", "TemplateId"] (per-template breakdown for Logs Insights / + dashboards). CloudWatch materializes only the exact dimension sets listed + here and does NOT auto-aggregate, so the alarm's single-dimension query + would receive no data unless ["ParseMethod"] is emitted explicitly.""" emf = { "_aws": { "CloudWatchMetrics": [ { "Namespace": METRIC_NAMESPACE, - "Dimensions": [["ParseMethod", "TemplateId"]], + "Dimensions": [["ParseMethod"], ["ParseMethod", "TemplateId"]], "Metrics": [{"Name": METRIC_NAME, "Unit": "Count"}], } ], diff --git a/lambdas/wo/email_processor/tests/test_bedrock_fallback.py b/lambdas/wo/email_processor/tests/test_bedrock_fallback.py index 4fb4f14..864bcdc 100644 --- a/lambdas/wo/email_processor/tests/test_bedrock_fallback.py +++ b/lambdas/wo/email_processor/tests/test_bedrock_fallback.py @@ -165,8 +165,13 @@ def test_emit_parse_metric_writes_emf(capsys): assert emf["TemplateId"] == "update_plaintext" assert emf["ReasonCode"] == "ok" assert emf["ParseOutcome"] == 1 - dims = emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"][0] - assert dims == ["ParseMethod", "TemplateId"] + dims = emf["_aws"]["CloudWatchMetrics"][0]["Dimensions"] + # Two dimension sets must be published: the ParseMethod-only aggregate that + # the fallback-rate alarm queries, AND the per-template breakdown. Without + # the ["ParseMethod"] set the alarm's single-dimension series never receives + # data and can never fire (regression guard for the coverage-collapse alarm). + assert ["ParseMethod"] in dims + assert ["ParseMethod", "TemplateId"] in dims assert ( emf["_aws"]["CloudWatchMetrics"][0]["Namespace"] == "Seahaven/WorkorderIngest" )