@@ -136,6 +136,50 @@ describe("alert-engine", () => {
136136 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
137137 } ) ;
138138
139+ it ( "prefixes a dispatched alert with the staging source-env label" , async ( ) => {
140+ const e = engine ( {
141+ env : { dashboardUrl : "https://d" , repo : "r/r" , sourceEnv : "staging" } ,
142+ } ) ;
143+ e . start ( ) ;
144+ e . reload ( [ baseRule ( ) ] ) ;
145+ bus . emit ( "status.changed" , {
146+ outcome : {
147+ previousState : "green" ,
148+ newState : "red" ,
149+ transition : "green_to_red" ,
150+ failCount : 1 ,
151+ firstFailureAt : "2026-04-20T00:00:00Z" ,
152+ } ,
153+ result : probeRes ( "red" ) ,
154+ } ) ;
155+ await new Promise ( ( r ) => setImmediate ( r ) ) ;
156+ expect ( tgt . sent ) . toHaveLength ( 1 ) ;
157+ const text = ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
158+ expect ( text ) . toBe ( "[staging] RED mastra" ) ;
159+ } ) ;
160+
161+ it ( "prefixes a dispatched alert with the production source-env label" , async ( ) => {
162+ const e = engine ( {
163+ env : { dashboardUrl : "https://d" , repo : "r/r" , sourceEnv : "production" } ,
164+ } ) ;
165+ e . start ( ) ;
166+ e . reload ( [ baseRule ( ) ] ) ;
167+ bus . emit ( "status.changed" , {
168+ outcome : {
169+ previousState : "green" ,
170+ newState : "red" ,
171+ transition : "green_to_red" ,
172+ failCount : 1 ,
173+ firstFailureAt : "2026-04-20T00:00:00Z" ,
174+ } ,
175+ result : probeRes ( "red" ) ,
176+ } ) ;
177+ await new Promise ( ( r ) => setImmediate ( r ) ) ;
178+ expect ( tgt . sent ) . toHaveLength ( 1 ) ;
179+ const text = ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
180+ expect ( text ) . toBe ( "[production] RED mastra" ) ;
181+ } ) ;
182+
139183 it ( "does NOT dispatch unrelated transition" , async ( ) => {
140184 const e = engine ( ) ;
141185 e . start ( ) ;
@@ -349,7 +393,7 @@ describe("alert-engine", () => {
349393 await new Promise ( ( r ) => setImmediate ( r ) ) ;
350394 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
351395 const payload = ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
352- expect ( payload ) . toBe ( "!CH critical" ) ;
396+ expect ( payload ) . toBe ( "[unknown] !CH critical" ) ;
353397 } ) ;
354398
355399 it ( "on_error rule fires on transition===error" , async ( ) => {
@@ -378,7 +422,7 @@ describe("alert-engine", () => {
378422 await new Promise ( ( r ) => setImmediate ( r ) ) ;
379423 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
380424 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
381- "ERR: boom" ,
425+ "[unknown] ERR: boom" ,
382426 ) ;
383427 } ) ;
384428
@@ -413,7 +457,7 @@ describe("alert-engine", () => {
413457 await new Promise ( ( r ) => setImmediate ( r ) ) ;
414458 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
415459 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
416- "MID" ,
460+ "[unknown] MID" ,
417461 ) ;
418462 } ) ;
419463
@@ -452,7 +496,7 @@ describe("alert-engine", () => {
452496 await new Promise ( ( r ) => setImmediate ( r ) ) ;
453497 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
454498 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
455- "1 drifted" ,
499+ "[unknown] 1 drifted" ,
456500 ) ;
457501 } ) ;
458502
@@ -491,7 +535,7 @@ describe("alert-engine", () => {
491535 await new Promise ( ( r ) => setImmediate ( r ) ) ;
492536 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
493537 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
494- "1 unwired" ,
538+ "[unknown] 1 unwired" ,
495539 ) ;
496540 } ) ;
497541
@@ -542,7 +586,7 @@ describe("alert-engine", () => {
542586 // must be empty — this is what we're actually testing.
543587 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
544588 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
545- "" ,
589+ "[unknown] " ,
546590 ) ;
547591 } ) ;
548592
@@ -909,7 +953,7 @@ describe("alert-engine (additional behaviors)", () => {
909953 await new Promise ( ( r ) => setImmediate ( r ) ) ;
910954 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
911955 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
912- "DRIFT 2" ,
956+ "[unknown] DRIFT 2" ,
913957 ) ;
914958 } ) ;
915959
@@ -1002,7 +1046,7 @@ describe("alert-engine (additional behaviors)", () => {
10021046 await new Promise ( ( r ) => setImmediate ( r ) ) ;
10031047 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
10041048 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1005- "critical" ,
1049+ "[unknown] critical" ,
10061050 ) ;
10071051 } ) ;
10081052
@@ -1042,7 +1086,7 @@ describe("alert-engine (additional behaviors)", () => {
10421086 await new Promise ( ( r ) => setImmediate ( r ) ) ;
10431087 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
10441088 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1045- "expected=17 actual=14 drift=3" ,
1089+ "[unknown] expected=17 actual=14 drift=3" ,
10461090 ) ;
10471091 } ) ;
10481092
@@ -1165,7 +1209,7 @@ describe("alert-engine (additional behaviors)", () => {
11651209 expect ( tgt . sent ) . toHaveLength ( 3 ) ;
11661210 for ( const s of tgt . sent ) {
11671211 expect ( ( s as { payload : { text : string } } ) . payload . text ) . toBe (
1168- "Ecritical" ,
1212+ "[unknown] Ecritical" ,
11691213 ) ;
11701214 }
11711215 } ) ;
@@ -1306,7 +1350,7 @@ describe("alert-engine (additional behaviors)", () => {
13061350 await new Promise ( ( r ) => setImmediate ( r ) ) ;
13071351 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
13081352 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1309- "ERR" ,
1353+ "[unknown] ERR" ,
13101354 ) ;
13111355 } ) ;
13121356
@@ -1358,7 +1402,7 @@ describe("alert-engine (additional behaviors)", () => {
13581402 await new Promise ( ( r ) => setImmediate ( r ) ) ;
13591403 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
13601404 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1361- "GATED" ,
1405+ "[unknown] GATED" ,
13621406 ) ;
13631407 } ) ;
13641408
@@ -1425,7 +1469,7 @@ describe("alert-engine (additional behaviors)", () => {
14251469 await new Promise ( ( r ) => setImmediate ( r ) ) ;
14261470 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
14271471 expect ( ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1428- "run=https://ci/99 id=99" ,
1472+ "[unknown] run=https://ci/99 id=99" ,
14291473 ) ;
14301474 } ) ;
14311475} ) ;
@@ -1568,7 +1612,7 @@ describe("alert-engine isRedTick flag (F1.6)", () => {
15681612 await new Promise ( ( r ) => setImmediate ( r ) ) ;
15691613 expect ( sent ) . toHaveLength ( 1 ) ;
15701614 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
1571- expect ( text ) . toBe ( expected ? "RED" : "NOT" ) ;
1615+ expect ( text ) . toBe ( expected ? "[unknown] RED" : "[unknown] NOT" ) ;
15721616 e . stop ( ) ;
15731617 }
15741618
@@ -1716,7 +1760,7 @@ describe("alert-engine dispatchCronAlert fresh-red + error state (F1.2/F1.3)", (
17161760 // as "green", skipping the error branch). Post-fix: routed via onError.
17171761 expect ( sent ) . toHaveLength ( 1 ) ;
17181762 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
1719- expect ( text ) . toBe ( "ERR: GHCR 500" ) ;
1763+ expect ( text ) . toBe ( "[unknown] ERR: GHCR 500" ) ;
17201764 e . stop ( ) ;
17211765 } ) ;
17221766} ) ;
@@ -1784,7 +1828,7 @@ describe("alert-engine set_errored (F4.3)", () => {
17841828 await new Promise ( ( r ) => setImmediate ( r ) ) ;
17851829 expect ( sent ) . toHaveLength ( 1 ) ;
17861830 expect ( ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
1787- "ERR 1" ,
1831+ "[unknown] ERR 1" ,
17881832 ) ;
17891833 e . stop ( ) ;
17901834 } ) ;
@@ -1894,7 +1938,7 @@ describe("alert-engine R7 (A1/A4/A5/A9)", () => {
18941938 } ) ;
18951939 await new Promise ( ( r ) => setImmediate ( r ) ) ;
18961940 expect ( sent ) . toHaveLength ( 1 ) ;
1897- expect ( ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe ( "ERR" ) ;
1941+ expect ( ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe ( "[unknown] ERR" ) ;
18981942 e . stop ( ) ;
18991943 } ) ;
19001944
@@ -2296,7 +2340,7 @@ describe("alert-engine trigger.isRedTick coverage (HF-A2)", () => {
22962340 await new Promise ( ( r ) => setImmediate ( r ) ) ;
22972341 expect ( sent ) . toHaveLength ( 1 ) ;
22982342 expect ( ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ) . toBe (
2299- "RED" ,
2343+ "[unknown] RED" ,
23002344 ) ;
23012345 e . stop ( ) ;
23022346 } ) ;
@@ -2371,7 +2415,7 @@ describe("alert-engine escalation mention threading (HF-A3)", () => {
23712415 await new Promise ( ( r ) => setImmediate ( r ) ) ;
23722416 expect ( sent ) . toHaveLength ( 1 ) ;
23732417 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
2374- expect ( text ) . toBe ( "ping @oncall sev=error" ) ;
2418+ expect ( text ) . toBe ( "[unknown] ping @oncall sev=error" ) ;
23752419 e . stop ( ) ;
23762420 } ) ;
23772421
@@ -2436,7 +2480,7 @@ describe("alert-engine escalation mention threading (HF-A3)", () => {
24362480 await new Promise ( ( r ) => setImmediate ( r ) ) ;
24372481 expect ( sent ) . toHaveLength ( 1 ) ;
24382482 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
2439- expect ( text ) . toBe ( "m=[]" ) ;
2483+ expect ( text ) . toBe ( "[unknown] m=[]" ) ;
24402484 e . stop ( ) ;
24412485 } ) ;
24422486} ) ;
@@ -2578,7 +2622,7 @@ describe("alert-engine dispatchCronAlert preserves error state (HF-A6)", () => {
25782622 expect ( sent ) . toHaveLength ( 1 ) ;
25792623 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
25802624 // Routed to onError; main template would print "main".
2581- expect ( text ) . toBe ( "ERR GHCR 500" ) ;
2625+ expect ( text ) . toBe ( "[unknown] ERR GHCR 500" ) ;
25822626 e . stop ( ) ;
25832627 } ) ;
25842628} ) ;
@@ -2643,7 +2687,7 @@ describe("alert-engine dispatchCronAlert onError-only rule", () => {
26432687 await new Promise ( ( r ) => setImmediate ( r ) ) ;
26442688 expect ( sent ) . toHaveLength ( 1 ) ;
26452689 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
2646- expect ( text ) . toBe ( "errored! GHCR 500" ) ;
2690+ expect ( text ) . toBe ( "[unknown] errored! GHCR 500" ) ;
26472691 e . stop ( ) ;
26482692 } ) ;
26492693} ) ;
@@ -2860,7 +2904,7 @@ describe("alert-engine R24: dispatchCronAlert bootstrap gate asymmetry", () => {
28602904 await new Promise ( ( r ) => setImmediate ( r ) ) ;
28612905 expect ( sent ) . toHaveLength ( 1 ) ;
28622906 const text = ( sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
2863- expect ( text ) . toBe ( "drifted 1" ) ;
2907+ expect ( text ) . toBe ( "[unknown] drifted 1" ) ;
28642908 e . stop ( ) ;
28652909 } ) ;
28662910
@@ -3548,4 +3592,36 @@ describe("alert-engine aggregation ingress (A1)", () => {
35483592 expect ( tgt . sent ) . toHaveLength ( 1 ) ;
35493593 e . stop ( ) ;
35503594 } ) ;
3595+
3596+ it ( "prefixes the fleet aggregation alert with the source-env label" , async ( ) => {
3597+ // The aggregation flush path bypasses renderer.render (it Mustache-renders
3598+ // the aggregation template directly), so this pins that it still carries
3599+ // the same source-env prefix as per-key alerts.
3600+ const e = engine ( {
3601+ env : { dashboardUrl : "https://d" , repo : "r/r" , sourceEnv : "production" } ,
3602+ } ) ;
3603+ e . start ( ) ;
3604+ e . reload ( [ aggregatedSmokeRule ( ) ] ) ;
3605+ bus . emit ( "status.changed" , {
3606+ outcome : {
3607+ previousState : "green" ,
3608+ newState : "red" ,
3609+ transition : "green_to_red" ,
3610+ failCount : 1 ,
3611+ firstFailureAt : "2026-04-20T00:00:00Z" ,
3612+ } ,
3613+ result : {
3614+ key : "smoke:mastra" ,
3615+ state : "red" ,
3616+ signal : { slug : "mastra" , dimension : "smoke" } ,
3617+ observedAt : "2026-04-20T00:00:00Z" ,
3618+ } ,
3619+ } ) ;
3620+ await new Promise ( ( r ) => setImmediate ( r ) ) ;
3621+ await new Promise ( ( r ) => setImmediate ( r ) ) ;
3622+ expect ( tgt . sent ) . toHaveLength ( 1 ) ;
3623+ const text = ( tgt . sent [ 0 ] as { payload : { text : string } } ) . payload . text ;
3624+ expect ( text ) . toBe ( "[production] <!channel> fleet red 1" ) ;
3625+ e . stop ( ) ;
3626+ } ) ;
35513627} ) ;
0 commit comments