@@ -44,6 +44,7 @@ function generateScenarioData(scenario: Scenario, forceAnomaly: boolean): Metric
4444 const points : MetricDataPoint [ ] = [ ] ;
4545 const count = 20 ;
4646 const now = Date . now ( ) ;
47+ const pointIntervalMs = scenario === 'spike' && forceAnomaly ? 30_000 : 60_000 ;
4748
4849 // Calculate TTL expiry: now + 40 seconds
4950 const ttlExpiry = new Date ( now + SEED_TTL_SECONDS * 1000 ) . toISOString ( ) ;
@@ -53,7 +54,7 @@ function generateScenarioData(scenario: Scenario, forceAnomaly: boolean): Metric
5354
5455 for ( let i = 0 ; i < count ; i ++ ) {
5556 const t = i / ( count - 1 ) ; // 0 to 1
56- const timestamp = new Date ( now - ( count - 1 - i ) * 60_000 ) . toISOString ( ) ;
57+ const timestamp = new Date ( now - ( count - 1 - i ) * pointIntervalMs ) . toISOString ( ) ;
5758
5859 let cpuUsage : number ;
5960 let txPoolPending : number ;
@@ -76,15 +77,18 @@ function generateScenarioData(scenario: Scenario, forceAnomaly: boolean): Metric
7677 break ;
7778
7879 case 'spike' :
79- if ( i < count - 5 ) {
80+ // Keep a longer deterministic tail for forceAnomaly mode so the
81+ // 5-minute monotonic window always lands on increasing data points.
82+ const spikeTailLength = forceAnomaly ? 12 : 5 ;
83+ if ( i < count - spikeTailLength ) {
8084 cpuUsage = jitter ( 30 , 3 ) ;
8185 txPoolPending = Math . round ( jitter ( 50 , 15 ) ) ;
8286 gasUsedRatio = jitter ( 0.25 , 0.05 ) ;
8387 blockInterval = jitter ( 2.0 , 0.2 ) ;
8488 } else {
8589 // Keep the tail deterministic and strictly increasing so monotonic rule
8690 // consistently triggers in demo/repro environments.
87- const tailIndex = i - ( count - 5 ) ; // 0..4
91+ const tailIndex = i - ( count - spikeTailLength ) ;
8892 const baseTx = forceAnomaly ? 5000 : 2400 ;
8993 const stepTx = forceAnomaly ? 1200 : 500 ;
9094
0 commit comments