@prism-d1/cli 1.0.26 → 1.0.28

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.
Files changed (56) hide show
  1. package/dist/assets/eval-harness/README.md +114 -0
  2. package/dist/assets/eval-harness/eval-config.json +10 -0
  3. package/dist/assets/eval-harness/rubrics/agent-quality.json +79 -0
  4. package/dist/assets/eval-harness/rubrics/api-response-quality.json +45 -0
  5. package/dist/assets/eval-harness/rubrics/code-quality.json +98 -0
  6. package/dist/assets/eval-harness/rubrics/security-compliance.json +145 -0
  7. package/dist/assets/eval-harness/rubrics/spec-compliance.json +67 -0
  8. package/dist/assets/eval-harness/run-eval.sh +122 -0
  9. package/dist/assets/github-workflows/README.md +110 -0
  10. package/dist/assets/github-workflows/prism-agent-eval.yml +313 -0
  11. package/dist/assets/github-workflows/prism-ai-metrics.yml +261 -0
  12. package/dist/assets/github-workflows/prism-dora-weekly.yml +334 -0
  13. package/dist/assets/github-workflows/prism-eval-gate.yml +310 -0
  14. package/dist/assets/infra/bin/app.ts +56 -0
  15. package/dist/assets/infra/cdk.json +12 -0
  16. package/dist/assets/infra/lib/api-stack.ts +347 -0
  17. package/dist/assets/infra/lib/constructs/bedrock-guardrail-construct.ts +201 -0
  18. package/dist/assets/infra/lib/constructs/guardrail-enforcer-construct.ts +59 -0
  19. package/dist/assets/infra/lib/constructs/prism-vpc-construct.ts +75 -0
  20. package/dist/assets/infra/lib/constructs/security-agent-construct.ts +266 -0
  21. package/dist/assets/infra/lib/dashboard-stack.ts +1392 -0
  22. package/dist/assets/infra/lib/lambda/api-handler.ts +477 -0
  23. package/dist/assets/infra/lib/lambda/defect-correlator.ts +142 -0
  24. package/dist/assets/infra/lib/lambda/exfiltration-detector.ts +100 -0
  25. package/dist/assets/infra/lib/lambda/layers/guardrail-enforcer/nodejs/guardrail-enforcer.js +53 -0
  26. package/dist/assets/infra/lib/lambda/metrics-processor.ts +748 -0
  27. package/dist/assets/infra/lib/lambda/security-agent-processor.ts +231 -0
  28. package/dist/assets/infra/lib/lambda/security-remediation-tracker.ts +120 -0
  29. package/dist/assets/infra/lib/lambda/security-response-automator.ts +130 -0
  30. package/dist/assets/infra/lib/lambda/spec-to-code-calculator.ts +123 -0
  31. package/dist/assets/infra/lib/metrics-pipeline-stack.ts +701 -0
  32. package/dist/assets/infra/package.json +23 -0
  33. package/dist/assets/infra/tsconfig.json +24 -0
  34. package/dist/src/commands/bootstrapper/install-eval-harness.d.ts.map +1 -1
  35. package/dist/src/commands/bootstrapper/install-eval-harness.js +3 -4
  36. package/dist/src/commands/bootstrapper/install-eval-harness.js.map +1 -1
  37. package/dist/src/commands/bootstrapper/install-git-hooks.d.ts.map +1 -1
  38. package/dist/src/commands/bootstrapper/install-git-hooks.js +2 -5
  39. package/dist/src/commands/bootstrapper/install-git-hooks.js.map +1 -1
  40. package/dist/src/commands/securityagent/setup.d.ts.map +1 -1
  41. package/dist/src/commands/securityagent/setup.js +2 -3
  42. package/dist/src/commands/securityagent/setup.js.map +1 -1
  43. package/dist/src/commands/workshop/deploy-infra.d.ts.map +1 -1
  44. package/dist/src/commands/workshop/deploy-infra.js +2 -3
  45. package/dist/src/commands/workshop/deploy-infra.js.map +1 -1
  46. package/dist/src/commands/workshop/generate-demo-data.d.ts.map +1 -1
  47. package/dist/src/commands/workshop/generate-demo-data.js +3 -8
  48. package/dist/src/commands/workshop/generate-demo-data.js.map +1 -1
  49. package/dist/src/commands/workshop/perform-pen-test.d.ts.map +1 -1
  50. package/dist/src/commands/workshop/perform-pen-test.js +5 -14
  51. package/dist/src/commands/workshop/perform-pen-test.js.map +1 -1
  52. package/dist/src/utils/root.d.ts +6 -0
  53. package/dist/src/utils/root.d.ts.map +1 -1
  54. package/dist/src/utils/root.js +29 -0
  55. package/dist/src/utils/root.js.map +1 -1
  56. package/package.json +2 -2
