@resolveio/server-lib 22.3.145 → 22.3.147

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.
@@ -47,6 +47,8 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
47
47
  return to.concat(ar || Array.prototype.slice.call(from));
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.buildResolveIOAIManagerHotfixFirstReleasePolicy = buildResolveIOAIManagerHotfixFirstReleasePolicy;
51
+ exports.isResolveIOAIManagerSafeAutoDispatch = isResolveIOAIManagerSafeAutoDispatch;
50
52
  exports.normalizeResolveIOAIManagerFailureClass = normalizeResolveIOAIManagerFailureClass;
51
53
  exports.fingerprintResolveIOAIManagerBlocker = fingerprintResolveIOAIManagerBlocker;
52
54
  exports.hashResolveIOAIManagerEvidence = hashResolveIOAIManagerEvidence;
@@ -56,9 +58,11 @@ exports.buildResolveIOAIManagerRecoveryActionPacket = buildResolveIOAIManagerRec
56
58
  exports.decideResolveIOAIManagerRecoveryActionDispatch = decideResolveIOAIManagerRecoveryActionDispatch;
57
59
  exports.appendResolveIOAIManagerRecoveryActionDispatch = appendResolveIOAIManagerRecoveryActionDispatch;
58
60
  exports.buildResolveIOAIManagerRecoveryExecutionDirective = buildResolveIOAIManagerRecoveryExecutionDirective;
61
+ exports.assessResolveIOAIManagerEvidenceChange = assessResolveIOAIManagerEvidenceChange;
59
62
  exports.decideResolveIOAIManagerRecoveryGate = decideResolveIOAIManagerRecoveryGate;
60
63
  exports.buildResolveIOAIManagerRecoveryPlan = buildResolveIOAIManagerRecoveryPlan;
61
64
  exports.decideResolveIOAIManagerPolicy = decideResolveIOAIManagerPolicy;
65
+ exports.buildResolveIOAIManagerRecoveryReplayMatrix = buildResolveIOAIManagerRecoveryReplayMatrix;
62
66
  function cleanText(value, max) {
63
67
  if (max === void 0) { max = 2000; }
64
68
  return String(value || '').replace(/\s+/g, ' ').trim().slice(0, max);
@@ -92,6 +96,323 @@ function cleanList(values, limit, max) {
92
96
  }
93
97
  return result;
94
98
  }
