@yasserkhanorg/e2e-agents 0.3.3 → 0.3.4

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.
@@ -14,6 +14,8 @@ export interface DecisionSummary {
14
14
  }
15
15
  export interface PlanReport {
16
16
  schemaVersion: '1.0.0';
17
+ runId: string;
18
+ sourceRunId?: string;
17
19
  generatedAt: string;
18
20
  source: 'impact';
19
21
  runSet: RecommendedRunSet;
@@ -23,6 +25,13 @@ export interface PlanReport {
23
25
  requiredNewTests: string[];
24
26
  policy: PolicyEvaluation;
25
27
  decision: DecisionSummary;
28
+ enforcement: {
29
+ mode: PolicyConfig['enforcementMode'];
30
+ blockOnActions: CiAction[];
31
+ matchedAction: boolean;
32
+ shouldFail: boolean;
33
+ summary: string;
34
+ };
26
35
  insights?: {
27
36
  flaky?: {
28
37
  highRiskRecommendedTests: Array<{
@@ -79,6 +88,7 @@ export interface PlanReport {
79
88
  warnings: number;
80
89
  };
81
90
  }
91
+ export declare function refreshPlanEnforcement(plan: PlanReport): PlanReport;
82
92
  export declare function buildPlanFromImpactReport(impact: ReportData, policyOverride?: Partial<PolicyConfig>): PlanReport;
83
93
  export declare function attachDeveloperActions(plan: PlanReport, context: {
84
94
  appPath: string;
@@ -87,5 +97,34 @@ export declare function attachDeveloperActions(plan: PlanReport, context: {
87
97
  }): PlanReport;
88
98
  export declare function writePlanReport(appRoot: string, plan: PlanReport): string;
89
99
  export declare function renderCiSummaryMarkdown(plan: PlanReport): string;
100
+ export interface PlanMetricEvent {
101
+ schemaVersion: '1.0.0';
102
+ timestamp: string;
103
+ runId: string;
104
+ sourceRunId?: string;
105
+ action: CiAction;
106
+ runSet: RecommendedRunSet;
107
+ confidence: number;
108
+ changedFiles: number;
109
+ impactedFlows: number;
110
+ uncoveredP0P1Flows: number;
111
+ warnings: number;
112
+ enforcementMode: PolicyConfig['enforcementMode'];
113
+ enforcementShouldFail: boolean;
114
+ }
115
+ export interface PlanMetricsSummary {
116
+ schemaVersion: '1.0.0';
117
+ generatedAt: string;
118
+ totalRuns: number;
119
+ averageConfidence: number;
120
+ byAction: Record<CiAction, number>;
121
+ byRunSet: Record<RecommendedRunSet, number>;
122
+ blockingRecommendations: number;
123
+ blockingRate: number;
124
+ }
125
+ export declare function appendPlanMetrics(appRoot: string, plan: PlanReport): {
126
+ eventsPath: string;
127
+ summaryPath: string;
128
+ };
90
129
  export declare function writeCiSummary(appRoot: string, markdown: string, relativePath?: string): string;
91
130
  //# sourceMappingURL=plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/agent/plan.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAC9D,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAC7B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE;YACJ,wBAAwB,EAAE,KAAK,CAAC;gBAC5B,IAAI,EAAE,MAAM,CAAC;gBACb,SAAS,EAAE,MAAM,CAAC;gBAClB,WAAW,CAAC,EAAE,MAAM,CAAC;gBACrB,YAAY,CAAC,EAAE,MAAM,CAAC;gBACtB,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAC;gBACjC,SAAS,CAAC,EAAE,MAAM,CAAC;gBACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;gBAClB,UAAU,CAAC,EAAE,OAAO,CAAC;gBACrB,eAAe,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,kBAAkB,CAAC;gBACzD,aAAa,CAAC,EAAE,MAAM,CAAC;aAC1B,CAAC,CAAC;YACH,2BAA2B,EAAE,MAAM,EAAE,CAAC;YACtC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;SAC5B,CAAC;QACF,YAAY,CAAC,EAAE;YACX,MAAM,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAA;aAAC,CAAC,CAAC;YAClF,QAAQ,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAA;aAAC,CAAC,CAAC;SACvF,CAAC;QACF,WAAW,CAAC,EAAE;YACV,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;YACf,iBAAiB,EAAE,MAAM,CAAC;SAC7B,CAAC;KACL,CAAC;IACF,WAAW,CAAC,EAAE;QACV,iCAAiC,EAAE,OAAO,CAAC;QAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,OAAO,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;CACL;AAgLD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CAsChH;AAED,wBAAgB,sBAAsB,CAClC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,GACjE,UAAU,CAoBZ;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMzE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA+FhE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAiC,GAAG,MAAM,CAMvH"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/agent/plan.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAC9D,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAC7B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,WAAW,EAAE;QACT,IAAI,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;QACtC,cAAc,EAAE,QAAQ,EAAE,CAAC;QAC3B,aAAa,EAAE,OAAO,CAAC;QACvB,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE;YACJ,wBAAwB,EAAE,KAAK,CAAC;gBAC5B,IAAI,EAAE,MAAM,CAAC;gBACb,SAAS,EAAE,MAAM,CAAC;gBAClB,WAAW,CAAC,EAAE,MAAM,CAAC;gBACrB,YAAY,CAAC,EAAE,MAAM,CAAC;gBACtB,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAC;gBACjC,SAAS,CAAC,EAAE,MAAM,CAAC;gBACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;gBAClB,UAAU,CAAC,EAAE,OAAO,CAAC;gBACrB,eAAe,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,kBAAkB,CAAC;gBACzD,aAAa,CAAC,EAAE,MAAM,CAAC;aAC1B,CAAC,CAAC;YACH,2BAA2B,EAAE,MAAM,EAAE,CAAC;YACtC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;SAC5B,CAAC;QACF,YAAY,CAAC,EAAE;YACX,MAAM,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAA;aAAC,CAAC,CAAC;YAClF,QAAQ,EAAE,KAAK,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAA;aAAC,CAAC,CAAC;SACvF,CAAC;QACF,WAAW,CAAC,EAAE;YACV,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;YACf,iBAAiB,EAAE,MAAM,CAAC;SAC7B,CAAC;KACL,CAAC;IACF,WAAW,CAAC,EAAE;QACV,iCAAiC,EAAE,OAAO,CAAC;QAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,OAAO,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;CACL;AAoOD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAKnE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CA4ChH;AAED,wBAAgB,sBAAsB,CAClC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,GACjE,UAAU,CAoBZ;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMzE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAiGhE;AAED,MAAM,WAAW,eAAe;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,qBAAqB,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC5C,uBAAuB,EAAE,MAAM,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACxB;AAqBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,CAkE9G;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAiC,GAAG,MAAM,CAMvH"}
@@ -2,10 +2,12 @@
2
2
  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
3
  // See LICENSE.txt for license information.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.refreshPlanEnforcement = refreshPlanEnforcement;
5
6
  exports.buildPlanFromImpactReport = buildPlanFromImpactReport;
6
7
  exports.attachDeveloperActions = attachDeveloperActions;
7
8
  exports.writePlanReport = writePlanReport;
8
9
  exports.renderCiSummaryMarkdown = renderCiSummaryMarkdown;
10
+ exports.appendPlanMetrics = appendPlanMetrics;
9
11
  exports.writeCiSummary = writeCiSummary;
10
12
  const fs_1 = require("fs");
11
13
  const path_1 = require("path");
@@ -27,6 +29,8 @@ const DEFAULT_POLICY = {
27
29
  '**/*.sql',
28
30
  '**/webhook/**',
29
31
  ],
32
+ enforcementMode: 'advisory',
33
+ blockOnActions: ['must-add-tests'],
30
34
  };
31
35
  function countPriority(flows) {
32
36
  const counts = { p0: 0, p1: 0, p2: 0 };
@@ -172,6 +176,61 @@ function buildDecision(runSet, confidence, impact, policy) {
172
176
  summary: `Execute the ${runSet} suite for this change set.`,
173
177
  };
174
178
  }
179
+ function evaluateEnforcement(decision, policy) {
180
+ const blockOnActions = (policy.blockOnActions && policy.blockOnActions.length > 0)
181
+ ? [...policy.blockOnActions]
182
+ : ['must-add-tests'];
183
+ const matchedAction = blockOnActions.includes(decision.action);
184
+ if (policy.enforcementMode === 'block' && matchedAction) {
185
+ return {
186
+ mode: policy.enforcementMode,
187
+ blockOnActions,
188
+ matchedAction,
189
+ shouldFail: true,
190
+ summary: `Blocking mode active: decision "${decision.action}" is configured to fail CI.`,
191
+ };
192
+ }
193
+ if (policy.enforcementMode === 'warn' && matchedAction) {
194
+ return {
195
+ mode: policy.enforcementMode,
196
+ blockOnActions,
197
+ matchedAction,
198
+ shouldFail: false,
199
+ summary: `Warning mode active: decision "${decision.action}" is advisory-only for CI.`,
200
+ };
201
+ }
202
+ if (policy.enforcementMode === 'block') {
203
+ return {
204
+ mode: policy.enforcementMode,
205
+ blockOnActions,
206
+ matchedAction,
207
+ shouldFail: false,
208
+ summary: `Blocking mode active, but decision "${decision.action}" is not configured for CI failure.`,
209
+ };
210
+ }
211
+ if (policy.enforcementMode === 'warn') {
212
+ return {
213
+ mode: policy.enforcementMode,
214
+ blockOnActions,
215
+ matchedAction,
216
+ shouldFail: false,
217
+ summary: `Warning mode active, but decision "${decision.action}" is not configured for warning.`,
218
+ };
219
+ }
220
+ return {
221
+ mode: policy.enforcementMode,
222
+ blockOnActions,
223
+ matchedAction,
224
+ shouldFail: false,
225
+ summary: 'Advisory mode active: recommendations do not fail CI by default.',
226
+ };
227
+ }
228
+ function refreshPlanEnforcement(plan) {
229
+ return {
230
+ ...plan,
231
+ enforcement: evaluateEnforcement(plan.decision, plan.policy.applied),
232
+ };
233
+ }
175
234
  function buildPlanFromImpactReport(impact, policyOverride) {
176
235
  if (impact.mode !== 'impact') {
177
236
  throw new Error(`Plan generation requires impact report data, received mode=${impact.mode}`);
@@ -181,9 +240,14 @@ function buildPlanFromImpactReport(impact, policyOverride) {
181
240
  const confidence = computeConfidence(impact, p0, p1);
182
241
  const runSet = pickRunSet(impact, p0, confidence, policy);
183
242
  const decision = buildDecision(runSet.runSet, confidence, impact, policy);
243
+ const enforcement = evaluateEnforcement(decision, policy);
184
244
  const requiredNewTests = impact.gaps.map((flow) => `${flow.id}: ${flow.name}`);
245
+ const sourceRunId = impact.runMetadata?.runId;
246
+ const runId = `plan-${sourceRunId || Date.now().toString(36)}`;
185
247
  return {
186
248
  schemaVersion: '1.0.0',
249
+ runId,
250
+ sourceRunId,
187
251
  generatedAt: new Date().toISOString(),
188
252
  source: 'impact',
189
253
  runSet: runSet.runSet,
@@ -197,6 +261,7 @@ function buildPlanFromImpactReport(impact, policyOverride) {
197
261
  applied: policy,
198
262
  },
199
263
  decision,
264
+ enforcement,
200
265
  metrics: {
201
266
  changedFiles: impact.changedFiles.length,
202
267
  impactedFlows: impact.flows.length,
@@ -243,6 +308,8 @@ function renderCiSummaryMarkdown(plan) {
243
308
  lines.push(`- Run set: \`${plan.runSet}\``);
244
309
  lines.push(`- Confidence: \`${plan.confidence}\``);
245
310
  lines.push(`- Summary: ${plan.decision.summary}`);
311
+ lines.push(`- Enforcement: mode=\`${plan.enforcement.mode}\`, shouldFail=\`${plan.enforcement.shouldFail}\``);
312
+ lines.push(`- Enforcement detail: ${plan.enforcement.summary}`);
246
313
  if (plan.policy.triggeredRules.length > 0) {
247
314
  lines.push(`- Policy triggers: ${plan.policy.triggeredRules.join(', ')}`);
248
315
  }
@@ -322,6 +389,85 @@ function renderCiSummaryMarkdown(plan) {
322
389
  }
323
390
  return lines.join('\n');
324
391
  }
392
+ const PLAN_METRICS_EVENTS_PATH = '.e2e-ai-agents/metrics.jsonl';
393
+ const PLAN_METRICS_SUMMARY_PATH = '.e2e-ai-agents/metrics-summary.json';
394
+ function parsePlanMetricLine(line) {
395
+ const trimmed = line.trim();
396
+ if (!trimmed) {
397
+ return null;
398
+ }
399
+ try {
400
+ const parsed = JSON.parse(trimmed);
401
+ if (!parsed || parsed.schemaVersion !== '1.0.0' || typeof parsed.runId !== 'string') {
402
+ return null;
403
+ }
404
+ return parsed;
405
+ }
406
+ catch {
407
+ return null;
408
+ }
409
+ }
410
+ function appendPlanMetrics(appRoot, plan) {
411
+ const baseDir = (0, path_1.join)(appRoot, '.e2e-ai-agents');
412
+ (0, fs_1.mkdirSync)(baseDir, { recursive: true });
413
+ const eventsPath = (0, path_1.join)(appRoot, PLAN_METRICS_EVENTS_PATH);
414
+ const summaryPath = (0, path_1.join)(appRoot, PLAN_METRICS_SUMMARY_PATH);
415
+ const event = {
416
+ schemaVersion: '1.0.0',
417
+ timestamp: new Date().toISOString(),
418
+ runId: plan.runId,
419
+ sourceRunId: plan.sourceRunId,
420
+ action: plan.decision.action,
421
+ runSet: plan.runSet,
422
+ confidence: plan.confidence,
423
+ changedFiles: plan.metrics.changedFiles,
424
+ impactedFlows: plan.metrics.impactedFlows,
425
+ uncoveredP0P1Flows: plan.metrics.uncoveredP0P1Flows,
426
+ warnings: plan.metrics.warnings,
427
+ enforcementMode: plan.enforcement.mode,
428
+ enforcementShouldFail: plan.enforcement.shouldFail,
429
+ };
430
+ (0, fs_1.appendFileSync)(eventsPath, `${JSON.stringify(event)}\n`, 'utf-8');
431
+ const allEvents = (0, fs_1.existsSync)(eventsPath)
432
+ ? (0, fs_1.readFileSync)(eventsPath, 'utf-8')
433
+ .split('\n')
434
+ .map(parsePlanMetricLine)
435
+ .filter((item) => Boolean(item))
436
+ : [event];
437
+ const byAction = {
438
+ 'run-now': 0,
439
+ 'must-add-tests': 0,
440
+ 'safe-to-merge': 0,
441
+ };
442
+ const byRunSet = {
443
+ smoke: 0,
444
+ targeted: 0,
445
+ full: 0,
446
+ };
447
+ let totalConfidence = 0;
448
+ let blockingRecommendations = 0;
449
+ for (const metricEvent of allEvents) {
450
+ byAction[metricEvent.action] += 1;
451
+ byRunSet[metricEvent.runSet] += 1;
452
+ totalConfidence += metricEvent.confidence;
453
+ if (metricEvent.enforcementShouldFail) {
454
+ blockingRecommendations += 1;
455
+ }
456
+ }
457
+ const totalRuns = allEvents.length;
458
+ const summary = {
459
+ schemaVersion: '1.0.0',
460
+ generatedAt: new Date().toISOString(),
461
+ totalRuns,
462
+ averageConfidence: totalRuns > 0 ? Number((totalConfidence / totalRuns).toFixed(2)) : 0,
463
+ byAction,
464
+ byRunSet,
465
+ blockingRecommendations,
466
+ blockingRate: totalRuns > 0 ? Number((blockingRecommendations / totalRuns).toFixed(4)) : 0,
467
+ };
468
+ (0, fs_1.writeFileSync)(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');
469
+ return { eventsPath, summaryPath };
470
+ }
325
471
  function writeCiSummary(appRoot, markdown, relativePath = '.e2e-ai-agents/ci-summary.md') {
326
472
  const fullPath = (0, path_1.join)(appRoot, relativePath);
327
473
  const dir = (0, path_1.dirname)(fullPath);
@@ -5,6 +5,15 @@ import type { DataTestIdSuggestion } from './selectors.js';
5
5
  import type { GapTestSuggestion } from './gap_suggestions.js';
6
6
  export interface ReportData {
7
7
  mode: 'impact' | 'gap';
8
+ runMetadata?: {
9
+ runId: string;
10
+ startedAt: string;
11
+ completedAt: string;
12
+ durationMs: number;
13
+ sinceRef: string;
14
+ appPath: string;
15
+ testsRoot: string;
16
+ };
8
17
  changedFiles: string[];
9
18
  flows: FlowImpact[];
10
19
  coverage: FlowCoverage[];
@@ -65,6 +74,8 @@ export interface ReportData {
65
74
  generateStatus: string;
66
75
  healStatus?: string;
67
76
  error?: string;
77
+ failureCategory?: string;
78
+ failureCode?: string;
68
79
  }>;
69
80
  warnings: string[];
70
81
  mcp?: {
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/agent/report.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/agent/report.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,12 @@ function writeReport(appRoot, config, data) {
45
45
  const markdownLines = [];
46
46
  markdownLines.push(`# ${data.mode === 'impact' ? 'Impact Analysis' : 'Gap Analysis'} Report`);
47
47
  markdownLines.push('');
48
+ if (data.runMetadata) {
49
+ markdownLines.push(`Run ID: ${data.runMetadata.runId}`);
50
+ markdownLines.push(`Run window: ${data.runMetadata.startedAt} -> ${data.runMetadata.completedAt}`);
51
+ markdownLines.push(`Run duration (ms): ${data.runMetadata.durationMs}`);
52
+ markdownLines.push(`Since ref: ${data.runMetadata.sinceRef}`);
53
+ }
48
54
  markdownLines.push(`Framework: ${data.framework}`);
49
55
  markdownLines.push(`Test Patterns: ${data.testPatterns.join(', ') || 'None'}`);
50
56
  if (data.flowCatalog) {
@@ -103,6 +109,9 @@ function writeReport(appRoot, config, data) {
103
109
  if (result.error) {
104
110
  markdownLines.push(` Error: ${result.error}`);
105
111
  }
112
+ if (result.failureCategory || result.failureCode) {
113
+ markdownLines.push(` Failure taxonomy: category=${result.failureCategory || 'unknown'} code=${result.failureCode || 'unknown'}`);
114
+ }
106
115
  }
107
116
  if (data.pipeline.warnings.length > 0) {
108
117
  markdownLines.push('Pipeline warnings:');
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAiN7C,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6LzF;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0MtF"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAoN7C,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,OAAO,CAAC;CAClB;AAYD,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAyMzF;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsNtF"}
@@ -118,11 +118,14 @@ function buildRecommendedTestsFromCoverage(flows, coverage) {
118
118
  const flow = flowMap.get(entry.flowId);
119
119
  const flagSummary = (0, flags_js_1.formatFlags)(flow?.flags || []);
120
120
  for (const test of entry.coveredBy) {
121
- if (!testNotes.has(test)) {
122
- testNotes.set(test, new Set());
121
+ const normalizedTest = (0, utils_js_1.normalizePath)(test)
122
+ .replace(/^\.\//, '')
123
+ .replace(/^e2e-tests\/playwright\//, '');
124
+ if (!testNotes.has(normalizedTest)) {
125
+ testNotes.set(normalizedTest, new Set());
123
126
  }
124
127
  if (flagSummary !== 'none') {
125
- testNotes.get(test)?.add(flagSummary);
128
+ testNotes.get(normalizedTest)?.add(flagSummary);
126
129
  }
127
130
  }
128
131
  }
@@ -194,12 +197,24 @@ function classifyImpactModelConfidence(flowMapping, testMapping, dependencyGraph
194
197
  }
195
198
  return 'low';
196
199
  }
200
+ function createRunId(mode) {
201
+ const ciRunId = process.env.GITHUB_RUN_ID;
202
+ const entropy = Math.random().toString(36).slice(2, 8);
203
+ const ts = Date.now().toString(36);
204
+ if (ciRunId) {
205
+ return `${mode}-gh-${ciRunId}-${ts}-${entropy}`;
206
+ }
207
+ return `${mode}-local-${ts}-${entropy}`;
208
+ }
197
209
  async function runImpact(_config, _options) {
198
210
  ensureAppRoot(_config.path);
199
211
  if (_config.testsRoot) {
200
212
  ensureAppRoot(_config.testsRoot);
201
213
  }
202
214
  const deadline = Date.now() + _config.timeLimitMinutes * 60 * 1000;
215
+ const runStartedAt = new Date().toISOString();
216
+ const runStartedTs = Date.now();
217
+ const runId = createRunId('impact');
203
218
  const warnings = [];
204
219
  const testsRoot = _config.testsRoot || _config.path;
205
220
  const frameworkDetection = (0, framework_js_1.detectFramework)(testsRoot, _config.framework);
@@ -272,7 +287,7 @@ async function runImpact(_config, _options) {
272
287
  coverageMap.set(entry.flowId, entry.coveredBy);
273
288
  }
274
289
  gaps = computeGaps(flows, coverageMap);
275
- recommendedTests = buildRecommendedTestsWithFlags(flows, testsByFlow);
290
+ recommendedTests = buildRecommendedTestsFromCoverage(flows, coverage);
276
291
  }
277
292
  else {
278
293
  const traceability = (0, traceability_js_1.mapTraceabilityToFlows)(testsRoot, _config.impact.traceability, flows);
@@ -324,6 +339,15 @@ async function runImpact(_config, _options) {
324
339
  const reportRoot = testsRoot;
325
340
  const report = (0, report_js_1.writeReport)(reportRoot, _config, {
326
341
  mode: 'impact',
342
+ runMetadata: {
343
+ runId,
344
+ startedAt: runStartedAt,
345
+ completedAt: new Date().toISOString(),
346
+ durationMs: Date.now() - runStartedTs,
347
+ sinceRef: _config.git.since,
348
+ appPath: _config.path,
349
+ testsRoot,
350
+ },
327
351
  changedFiles,
328
352
  flows: sortFlows(flows),
329
353
  coverage,
@@ -370,6 +394,9 @@ async function runGap(_config, _options) {
370
394
  ensureAppRoot(_config.testsRoot);
371
395
  }
372
396
  const deadline = Date.now() + _config.timeLimitMinutes * 60 * 1000;
397
+ const runStartedAt = new Date().toISOString();
398
+ const runStartedTs = Date.now();
399
+ const runId = createRunId('gap');
373
400
  const warnings = [];
374
401
  const testsRoot = _config.testsRoot || _config.path;
375
402
  const frameworkDetection = (0, framework_js_1.detectFramework)(testsRoot, _config.framework);
@@ -454,7 +481,7 @@ async function runGap(_config, _options) {
454
481
  coverageMap.set(entry.flowId, entry.coveredBy);
455
482
  }
456
483
  gaps = computeGaps(flows, coverageMap);
457
- recommendedTests = buildRecommendedTestsWithFlags(flows, testsByFlow);
484
+ recommendedTests = buildRecommendedTestsFromCoverage(flows, coverage);
458
485
  }
459
486
  else {
460
487
  const traceability = (0, traceability_js_1.mapTraceabilityToFlows)(testsRoot, _config.impact.traceability, flows);
@@ -506,6 +533,15 @@ async function runGap(_config, _options) {
506
533
  const reportRoot = testsRoot;
507
534
  const report = (0, report_js_1.writeReport)(reportRoot, _config, {
508
535
  mode: 'gap',
536
+ runMetadata: {
537
+ runId,
538
+ startedAt: runStartedAt,
539
+ completedAt: new Date().toISOString(),
540
+ durationMs: Date.now() - runStartedTs,
541
+ sinceRef: _config.git.since,
542
+ appPath: _config.path,
543
+ testsRoot,
544
+ },
509
545
  changedFiles,
510
546
  flows: sortFlows(flows),
511
547
  coverage,
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAyB,KAAK,6BAA6B,EAAE,KAAK,4BAA4B,EAAC,MAAM,oBAAoB,CAAC;AACjI,OAAO,EAEH,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAGH,KAAK,yBAAyB,EACjC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACvD,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA0BD,wBAAsB,aAAa,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzF;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOpF;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwBjG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,6BAA6B,GAAG,4BAA4B,CAE1G;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,GAAG,wBAAwB,CASlG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,6BAA6B,GAAG,yBAAyB,CAkBrG"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAEtE,OAAO,EAOH,KAAK,UAAU,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAyB,KAAK,6BAA6B,EAAE,KAAK,4BAA4B,EAAC,MAAM,oBAAoB,CAAC;AACjI,OAAO,EAEH,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAGH,KAAK,yBAAyB,EACjC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACvD,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA0BD,wBAAsB,aAAa,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzF;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOpF;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyBjG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,6BAA6B,GAAG,4BAA4B,CAE1G;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,4BAA4B,GAAG,wBAAwB,CASlG;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,6BAA6B,GAAG,yBAAyB,CAkBrG"}
package/dist/api.js CHANGED
@@ -70,6 +70,7 @@ async function recommendTests(options = {}) {
70
70
  const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
71
71
  const ciSummaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
72
72
  const ciSummaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, ciSummaryMarkdown);
73
+ (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
73
74
  return {
74
75
  report,
75
76
  reportPath: impactPath,
package/dist/cli.js CHANGED
@@ -89,6 +89,7 @@ function printUsage() {
89
89
  ' --pipeline-base-url Base URL for Playwright runs',
90
90
  ' --pipeline-browser Browser: chrome|chromium|firefox|webkit',
91
91
  ' --pipeline-headless Run in headless mode',
92
+ ' --pipeline-headed Run in headed mode',
92
93
  ' --pipeline-project Playwright project name',
93
94
  ' --pipeline-parallel Enable parallel mode in generator',
94
95
  ' --pipeline-dry-run Do not execute pipeline (report only)',
@@ -103,6 +104,8 @@ function printUsage() {
103
104
  ' --policy-safe-merge-confidence <n> Confidence needed for safe-to-merge',
104
105
  ' --policy-force-full-on-warnings <n> Escalate to full at warning count',
105
106
  ' --policy-risky-patterns <globs> Comma-separated risky file globs',
107
+ ' --policy-enforcement-mode <mode> advisory | warn | block',
108
+ ' --policy-block-actions <actions> Comma-separated CI actions to block/warn',
106
109
  ' --ci-comment-path <path> Write CI markdown summary',
107
110
  ' --github-output <path> Write GitHub Actions outputs',
108
111
  ' --fail-on-must-add-tests Exit non-zero on must-add-tests decision',
@@ -246,6 +249,10 @@ function parseArgs(argv) {
246
249
  parsed.pipelineHeadless = true;
247
250
  continue;
248
251
  }
252
+ if (arg === '--pipeline-headed') {
253
+ parsed.pipelineHeadless = false;
254
+ continue;
255
+ }
249
256
  if (arg === '--pipeline-project' && next) {
250
257
  parsed.pipelineProject = next;
251
258
  i += 1;
@@ -322,6 +329,21 @@ function parseArgs(argv) {
322
329
  i += 1;
323
330
  continue;
324
331
  }
332
+ if (arg === '--policy-enforcement-mode' && next) {
333
+ if (next === 'advisory' || next === 'warn' || next === 'block') {
334
+ parsed.policyEnforcementMode = next;
335
+ }
336
+ i += 1;
337
+ continue;
338
+ }
339
+ if (arg === '--policy-block-actions' && next) {
340
+ parsed.policyBlockActions = next
341
+ .split(',')
342
+ .map((value) => value.trim())
343
+ .filter((value) => (value === 'run-now' || value === 'must-add-tests' || value === 'safe-to-merge'));
344
+ i += 1;
345
+ continue;
346
+ }
325
347
  if (arg === '--ci-comment-path' && next) {
326
348
  parsed.ciCommentPath = next;
327
349
  i += 1;
@@ -796,12 +818,16 @@ async function main() {
796
818
  policy: args.policyMinConfidence !== undefined ||
797
819
  args.policySafeMergeConfidence !== undefined ||
798
820
  args.policyWarningsThreshold !== undefined ||
799
- (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0)
821
+ (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0) ||
822
+ args.policyEnforcementMode !== undefined ||
823
+ (args.policyBlockActions && args.policyBlockActions.length > 0)
800
824
  ? {
801
825
  minConfidenceForTargeted: args.policyMinConfidence,
802
826
  safeMergeMinConfidence: args.policySafeMergeConfidence,
803
827
  forceFullOnWarningsAtOrAbove: args.policyWarningsThreshold,
804
828
  riskyFilePatterns: args.policyRiskyPatterns,
829
+ enforcementMode: args.policyEnforcementMode,
830
+ blockOnActions: args.policyBlockActions,
805
831
  }
806
832
  : undefined,
807
833
  });
@@ -830,24 +856,33 @@ async function main() {
830
856
  const planPath = (0, plan_js_1.writePlanReport)(reportRoot, plan);
831
857
  const summaryMarkdown = (0, plan_js_1.renderCiSummaryMarkdown)(plan);
832
858
  const summaryPath = (0, plan_js_1.writeCiSummary)(reportRoot, summaryMarkdown, args.ciCommentPath);
859
+ const metrics = (0, plan_js_1.appendPlanMetrics)(reportRoot, plan);
833
860
  const ghaOutput = args.githubOutputPath || process.env.GITHUB_OUTPUT;
834
861
  if (ghaOutput) {
835
862
  (0, fs_1.appendFileSync)(ghaOutput, `run_set=${plan.runSet}\n`);
836
863
  (0, fs_1.appendFileSync)(ghaOutput, `action=${plan.decision.action}\n`);
837
864
  (0, fs_1.appendFileSync)(ghaOutput, `confidence=${plan.confidence}\n`);
865
+ (0, fs_1.appendFileSync)(ghaOutput, `enforcement_mode=${plan.enforcement.mode}\n`);
866
+ (0, fs_1.appendFileSync)(ghaOutput, `enforcement_should_fail=${plan.enforcement.shouldFail}\n`);
838
867
  (0, fs_1.appendFileSync)(ghaOutput, `recommended_tests_count=${plan.recommendedTests.length}\n`);
839
868
  (0, fs_1.appendFileSync)(ghaOutput, `required_new_tests_count=${plan.requiredNewTests.length}\n`);
840
869
  (0, fs_1.appendFileSync)(ghaOutput, `plan_path=${planPath}\n`);
841
870
  (0, fs_1.appendFileSync)(ghaOutput, `summary_path=${summaryPath}\n`);
871
+ (0, fs_1.appendFileSync)(ghaOutput, `metrics_events_path=${metrics.eventsPath}\n`);
872
+ (0, fs_1.appendFileSync)(ghaOutput, `metrics_summary_path=${metrics.summaryPath}\n`);
842
873
  }
843
874
  // eslint-disable-next-line no-console
844
875
  console.log(`Suggested run set: ${plan.runSet} (confidence ${plan.confidence})`);
845
876
  // eslint-disable-next-line no-console
846
877
  console.log(`Decision: ${plan.decision.action} - ${plan.decision.summary}`);
847
878
  // eslint-disable-next-line no-console
879
+ console.log(`Enforcement: ${plan.enforcement.mode} (shouldFail=${plan.enforcement.shouldFail})`);
880
+ // eslint-disable-next-line no-console
848
881
  console.log(`Plan data: ${planPath}`);
849
882
  // eslint-disable-next-line no-console
850
883
  console.log(`CI summary: ${summaryPath}`);
884
+ // eslint-disable-next-line no-console
885
+ console.log(`Plan metrics: ${metrics.summaryPath}`);
851
886
  if (plan.nextActions) {
852
887
  // eslint-disable-next-line no-console
853
888
  console.log(`Next action (run existing): ${plan.nextActions.runRecommendedTests || plan.nextActions.runSmokeSuite}`);
@@ -856,7 +891,8 @@ async function main() {
856
891
  // eslint-disable-next-line no-console
857
892
  console.log(`Next action (heal): ${plan.nextActions.healGeneratedTests}`);
858
893
  }
859
- if (args.failOnMustAddTests && plan.decision.action === 'must-add-tests') {
894
+ const failOnLegacyFlag = args.failOnMustAddTests && plan.decision.action === 'must-add-tests';
895
+ if (failOnLegacyFlag || plan.enforcement.shouldFail) {
860
896
  process.exit(2);
861
897
  }
862
898
  return;
@@ -72,6 +72,7 @@ const DEFAULT_CONFIG = {
72
72
  scenarios: 3,
73
73
  outputDir: 'specs/functional/ai-assisted',
74
74
  heal: true,
75
+ project: 'chrome',
75
76
  mcp: false,
76
77
  mcpAllowFallback: false,
77
78
  },
@@ -120,6 +121,8 @@ const DEFAULT_CONFIG = {
120
121
  '**/*.sql',
121
122
  '**/webhook/**',
122
123
  ],
124
+ enforcementMode: 'advisory',
125
+ blockOnActions: ['must-add-tests'],
123
126
  },
124
127
  flags: {
125
128
  defaultState: 'on',
@@ -266,6 +269,18 @@ function normalizeFlagState(value) {
266
269
  }
267
270
  return undefined;
268
271
  }
272
+ function normalizePolicyEnforcementMode(value) {
273
+ if (value === 'advisory' || value === 'warn' || value === 'block') {
274
+ return value;
275
+ }
276
+ return undefined;
277
+ }
278
+ function normalizePolicyBlockAction(value) {
279
+ if (value === 'run-now' || value === 'must-add-tests' || value === 'safe-to-merge') {
280
+ return value;
281
+ }
282
+ return undefined;
283
+ }
269
284
  function extractConfigPatch(raw) {
270
285
  const patch = {};
271
286
  if (typeof raw.path === 'string') {
@@ -462,6 +477,11 @@ function extractConfigPatch(raw) {
462
477
  }
463
478
  if (raw.policy && typeof raw.policy === 'object') {
464
479
  const policy = raw.policy;
480
+ const blockOnActions = Array.isArray(policy.blockOnActions)
481
+ ? policy.blockOnActions
482
+ .map((value) => normalizePolicyBlockAction(value))
483
+ .filter((value) => Boolean(value))
484
+ : DEFAULT_CONFIG.policy.blockOnActions;
465
485
  patch.policy = {
466
486
  minConfidenceForTargeted: coerceNumber(policy.minConfidenceForTargeted) ?? DEFAULT_CONFIG.policy.minConfidenceForTargeted,
467
487
  safeMergeMinConfidence: coerceNumber(policy.safeMergeMinConfidence) ?? DEFAULT_CONFIG.policy.safeMergeMinConfidence,
@@ -475,6 +495,8 @@ function extractConfigPatch(raw) {
475
495
  riskyFilePatterns: Array.isArray(policy.riskyFilePatterns)
476
496
  ? policy.riskyFilePatterns.filter((pattern) => typeof pattern === 'string')
477
497
  : DEFAULT_CONFIG.policy.riskyFilePatterns,
498
+ enforcementMode: normalizePolicyEnforcementMode(policy.enforcementMode) ?? DEFAULT_CONFIG.policy.enforcementMode,
499
+ blockOnActions: blockOnActions.length > 0 ? blockOnActions : DEFAULT_CONFIG.policy.blockOnActions,
478
500
  };
479
501
  }
480
502
  if (raw.flags && typeof raw.flags === 'object') {
@@ -610,6 +632,12 @@ export function resolveConfig(cwd, configPath, overrides) {
610
632
  if (overrides.policy.riskyFilePatterns && overrides.policy.riskyFilePatterns.length > 0) {
611
633
  policyPatch.riskyFilePatterns = overrides.policy.riskyFilePatterns;
612
634
  }
635
+ if (overrides.policy.enforcementMode !== undefined) {
636
+ policyPatch.enforcementMode = overrides.policy.enforcementMode;
637
+ }
638
+ if (overrides.policy.blockOnActions && overrides.policy.blockOnActions.length > 0) {
639
+ policyPatch.blockOnActions = overrides.policy.blockOnActions;
640
+ }
613
641
  config.policy = { ...config.policy, ...policyPatch };
614
642
  }
615
643
  if (overrides?.specPDF) {