@@ -0,0 +1,1392 @@
1
+ import * as cdk from 'aws-cdk-lib';
2
+ import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
3
+ import { Construct } from 'constructs';
4
+ import { NagSuppressions } from 'cdk-nag';
5
+
6
+ const METRIC_NAMESPACE = 'PRISM/D1/Velocity';
7
+ const DEFAULT_PERIOD = cdk.Duration.hours(1);
8
+
9
+ export class DashboardStack extends cdk.Stack {
10
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
11
+ super(scope, id, props);
12
+
13
+ // cdk-nag: CloudWatch alarms in this stack are for observability dashboards.
14
+ // SNS alarm actions are configured by operators post-deployment.
15
+ NagSuppressions.addStackSuppressions(this, [
16
+ {
17
+ id: 'AwsSolutions-SNS2',
18
+ reason: 'Alarm notification topics are configured post-deployment by operators.',
19
+ },
20
+ {
21
+ id: 'AwsSolutions-SNS3',
22
+ reason: 'Alarm notification topics are configured post-deployment by operators.',
23
+ },
24
+ ]);
25
+
26
+ // =======================================================
27
+ // Dashboard 1: Team Velocity
28
+ // =======================================================
29
+ const teamDashboard = new cloudwatch.Dashboard(this, 'TeamVelocityDashboard', {
30
+ dashboardName: 'PRISM-D1-Team-Velocity',
31
+ defaultInterval: cdk.Duration.days(7),
32
+ });
33
+
34
+ // --- AI Acceptance Rate (time series) ---
35
+ const aiAcceptanceMetric = new cloudwatch.Metric({
36
+ namespace: METRIC_NAMESPACE,
37
+ metricName: 'AIAcceptanceRate',
38
+ statistic: 'Average',
39
+ period: DEFAULT_PERIOD,
40
+ label: 'AI Acceptance Rate (%)',
41
+ });
42
+
43
+ teamDashboard.addWidgets(
44
+ new cloudwatch.TextWidget({
45
+ markdown: '# PRISM D1 - Team Velocity Dashboard\nReal-time AI-enhanced DORA metrics for engineering teams.',
46
+ width: 24,
47
+ height: 1,
48
+ }),
49
+ );
50
+
51
+ teamDashboard.addWidgets(
52
+ new cloudwatch.GraphWidget({
53
+ title: 'AI Acceptance Rate',
54
+ left: [aiAcceptanceMetric],
55
+ width: 12,
56
+ height: 6,
57
+ leftYAxis: { min: 0, max: 100, label: 'Percent' },
58
+ statistic: 'Average',
59
+ period: DEFAULT_PERIOD,
60
+ }),
61
+ new cloudwatch.GraphWidget({
62
+ title: 'Deployment Frequency',
63
+ left: [
64
+ new cloudwatch.Metric({
65
+ namespace: METRIC_NAMESPACE,
66
+ metricName: 'DeploymentFrequency',
67
+ statistic: 'Sum',
68
+ period: cdk.Duration.days(1),
69
+ label: 'Deploys / Day',
70
+ }),
71
+ ],
72
+ width: 12,
73
+ height: 6,
74
+ view: cloudwatch.GraphWidgetView.BAR,
75
+ leftYAxis: { min: 0, label: 'Deployments' },
76
+ }),
77
+ );
78
+
79
+ teamDashboard.addWidgets(
80
+ new cloudwatch.GraphWidget({
81
+ title: 'Lead Time for Changes',
82
+ left: [
83
+ new cloudwatch.Metric({
84
+ namespace: METRIC_NAMESPACE,
85
+ metricName: 'LeadTimeForChanges',
86
+ statistic: 'Average',
87
+ period: DEFAULT_PERIOD,
88
+ label: 'Lead Time (seconds)',
89
+ }),
90
+ ],
91
+ width: 8,
92
+ height: 6,
93
+ leftYAxis: { min: 0, label: 'Seconds' },
94
+ }),
95
+ new cloudwatch.GaugeWidget({
96
+ title: 'Eval Gate Pass Rate',
97
+ metrics: [
98
+ new cloudwatch.Metric({
99
+ namespace: METRIC_NAMESPACE,
100
+ metricName: 'EvalGatePassRate',
101
+ statistic: 'Average',
102
+ period: cdk.Duration.days(1),
103
+ label: 'Eval Pass Rate (%)',
104
+ }),
105
+ ],
106
+ width: 8,
107
+ height: 6,
108
+ leftYAxis: { min: 0, max: 100 },
109
+ }),
110
+ new cloudwatch.GraphWidget({
111
+ title: 'AI Test Coverage Delta',
112
+ left: [
113
+ new cloudwatch.Metric({
114
+ namespace: METRIC_NAMESPACE,
115
+ metricName: 'AITestCoverageDelta',
116
+ statistic: 'Average',
117
+ period: DEFAULT_PERIOD,
118
+ label: 'Coverage Delta (%)',
119
+ }),
120
+ ],
121
+ width: 8,
122
+ height: 6,
123
+ leftYAxis: { label: 'Percent' },
124
+ }),
125
+ );
126
+
127
+ // --- Additional team metrics row ---
128
+ teamDashboard.addWidgets(
129
+ new cloudwatch.GraphWidget({
130
+ title: 'Change Failure Rate',
131
+ left: [
132
+ new cloudwatch.Metric({
133
+ namespace: METRIC_NAMESPACE,
134
+ metricName: 'ChangeFailureRate',
135
+ statistic: 'Average',
136
+ period: DEFAULT_PERIOD,
137
+ label: 'Change Failure Rate (%)',
138
+ }),
139
+ ],
140
+ width: 8,
141
+ height: 6,
142
+ leftYAxis: { min: 0, max: 100, label: 'Percent' },
143
+ }),
144
+ new cloudwatch.GraphWidget({
145
+ title: 'Mean Time to Recovery',
146
+ left: [
147
+ new cloudwatch.Metric({
148
+ namespace: METRIC_NAMESPACE,
149
+ metricName: 'MTTR',
150
+ statistic: 'Average',
151
+ period: DEFAULT_PERIOD,
152
+ label: 'MTTR (seconds)',
153
+ }),
154
+ ],
155
+ width: 8,
156
+ height: 6,
157
+ leftYAxis: { min: 0, label: 'Seconds' },
158
+ }),
159
+ new cloudwatch.GraphWidget({
160
+ title: 'AI to Merge Ratio',
161
+ left: [
162
+ new cloudwatch.Metric({
163
+ namespace: METRIC_NAMESPACE,
164
+ metricName: 'AIToMergeRatio',
165
+ statistic: 'Average',
166
+ period: DEFAULT_PERIOD,
167
+ label: 'AI-to-Merge Ratio (%)',
168
+ }),
169
+ ],
170
+ width: 8,
171
+ height: 6,
172
+ leftYAxis: { min: 0, max: 100, label: 'Percent' },
173
+ }),
174
+ );
175
+
176
+ // --- Agent Metrics Row ---
177
+ const teamId = 'ALL';
178
+ const repository = 'ALL';
179
+
180
+ teamDashboard.addWidgets(
181
+ new cloudwatch.TextWidget({
182
+ markdown: '### AI Token Usage & Cost',
183
+ width: 24,
184
+ height: 1,
185
+ }),
186
+ );
187
+
188
+ teamDashboard.addWidgets(
189
+ new cloudwatch.GraphWidget({
190
+ title: 'AI Input Tokens per PR',
191
+ left: [
192
+ new cloudwatch.Metric({
193
+ namespace: METRIC_NAMESPACE,
194
+ metricName: 'AIInputTokens',
195
+ statistic: 'Sum',
196
+ period: DEFAULT_PERIOD,
197
+ label: 'Input Tokens',
198
+ }),
199
+ ],
200
+ width: 8,
201
+ height: 6,
202
+ leftYAxis: { min: 0, label: 'Tokens' },
203
+ }),
204
+ new cloudwatch.GraphWidget({
205
+ title: 'AI Output Tokens per PR',
206
+ left: [
207
+ new cloudwatch.Metric({
208
+ namespace: METRIC_NAMESPACE,
209
+ metricName: 'AIOutputTokens',
210
+ statistic: 'Sum',
211
+ period: DEFAULT_PERIOD,
212
+ label: 'Output Tokens',
213
+ }),
214
+ ],
215
+ width: 8,
216
+ height: 6,
217
+ leftYAxis: { min: 0, label: 'Tokens' },
218
+ }),
219
+ new cloudwatch.GraphWidget({
220
+ title: 'AI Cost per PR (USD)',
221
+ left: [
222
+ new cloudwatch.Metric({
223
+ namespace: METRIC_NAMESPACE,
224
+ metricName: 'AICostUSD',
225
+ statistic: 'Sum',
226
+ period: DEFAULT_PERIOD,
227
+ label: 'Cost (USD)',
228
+ }),
229
+ ],
230
+ width: 8,
231
+ height: 6,
232
+ leftYAxis: { min: 0, label: 'USD' },
233
+ }),
234
+ );
235
+
236
+ teamDashboard.addWidgets(
237
+ new cloudwatch.TextWidget({
238
+ markdown: '### Agent Operations',
239
+ width: 24,
240
+ height: 1,
241
+ }),
242
+ );
243
+
244
+ teamDashboard.addWidgets(
245
+ new cloudwatch.GraphWidget({
246
+ title: 'Agent Invocations',
247
+ left: [
248
+ new cloudwatch.Metric({
249
+ namespace: METRIC_NAMESPACE,
250
+ metricName: 'AgentInvocationCount',
251
+ dimensionsMap: { TeamId: teamId, Repository: repository },
252
+ statistic: 'Sum',
253
+ period: DEFAULT_PERIOD,
254
+ label: 'Invocations',
255
+ }),
256
+ ],
257
+ width: 8,
258
+ height: 6,
259
+ }),
260
+ new cloudwatch.GraphWidget({
261
+ title: 'Agent Success Rate',
262
+ left: [
263
+ new cloudwatch.Metric({
264
+ namespace: METRIC_NAMESPACE,
265
+ metricName: 'AgentSuccessRate',
266
+ dimensionsMap: { TeamId: teamId, Repository: repository },
267
+ statistic: 'Average',
268
+ period: DEFAULT_PERIOD,
269
+ label: 'Success Rate (%)',
270
+ }),
271
+ ],
272
+ width: 8,
273
+ height: 6,
274
+ leftYAxis: { min: 0, max: 100, label: 'Percent' },
275
+ }),
276
+ new cloudwatch.GraphWidget({
277
+ title: 'Agent Avg Duration',
278
+ left: [
279
+ new cloudwatch.Metric({
280
+ namespace: METRIC_NAMESPACE,
281
+ metricName: 'AgentDurationMs',
282
+ dimensionsMap: { TeamId: teamId, Repository: repository },
283
+ statistic: 'Average',
284
+ period: DEFAULT_PERIOD,
285
+ label: 'Duration (ms)',
286
+ }),
287
+ ],
288
+ width: 8,
289
+ height: 6,
290
+ }),
291
+ );
292
+
293
+ // =======================================================
294
+ // Dashboard 2: Executive Readout
295
+ // =======================================================
296
+ const execDashboard = new cloudwatch.Dashboard(this, 'ExecutiveReadoutDashboard', {
297
+ dashboardName: 'PRISM-D1-Executive-Readout',
298
+ defaultInterval: cdk.Duration.days(30),
299
+ });
300
+
301
+ execDashboard.addWidgets(
302
+ new cloudwatch.TextWidget({
303
+ markdown: '# PRISM D1 - Executive Readout\nLeadership view of AI-assisted engineering velocity and DORA performance.',
304
+ width: 24,
305
+ height: 1,
306
+ }),
307
+ );
308
+
309
+ // --- PRISM Level Progress + Enhanced DORA Summary row ---
310
+ execDashboard.addWidgets(
311
+ new cloudwatch.SingleValueWidget({
312
+ title: 'PRISM Level Progress',
313
+ metrics: [
314
+ new cloudwatch.Metric({
315
+ namespace: METRIC_NAMESPACE,
316
+ metricName: 'PRISMLevel',
317
+ statistic: 'Maximum',
318
+ period: cdk.Duration.days(1),
319
+ label: 'Current PRISM Level',
320
+ }),
321
+ ],
322
+ width: 6,
323
+ height: 4,
324
+ }),
325
+ new cloudwatch.SingleValueWidget({
326
+ title: 'Enhanced DORA Summary',
327
+ metrics: [
328
+ new cloudwatch.Metric({
329
+ namespace: METRIC_NAMESPACE,
330
+ metricName: 'DeploymentFrequency',
331
+ statistic: 'Sum',
332
+ period: cdk.Duration.days(7),
333
+ label: 'Deploy Freq (7d)',
334
+ }),
335
+ new cloudwatch.Metric({
336
+ namespace: METRIC_NAMESPACE,
337
+ metricName: 'LeadTimeForChanges',
338
+ statistic: 'Average',
339
+ period: cdk.Duration.days(7),
340
+ label: 'Avg Lead Time (s)',
341
+ }),
342
+ new cloudwatch.Metric({
343
+ namespace: METRIC_NAMESPACE,
344
+ metricName: 'ChangeFailureRate',
345
+ statistic: 'Average',
346
+ period: cdk.Duration.days(7),
347
+ label: 'Change Fail Rate (%)',
348
+ }),
349
+ new cloudwatch.Metric({
350
+ namespace: METRIC_NAMESPACE,
351
+ metricName: 'MTTR',
352
+ statistic: 'Average',
353
+ period: cdk.Duration.days(7),
354
+ label: 'Avg MTTR (s)',
355
+ }),
356
+ ],
357
+ width: 12,
358
+ height: 4,
359
+ }),
360
+ new cloudwatch.SingleValueWidget({
361
+ title: 'AI Cost & Cycle Time',
362
+ metrics: [
363
+ new cloudwatch.Metric({
364
+ namespace: METRIC_NAMESPACE,
365
+ metricName: 'AICostUSD',
366
+ statistic: 'Sum',
367
+ period: cdk.Duration.days(7),
368
+ label: 'Total AI Cost (7d, USD)',
369
+ }),
370
+ new cloudwatch.Metric({
371
+ namespace: METRIC_NAMESPACE,
372
+ metricName: 'SpecToCodeHours',
373
+ statistic: 'Average',
374
+ period: cdk.Duration.days(7),
375
+ label: 'Avg Spec-to-Code (hrs)',
376
+ }),
377
+ ],
378
+ width: 6,
379
+ height: 4,
380
+ }),
381
+ );
382
+
383
+ // --- AI Contribution Trend + Feature Cycle Time Trend ---
384
+ execDashboard.addWidgets(
385
+ new cloudwatch.GraphWidget({
386
+ title: 'AI Contribution Trend',
387
+ left: [
388
+ new cloudwatch.Metric({
389
+ namespace: METRIC_NAMESPACE,
390
+ metricName: 'AIAcceptanceRate',
391
+ statistic: 'Average',
392
+ period: cdk.Duration.days(1),
393
+ label: 'AI Acceptance Rate (%)',
394
+ }),
395
+ new cloudwatch.Metric({
396
+ namespace: METRIC_NAMESPACE,
397
+ metricName: 'AIToMergeRatio',
398
+ statistic: 'Average',
399
+ period: cdk.Duration.days(1),
400
+ label: 'AI-to-Merge Ratio (%)',
401
+ }),
402
+ new cloudwatch.Metric({
403
+ namespace: METRIC_NAMESPACE,
404
+ metricName: 'AITestCoverageDelta',
405
+ statistic: 'Average',
406
+ period: cdk.Duration.days(1),
407
+ label: 'AI Test Coverage Delta (%)',
408
+ }),
409
+ ],
410
+ width: 12,
411
+ height: 6,
412
+ leftYAxis: { min: 0, max: 100, label: 'Percent' },
413
+ }),
414
+ new cloudwatch.GraphWidget({
415
+ title: 'Feature Cycle Time Trend',
416
+ left: [
417
+ new cloudwatch.Metric({
418
+ namespace: METRIC_NAMESPACE,
419
+ metricName: 'SpecToCodeHours',
420
+ statistic: 'Average',
421
+ period: cdk.Duration.days(1),
422
+ label: 'Spec-to-Code (hrs)',
423
+ }),
424
+ new cloudwatch.Metric({
425
+ namespace: METRIC_NAMESPACE,
426
+ metricName: 'LeadTimeForChanges',
427
+ statistic: 'Average',
428
+ period: cdk.Duration.days(1),
429
+ label: 'Lead Time (seconds)',
430
+ }),
431
+ ],
432
+ width: 12,
433
+ height: 6,
434
+ }),
435
+ );
436
+
437
+ // --- Eval gate, quality, and cost trend row ---
438
+ execDashboard.addWidgets(
439
+ new cloudwatch.GaugeWidget({
440
+ title: 'Eval Gate Pass Rate',
441
+ metrics: [
442
+ new cloudwatch.Metric({
443
+ namespace: METRIC_NAMESPACE,
444
+ metricName: 'EvalGatePassRate',
445
+ statistic: 'Average',
446
+ period: cdk.Duration.days(7),
447
+ label: 'Eval Pass Rate (%)',
448
+ }),
449
+ ],
450
+ width: 6,
451
+ height: 6,
452
+ leftYAxis: { min: 0, max: 100 },
453
+ }),
454
+ new cloudwatch.GraphWidget({
455
+ title: 'Post-Merge Defect Rate',
456
+ left: [
457
+ new cloudwatch.Metric({
458
+ namespace: METRIC_NAMESPACE,
459
+ metricName: 'PostMergeDefectRate',
460
+ statistic: 'Average',
461
+ period: cdk.Duration.days(1),
462
+ label: 'Defect Rate (%)',
463
+ }),
464
+ ],
465
+ width: 6,
466
+ height: 6,
467
+ leftYAxis: { min: 0, label: 'Percent' },
468
+ }),
469
+ new cloudwatch.GraphWidget({
470
+ title: 'AI Cost Trend (Weekly)',
471
+ left: [
472
+ new cloudwatch.Metric({
473
+ namespace: METRIC_NAMESPACE,
474
+ metricName: 'AICostUSD',
475
+ statistic: 'Sum',
476
+ period: cdk.Duration.days(7),
477
+ label: 'Total Cost (USD)',
478
+ }),
479
+ ],
480
+ width: 6,
481
+ height: 6,
482
+ leftYAxis: { min: 0, label: 'USD' },
483
+ }),
484
+ new cloudwatch.GraphWidget({
485
+ title: 'Deployment Frequency (Weekly)',
486
+ left: [
487
+ new cloudwatch.Metric({
488
+ namespace: METRIC_NAMESPACE,
489
+ metricName: 'DeploymentFrequency',
490
+ statistic: 'Sum',
491
+ period: cdk.Duration.days(7),
492
+ label: 'Deploys / Week',
493
+ }),
494
+ ],
495
+ width: 6,
496
+ height: 6,
497
+ view: cloudwatch.GraphWidgetView.BAR,
498
+ leftYAxis: { min: 0, label: 'Deployments' },
499
+ }),
500
+ );
501
+
502
+ // =======================================================
503
+ // CloudWatch Alarms
504
+ // =======================================================
505
+
506
+ // Alarm: AI acceptance rate dropping below 20%
507
+ new cloudwatch.Alarm(this, 'AiAcceptanceRateLowAlarm', {
508
+ alarmName: 'PRISM-D1-AIAcceptanceRate-Low',
509
+ alarmDescription: 'AI acceptance rate has dropped below 20%, indicating potential issues with AI-generated code quality or review friction.',
510
+ metric: new cloudwatch.Metric({
511
+ namespace: METRIC_NAMESPACE,
512
+ metricName: 'AIAcceptanceRate',
513
+ statistic: 'Average',
514
+ period: cdk.Duration.hours(6),
515
+ }),
516
+ threshold: 20,
517
+ evaluationPeriods: 3,
518
+ datapointsToAlarm: 2,
519
+ comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
520
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
521
+ });
522
+
523
+ // Alarm: Eval gate pass rate dropping below 70%
524
+ new cloudwatch.Alarm(this, 'EvalGatePassRateLowAlarm', {
525
+ alarmName: 'PRISM-D1-EvalGatePassRate-Low',
526
+ alarmDescription: 'Eval gate pass rate has dropped below 70%, indicating degraded quality in AI-generated outputs.',
527
+ metric: new cloudwatch.Metric({
528
+ namespace: METRIC_NAMESPACE,
529
+ metricName: 'EvalGatePassRate',
530
+ statistic: 'Average',
531
+ period: cdk.Duration.hours(6),
532
+ }),
533
+ threshold: 70,
534
+ evaluationPeriods: 3,
535
+ datapointsToAlarm: 2,
536
+ comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
537
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
538
+ });
539
+
540
+ // Alarm: Change failure rate exceeding 20%
541
+ new cloudwatch.Alarm(this, 'ChangeFailureRateHighAlarm', {
542
+ alarmName: 'PRISM-D1-ChangeFailureRate-High',
543
+ alarmDescription: 'Change failure rate exceeds 20%, indicating deployment quality regression.',
544
+ metric: new cloudwatch.Metric({
545
+ namespace: METRIC_NAMESPACE,
546
+ metricName: 'ChangeFailureRate',
547
+ statistic: 'Average',
548
+ period: cdk.Duration.hours(6),
549
+ }),
550
+ threshold: 20,
551
+ evaluationPeriods: 3,
552
+ datapointsToAlarm: 2,
553
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
554
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
555
+ });
556
+
557
+ // =======================================================
558
+ // Team Dashboard: Eval Gate by Rubric (Pillar 2)
559
+ // =======================================================
560
+ teamDashboard.addWidgets(
561
+ new cloudwatch.TextWidget({
562
+ markdown: '### Eval Gate Quality by Rubric',
563
+ width: 24,
564
+ height: 1,
565
+ }),
566
+ );
567
+
568
+ const rubricNames = ['code-quality', 'api-response-quality', 'agent-quality', 'security-compliance', 'spec-compliance'];
569
+ teamDashboard.addWidgets(
570
+ new cloudwatch.GraphWidget({
571
+ title: 'Eval Pass Rate by Rubric',
572
+ left: rubricNames.map((rubric) =>
573
+ new cloudwatch.Metric({
574
+ namespace: METRIC_NAMESPACE,
575
+ metricName: 'EvalGatePassRateByRubric',
576
+ dimensionsMap: { RubricName: rubric },
577
+ statistic: 'Average',
578
+ period: cdk.Duration.days(1),
579
+ label: rubric,
580
+ }),
581
+ ),
582
+ width: 12,
583
+ height: 6,
584
+ leftYAxis: { min: 0, max: 100, label: 'Pass Rate (%)' },
585
+ }),
586
+ new cloudwatch.GraphWidget({
587
+ title: 'Eval Score Trend',
588
+ left: [
589
+ new cloudwatch.Metric({
590
+ namespace: METRIC_NAMESPACE,
591
+ metricName: 'EvalScore',
592
+ statistic: 'Average',
593
+ period: cdk.Duration.days(1),
594
+ label: 'Avg Eval Score',
595
+ }),
596
+ ],
597
+ width: 12,
598
+ height: 6,
599
+ leftYAxis: { min: 0, max: 1, label: 'Score (0-1)' },
600
+ }),
601
+ );
602
+
603
+ // =======================================================
604
+ // Team Dashboard: Guardrails & Safety (Pillar 4)
605
+ // =======================================================
606
+ teamDashboard.addWidgets(
607
+ new cloudwatch.TextWidget({
608
+ markdown: '### Guardrails & Safety',
609
+ width: 24,
610
+ height: 1,
611
+ }),
612
+ );
613
+
614
+ const guardrailCategories = ['CONTENT_FILTER', 'DENIED_TOPIC', 'SENSITIVE_INFO', 'WORD_FILTER'];
615
+ teamDashboard.addWidgets(
616
+ new cloudwatch.GraphWidget({
617
+ title: 'Guardrail Triggers by Category',
618
+ left: guardrailCategories.map((category) =>
619
+ new cloudwatch.Metric({
620
+ namespace: METRIC_NAMESPACE,
621
+ metricName: 'GuardrailTriggerCount',
622
+ dimensionsMap: { TriggerCategory: category },
623
+ statistic: 'Sum',
624
+ period: DEFAULT_PERIOD,
625
+ label: category,
626
+ }),
627
+ ),
628
+ width: 12,
629
+ height: 6,
630
+ leftYAxis: { min: 0, label: 'Triggers' },
631
+ view: cloudwatch.GraphWidgetView.BAR,
632
+ }),
633
+ new cloudwatch.GraphWidget({
634
+ title: 'Guardrail Actions: Block vs Anonymize',
635
+ left: [
636
+ new cloudwatch.Metric({
637
+ namespace: METRIC_NAMESPACE,
638
+ metricName: 'GuardrailBlockCount',
639
+ statistic: 'Sum',
640
+ period: DEFAULT_PERIOD,
641
+ label: 'Blocked',
642
+ }),
643
+ new cloudwatch.Metric({
644
+ namespace: METRIC_NAMESPACE,
645
+ metricName: 'GuardrailAnonymizeCount',
646
+ statistic: 'Sum',
647
+ period: DEFAULT_PERIOD,
648
+ label: 'Anonymized',
649
+ }),
650
+ ],
651
+ width: 12,
652
+ height: 6,
653
+ leftYAxis: { min: 0, label: 'Count' },
654
+ }),
655
+ );
656
+
657
+ // =======================================================
658
+ // Team Dashboard: MCP Tool Governance (Pillar 3)
659
+ // =======================================================
660
+ teamDashboard.addWidgets(
661
+ new cloudwatch.TextWidget({
662
+ markdown: '### MCP Tool Governance',
663
+ width: 24,
664
+ height: 1,
665
+ }),
666
+ );
667
+
668
+ teamDashboard.addWidgets(
669
+ new cloudwatch.GraphWidget({
670
+ title: 'MCP Tool Call Volume',
671
+ left: [
672
+ new cloudwatch.Metric({
673
+ namespace: METRIC_NAMESPACE,
674
+ metricName: 'MCPToolCallCount',
675
+ statistic: 'Sum',
676
+ period: DEFAULT_PERIOD,
677
+ label: 'Tool Calls',
678
+ }),
679
+ ],
680
+ width: 12,
681
+ height: 6,
682
+ leftYAxis: { min: 0, label: 'Calls' },
683
+ }),
684
+ new cloudwatch.GraphWidget({
685
+ title: 'MCP Auth Denied Rate',
686
+ left: [
687
+ new cloudwatch.Metric({
688
+ namespace: METRIC_NAMESPACE,
689
+ metricName: 'MCPAuthDeniedCount',
690
+ statistic: 'Sum',
691
+ period: DEFAULT_PERIOD,
692
+ label: 'Denied Calls',
693
+ }),
694
+ ],
695
+ width: 12,
696
+ height: 6,
697
+ leftYAxis: { min: 0, label: 'Denied' },
698
+ }),
699
+ );
700
+
701
+ // =======================================================
702
+ // Team Dashboard: Cost Intelligence (Pillar 5)
703
+ // =======================================================
704
+ teamDashboard.addWidgets(
705
+ new cloudwatch.TextWidget({
706
+ markdown: '### Cost Intelligence',
707
+ width: 24,
708
+ height: 1,
709
+ }),
710
+ );
711
+
712
+ teamDashboard.addWidgets(
713
+ new cloudwatch.GraphWidget({
714
+ title: 'Bedrock Cost (USD)',
715
+ left: [
716
+ new cloudwatch.Metric({
717
+ namespace: METRIC_NAMESPACE,
718
+ metricName: 'BedrockCostUSD',
719
+ statistic: 'Sum',
720
+ period: cdk.Duration.days(1),
721
+ label: 'Daily Cost ($)',
722
+ }),
723
+ ],
724
+ width: 12,
725
+ height: 6,
726
+ leftYAxis: { min: 0, label: 'USD' },
727
+ }),
728
+ new cloudwatch.GraphWidget({
729
+ title: 'Token Efficiency',
730
+ left: [
731
+ new cloudwatch.Metric({
732
+ namespace: METRIC_NAMESPACE,
733
+ metricName: 'TokenEfficiency',
734
+ statistic: 'Average',
735
+ period: cdk.Duration.days(1),
736
+ label: 'Tokens per Line Changed',
737
+ }),
738
+ ],
739
+ width: 12,
740
+ height: 6,
741
+ leftYAxis: { min: 0, label: 'Tokens/Line' },
742
+ }),
743
+ );
744
+
745
+ // =======================================================
746
+ // Team Dashboard: AI Attribution (Pillar 7)
747
+ // =======================================================
748
+ teamDashboard.addWidgets(
749
+ new cloudwatch.TextWidget({
750
+ markdown: '### AI Attribution & Quality',
751
+ width: 24,
752
+ height: 1,
753
+ }),
754
+ );
755
+
756
+ teamDashboard.addWidgets(
757
+ new cloudwatch.GraphWidget({
758
+ title: 'Defect Rate: AI vs Human Code',
759
+ left: [
760
+ new cloudwatch.Metric({
761
+ namespace: METRIC_NAMESPACE,
762
+ metricName: 'PostMergeDefectRateAI',
763
+ statistic: 'Average',
764
+ period: cdk.Duration.days(1),
765
+ label: 'AI Code Defect Rate (%)',
766
+ }),
767
+ new cloudwatch.Metric({
768
+ namespace: METRIC_NAMESPACE,
769
+ metricName: 'PostMergeDefectRateHuman',
770
+ statistic: 'Average',
771
+ period: cdk.Duration.days(1),
772
+ label: 'Human Code Defect Rate (%)',
773
+ }),
774
+ ],
775
+ width: 12,
776
+ height: 6,
777
+ leftYAxis: { min: 0, label: 'Defect Rate (%)' },
778
+ }),
779
+ new cloudwatch.GraphWidget({
780
+ title: 'Spec-to-Code Hours',
781
+ left: [
782
+ new cloudwatch.Metric({
783
+ namespace: METRIC_NAMESPACE,
784
+ metricName: 'SpecToCodeHours',
785
+ statistic: 'Average',
786
+ period: cdk.Duration.days(1),
787
+ label: 'Avg Hours',
788
+ }),
789
+ ],
790
+ width: 12,
791
+ height: 6,
792
+ leftYAxis: { min: 0, label: 'Hours' },
793
+ }),
794
+ );
795
+
796
+ // =======================================================
797
+ // Team Dashboard: Security Agent Findings
798
+ // =======================================================
799
+ teamDashboard.addWidgets(
800
+ new cloudwatch.TextWidget({
801
+ markdown: '### Security Agent Findings',
802
+ width: 24,
803
+ height: 1,
804
+ }),
805
+ );
806
+
807
+ teamDashboard.addWidgets(
808
+ new cloudwatch.SingleValueWidget({
809
+ title: 'Open Critical/High Findings',
810
+ metrics: [
811
+ new cloudwatch.Metric({
812
+ namespace: METRIC_NAMESPACE,
813
+ metricName: 'SecurityCriticalFindingCount',
814
+ statistic: 'Sum',
815
+ period: cdk.Duration.days(7),
816
+ label: 'Critical + High',
817
+ }),
818
+ ],
819
+ width: 6,
820
+ height: 4,
821
+ }),
822
+ new cloudwatch.GraphWidget({
823
+ title: 'Finding Trend by Severity',
824
+ left: ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].map((severity) =>
825
+ new cloudwatch.Metric({
826
+ namespace: METRIC_NAMESPACE,
827
+ metricName: 'SecurityFindingCount',
828
+ dimensionsMap: { Severity: severity },
829
+ statistic: 'Sum',
830
+ period: cdk.Duration.days(1),
831
+ label: severity,
832
+ }),
833
+ ),
834
+ width: 8,
835
+ height: 4,
836
+ leftYAxis: { min: 0, label: 'Findings' },
837
+ }),
838
+ new cloudwatch.SingleValueWidget({
839
+ title: 'Avg Remediation (hrs)',
840
+ metrics: [
841
+ new cloudwatch.Metric({
842
+ namespace: METRIC_NAMESPACE,
843
+ metricName: 'SecurityRemediationTimeHours',
844
+ statistic: 'Average',
845
+ period: cdk.Duration.days(7),
846
+ label: 'Hours',
847
+ }),
848
+ ],
849
+ width: 4,
850
+ height: 4,
851
+ }),
852
+ );
853
+
854
+ teamDashboard.addWidgets(
855
+ new cloudwatch.GraphWidget({
856
+ title: 'Findings: AI vs Human Code',
857
+ left: [
858
+ new cloudwatch.Metric({
859
+ namespace: METRIC_NAMESPACE,
860
+ metricName: 'SecurityFindingByOrigin',
861
+ dimensionsMap: { AIOrigin: 'ai-assisted' },
862
+ statistic: 'Sum',
863
+ period: cdk.Duration.days(1),
864
+ label: 'AI Code',
865
+ }),
866
+ new cloudwatch.Metric({
867
+ namespace: METRIC_NAMESPACE,
868
+ metricName: 'SecurityFindingByOrigin',
869
+ dimensionsMap: { AIOrigin: 'human' },
870
+ statistic: 'Sum',
871
+ period: cdk.Duration.days(1),
872
+ label: 'Human Code',
873
+ }),
874
+ ],
875
+ width: 12,
876
+ height: 6,
877
+ leftYAxis: { min: 0, label: 'Findings' },
878
+ }),
879
+ new cloudwatch.GraphWidget({
880
+ title: 'Remediation Time: AI vs Human',
881
+ left: [
882
+ new cloudwatch.Metric({
883
+ namespace: METRIC_NAMESPACE,
884
+ metricName: 'SecurityRemediationTimeHours',
885
+ dimensionsMap: { AIOrigin: 'ai-assisted' },
886
+ statistic: 'Average',
887
+ period: cdk.Duration.days(1),
888
+ label: 'AI Code Fix Time (hrs)',
889
+ }),
890
+ new cloudwatch.Metric({
891
+ namespace: METRIC_NAMESPACE,
892
+ metricName: 'SecurityRemediationTimeHours',
893
+ dimensionsMap: { AIOrigin: 'human' },
894
+ statistic: 'Average',
895
+ period: cdk.Duration.days(1),
896
+ label: 'Human Code Fix Time (hrs)',
897
+ }),
898
+ ],
899
+ width: 12,
900
+ height: 6,
901
+ leftYAxis: { min: 0, label: 'Hours' },
902
+ }),
903
+ );
904
+
905
+ teamDashboard.addWidgets(
906
+ new cloudwatch.GraphWidget({
907
+ title: 'Findings by Phase',
908
+ left: ['design_review', 'code_review', 'pen_test'].map((phase) =>
909
+ new cloudwatch.Metric({
910
+ namespace: METRIC_NAMESPACE,
911
+ metricName: 'SecurityFindingCount',
912
+ dimensionsMap: { Phase: phase },
913
+ statistic: 'Sum',
914
+ period: cdk.Duration.days(7),
915
+ label: phase.replace('_', ' '),
916
+ }),
917
+ ),
918
+ width: 12,
919
+ height: 6,
920
+ leftYAxis: { min: 0, label: 'Findings' },
921
+ view: cloudwatch.GraphWidgetView.BAR,
922
+ }),
923
+ new cloudwatch.GraphWidget({
924
+ title: 'Security Scan Volume',
925
+ left: [
926
+ new cloudwatch.Metric({
927
+ namespace: METRIC_NAMESPACE,
928
+ metricName: 'SecurityScanCount',
929
+ statistic: 'Sum',
930
+ period: cdk.Duration.days(1),
931
+ label: 'Scans / Day',
932
+ }),
933
+ ],
934
+ width: 12,
935
+ height: 6,
936
+ leftYAxis: { min: 0, label: 'Scans' },
937
+ }),
938
+ );
939
+
940
+ // =======================================================
941
+ // Executive Dashboard: Security & Compliance section
942
+ // =======================================================
943
+ execDashboard.addWidgets(
944
+ new cloudwatch.TextWidget({
945
+ markdown: '### Security & Compliance',
946
+ width: 24,
947
+ height: 1,
948
+ }),
949
+ );
950
+
951
+ execDashboard.addWidgets(
952
+ new cloudwatch.SingleValueWidget({
953
+ title: 'Guardrail Blocks (7d)',
954
+ metrics: [
955
+ new cloudwatch.Metric({
956
+ namespace: METRIC_NAMESPACE,
957
+ metricName: 'GuardrailBlockCount',
958
+ statistic: 'Sum',
959
+ period: cdk.Duration.days(7),
960
+ label: 'Blocks',
961
+ }),
962
+ ],
963
+ width: 6,
964
+ height: 4,
965
+ }),
966
+ new cloudwatch.GraphWidget({
967
+ title: 'Guardrail Trigger Trend',
968
+ left: [
969
+ new cloudwatch.Metric({
970
+ namespace: METRIC_NAMESPACE,
971
+ metricName: 'GuardrailTriggerCount',
972
+ statistic: 'Sum',
973
+ period: cdk.Duration.days(1),
974
+ label: 'Daily Triggers',
975
+ }),
976
+ ],
977
+ width: 9,
978
+ height: 4,
979
+ leftYAxis: { min: 0, label: 'Count' },
980
+ }),
981
+ new cloudwatch.SingleValueWidget({
982
+ title: 'MCP Auth Denied (7d)',
983
+ metrics: [
984
+ new cloudwatch.Metric({
985
+ namespace: METRIC_NAMESPACE,
986
+ metricName: 'MCPAuthDeniedCount',
987
+ statistic: 'Sum',
988
+ period: cdk.Duration.days(7),
989
+ label: 'Denied',
990
+ }),
991
+ ],
992
+ width: 3,
993
+ height: 4,
994
+ }),
995
+ new cloudwatch.SingleValueWidget({
996
+ title: 'Exfiltration Alerts (7d)',
997
+ metrics: [
998
+ new cloudwatch.Metric({
999
+ namespace: METRIC_NAMESPACE,
1000
+ metricName: 'ExfiltrationAlertCount',
1001
+ statistic: 'Sum',
1002
+ period: cdk.Duration.days(7),
1003
+ label: 'Alerts',
1004
+ }),
1005
+ ],
1006
+ width: 6,
1007
+ height: 4,
1008
+ }),
1009
+ );
1010
+
1011
+ // =======================================================
1012
+ // Executive Dashboard: Cost Intelligence
1013
+ // =======================================================
1014
+ execDashboard.addWidgets(
1015
+ new cloudwatch.TextWidget({
1016
+ markdown: '### Cost Intelligence',
1017
+ width: 24,
1018
+ height: 1,
1019
+ }),
1020
+ );
1021
+
1022
+ execDashboard.addWidgets(
1023
+ new cloudwatch.GraphWidget({
1024
+ title: 'Weekly Bedrock Cost',
1025
+ left: [
1026
+ new cloudwatch.Metric({
1027
+ namespace: METRIC_NAMESPACE,
1028
+ metricName: 'BedrockCostUSD',
1029
+ statistic: 'Sum',
1030
+ period: cdk.Duration.days(7),
1031
+ label: 'Weekly Cost ($)',
1032
+ }),
1033
+ ],
1034
+ width: 12,
1035
+ height: 6,
1036
+ leftYAxis: { min: 0, label: 'USD' },
1037
+ view: cloudwatch.GraphWidgetView.BAR,
1038
+ }),
1039
+ new cloudwatch.SingleValueWidget({
1040
+ title: 'AI vs Human Defect Rate',
1041
+ metrics: [
1042
+ new cloudwatch.Metric({
1043
+ namespace: METRIC_NAMESPACE,
1044
+ metricName: 'PostMergeDefectRateAI',
1045
+ statistic: 'Average',
1046
+ period: cdk.Duration.days(7),
1047
+ label: 'AI Defect Rate (%)',
1048
+ }),
1049
+ new cloudwatch.Metric({
1050
+ namespace: METRIC_NAMESPACE,
1051
+ metricName: 'PostMergeDefectRateHuman',
1052
+ statistic: 'Average',
1053
+ period: cdk.Duration.days(7),
1054
+ label: 'Human Defect Rate (%)',
1055
+ }),
1056
+ ],
1057
+ width: 6,
1058
+ height: 6,
1059
+ }),
1060
+ );
1061
+
1062
+ // =======================================================
1063
+ // Dashboard 3: CISO Compliance
1064
+ // =======================================================
1065
+ const cisoDashboard = new cloudwatch.Dashboard(this, 'CISOComplianceDashboard', {
1066
+ dashboardName: 'PRISM-D1-CISO-Compliance',
1067
+ defaultInterval: cdk.Duration.days(30),
1068
+ });
1069
+
1070
+ cisoDashboard.addWidgets(
1071
+ new cloudwatch.TextWidget({
1072
+ markdown: '# PRISM D1 - CISO Compliance Dashboard\nSecurity posture, remediation SLAs, and AI code risk profile across all teams.',
1073
+ width: 24,
1074
+ height: 1,
1075
+ }),
1076
+ );
1077
+
1078
+ cisoDashboard.addWidgets(
1079
+ new cloudwatch.SingleValueWidget({
1080
+ title: 'Open Critical Findings',
1081
+ metrics: [
1082
+ new cloudwatch.Metric({
1083
+ namespace: METRIC_NAMESPACE,
1084
+ metricName: 'SecurityCriticalFindingCount',
1085
+ statistic: 'Sum',
1086
+ period: cdk.Duration.days(30),
1087
+ label: 'Critical + High (30d)',
1088
+ }),
1089
+ ],
1090
+ width: 6,
1091
+ height: 4,
1092
+ }),
1093
+ new cloudwatch.SingleValueWidget({
1094
+ title: 'Avg Remediation Time',
1095
+ metrics: [
1096
+ new cloudwatch.Metric({
1097
+ namespace: METRIC_NAMESPACE,
1098
+ metricName: 'SecurityRemediationTimeHours',
1099
+ statistic: 'Average',
1100
+ period: cdk.Duration.days(30),
1101
+ label: 'Hours (30d avg)',
1102
+ }),
1103
+ ],
1104
+ width: 6,
1105
+ height: 4,
1106
+ }),
1107
+ new cloudwatch.SingleValueWidget({
1108
+ title: 'Security Scans Run',
1109
+ metrics: [
1110
+ new cloudwatch.Metric({
1111
+ namespace: METRIC_NAMESPACE,
1112
+ metricName: 'SecurityScanCount',
1113
+ statistic: 'Sum',
1114
+ period: cdk.Duration.days(30),
1115
+ label: 'Scans (30d)',
1116
+ }),
1117
+ ],
1118
+ width: 6,
1119
+ height: 4,
1120
+ }),
1121
+ );
1122
+
1123
+ cisoDashboard.addWidgets(
1124
+ new cloudwatch.TextWidget({
1125
+ markdown: '### AI Code Risk Profile',
1126
+ width: 24,
1127
+ height: 1,
1128
+ }),
1129
+ );
1130
+
1131
+ cisoDashboard.addWidgets(
1132
+ new cloudwatch.GraphWidget({
1133
+ title: 'Security Findings: AI vs Human Code',
1134
+ left: [
1135
+ new cloudwatch.Metric({
1136
+ namespace: METRIC_NAMESPACE,
1137
+ metricName: 'SecurityFindingByOrigin',
1138
+ dimensionsMap: { AIOrigin: 'ai-assisted' },
1139
+ statistic: 'Sum',
1140
+ period: cdk.Duration.days(7),
1141
+ label: 'AI Code Findings',
1142
+ }),
1143
+ new cloudwatch.Metric({
1144
+ namespace: METRIC_NAMESPACE,
1145
+ metricName: 'SecurityFindingByOrigin',
1146
+ dimensionsMap: { AIOrigin: 'human' },
1147
+ statistic: 'Sum',
1148
+ period: cdk.Duration.days(7),
1149
+ label: 'Human Code Findings',
1150
+ }),
1151
+ ],
1152
+ width: 12,
1153
+ height: 6,
1154
+ leftYAxis: { min: 0, label: 'Findings' },
1155
+ }),
1156
+ new cloudwatch.GraphWidget({
1157
+ title: 'Remediation Time by Code Origin',
1158
+ left: [
1159
+ new cloudwatch.Metric({
1160
+ namespace: METRIC_NAMESPACE,
1161
+ metricName: 'SecurityRemediationTimeHours',
1162
+ dimensionsMap: { AIOrigin: 'ai-assisted' },
1163
+ statistic: 'Average',
1164
+ period: cdk.Duration.days(7),
1165
+ label: 'AI Code (hrs)',
1166
+ }),
1167
+ new cloudwatch.Metric({
1168
+ namespace: METRIC_NAMESPACE,
1169
+ metricName: 'SecurityRemediationTimeHours',
1170
+ dimensionsMap: { AIOrigin: 'human' },
1171
+ statistic: 'Average',
1172
+ period: cdk.Duration.days(7),
1173
+ label: 'Human Code (hrs)',
1174
+ }),
1175
+ ],
1176
+ width: 12,
1177
+ height: 6,
1178
+ leftYAxis: { min: 0, label: 'Hours' },
1179
+ }),
1180
+ );
1181
+
1182
+ cisoDashboard.addWidgets(
1183
+ new cloudwatch.TextWidget({
1184
+ markdown: '### Shift-Left Effectiveness',
1185
+ width: 24,
1186
+ height: 1,
1187
+ }),
1188
+ );
1189
+
1190
+ cisoDashboard.addWidgets(
1191
+ new cloudwatch.GraphWidget({
1192
+ title: 'Findings by Phase (Monthly Trend)',
1193
+ left: ['design_review', 'code_review', 'pen_test'].map((phase) =>
1194
+ new cloudwatch.Metric({
1195
+ namespace: METRIC_NAMESPACE,
1196
+ metricName: 'SecurityFindingCount',
1197
+ dimensionsMap: { Phase: phase },
1198
+ statistic: 'Sum',
1199
+ period: cdk.Duration.days(7),
1200
+ label: phase.replace('_', ' '),
1201
+ }),
1202
+ ),
1203
+ width: 12,
1204
+ height: 6,
1205
+ leftYAxis: { min: 0, label: 'Findings' },
1206
+ view: cloudwatch.GraphWidgetView.BAR,
1207
+ }),
1208
+ new cloudwatch.GraphWidget({
1209
+ title: 'Guardrail + Exfiltration Trends',
1210
+ left: [
1211
+ new cloudwatch.Metric({
1212
+ namespace: METRIC_NAMESPACE,
1213
+ metricName: 'GuardrailBlockCount',
1214
+ statistic: 'Sum',
1215
+ period: cdk.Duration.days(7),
1216
+ label: 'Guardrail Blocks',
1217
+ }),
1218
+ new cloudwatch.Metric({
1219
+ namespace: METRIC_NAMESPACE,
1220
+ metricName: 'ExfiltrationAlertCount',
1221
+ statistic: 'Sum',
1222
+ period: cdk.Duration.days(7),
1223
+ label: 'Exfiltration Alerts',
1224
+ }),
1225
+ new cloudwatch.Metric({
1226
+ namespace: METRIC_NAMESPACE,
1227
+ metricName: 'MCPAuthDeniedCount',
1228
+ statistic: 'Sum',
1229
+ period: cdk.Duration.days(7),
1230
+ label: 'MCP Auth Denied',
1231
+ }),
1232
+ ],
1233
+ width: 12,
1234
+ height: 6,
1235
+ leftYAxis: { min: 0, label: 'Count' },
1236
+ }),
1237
+ );
1238
+
1239
+ new cdk.CfnOutput(this, 'CISODashboardUrl', {
1240
+ value: `https://${this.region}.console.aws.amazon.com/cloudwatch/home?region=${this.region}#dashboards:name=PRISM-D1-CISO-Compliance`,
1241
+ description: 'CISO Compliance Dashboard URL',
1242
+ });
1243
+
1244
+ // =======================================================
1245
+ // Security Agent Alarms
1246
+ // =======================================================
1247
+
1248
+ new cloudwatch.Alarm(this, 'SecurityCriticalFindingAlarm', {
1249
+ alarmName: 'PRISM-D1-SecurityCriticalFinding',
1250
+ alarmDescription: 'Critical or High security finding detected by AWS Security Agent.',
1251
+ metric: new cloudwatch.Metric({
1252
+ namespace: METRIC_NAMESPACE,
1253
+ metricName: 'SecurityCriticalFindingCount',
1254
+ statistic: 'Sum',
1255
+ period: cdk.Duration.hours(1),
1256
+ }),
1257
+ threshold: 1,
1258
+ evaluationPeriods: 1,
1259
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
1260
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1261
+ });
1262
+
1263
+ new cloudwatch.Alarm(this, 'SecurityRemediationSLAAlarm', {
1264
+ alarmName: 'PRISM-D1-SecurityRemediationSLA',
1265
+ alarmDescription: 'Average security finding remediation time exceeds 72 hours.',
1266
+ metric: new cloudwatch.Metric({
1267
+ namespace: METRIC_NAMESPACE,
1268
+ metricName: 'SecurityRemediationTimeHours',
1269
+ statistic: 'Average',
1270
+ period: cdk.Duration.days(1),
1271
+ }),
1272
+ threshold: 72,
1273
+ evaluationPeriods: 1,
1274
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
1275
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1276
+ });
1277
+
1278
+ new cloudwatch.Alarm(this, 'SecurityFindingRateHighAlarm', {
1279
+ alarmName: 'PRISM-D1-SecurityFindingRate-High',
1280
+ alarmDescription: 'Security finding count exceeds 50 in 6 hours — systemic quality issue.',
1281
+ metric: new cloudwatch.Metric({
1282
+ namespace: METRIC_NAMESPACE,
1283
+ metricName: 'SecurityFindingCount',
1284
+ statistic: 'Sum',
1285
+ period: cdk.Duration.hours(6),
1286
+ }),
1287
+ threshold: 50,
1288
+ evaluationPeriods: 1,
1289
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
1290
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1291
+ });
1292
+
1293
+ // =======================================================
1294
+ // Existing Alarms
1295
+ // =======================================================
1296
+
1297
+ // Alarm: Guardrail block rate exceeding threshold
1298
+ new cloudwatch.Alarm(this, 'GuardrailBlockRateHighAlarm', {
1299
+ alarmName: 'PRISM-D1-GuardrailBlockRate-High',
1300
+ alarmDescription: 'Guardrail block count exceeds 50 per hour, indicating potential prompt attack or misconfiguration.',
1301
+ metric: new cloudwatch.Metric({
1302
+ namespace: METRIC_NAMESPACE,
1303
+ metricName: 'GuardrailBlockCount',
1304
+ statistic: 'Sum',
1305
+ period: cdk.Duration.hours(1),
1306
+ }),
1307
+ threshold: 50,
1308
+ evaluationPeriods: 2,
1309
+ datapointsToAlarm: 2,
1310
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
1311
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1312
+ });
1313
+
1314
+ // Alarm: Daily Bedrock cost exceeding budget
1315
+ new cloudwatch.Alarm(this, 'BedrockDailyCostHighAlarm', {
1316
+ alarmName: 'PRISM-D1-BedrockDailyCost-High',
1317
+ alarmDescription: 'Daily Bedrock cost exceeds $100 threshold.',
1318
+ metric: new cloudwatch.Metric({
1319
+ namespace: METRIC_NAMESPACE,
1320
+ metricName: 'BedrockCostUSD',
1321
+ statistic: 'Sum',
1322
+ period: cdk.Duration.days(1),
1323
+ }),
1324
+ threshold: 100,
1325
+ evaluationPeriods: 1,
1326
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
1327
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1328
+ });
1329
+
1330
+ // Alarm: Token efficiency below threshold
1331
+ new cloudwatch.Alarm(this, 'TokenEfficiencyLowAlarm', {
1332
+ alarmName: 'PRISM-D1-TokenEfficiency-Low',
1333
+ alarmDescription: 'Token efficiency is low — high token consumption relative to code output.',
1334
+ metric: new cloudwatch.Metric({
1335
+ namespace: METRIC_NAMESPACE,
1336
+ metricName: 'TokenEfficiency',
1337
+ statistic: 'Average',
1338
+ period: cdk.Duration.hours(6),
1339
+ }),
1340
+ threshold: 500,
1341
+ evaluationPeriods: 3,
1342
+ datapointsToAlarm: 2,
1343
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
1344
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1345
+ });
1346
+
1347
+ // Alarm: Exfiltration detection
1348
+ new cloudwatch.Alarm(this, 'ExfiltrationAlertAlarm', {
1349
+ alarmName: 'PRISM-D1-ExfiltrationAlert',
1350
+ alarmDescription: 'Data exfiltration pattern detected — anomalous read volume on PRISM tables.',
1351
+ metric: new cloudwatch.Metric({
1352
+ namespace: METRIC_NAMESPACE,
1353
+ metricName: 'ExfiltrationAlertCount',
1354
+ statistic: 'Sum',
1355
+ period: cdk.Duration.hours(1),
1356
+ }),
1357
+ threshold: 1,
1358
+ evaluationPeriods: 1,
1359
+ comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
1360
+ treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
1361
+ });
1362
+
1363
+ // Alarm: Agent success rate dropping below 80%
1364
+ new cloudwatch.Alarm(this, 'AgentSuccessRateAlarm', {
1365
+ metric: new cloudwatch.Metric({
1366
+ namespace: METRIC_NAMESPACE,
1367
+ metricName: 'AgentSuccessRate',
1368
+ dimensionsMap: { TeamId: teamId, Repository: repository },
1369
+ statistic: 'Average',
1370
+ period: cdk.Duration.hours(1),
1371
+ }),
1372
+ threshold: 80,
1373
+ evaluationPeriods: 3,
1374
+ comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
1375
+ alarmDescription: 'Agent success rate below 80% for 3 consecutive hours',
1376
+ alarmName: `prism-d1-${teamId}-agent-success-rate`,
1377
+ });
1378
+
1379
+ // -------------------------------------------------------
1380
+ // Outputs
1381
+ // -------------------------------------------------------
1382
+ new cdk.CfnOutput(this, 'TeamDashboardUrl', {
1383
+ value: `https://${this.region}.console.aws.amazon.com/cloudwatch/home?region=${this.region}#dashboards:name=PRISM-D1-Team-Velocity`,
1384
+ description: 'Team Velocity Dashboard URL',
1385
+ });
1386
+
1387
+ new cdk.CfnOutput(this, 'ExecDashboardUrl', {
1388
+ value: `https://${this.region}.console.aws.amazon.com/cloudwatch/home?region=${this.region}#dashboards:name=PRISM-D1-Executive-Readout`,
1389
+ description: 'Executive Readout Dashboard URL',
1390
+ });
1391
+ }
1392
+ }