99
+ function releaseStatusIsBlocked(value) {
100
+ return /fail|error|blocked|missing|empty|not ready|not_ready|denied|invalid|timeout|stale/i.test(value);
101
+ }
102
+ function releaseStatusIsTerminalDeploy(value) {
103
+ return /requested|queued|in.?progress|completed|complete|success|succeeded|pass|passed|published|deployed|failed|error|blocked/i.test(value);
104
+ }
105
+ function buildReleaseAllowedActions(action) {
106
+ if (action === 'wait_for_active_deploy') {
107
+ return ['wait_for_current_deploy_result', 'collect_current_deploy_log', 'refresh_release_snapshot'];
108
+ }
109
+ if (action === 'block_duplicate_deploy') {
110
+ return ['collect_existing_deploy_evidence', 'hotfix_release_artifact', 'request_force_deploy_with_reason'];
111
+ }
112
+ if (action === 'block_duplicate_publish') {
113
+ return ['collect_existing_publish_evidence', 'hotfix_release_artifact', 'request_force_publish_with_reason'];
114
+ }
115
+ if (action === 'review_force_deploy') {
116
+ return ['verify_force_deploy_reason', 'rerun_failed_release_gate_once', 'record_force_deploy_evidence'];
117
+ }
118
+ if (action === 'deploy_new_artifact_after_release_gate') {
119
+ return ['verify_new_artifact_fingerprint', 'run_release_gate_once', 'record_publish_deploy_evidence'];
120
+ }
121
+ if (action === 'build_artifact_first') {
122
+ return ['build_missing_release_artifact', 'record_artifact_fingerprint', 'run_release_gate_once'];
123
+ }
124
+ return ['hotfix_live_runner_or_release_artifact', 'rerun_failed_release_gate_only', 'record_release_evidence'];
125
+ }
126
+ function hotfixStep(id, label, channel, options) {
127
+ if (options === void 0) { options = {}; }
128
+ return {
129
+ id: id,
130
+ label: label,
131
+ channel: channel,
132
+ safeToAutoRun: options.safeToAutoRun === true,
133
+ requiresFullDeploy: options.requiresFullDeploy === true,
134
+ requiredEvidence: options.requiredEvidence || [],
135
+ successEvidence: options.successEvidence || [],
136
+ blockedBy: options.blockedBy || []
137
+ };
138
+ }
139
+ function buildHotfixFirstReleasePlan(input) {
140
+ var surface = cleanText(input.surface || 'runner', 80)
141
+ .toLowerCase()
142
+ .replace(/[^a-z0-9]+/g, '_')
143
+ .replace(/^_+|_+$/g, '') || 'runner';
144
+ var commonAcceptance = [
145
+ 'failed release gate is rerun once and passes',
146
+ 'artifact fingerprint or hotfix evidence is recorded',
147
+ 'operator console shows no active duplicate deploy blocker'
148
+ ];
149
+ if (input.action === 'wait_for_active_deploy') {
150
+ return {
151
+ planId: "".concat(surface, ":wait_for_active_deploy"),
152
+ label: 'Wait For Active Deploy',
153
+ recommendedChannel: 'release_artifact',
154
+ reason: input.reason,
155
+ fullDeployAvoided: true,
156
+ steps: [
157
+ hotfixStep('collect_active_deploy_log', 'Collect active deploy log', 'release_artifact', {
158
+ safeToAutoRun: true,
159
+ requiredEvidence: ['active deploy id or release operation id'],
160
+ successEvidence: ['terminal deploy status and release log captured']
161
+ })
162
+ ],
163
+ acceptanceEvidence: commonAcceptance,
164
+ escalationTriggers: ['active deploy exceeds timeout', 'active deploy fails without a release log']
165
+ };
166
+ }
167
+ if (input.action === 'deploy_new_artifact_after_release_gate') {
168
+ return {
169
+ planId: "".concat(surface, ":deploy_new_artifact_after_release_gate"),
170
+ label: 'Deploy New Artifact After Release Gate',
171
+ recommendedChannel: 'new_artifact_deploy',
172
+ reason: input.reason,
173
+ fullDeployAvoided: false,
174
+ steps: [
175
+ hotfixStep('verify_new_artifact_fingerprint', 'Verify new artifact fingerprint', 'new_artifact_deploy', {
176
+ safeToAutoRun: true,
177
+ requiresFullDeploy: true,
178
+ requiredEvidence: ['current artifact fingerprint differs from last deployed fingerprint'],
179
+ successEvidence: ['release gate permits one full deploy']
180
+ })
181
+ ],
182
+ acceptanceEvidence: [
183
+ 'new artifact fingerprint recorded',
184
+ 'release gate passed before full deploy',
185
+ 'deploy/publish result recorded'
186
+ ],
187
+ escalationTriggers: ['artifact fingerprint matches last deploy', 'release gate fails']
188
+ };
189
+ }
190
+ if (input.action === 'build_artifact_first') {
191
+ return {
192
+ planId: "".concat(surface, ":build_artifact_first"),
193
+ label: 'Build Artifact First',
194
+ recommendedChannel: 'artifact_build',
195
+ reason: input.reason,
196
+ fullDeployAvoided: true,
197
+ steps: [
198
+ hotfixStep('build_once_record_fingerprint', 'Build once and record fingerprint', 'artifact_build', {
199
+ safeToAutoRun: false,
200
+ requiredEvidence: ['missing artifact fingerprint', 'release gate reason for building'],
201
+ successEvidence: ['artifact fingerprint recorded before any deploy']
202
+ })
203
+ ],
204
+ acceptanceEvidence: ['artifact fingerprint exists', 'no deploy queued before release gate'],
205
+ escalationTriggers: ['build repeats without a new fingerprint', 'build failure lacks compile log']
206
+ };
207
+ }
208
+ if (input.action === 'review_force_deploy') {
209
+ return {
210
+ planId: "".concat(surface, ":review_force_deploy"),
211
+ label: 'Review Force Deploy',
212
+ recommendedChannel: 'force_deploy_review',
213
+ reason: input.reason,
214
+ fullDeployAvoided: false,
215
+ steps: [
216
+ hotfixStep('verify_force_deploy_reason', 'Verify force deploy reason', 'force_deploy_review', {
217
+ requiredEvidence: ['explicit force_deploy reason', 'why hotfix cannot resolve the release blocker'],
218
+ successEvidence: ['one allowed deploy attempt is recorded with reason']
219
+ })
220
+ ],
221
+ acceptanceEvidence: ['force deploy evidence recorded', 'duplicate fingerprint exception is auditable'],
222
+ escalationTriggers: ['force deploy reason is empty', 'same force deploy fails twice']
223
+ };
224
+ }
225
+ return {
226
+ planId: "".concat(surface, ":").concat(input.action),
227
+ label: input.action === 'block_duplicate_deploy'
228
+ ? 'Hotfix Or Force Deploy'
229
+ : input.action === 'block_duplicate_publish'
230
+ ? 'Hotfix Or Force Publish'
231
+ : 'Hotfix Release Artifact',
232
+ recommendedChannel: input.action === 'block_duplicate_publish'
233
+ ? 'release_artifact'
234
+ : input.releaseBlocked ? 'release_artifact' : 'static_ui',
235
+ reason: input.reason,
236
+ fullDeployAvoided: true,
237
+ steps: [
238
+ hotfixStep('static_ui_hotfix', 'Static UI hotfix', 'static_ui', {
239
+ safeToAutoRun: true,
240
+ requiredEvidence: ['built dist path', 'target S3 bucket and CloudFront distribution', 'changed frontend bundle or asset list'],
241
+ successEvidence: ['S3 upload result', 'CloudFront invalidation id', 'public bundle contains expected hotfix marker']
242
+ }),
243
+ hotfixStep('backend_js_hotfix', 'Backend JS hotfix', 'backend_js', {
244
+ requiredEvidence: ['compiled JS artifact path', 'target backend host', 'diff limited to runner/operator code'],
245
+ successEvidence: ['remote file checksum', 'service restart or process reload evidence', 'self-test passes']
246
+ }),
247
+ hotfixStep('config_seed_cache_restart', 'Config, seed, cache, or restart repair', 'config', {
248
+ safeToAutoRun: true,
249
+ requiredEvidence: ['exact config/seed/cache key or process name', 'why no product artifact changed'],
250
+ successEvidence: ['release gate rerun result', 'operator console release snapshot refreshed']
251
+ })
252
+ ],
253
+ acceptanceEvidence: commonAcceptance,
254
+ escalationTriggers: [
255
+ 'hotfix changes customer-facing product code outside the release artifact',
256
+ 'same release gate fails twice after hotfix evidence',
257
+ 'same publish fingerprint is retried without force_publish evidence',
258
+ 'force deploy is requested without a reason'
259
+ ]
260
+ };
261
+ }
262
+ function buildResolveIOAIManagerHotfixFirstReleasePolicy(input) {
263
+ if (input === void 0) { input = {}; }
264
+ var surface = cleanText(input.surface || 'runner', 120);
265
+ var deployStatus = cleanText(input.deployStatus, 160);
266
+ var publishStatus = cleanText(input.publishStatus, 160);
267
+ var sampleDataStatus = cleanText(input.sampleDataStatus, 160);
268
+ var deployFingerprint = cleanText(input.deployFingerprint, 240);
269
+ var lastDeployFingerprint = cleanText(input.lastDeployFingerprint, 240);
270
+ var publishFingerprint = cleanText(input.publishFingerprint, 240);
271
+ var lastPublishFingerprint = cleanText(input.lastPublishFingerprint, 240);
272
+ var fingerprintMatchesLast = !!deployFingerprint && !!lastDeployFingerprint && deployFingerprint === lastDeployFingerprint;
273
+ var publishFingerprintMatchesLast = !!publishFingerprint && !!lastPublishFingerprint && publishFingerprint === lastPublishFingerprint;
274
+ var releaseBlocked = [deployStatus, publishStatus, sampleDataStatus].some(releaseStatusIsBlocked);
275
+ var duplicateDeployBlocked = fingerprintMatchesLast
276
+ && input.forceDeploy !== true
277
+ && releaseStatusIsTerminalDeploy([deployStatus, publishStatus].filter(Boolean).join(' '));
278
+ var duplicatePublishBlocked = publishFingerprintMatchesLast
279
+ && input.forcePublish !== true
280
+ && releaseStatusIsBlocked(publishStatus)
281
+ && releaseStatusIsTerminalDeploy(publishStatus);
282
+ var recommendedAction = 'hotfix_release_artifact';
283
+ var label = 'Hotfix Release Artifact';
284
+ var reason = 'Repair the live runner/operator code, release config, domain, seed-data, or release artifact and rerun only the failed release gate.';
285
+ var fullDeployAllowed = false;
286
+ if (input.activeDeploy === true) {
287
+ recommendedAction = 'wait_for_active_deploy';
288
+ label = 'Wait For Active Deploy';
289
+ reason = 'A deploy is already queued or running; collect that result before spending another deploy cycle.';
290
+ }
291
+ else if (duplicateDeployBlocked) {
292
+ recommendedAction = 'block_duplicate_deploy';
293
+ label = 'Block Duplicate Deploy';
294
+ reason = 'The same artifact fingerprint already has release status; hotfix or provide explicit force_deploy evidence before rerunning it.';
295
+ }
296
+ else if (duplicatePublishBlocked) {
297
+ recommendedAction = 'block_duplicate_publish';
298
+ label = 'Block Duplicate Publish';
299
+ reason = 'The same publish fingerprint already has release status; hotfix the release artifact or provide explicit force_publish evidence before rerunning publish.';
300
+ }
301
+ else if ((fingerprintMatchesLast && input.forceDeploy === true) || (publishFingerprintMatchesLast && input.forcePublish === true)) {
302
+ recommendedAction = 'review_force_deploy';
303
+ label = 'Review Force Deploy';
304
+ reason = input.forcePublish === true
305
+ ? 'force_publish was explicitly requested for a repeated publish artifact; require a reason and rerun only the failed release gate.'
306
+ : 'force_deploy was explicitly requested for a repeated artifact; require a reason and rerun only the failed release gate.';
307
+ fullDeployAllowed = true;
308
+ }
309
+ else if (input.hasNewArtifact === false && !releaseBlocked) {
310
+ recommendedAction = 'build_artifact_first';
311
+ label = 'Build Artifact First';
312
+ reason = 'No release artifact fingerprint is available yet; build once, record the fingerprint, and avoid repeated deploys.';
313
+ }
314
+ else if (input.hasNewArtifact === true && !releaseBlocked) {
315
+ recommendedAction = 'deploy_new_artifact_after_release_gate';
316
+ label = 'Deploy New Artifact After Release Gate';
317
+ reason = 'A new artifact can be deployed after the release gate proves the artifact is new and ready.';
318
+ fullDeployAllowed = true;
319
+ }
320
+ var allowedActions = buildReleaseAllowedActions(recommendedAction);
321
+ var hotfixPlan = buildHotfixFirstReleasePlan({
322
+ action: recommendedAction,
323
+ surface: surface,
324
+ reason: reason,
325
+ releaseBlocked: releaseBlocked
326
+ });
327
+ return {
328
+ policy: 'hotfix_first',
329
+ surface: surface,
330
+ recommendedAction: recommendedAction,
331
+ label: label,
332
+ reason: reason,
333
+ hotfixPreferred: recommendedAction !== 'deploy_new_artifact_after_release_gate' && recommendedAction !== 'review_force_deploy',
334
+ fullDeployAllowed: fullDeployAllowed,
335
+ duplicateDeployBlocked: duplicateDeployBlocked,
336
+ duplicatePublishBlocked: duplicatePublishBlocked,
337
+ forceDeployRequiredToRepeat: true,
338
+ forcePublishRequiredToRepeat: true,
339
+ statuses: {
340
+ deploy: deployStatus,
341
+ publish: publishStatus,
342
+ sampleData: sampleDataStatus
343
+ },
344
+ fingerprints: {
345
+ current: deployFingerprint,
346
+ last: lastDeployFingerprint,
347
+ matchesLast: fingerprintMatchesLast,
348
+ publishCurrent: publishFingerprint,
349
+ publishLast: lastPublishFingerprint,
350
+ publishMatchesLast: publishFingerprintMatchesLast
351
+ },
352
+ hotfixPlan: hotfixPlan,
353
+ allowedActions: allowedActions,
354
+ forbiddenActions: [
355
+ 'rerun full builder loop for a release-only blocker',
356
+ 'queue duplicate deploy without force_deploy evidence',
357
+ 'queue duplicate publish without force_publish evidence',
358
+ 'publish/deploy again before reading the failed release log',
359
+ 'change core workflow after business proof passed unless release evidence proves it is required'
360
+ ],
361
+ requiredEvidence: [
362
+ 'failed release gate log or active deploy result',
363
+ 'deploy artifact fingerprint comparison',
364
+ 'publish artifact fingerprint comparison when publish is the failed gate',
365
+ 'hotfix/config/seed/domain change evidence when no new product artifact is needed',
366
+ 'force_deploy or force_publish reason when repeating the same fingerprint'
367
+ ]
368
+ };
369
+ }
370
+ function isResolveIOAIManagerSafeAutoDispatch(input) {
371
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
372
+ var normalizedInput = typeof input === 'string'
373
+ ? { dispatchAction: input }
374
+ : (input || {});
375
+ var dispatchAction = cleanText(normalizedInput.dispatchAction || ((_a = normalizedInput.directive) === null || _a === void 0 ? void 0 : _a.dispatchAction), 120);
376
+ var directiveAllowed = (_b = normalizedInput.directive) === null || _b === void 0 ? void 0 : _b.allowed;
377
+ var actionAutoRunnable = (_c = normalizedInput.action) === null || _c === void 0 ? void 0 : _c.autoRunnable;
378
+ var productRepairAllowed = ((_d = normalizedInput.directive) === null || _d === void 0 ? void 0 : _d.canRunProductRepair) === true
379
+ || ((_e = normalizedInput.directive) === null || _e === void 0 ? void 0 : _e.productRepairAllowed) === true
380
+ || ((_f = normalizedInput.action) === null || _f === void 0 ? void 0 : _f.productRepairAllowed) === true;
381
+ var expensiveModelAllowed = ((_g = normalizedInput.directive) === null || _g === void 0 ? void 0 : _g.canRunExpensiveModel) === true
382
+ || ((_h = normalizedInput.directive) === null || _h === void 0 ? void 0 : _h.expensiveModelAllowed) === true
383
+ || ((_j = normalizedInput.action) === null || _j === void 0 ? void 0 : _j.expensiveModelAllowed) === true;
384
+ if (!dispatchAction || dispatchAction === 'park_manual') {
385
+ return false;
386
+ }
387
+ if (directiveAllowed === false || actionAutoRunnable === false) {
388
+ return false;
389
+ }
390
+ if ([
391
+ 'run_evidence_probe',
392
+ 'run_read_only_diagnosis',
393
+ 'run_infra_repair',
394
+ 'run_compile_repair'
395
+ ].includes(dispatchAction)) {
396
+ return true;
397
+ }
398
+ if (dispatchAction === 'run_release_repair') {
399
+ return normalizedInput.includeReleaseRepair !== false;
400
+ }
401
+ if (dispatchAction === 'run_journey_contract_repair') {
402
+ return normalizedInput.includeJourneyContractRepair !== false && productRepairAllowed !== true;
403
+ }
404
+ if (dispatchAction === 'run_business_assertion_repair') {
405
+ return normalizedInput.includeBusinessProofRepair === true
406
+ && productRepairAllowed !== true
407
+ && expensiveModelAllowed !== true;
408
+ }
409
+ if (dispatchAction === 'run_targeted_product_repair') {
410
+ return normalizedInput.includeProductRepair === true
411
+ && productRepairAllowed === true
412
+ && expensiveModelAllowed !== true;
413
+ }
414
+ return dispatchAction === 'advance' || dispatchAction === 'continue_gate';
415
+ }
95
416
  function isoNow(value) {
96
417
  if (value instanceof Date) {
97
418
  return value.toISOString();
@@ -399,13 +720,23 @@ function buildResolveIOAIManagerRecoveryActionPacket(input) {
399
720
  || checkpoint.status === 'parked'
400
721
  || checkpoint.attempts >= checkpoint.maxAttemptsBeforePark;
401
722
  var stopWhen = Array.from(new Set(__spreadArray(__spreadArray([], __read(probe.stopConditions), false), __read((requireNewEvidence ? ['same blocker fingerprint and same evidence hash after probe'] : [])), false).map(function (entry) { return cleanText(entry, 500); }).filter(Boolean))).slice(0, 30);
402
- var resetLoopWhen = Array.from(new Set(__spreadArray(__spreadArray(__spreadArray([], __read(checkpoint.loopResetEvidence), false), __read(probe.loopResetEvidence), false), __read((requireNewEvidence ? ['changed evidence hash', 'new artifact path'] : [])), false).map(function (entry) { return cleanText(entry, 500); }).filter(Boolean))).slice(0, 30);
723
+ var resetLoopWhen = Array.from(new Set(__spreadArray(__spreadArray(__spreadArray([], __read(checkpoint.loopResetEvidence), false), __read(probe.loopResetEvidence), false), __read((requireNewEvidence ? [
724
+ 'new material evidence: changed blocker, validated diagnosis/journey, business proof, compile/infra/release proof, or new actionable trace',
725
+ 'weak hash-only evidence does not reset the loop'
726
+ ] : [])), false).map(function (entry) { return cleanText(entry, 500); }).filter(Boolean))).slice(0, 30);
403
727
  var nextCommands = probe.steps
404
728
  .map(function (step) { return cleanText(step.commandHint || "".concat(step.kind, ":").concat(step.id), 500); })
405
729
  .filter(Boolean)
406
730
  .slice(0, 12);
731
+ var releasePolicy = mode === 'repair_release'
732
+ ? buildResolveIOAIManagerHotfixFirstReleasePolicy({
733
+ surface: 'manager_recovery',
734
+ deployStatus: checkpoint.stepType,
735
+ publishStatus: checkpoint.lane
736
+ })
737
+ : undefined;
407
738
  var now = isoNow(input.now);
408
- return __assign(__assign({ actionId: stableHash('mgr-action', {
739
+ return __assign(__assign(__assign({ actionId: stableHash('mgr-action', {
409
740
  checkpointId: checkpoint.checkpointId,
410
741
  probeId: probe.probeId,
411
742
  mode: mode,
@@ -413,12 +744,16 @@ function buildResolveIOAIManagerRecoveryActionPacket(input) {
413
744
  blockerFingerprint: checkpoint.blockerFingerprint
414
745
  }), checkpointId: checkpoint.checkpointId, probeId: probe.probeId, recoveryClass: checkpoint.recoveryClass, mode: mode, label: recoveryActionLabelFor(mode, checkpoint), lane: checkpoint.lane, stepType: checkpoint.stepType, primaryStepKind: (primaryStep === null || primaryStep === void 0 ? void 0 : primaryStep.kind) || 'none', objective: mode === 'collect_evidence'
415
746
  ? probe.objective
416
- : cleanText(checkpoint.objective || probe.objective, 1000), evidenceOnly: evidenceOnly, autoRunnable: recoveryActionAutoRunnable(mode, probe), productRepairAllowed: checkpoint.productRepairAllowed && !evidenceOnly && checkpoint.status !== 'parked', expensiveModelAllowed: checkpoint.expensiveModelAllowed && checkpoint.status !== 'parked', canResetLoopAfterEvidence: resetLoopWhen.length > 0, maxAttemptsBeforePark: checkpoint.maxAttemptsBeforePark, requiredArtifacts: probe.requiredArtifacts.slice(0, 20), nextCommands: nextCommands, successCriteria: probe.acceptanceEvidence.slice(0, 20), retryPolicy: {
747
+ : cleanText(checkpoint.objective || probe.objective, 1000), evidenceOnly: evidenceOnly, autoRunnable: recoveryActionAutoRunnable(mode, probe), productRepairAllowed: checkpoint.productRepairAllowed && !evidenceOnly && checkpoint.status !== 'parked', expensiveModelAllowed: checkpoint.expensiveModelAllowed && checkpoint.status !== 'parked', canResetLoopAfterEvidence: resetLoopWhen.length > 0, maxAttemptsBeforePark: checkpoint.maxAttemptsBeforePark, requiredArtifacts: probe.requiredArtifacts.slice(0, 20), nextCommands: releasePolicy
748
+ ? Array.from(new Set(__spreadArray(__spreadArray([], __read(nextCommands), false), __read(releasePolicy.allowedActions), false))).slice(0, 12)
749
+ : nextCommands, successCriteria: releasePolicy
750
+ ? Array.from(new Set(__spreadArray(__spreadArray([], __read(probe.acceptanceEvidence), false), __read(releasePolicy.requiredEvidence), false))).slice(0, 20)
751
+ : probe.acceptanceEvidence.slice(0, 20), retryPolicy: {
417
752
  allowImmediateRetry: checkpoint.status !== 'parked' && mode !== 'manual_review',
418
753
  requireNewEvidence: requireNewEvidence,
419
754
  resetLoopWhen: resetLoopWhen,
420
755
  stopWhen: stopWhen
421
- } }, (mode === 'collect_evidence' ? { blockedReason: 'Manager parked this loop until the recovery action records new evidence.' } : {})), { createdAt: now });
756
+ } }, (releasePolicy ? { releasePolicy: releasePolicy } : {})), (mode === 'collect_evidence' ? { blockedReason: 'Manager parked this loop until the recovery action records new evidence.' } : {})), { createdAt: now });
422
757
  }
423
758
  function dispatchActionForMode(mode) {
424
759
  var map = {
@@ -592,7 +927,7 @@ function recoveryDirectiveReason(action, decision, current) {
592
927
  ].filter(Boolean).join(' | ');
593
928
  }
594
929
  function buildResolveIOAIManagerRecoveryExecutionDirective(input) {
595
- var _a, _b, _c, _d;
930
+ var _a, _b, _c, _d, _e;
596
931
  var action = input.action;
597
932
  var dispatchDecision = input.dispatchDecision || decideResolveIOAIManagerRecoveryActionDispatch({
598
933
  action: action,
@@ -606,7 +941,7 @@ function buildResolveIOAIManagerRecoveryExecutionDirective(input) {
606
941
  var stepType = cleanText((action === null || action === void 0 ? void 0 : action.stepType) || ((_b = input.current) === null || _b === void 0 ? void 0 : _b.stepType) || '', 120);
607
942
  var rerunReason = recoveryDirectiveReason(action, dispatchDecision, input.current);
608
943
  var surface = cleanText(input.surface || 'runner', 120);
609
- return __assign(__assign({ directiveId: stableHash('mgr-directive', {
944
+ return __assign(__assign(__assign({ directiveId: stableHash('mgr-directive', {
610
945
  surface: surface,
611
946
  actionId: (action === null || action === void 0 ? void 0 : action.actionId) || '',
612
947
  dispatchAction: dispatchAction,
@@ -614,14 +949,100 @@ function buildResolveIOAIManagerRecoveryExecutionDirective(input) {
614
949
  evidenceHash: ((_c = dispatchDecision.dispatchRecord) === null || _c === void 0 ? void 0 : _c.evidenceHash) || ((_d = input.current) === null || _d === void 0 ? void 0 : _d.evidenceHash) || ''
615
950
  }), surface: surface, dispatchAction: dispatchAction, phase: phase, allowed: dispatchDecision.allowed, status: dispatchDecision.status, reason: dispatchDecision.reason, lane: lane, stepType: stepType, nextActionLabel: cleanText(action === null || action === void 0 ? void 0 : action.label, 160) || dispatchAction, rerunReason: rerunReason, evidenceOnly: (action === null || action === void 0 ? void 0 : action.evidenceOnly) !== false || dispatchAction === 'run_evidence_probe', autoRunnable: (action === null || action === void 0 ? void 0 : action.autoRunnable) === true, productRepairAllowed: (action === null || action === void 0 ? void 0 : action.productRepairAllowed) === true, expensiveModelAllowed: (action === null || action === void 0 ? void 0 : action.expensiveModelAllowed) === true, canRunProductRepair: dispatchDecision.canRunProductRepair, canRunExpensiveModel: dispatchDecision.canRunExpensiveModel, canResetLoopAfterEvidence: (action === null || action === void 0 ? void 0 : action.canResetLoopAfterEvidence) === true, requiresNewEvidence: dispatchDecision.requiresNewEvidence, newEvidence: dispatchDecision.newEvidence, maxAttemptsBeforePark: Math.max(1, Number((action === null || action === void 0 ? void 0 : action.maxAttemptsBeforePark) || 1) || 1), requiredArtifacts: cleanList(action === null || action === void 0 ? void 0 : action.requiredArtifacts, 20, 500), nextCommands: cleanList(action === null || action === void 0 ? void 0 : action.nextCommands, 20, 500), successCriteria: cleanList(action === null || action === void 0 ? void 0 : action.successCriteria, 20, 500), forbiddenActions: (action === null || action === void 0 ? void 0 : action.productRepairAllowed) === true
616
951
  ? []
617
- : ['Do not run product-code repair from this directive unless canRunProductRepair is true.'] }, (dispatchDecision.dispatchRecord ? { dispatchRecord: dispatchDecision.dispatchRecord } : {})), { createdAt: now });
952
+ : Array.from(new Set(__spreadArray([
953
+ 'Do not run product-code repair from this directive unless canRunProductRepair is true.'
954
+ ], __read((((_e = action === null || action === void 0 ? void 0 : action.releasePolicy) === null || _e === void 0 ? void 0 : _e.forbiddenActions) || [])), false))).slice(0, 20) }, (dispatchDecision.dispatchRecord ? { dispatchRecord: dispatchDecision.dispatchRecord } : {})), ((action === null || action === void 0 ? void 0 : action.releasePolicy) ? { releasePolicy: action.releasePolicy } : {})), { createdAt: now });
618
955
  }
619
- function listHasNewEntry(current, previous) {
956
+ function newListEntries(current, previous) {
620
957
  var existing = new Set(previous.map(function (entry) { return cleanText(entry, 500); }).filter(Boolean));
621
- return current.some(function (entry) {
622
- var normalized = cleanText(entry, 500);
623
- return !!normalized && !existing.has(normalized);
624
- });
958
+ return current
959
+ .map(function (entry) { return cleanText(entry, 500); })
960
+ .filter(function (entry) { return entry && !existing.has(entry); });
961
+ }
962
+ function recordLane(value) {
963
+ return cleanText(value === null || value === void 0 ? void 0 : value.lane, 80);
964
+ }
965
+ function recordStepType(value) {
966
+ return cleanText(value === null || value === void 0 ? void 0 : value.stepType, 80);
967
+ }
968
+ function recordFailureClass(value) {
969
+ var valueText = cleanText(value === null || value === void 0 ? void 0 : value.failureClass, 80);
970
+ return valueText ? normalizeResolveIOAIManagerFailureClass(valueText) : '';
971
+ }
972
+ function materialEvidenceText(record, paths) {
973
+ if (paths === void 0) { paths = []; }
974
+ return __spreadArray([
975
+ cleanText(record === null || record === void 0 ? void 0 : record.blocker, 1200),
976
+ cleanText(record === null || record === void 0 ? void 0 : record.summary, 1200)
977
+ ], __read(paths.map(function (path) { return cleanText(path, 500); })), false).filter(Boolean).join(' ').toLowerCase();
978
+ }
979
+ function hasMaterialEvidenceLanguage(text) {
980
+ return /(business|assertion|proof|before|after|dom|trace|stack|network|mongo|query|diff|scope|diagnosis|journey|contract|workflow|compile|build|preflight|infra|chrome|puppeteer|release|publish|deploy|sample|seed|route|auth|hydration|performance|slow|owner|root.?cause|repro|pdf|export|upload|import|filter|invoice|saved|calculated|comparison|report)/i.test(text);
981
+ }
982
+ function assessResolveIOAIManagerEvidenceChange(previous, current, fallbackObjective) {
983
+ var currentRecord = current || {};
984
+ var previousRecord = previous;
985
+ var previousEvidenceHash = previousRecord ? cleanText(previousRecord.evidenceHash, 160) || hashResolveIOAIManagerEvidence(previousRecord) : '';
986
+ var currentEvidenceHash = hashResolveIOAIManagerEvidence(currentRecord);
987
+ var previousBlockerFingerprint = previousRecord
988
+ ? cleanText(previousRecord.blockerFingerprint, 160) || resolveResolveIOAIManagerBlockerFingerprint(previousRecord, fallbackObjective)
989
+ : '';
990
+ var currentBlockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(currentRecord, fallbackObjective);
991
+ var previousChangedFiles = cleanList(previousRecord === null || previousRecord === void 0 ? void 0 : previousRecord.changedFiles, 80, 500);
992
+ var currentChangedFiles = cleanList(currentRecord.changedFiles, 80, 500);
993
+ var previousArtifactPaths = cleanList(previousRecord === null || previousRecord === void 0 ? void 0 : previousRecord.artifactPaths, 80, 500);
994
+ var currentArtifactPaths = cleanList(currentRecord.artifactPaths, 80, 500);
995
+ var addedChangedFiles = newListEntries(currentChangedFiles, previousChangedFiles);
996
+ var addedArtifactPaths = newListEntries(currentArtifactPaths, previousArtifactPaths);
997
+ var signals = [];
998
+ if (previousEvidenceHash && currentEvidenceHash !== previousEvidenceHash) {
999
+ signals.push('evidence_hash_changed');
1000
+ }
1001
+ if (previousBlockerFingerprint && currentBlockerFingerprint !== previousBlockerFingerprint) {
1002
+ signals.push('blocker_fingerprint_changed');
1003
+ }
1004
+ if (recordLane(previousRecord) && recordLane(previousRecord) !== recordLane(currentRecord)) {
1005
+ signals.push('lane_changed');
1006
+ }
1007
+ if (recordStepType(previousRecord) && recordStepType(previousRecord) !== recordStepType(currentRecord)) {
1008
+ signals.push('step_type_changed');
1009
+ }
1010
+ if (recordFailureClass(previousRecord) && recordFailureClass(previousRecord) !== recordFailureClass(currentRecord)) {
1011
+ signals.push('failure_class_changed');
1012
+ }
1013
+ if (addedChangedFiles.length) {
1014
+ signals.push('changed_files_added');
1015
+ }
1016
+ if (addedArtifactPaths.length) {
1017
+ signals.push('artifact_paths_added');
1018
+ }
1019
+ if (isPassingOutcome(currentRecord)) {
1020
+ signals.push('passing_outcome');
1021
+ }
1022
+ var changed = signals.length > 0;
1023
+ var evidenceText = materialEvidenceText(currentRecord, addedArtifactPaths);
1024
+ var pathOrSummaryHasMaterialSignal = hasMaterialEvidenceLanguage(evidenceText);
1025
+ var proof = signals.includes('passing_outcome');
1026
+ var structuralChange = signals.some(function (signal) { return /blocker_fingerprint_changed|lane_changed|step_type_changed|failure_class_changed/.test(signal); });
1027
+ var materialArtifact = addedArtifactPaths.length > 0 && pathOrSummaryHasMaterialSignal;
1028
+ var materialChangedFiles = addedChangedFiles.length > 0
1029
+ && /diagnosis|owner|scope|diff|root.?cause|failing path|stack|trace|business|proof|compile|release|deploy|workflow|journey/i.test(evidenceText);
1030
+ var material = proof || structuralChange || materialArtifact || materialChangedFiles;
1031
+ var strength = proof
1032
+ ? 'proof'
1033
+ : material
1034
+ ? 'material'
1035
+ : changed
1036
+ ? 'weak'
1037
+ : 'none';
1038
+ return {
1039
+ changed: changed,
1040
+ material: material,
1041
+ strength: strength,
1042
+ signals: signals,
1043
+ evidenceHash: currentEvidenceHash,
1044
+ blockerFingerprint: currentBlockerFingerprint
1045
+ };
625
1046
  }
626
1047
  function proposedActionIsProductRepair(value) {
627
1048
  var normalized = cleanText(value, 120);
@@ -632,16 +1053,21 @@ function decideResolveIOAIManagerRecoveryGate(input) {
632
1053
  var checkpoint = input.checkpoint;
633
1054
  var hasCurrent = !!input.current;
634
1055
  var current = input.current || {};
635
- var blockerFingerprint = hasCurrent
636
- ? resolveResolveIOAIManagerBlockerFingerprint(current, checkpoint.objective)
637
- : checkpoint.blockerFingerprint;
638
- var evidenceHash = hasCurrent ? hashResolveIOAIManagerEvidence(current) : checkpoint.evidenceHash;
1056
+ var assessment = hasCurrent
1057
+ ? assessResolveIOAIManagerEvidenceChange(checkpoint, current, checkpoint.objective)
1058
+ : {
1059
+ changed: false,
1060
+ material: false,
1061
+ strength: 'none',
1062
+ signals: [],
1063
+ evidenceHash: checkpoint.evidenceHash,
1064
+ blockerFingerprint: checkpoint.blockerFingerprint
1065
+ };
1066
+ var blockerFingerprint = assessment.blockerFingerprint;
1067
+ var evidenceHash = assessment.evidenceHash;
639
1068
  var changedFiles = hasCurrent ? cleanList(current.changedFiles, 80, 500) : checkpoint.changedFiles;
640
1069
  var artifactPaths = hasCurrent ? cleanList(current.artifactPaths, 80, 500) : checkpoint.artifactPaths;
641
- var evidenceChanged = evidenceHash !== checkpoint.evidenceHash
642
- || blockerFingerprint !== checkpoint.blockerFingerprint
643
- || listHasNewEntry(changedFiles, checkpoint.changedFiles)
644
- || listHasNewEntry(artifactPaths, checkpoint.artifactPaths);
1070
+ var evidenceChanged = assessment.changed;
645
1071
  var proposedAction = cleanText(input.proposedAction || checkpoint.allowedAction, 120);
646
1072
  var productRepairRequested = proposedActionIsProductRepair(proposedAction);
647
1073
  var missingEvidence = checkpoint.requiredEvidence.filter(function (required) {
@@ -659,7 +1085,7 @@ function decideResolveIOAIManagerRecoveryGate(input) {
659
1085
  });
660
1086
  var makeDecision = function (action, reason, overrides) {
661
1087
  if (overrides === void 0) { overrides = {}; }
662
- return (__assign({ action: action, reason: reason, canRunProductRepair: checkpoint.productRepairAllowed, canRunExpensiveModel: checkpoint.expensiveModelAllowed, shouldResetLoopBudget: false, shouldIncrementAttempt: action === 'allow', newEvidence: evidenceChanged, blockerFingerprint: blockerFingerprint, evidenceHash: evidenceHash, missingEvidence: missingEvidence, checkpoint: __assign(__assign({}, checkpoint), { updatedAt: isoNow(input.now) }) }, overrides));
1088
+ return (__assign({ action: action, reason: reason, canRunProductRepair: checkpoint.productRepairAllowed, canRunExpensiveModel: checkpoint.expensiveModelAllowed, shouldResetLoopBudget: false, shouldIncrementAttempt: action === 'allow', newEvidence: evidenceChanged, materialEvidence: assessment.material, evidenceStrength: assessment.strength, evidenceSignals: assessment.signals, blockerFingerprint: blockerFingerprint, evidenceHash: evidenceHash, missingEvidence: missingEvidence, checkpoint: __assign(__assign({}, checkpoint), { updatedAt: isoNow(input.now) }) }, overrides));
663
1089
  };
664
1090
  if (checkpoint.status === 'complete') {
665
1091
  return makeDecision('complete', 'recovery_gate_checkpoint_already_complete', {
@@ -689,6 +1115,13 @@ function decideResolveIOAIManagerRecoveryGate(input) {
689
1115
  shouldIncrementAttempt: false
690
1116
  });
691
1117
  }
1118
+ if (checkpoint.status === 'parked' && evidenceChanged && !assessment.material) {
1119
+ return makeDecision('collect_new_evidence', 'recovery_gate_weak_evidence_requires_material_signal', {
1120
+ canRunProductRepair: false,
1121
+ canRunExpensiveModel: false,
1122
+ shouldIncrementAttempt: false
1123
+ });
1124
+ }
692
1125
  if (checkpoint.attempts >= checkpoint.maxAttemptsBeforePark && !evidenceChanged) {
693
1126
  return makeDecision('collect_new_evidence', 'recovery_gate_attempt_limit_requires_new_evidence', {
694
1127
  canRunProductRepair: false,
@@ -696,11 +1129,18 @@ function decideResolveIOAIManagerRecoveryGate(input) {
696
1129
  shouldIncrementAttempt: false
697
1130
  });
698
1131
  }
1132
+ if (checkpoint.attempts >= checkpoint.maxAttemptsBeforePark && evidenceChanged && !assessment.material) {
1133
+ return makeDecision('collect_new_evidence', 'recovery_gate_attempt_limit_requires_material_evidence', {
1134
+ canRunProductRepair: false,
1135
+ canRunExpensiveModel: false,
1136
+ shouldIncrementAttempt: false
1137
+ });
1138
+ }
699
1139
  if (evidenceChanged) {
700
1140
  var currentFailureClass = normalizeResolveIOAIManagerFailureClass(current.failureClass);
701
1141
  var currentCanRunProductRepair = checkpoint.productRepairAllowed
702
1142
  || !/^(infra|compile|diagnosis|release)$/i.test(currentFailureClass);
703
- return makeDecision('allow', 'recovery_gate_new_evidence_unblocks_retry', {
1143
+ return makeDecision('allow', 'recovery_gate_new_material_evidence_unblocks_retry', {
704
1144
  canRunProductRepair: currentCanRunProductRepair,
705
1145
  canRunExpensiveModel: checkpoint.expensiveModelAllowed || currentCanRunProductRepair,
706
1146
  shouldResetLoopBudget: true
@@ -812,8 +1252,8 @@ function buildResolveIOAIManagerRecoveryPlan(input) {
812
1252
  'Do not rerun the same prompt or same repair.',
813
1253
  'Show the repeated blocker and evidence hash.',
814
1254
  'Collect a new artifact: failing DOM state, stack trace, network response, Mongo delta, compile log, or revised diagnosis.',
815
- 'Reset the loop only after the new evidence hash changes.'
816
- ], ['same failure count', 'blocker fingerprint', 'evidence hash'], ['changed evidence hash', 'new artifact path', 'revised diagnosis or journey contract'], ['alternate between two failed patches', 'increase loop budget without evidence', 'hide the park reason']);
1255
+ 'Reset the loop only after material evidence changes the blocker, proof, diagnosis, journey, or actionable trace.'
1256
+ ], ['same failure count', 'blocker fingerprint', 'evidence hash'], ['changed blocker fingerprint', 'business/compile/infra/release proof artifact', 'revised diagnosis or journey contract', 'new actionable trace or data delta'], ['alternate between two failed patches', 'increase loop budget without evidence', 'hide the park reason']);
817
1257
  }
818
1258
  if (failureClass === 'journey') {
819
1259
  return makePlan('journey_contract_repair', 'Repair Journey Contract', 'Fix the first/next/last workflow contract before app code work continues.', 'journey_contract_repair', false, true, [
@@ -824,12 +1264,12 @@ function buildResolveIOAIManagerRecoveryPlan(input) {
824
1264
  ], ['validated journey_contract JSON', 'CTA-to-action mapping', 'workflow QA assertions'], ['journey validation passes', 'new workflow QA rows generated'], ['build empty routes', 'add link-only dashboard actions', 'defer workflow design to wow pass']);
825
1265
  }
826
1266
  if (failureClass === 'release') {
827
- return makePlan('release_repair', 'Repair Release Gate', 'Repair deploy/publish/sample-data release evidence without rewriting working app flow.', 'release_repair_only', false, false, [
1267
+ return makePlan('release_repair', 'Repair Release Gate', 'Repair deploy/publish/sample-data release evidence with a hotfix-first path before any repeated full deploy.', 'release_repair_only', false, false, [
828
1268
  'Read the deploy/publish/sample-data log.',
829
1269
  'Identify whether the blocker is domain, asset, seed data, route, permission, or CDN.',
830
- 'Repair the release artifact/config/seed issue.',
1270
+ 'Prefer backend hotfix, release config, domain, cache, or seed-data repair when the product artifact already has business proof.',
831
1271
  'Rerun only the failed release gate.'
832
- ], ['deploy or publish log', 'sample-data status', 'failed release gate rerun'], ['release gate passes', 'new release blocker hash'], ['change core workflow after business QA passed', 'mark accepted from scorecard only', 'rerun full builder loop']);
1272
+ ], ['deploy or publish log', 'sample-data status', 'failed release gate rerun'], ['release gate passes', 'new release blocker hash'], ['change core workflow after business QA passed', 'mark accepted from scorecard only', 'rerun full builder loop', 'repeat deploy of same artifact without force_deploy evidence']);
833
1273
  }
834
1274
  if (failureClass === 'business' || failureClass === 'qa_evidence' || failureClass === 'route') {
835
1275
  return makePlan('business_proof_repair', 'Repair Business Proof', 'Fix the exact failing workflow assertion and prove before/action/after behavior.', 'business_repair', true, true, [
@@ -867,20 +1307,33 @@ function collectOpenTail(records) {
867
1307
  return tail;
868
1308
  }
869
1309
  function countSameFailure(records, current) {
870
- var currentKey = failureEvidenceKey(current);
1310
+ var currentKey = failureKey(current);
871
1311
  var count = 0;
872
1312
  for (var index = records.length - 1; index >= 0; index -= 1) {
873
1313
  var record = records[index];
874
1314
  if (isPassingOutcome(record)) {
875
1315
  break;
876
1316
  }
877
- if (failureEvidenceKey(record) !== currentKey) {
1317
+ if (failureKey(record) !== currentKey) {
878
1318
  break;
879
1319
  }
880
1320
  count += 1;
881
1321
  }
882
1322
  return count;
883
1323
  }
1324
+ function previousRecordForPolicy(history, current, currentWasExplicit) {
1325
+ if (!history.length) {
1326
+ return undefined;
1327
+ }
1328
+ if (!currentWasExplicit) {
1329
+ return history.length > 1 ? history[history.length - 2] : undefined;
1330
+ }
1331
+ var last = history[history.length - 1];
1332
+ if (failureEvidenceKey(last) === failureEvidenceKey(current)) {
1333
+ return history.length > 1 ? history[history.length - 2] : undefined;
1334
+ }
1335
+ return last;
1336
+ }
884
1337
  function countPingPong(records, current) {
885
1338
  var tail = collectOpenTail(records).filter(function (record) {
886
1339
  return cleanText(record.lane, 80) === cleanText(current.lane, 80)
@@ -907,14 +1360,24 @@ function countPingPong(records, current) {
907
1360
  }
908
1361
  function decideResolveIOAIManagerPolicy(input) {
909
1362
  var history = Array.isArray(input.history) ? input.history : [];
1363
+ var currentWasExplicit = !!input.current;
910
1364
  var current = input.current || history[history.length - 1] || {};
911
1365
  var failureClass = normalizeResolveIOAIManagerFailureClass(current.failureClass);
912
1366
  var blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current);
913
1367
  var evidenceHash = hashResolveIOAIManagerEvidence(current);
914
- var previous = history.length > 1 ? history[history.length - 2] : undefined;
1368
+ var previous = previousRecordForPolicy(history, current, currentWasExplicit);
915
1369
  var previousSameFailure = previous && failureKey(previous) === failureKey(current);
916
- var previousEvidenceHash = previousSameFailure ? hashResolveIOAIManagerEvidence(previous) : '';
917
- var newEvidence = !!previousSameFailure && !!previousEvidenceHash && previousEvidenceHash !== evidenceHash;
1370
+ var evidenceAssessment = previousSameFailure
1371
+ ? assessResolveIOAIManagerEvidenceChange(previous, current)
1372
+ : {
1373
+ changed: false,
1374
+ material: false,
1375
+ strength: 'none',
1376
+ signals: [],
1377
+ evidenceHash: evidenceHash,
1378
+ blockerFingerprint: blockerFingerprint
1379
+ };
1380
+ var newEvidence = !!previousSameFailure && evidenceAssessment.changed;
918
1381
  var sameFailureCount = countSameFailure(history, current);
919
1382
  var pingPongCount = countPingPong(history, current);
920
1383
  var maxSameFailureRepeats = Math.max(1, Number(input.maxSameFailureRepeats || 3) || 3);
@@ -928,6 +1391,9 @@ function decideResolveIOAIManagerPolicy(input) {
928
1391
  sameFailureCount: sameFailureCount,
929
1392
  pingPongCount: pingPongCount,
930
1393
  newEvidence: newEvidence,
1394
+ materialEvidence: evidenceAssessment.material,
1395
+ evidenceStrength: evidenceAssessment.strength,
1396
+ evidenceSignals: evidenceAssessment.signals,
931
1397
  loopBudgetShouldReset: false,
932
1398
  productRepairFailure: !infraClasses.has(failureClass) && !ignoredClasses.has(failureClass)
933
1399
  };
@@ -976,8 +1442,10 @@ function decideResolveIOAIManagerPolicy(input) {
976
1442
  if (pingPongCount >= maxPingPongTransitions) {
977
1443
  return withPlan('park_ping_pong', 'manager_policy_ping_pong_failure_loop');
978
1444
  }
979
- if (sameFailureCount >= maxSameFailureRepeats && !newEvidence) {
980
- return withPlan('park_repeated_failure', 'manager_policy_same_failure_without_new_evidence');
1445
+ if (sameFailureCount >= maxSameFailureRepeats && !evidenceAssessment.material) {
1446
+ return withPlan('park_repeated_failure', newEvidence
1447
+ ? 'manager_policy_same_failure_with_weak_evidence'
1448
+ : 'manager_policy_same_failure_without_new_evidence');
981
1449
  }
982
1450
  if (infraClasses.has(failureClass)) {
983
1451
  return withPlan('retry_infra', 'manager_policy_infra_failure_routes_to_infra_repair', {
@@ -985,13 +1453,311 @@ function decideResolveIOAIManagerPolicy(input) {
985
1453
  });
986
1454
  }
987
1455
  if (newEvidence || !previousSameFailure) {
1456
+ if (newEvidence && !evidenceAssessment.material) {
1457
+ return withPlan('continue', 'manager_policy_weak_evidence_does_not_reset_loop_budget');
1458
+ }
988
1459
  return withPlan('continue', newEvidence
989
- ? 'manager_policy_new_evidence_resets_loop_budget'
1460
+ ? 'manager_policy_new_material_evidence_resets_loop_budget'
990
1461
  : 'manager_policy_new_failure_or_lane', {
991
- loopBudgetShouldReset: true
1462
+ loopBudgetShouldReset: true,
1463
+ materialEvidence: newEvidence ? true : base.materialEvidence,
1464
+ evidenceStrength: newEvidence ? evidenceAssessment.strength : base.evidenceStrength
992
1465
  });
993
1466
  }
994
1467
  return withPlan('continue', 'manager_policy_retry_below_repeat_limit');
995
1468
  }
1469
+ function buildReplayDirectiveFromPlan(input) {
1470
+ var checkpoint = buildResolveIOAIManagerRecoveryCheckpoint({
1471
+ plan: input.plan,
1472
+ current: input.current,
1473
+ now: input.now
1474
+ });
1475
+ var probe = buildResolveIOAIManagerRecoveryEvidenceProbe({
1476
+ checkpoint: checkpoint,
1477
+ current: input.current,
1478
+ now: input.now
1479
+ });
1480
+ var action = buildResolveIOAIManagerRecoveryActionPacket({
1481
+ plan: input.plan,
1482
+ checkpoint: checkpoint,
1483
+ probe: probe,
1484
+ current: input.current,
1485
+ now: input.now
1486
+ });
1487
+ var dispatch = decideResolveIOAIManagerRecoveryActionDispatch({
1488
+ action: action,
1489
+ current: input.current,
1490
+ now: input.now
1491
+ });
1492
+ return buildResolveIOAIManagerRecoveryExecutionDirective({
1493
+ action: action,
1494
+ dispatchDecision: dispatch,
1495
+ current: input.current,
1496
+ surface: input.surface,
1497
+ now: input.now
1498
+ });
1499
+ }
1500
+ function replayCaseFromDirective(input) {
1501
+ var _a;
1502
+ var directive = input.directive;
1503
+ var safeAutoDispatch = isResolveIOAIManagerSafeAutoDispatch({
1504
+ dispatchAction: directive.dispatchAction,
1505
+ directive: directive,
1506
+ includeJourneyContractRepair: input.expectedSafeAutoDispatch && directive.dispatchAction === 'run_journey_contract_repair',
1507
+ includeReleaseRepair: input.expectedSafeAutoDispatch && directive.dispatchAction === 'run_release_repair',
1508
+ includeProductRepair: false
1509
+ });
1510
+ return __assign(__assign({ caseId: input.caseId, surface: input.surface, pass: input.pass && safeAutoDispatch === input.expectedSafeAutoDispatch, action: input.action, reason: input.reason, recoveryClass: cleanText(((_a = directive.dispatchRecord) === null || _a === void 0 ? void 0 : _a.mode) || directive.phase, 120), dispatchAction: directive.dispatchAction, safeAutoDispatch: safeAutoDispatch, expectedSafeAutoDispatch: input.expectedSafeAutoDispatch, productRepairAllowed: directive.canRunProductRepair === true, loopBudgetShouldReset: input.loopBudgetShouldReset === true, materialEvidence: input.materialEvidence === true, evidenceStrength: input.evidenceStrength || 'none' }, (directive.releasePolicy ? {
1511
+ hotfixFirst: directive.releasePolicy.policy === 'hotfix_first',
1512
+ fullDeployAllowed: directive.releasePolicy.fullDeployAllowed
1513
+ } : {})), { details: __assign({ phase: directive.phase, allowed: directive.allowed, reason: directive.reason, releasePolicy: directive.releasePolicy }, (input.details || {})) });
1514
+ }
1515
+ function buildResolveIOAIManagerRecoveryReplayMatrix(input) {
1516
+ var _a, _b;
1517
+ if (input === void 0) { input = {}; }
1518
+ var surface = cleanText(input.surface || 'runner', 120);
1519
+ var maxSameFailureRepeats = Math.max(1, Number(input.maxSameFailureRepeats || 2) || 2);
1520
+ var includeReleaseRepair = input.includeReleaseRepair !== false;
1521
+ var includeJourneyContractRepair = input.includeJourneyContractRepair !== false;
1522
+ var cases = [];
1523
+ var infraPolicy = decideResolveIOAIManagerPolicy({
1524
+ history: [
1525
+ { lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' }
1526
+ ],
1527
+ current: { lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' },
1528
+ maxSameFailureRepeats: maxSameFailureRepeats
1529
+ });
1530
+ var infraDirective = buildResolveIOAIManagerRecoveryExecutionDirective({
1531
+ action: infraPolicy.recoveryAction,
1532
+ current: { lane: 'qa', stepType: 'workflow_qa', outcome: 'needs_repair', failureClass: 'infra', blocker: 'Chrome executable missing.', evidenceHash: 'chrome-missing' },
1533
+ surface: surface
1534
+ });
1535
+ cases.push(replayCaseFromDirective({
1536
+ caseId: 'infra_routes_to_infra_repair',
1537
+ surface: surface,
1538
+ action: infraPolicy.action,
1539
+ reason: infraPolicy.reason,
1540
+ directive: infraDirective,
1541
+ expectedSafeAutoDispatch: true,
1542
+ pass: infraPolicy.action === 'retry_infra'
1543
+ && infraPolicy.recoveryPlan.recoveryClass === 'infra_repair'
1544
+ && infraDirective.dispatchAction === 'run_infra_repair'
1545
+ && infraDirective.canRunProductRepair === false,
1546
+ details: { recoveryClass: infraPolicy.recoveryPlan.recoveryClass }
1547
+ }));
1548
+ var releasePlan = buildResolveIOAIManagerRecoveryPlan({
1549
+ action: 'continue',
1550
+ reason: 'publish failed after business proof',
1551
+ failureClass: 'release',
1552
+ lane: 'publish',
1553
+ stepType: 'test_deploy',
1554
+ blocker: 'Domain publish failed for the current artifact.'
1555
+ });
1556
+ var releaseCurrent = {
1557
+ lane: 'publish',
1558
+ stepType: 'test_deploy',
1559
+ outcome: 'needs_repair',
1560
+ failureClass: 'release',
1561
+ blocker: 'Domain publish failed for the current artifact.',
1562
+ evidenceHash: 'release-domain-publish-failed',
1563
+ artifactPaths: ['runner-evidence/release-status.json']
1564
+ };
1565
+ var releaseDirective = buildReplayDirectiveFromPlan({
1566
+ surface: surface,
1567
+ plan: releasePlan,
1568
+ current: releaseCurrent
1569
+ });
1570
+ cases.push(replayCaseFromDirective({
1571
+ caseId: 'release_uses_hotfix_first_repair',
1572
+ surface: surface,
1573
+ action: 'continue',
1574
+ reason: 'publish failed after business proof',
1575
+ directive: releaseDirective,
1576
+ expectedSafeAutoDispatch: includeReleaseRepair,
1577
+ pass: releasePlan.recoveryClass === 'release_repair'
1578
+ && releaseDirective.dispatchAction === 'run_release_repair'
1579
+ && ((_a = releaseDirective.releasePolicy) === null || _a === void 0 ? void 0 : _a.policy) === 'hotfix_first'
1580
+ && ((_b = releaseDirective.releasePolicy) === null || _b === void 0 ? void 0 : _b.fullDeployAllowed) === false
1581
+ && releaseDirective.canRunProductRepair === false,
1582
+ details: { recoveryClass: releasePlan.recoveryClass }
1583
+ }));
1584
+ var journeyPlan = buildResolveIOAIManagerRecoveryPlan({
1585
+ action: 'continue',
1586
+ reason: 'journey contract missing first next last',
1587
+ failureClass: 'journey',
1588
+ lane: 'plan',
1589
+ stepType: 'journey_contract',
1590
+ blocker: 'Journey contract does not map the hub CTA to an action.'
1591
+ });
1592
+ var journeyDirective = buildReplayDirectiveFromPlan({
1593
+ surface: surface,
1594
+ plan: journeyPlan,
1595
+ current: {
1596
+ lane: 'plan',
1597
+ stepType: 'journey_contract',
1598
+ outcome: 'needs_repair',
1599
+ failureClass: 'journey',
1600
+ blocker: 'Journey contract does not map the hub CTA to an action.',
1601
+ evidenceHash: 'journey-contract-invalid'
1602
+ }
1603
+ });
1604
+ cases.push(replayCaseFromDirective({
1605
+ caseId: 'journey_repair_is_safe_only_for_aicoder_surfaces',
1606
+ surface: surface,
1607
+ action: 'continue',
1608
+ reason: 'journey contract missing first next last',
1609
+ directive: journeyDirective,
1610
+ expectedSafeAutoDispatch: includeJourneyContractRepair,
1611
+ pass: journeyPlan.recoveryClass === 'journey_contract_repair'
1612
+ && journeyDirective.dispatchAction === 'run_journey_contract_repair'
1613
+ && journeyDirective.canRunProductRepair === false,
1614
+ details: { recoveryClass: journeyPlan.recoveryClass }
1615
+ }));
1616
+ var weakPolicy = decideResolveIOAIManagerPolicy({
1617
+ history: [
1618
+ { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'save-before' },
1619
+ { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' }
1620
+ ],
1621
+ current: { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' },
1622
+ maxSameFailureRepeats: maxSameFailureRepeats
1623
+ });
1624
+ var weakDirective = buildResolveIOAIManagerRecoveryExecutionDirective({
1625
+ action: weakPolicy.recoveryAction,
1626
+ current: { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'renamed-hash-only' },
1627
+ surface: surface
1628
+ });
1629
+ cases.push(replayCaseFromDirective({
1630
+ caseId: 'weak_hash_only_evidence_stays_parked',
1631
+ surface: surface,
1632
+ action: weakPolicy.action,
1633
+ reason: weakPolicy.reason,
1634
+ directive: weakDirective,
1635
+ expectedSafeAutoDispatch: true,
1636
+ pass: weakPolicy.action === 'park_repeated_failure'
1637
+ && weakPolicy.materialEvidence === false
1638
+ && weakPolicy.loopBudgetShouldReset === false
1639
+ && weakDirective.dispatchAction === 'run_evidence_probe',
1640
+ loopBudgetShouldReset: weakPolicy.loopBudgetShouldReset,
1641
+ materialEvidence: weakPolicy.materialEvidence,
1642
+ evidenceStrength: weakPolicy.evidenceStrength,
1643
+ details: { evidenceSignals: weakPolicy.evidenceSignals }
1644
+ }));
1645
+ var materialPolicy = decideResolveIOAIManagerPolicy({
1646
+ history: [
1647
+ { lane: 'repair', stepType: 'repair', outcome: 'needs_repair', failureClass: 'product_code', blocker: 'Save action still throws TypeError.', evidenceHash: 'save-before', artifactPaths: ['qa/save-before.log'] },
1648
+ {
1649
+ lane: 'repair',
1650
+ stepType: 'repair',
1651
+ outcome: 'needs_repair',
1652
+ failureClass: 'product_code',
1653
+ blocker: 'Save action still throws TypeError.',
1654
+ summary: 'New DOM trace proves the click reaches the method with an undefined id.',
1655
+ evidenceHash: 'save-dom-trace',
1656
+ artifactPaths: ['qa/save-before.log', 'qa/save-dom-trace.json']
1657
+ }
1658
+ ],
1659
+ current: {
1660
+ lane: 'repair',
1661
+ stepType: 'repair',
1662
+ outcome: 'needs_repair',
1663
+ failureClass: 'product_code',
1664
+ blocker: 'Save action still throws TypeError.',
1665
+ summary: 'New DOM trace proves the click reaches the method with an undefined id.',
1666
+ evidenceHash: 'save-dom-trace',
1667
+ artifactPaths: ['qa/save-before.log', 'qa/save-dom-trace.json']
1668
+ },
1669
+ maxSameFailureRepeats: maxSameFailureRepeats
1670
+ });
1671
+ cases.push({
1672
+ caseId: 'material_evidence_resets_loop_budget',
1673
+ surface: surface,
1674
+ pass: materialPolicy.action === 'continue'
1675
+ && materialPolicy.materialEvidence === true
1676
+ && materialPolicy.loopBudgetShouldReset === true,
1677
+ action: materialPolicy.action,
1678
+ reason: materialPolicy.reason,
1679
+ recoveryClass: materialPolicy.recoveryPlan.recoveryClass,
1680
+ dispatchAction: materialPolicy.recoveryAction.mode,
1681
+ safeAutoDispatch: false,
1682
+ expectedSafeAutoDispatch: false,
1683
+ productRepairAllowed: materialPolicy.recoveryAction.productRepairAllowed,
1684
+ loopBudgetShouldReset: materialPolicy.loopBudgetShouldReset,
1685
+ materialEvidence: materialPolicy.materialEvidence,
1686
+ evidenceStrength: materialPolicy.evidenceStrength,
1687
+ details: { evidenceSignals: materialPolicy.evidenceSignals }
1688
+ });
1689
+ var productPlan = buildResolveIOAIManagerRecoveryPlan({
1690
+ action: 'continue',
1691
+ reason: 'business proof still failing',
1692
+ failureClass: 'product_code',
1693
+ lane: 'repair',
1694
+ stepType: 'repair',
1695
+ blocker: 'Save action still throws TypeError.',
1696
+ productRepairFailure: true
1697
+ });
1698
+ var productDirective = buildReplayDirectiveFromPlan({
1699
+ surface: surface,
1700
+ plan: productPlan,
1701
+ current: {
1702
+ lane: 'repair',
1703
+ stepType: 'repair',
1704
+ outcome: 'needs_repair',
1705
+ failureClass: 'product_code',
1706
+ blocker: 'Save action still throws TypeError.',
1707
+ evidenceHash: 'save-product-repair'
1708
+ }
1709
+ });
1710
+ var productSafe = isResolveIOAIManagerSafeAutoDispatch({
1711
+ dispatchAction: productDirective.dispatchAction,
1712
+ directive: productDirective,
1713
+ includeProductRepair: input.includeProductRepair === true
1714
+ });
1715
+ cases.push({
1716
+ caseId: 'product_repair_requires_explicit_operator_policy',
1717
+ surface: surface,
1718
+ pass: productPlan.recoveryClass === 'product_code_repair'
1719
+ && productDirective.dispatchAction === 'run_targeted_product_repair'
1720
+ && productSafe === false,
1721
+ action: 'continue',
1722
+ reason: 'business proof still failing',
1723
+ recoveryClass: productPlan.recoveryClass,
1724
+ dispatchAction: productDirective.dispatchAction,
1725
+ safeAutoDispatch: productSafe,
1726
+ expectedSafeAutoDispatch: false,
1727
+ productRepairAllowed: productDirective.canRunProductRepair,
1728
+ loopBudgetShouldReset: false,
1729
+ materialEvidence: false,
1730
+ evidenceStrength: 'none',
1731
+ details: { phase: productDirective.phase, allowed: productDirective.allowed }
1732
+ });
1733
+ var duplicateReleasePolicy = buildResolveIOAIManagerHotfixFirstReleasePolicy({
1734
+ surface: surface,
1735
+ deployFingerprint: "".concat(surface, "-artifact-sha"),
1736
+ lastDeployFingerprint: "".concat(surface, "-artifact-sha"),
1737
+ deployStatus: 'completed',
1738
+ publishStatus: 'published'
1739
+ });
1740
+ cases.push({
1741
+ caseId: 'duplicate_release_fingerprint_blocks_full_deploy',
1742
+ surface: surface,
1743
+ pass: duplicateReleasePolicy.recommendedAction === 'block_duplicate_deploy'
1744
+ && duplicateReleasePolicy.duplicateDeployBlocked === true
1745
+ && duplicateReleasePolicy.fullDeployAllowed === false,
1746
+ action: duplicateReleasePolicy.recommendedAction,
1747
+ reason: duplicateReleasePolicy.reason,
1748
+ recoveryClass: 'release_repair',
1749
+ dispatchAction: 'run_release_repair',
1750
+ safeAutoDispatch: includeReleaseRepair,
1751
+ expectedSafeAutoDispatch: includeReleaseRepair,
1752
+ productRepairAllowed: false,
1753
+ loopBudgetShouldReset: false,
1754
+ materialEvidence: false,
1755
+ evidenceStrength: 'none',
1756
+ hotfixFirst: true,
1757
+ fullDeployAllowed: duplicateReleasePolicy.fullDeployAllowed,
1758
+ details: { releasePolicy: duplicateReleasePolicy }
1759
+ });
1760
+ return cases;
1761
+ }
996
1762
 
997
1763
  //# sourceMappingURL=ai-runner-manager-policy.js.map