@resolveio/server-lib 22.3.157 → 22.3.159

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,9 +47,14 @@ 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.estimateResolveIOAIManagerRetryExpectedValue = estimateResolveIOAIManagerRetryExpectedValue;
51
+ exports.decideResolveIOAIManagerAutonomyPolicy = decideResolveIOAIManagerAutonomyPolicy;
50
52
  exports.normalizeResolveIOAIManagerHotfixEvidence = normalizeResolveIOAIManagerHotfixEvidence;
53
+ exports.evaluateResolveIOAIManagerHotfixGitProof = evaluateResolveIOAIManagerHotfixGitProof;
51
54
  exports.validateResolveIOAIManagerHotfixEvidence = validateResolveIOAIManagerHotfixEvidence;
55
+ exports.buildResolveIOAIManagerHotfixEvidenceRecord = buildResolveIOAIManagerHotfixEvidenceRecord;
52
56
  exports.decideResolveIOAIManagerHotfixContinuation = decideResolveIOAIManagerHotfixContinuation;
57
+ exports.decideResolveIOAIManagerHotfixCommitGuard = decideResolveIOAIManagerHotfixCommitGuard;
53
58
  exports.buildResolveIOAIManagerHotfixFirstReleasePolicy = buildResolveIOAIManagerHotfixFirstReleasePolicy;
54
59
  exports.isResolveIOAIManagerSafeAutoDispatch = isResolveIOAIManagerSafeAutoDispatch;
55
60
  exports.normalizeResolveIOAIManagerFailureClass = normalizeResolveIOAIManagerFailureClass;
@@ -114,6 +119,291 @@ var HOTFIX_CHANNELS = new Set([
114
119
  function cleanObject(value) {
115
120
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
116
121
  }
122
+ function normalizeAutonomyMode(value) {
123
+ var normalized = cleanText(value, 120).toLowerCase().replace(/[\s-]+/g, '_');
124
+ if (/^(monitor|monitor_only|watch|watch_only)$/.test(normalized)) {
125
+ return 'monitor_only';
126
+ }
127
+ if (/^(auto|auto_retry|auto_retry_within_budget|autonomous|within_budget)$/.test(normalized)) {
128
+ return 'auto_retry_within_budget';
129
+ }
130
+ return 'manual_only';
131
+ }
132
+ function normalizeManagerEvidenceStrength(value) {
133
+ var normalized = cleanText(value, 40).toLowerCase().replace(/[\s-]+/g, '_');
134
+ if (normalized === 'proof') {
135
+ return 'proof';
136
+ }
137
+ if (normalized === 'material' || normalized === 'strong') {
138
+ return 'material';
139
+ }
140
+ if (normalized === 'weak') {
141
+ return 'weak';
142
+ }
143
+ return 'none';
144
+ }
145
+ function nonNegativeMoney(value) {
146
+ var parsed = Number(value);
147
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
148
+ }
149
+ function finiteNumber(value) {
150
+ var parsed = Number(value);
151
+ return Number.isFinite(parsed) ? parsed : undefined;
152
+ }
153
+ function clampNumber(value, min, max) {
154
+ return Math.min(max, Math.max(min, value));
155
+ }
156
+ function isCheapEvidenceAutonomyAction(value) {
157
+ var action = cleanText(value, 120);
158
+ return !action || /^(run_evidence_probe|run_read_only_diagnosis|advance|continue_gate|park_manual)$/.test(action);
159
+ }
160
+ function estimateResolveIOAIManagerRetryExpectedValue(input) {
161
+ var e_2, _a;
162
+ if (input === void 0) { input = {}; }
163
+ var dispatchAction = cleanText(input.dispatchAction, 120);
164
+ var failureClass = normalizeResolveIOAIManagerFailureClass(input.failureClass);
165
+ var recoveryClass = cleanText(input.recoveryClass, 120);
166
+ var evidenceStrength = normalizeManagerEvidenceStrength(input.evidenceStrength);
167
+ var evidenceSignals = cleanList(input.evidenceSignals, 12, 120);
168
+ var projectedActionCostUsd = nonNegativeMoney(input.projectedActionCostUsd);
169
+ var budgetHardUsd = nonNegativeMoney(input.budgetHardUsd);
170
+ var materialEvidence = input.materialEvidence === true || evidenceStrength === 'material' || evidenceStrength === 'proof';
171
+ var newEvidence = input.newEvidence === true;
172
+ var signals = [];
173
+ var score = 0.1;
174
+ if (dispatchAction === 'run_evidence_probe') {
175
+ score = 0.75;
176
+ signals.push('cheap_evidence_probe');
177
+ }
178
+ else if (dispatchAction === 'run_read_only_diagnosis') {
179
+ score = 0.65;
180
+ signals.push('read_only_diagnosis');
181
+ }
182
+ else if (dispatchAction === 'advance' || dispatchAction === 'continue_gate') {
183
+ score = 0.55;
184
+ signals.push('gate_continuation');
185
+ }
186
+ else if (dispatchAction === 'run_infra_repair') {
187
+ score = 0.4;
188
+ signals.push('bounded_infra_repair');
189
+ }
190
+ else if (dispatchAction === 'run_compile_repair') {
191
+ score = 0.4;
192
+ signals.push('bounded_compile_repair');
193
+ }
194
+ else if (dispatchAction === 'run_release_repair') {
195
+ score = 0.3;
196
+ signals.push('bounded_release_repair');
197
+ }
198
+ else if (dispatchAction === 'run_journey_contract_repair' || dispatchAction === 'run_business_assertion_repair') {
199
+ score = 0.25;
200
+ signals.push('bounded_workflow_or_proof_repair');
201
+ }
202
+ else if (dispatchAction === 'run_targeted_product_repair') {
203
+ score = 0.15;
204
+ signals.push('targeted_product_repair');
205
+ }
206
+ if (evidenceStrength === 'proof') {
207
+ score += 0.3;
208
+ signals.push('proof_evidence');
209
+ }
210
+ else if (materialEvidence) {
211
+ score += 0.2;
212
+ signals.push('material_evidence');
213
+ }
214
+ else if (newEvidence) {
215
+ score += 0.1;
216
+ signals.push('new_evidence');
217
+ }
218
+ else if (evidenceStrength === 'weak') {
219
+ score -= 0.15;
220
+ signals.push('weak_evidence');
221
+ }
222
+ else if (!isCheapEvidenceAutonomyAction(dispatchAction)) {
223
+ score -= 0.25;
224
+ signals.push('no_material_evidence');
225
+ }
226
+ if ((failureClass === 'infra' || /infra/i.test(recoveryClass)) && dispatchAction === 'run_infra_repair') {
227
+ score += 0.15;
228
+ signals.push('failure_matches_infra_repair');
229
+ }
230
+ if ((failureClass === 'compile' || /compile/i.test(recoveryClass)) && dispatchAction === 'run_compile_repair') {
231
+ score += 0.15;
232
+ signals.push('failure_matches_compile_repair');
233
+ }
234
+ if (/release|publish|deploy/i.test("".concat(failureClass, " ").concat(recoveryClass)) && dispatchAction === 'run_release_repair') {
235
+ score += 0.1;
236
+ signals.push('failure_matches_release_repair');
237
+ }
238
+ if (projectedActionCostUsd >= 5) {
239
+ score -= 0.3;
240
+ signals.push('high_projected_cost');
241
+ }
242
+ else if (projectedActionCostUsd >= 2) {
243
+ score -= 0.18;
244
+ signals.push('medium_projected_cost');
245
+ }
246
+ else if (projectedActionCostUsd >= 1) {
247
+ score -= 0.08;
248
+ signals.push('low_projected_cost');
249
+ }
250
+ if (budgetHardUsd > 0 && projectedActionCostUsd > budgetHardUsd * 0.5) {
251
+ score -= 0.2;
252
+ signals.push('large_fraction_of_remaining_budget');
253
+ }
254
+ if (input.sameEvidenceAlreadyAttempted === true) {
255
+ score -= 0.35;
256
+ signals.push('same_evidence_already_attempted');
257
+ }
258
+ try {
259
+ for (var evidenceSignals_1 = __values(evidenceSignals), evidenceSignals_1_1 = evidenceSignals_1.next(); !evidenceSignals_1_1.done; evidenceSignals_1_1 = evidenceSignals_1.next()) {
260
+ var signal = evidenceSignals_1_1.value;
261
+ if (/passing_outcome|business|proof|diagnosis|compile|infra|release|artifact|fingerprint_changed|evidence_hash_changed/i.test(signal)) {
262
+ score += 0.03;
263
+ signals.push("evidence_signal:".concat(signal));
264
+ }
265
+ }
266
+ }
267
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
268
+ finally {
269
+ try {
270
+ if (evidenceSignals_1_1 && !evidenceSignals_1_1.done && (_a = evidenceSignals_1.return)) _a.call(evidenceSignals_1);
271
+ }
272
+ finally { if (e_2) throw e_2.error; }
273
+ }
274
+ var rounded = Number(clampNumber(score, -1, 1).toFixed(2));
275
+ var minScore = 0;
276
+ return {
277
+ score: rounded,
278
+ minScore: minScore,
279
+ positive: rounded >= minScore,
280
+ source: 'estimated',
281
+ confidence: evidenceStrength === 'proof' || materialEvidence ? 'medium' : 'low',
282
+ reason: rounded >= minScore
283
+ ? 'estimated_retry_value_is_non_negative'
284
+ : 'estimated_retry_value_is_negative',
285
+ signals: Array.from(new Set(signals)).slice(0, 16)
286
+ };
287
+ }
288
+ function decideResolveIOAIManagerAutonomyPolicy(input) {
289
+ var _a;
290
+ if (input === void 0) { input = {}; }
291
+ var mode = normalizeAutonomyMode(input.mode);
292
+ var currentSpendUsd = nonNegativeMoney(input.currentSpendUsd);
293
+ var projectedActionCostUsd = nonNegativeMoney(input.projectedActionCostUsd);
294
+ var projectedSpendUsd = currentSpendUsd + projectedActionCostUsd;
295
+ var budgetSoftUsd = nonNegativeMoney(input.budgetSoftUsd);
296
+ var budgetHardUsd = nonNegativeMoney(input.budgetHardUsd);
297
+ var softBudgetExceeded = budgetSoftUsd > 0 && projectedSpendUsd >= budgetSoftUsd;
298
+ var hardBudgetExceeded = budgetHardUsd > 0 && projectedSpendUsd > budgetHardUsd;
299
+ var dispatchAction = cleanText(input.dispatchAction, 120);
300
+ var evidenceStrength = normalizeManagerEvidenceStrength(input.evidenceStrength);
301
+ var evidenceSignals = cleanList(input.evidenceSignals, 12, 120);
302
+ var materialEvidence = input.materialEvidence === true || evidenceStrength === 'material' || evidenceStrength === 'proof';
303
+ var newEvidence = input.newEvidence === true;
304
+ var evidenceBacked = materialEvidence || newEvidence || evidenceSignals.some(function (signal) { return /material|proof|business|compile|infra|release|diagnosis|journey|workflow|artifact|hash_changed|fingerprint_changed/i.test(signal); });
305
+ var cheapEvidenceAction = isCheapEvidenceAutonomyAction(dispatchAction);
306
+ var expectedValueEstimate = estimateResolveIOAIManagerRetryExpectedValue({
307
+ dispatchAction: dispatchAction,
308
+ projectedActionCostUsd: projectedActionCostUsd,
309
+ currentSpendUsd: currentSpendUsd,
310
+ budgetHardUsd: budgetHardUsd,
311
+ evidenceStrength: evidenceStrength,
312
+ materialEvidence: materialEvidence,
313
+ newEvidence: newEvidence,
314
+ evidenceSignals: evidenceSignals,
315
+ sameEvidenceAlreadyAttempted: input.sameEvidenceAlreadyAttempted === true
316
+ });
317
+ var providedExpectedValue = finiteNumber(input.expectedValueScore);
318
+ var expectedValueKnown = providedExpectedValue !== undefined;
319
+ var expectedValueScore = expectedValueKnown ? providedExpectedValue : expectedValueEstimate.score;
320
+ var minExpectedValue = (_a = finiteNumber(input.minExpectedValueScore)) !== null && _a !== void 0 ? _a : expectedValueEstimate.minScore;
321
+ var expectedValuePositive = expectedValueScore >= minExpectedValue;
322
+ var expectedValueSource = expectedValueKnown ? 'provided' : 'estimated';
323
+ var requiredEvidence = __spreadArray(__spreadArray(__spreadArray(__spreadArray([
324
+ 'mode',
325
+ 'projectedActionCostUsd'
326
+ ], __read((budgetHardUsd > 0 ? ['budgetHardUsd'] : [])), false), __read((budgetSoftUsd > 0 ? ['budgetSoftUsd'] : [])), false), __read((dispatchAction ? ['dispatchAction'] : [])), false), __read((cheapEvidenceAction ? [] : ['materialEvidence or newEvidence', 'expectedValueScore when available'])), false);
327
+ var forbiddenActions = [];
328
+ var canAutoDispatch = false;
329
+ var canManualDispatch = true;
330
+ var blocked = false;
331
+ var requiresHumanApproval = true;
332
+ var reason = '';
333
+ var recommendedControl = mode;
334
+ if (mode === 'monitor_only') {
335
+ canManualDispatch = false;
336
+ reason = 'monitor_only_blocks_runner_dispatch';
337
+ forbiddenActions.push('Do not run recovery, repair, QA, publish, deploy, or customer reply actions while in Monitor Only mode.');
338
+ }
339
+ else if (mode === 'manual_only') {
340
+ reason = 'manual_only_requires_operator_dispatch';
341
+ forbiddenActions.push('Do not auto-dispatch manager recovery actions in Manual Only mode.');
342
+ }
343
+ else if (hardBudgetExceeded) {
344
+ blocked = true;
345
+ canAutoDispatch = false;
346
+ canManualDispatch = input.operatorApproved === true;
347
+ reason = 'auto_retry_budget_hard_cap_would_be_exceeded';
348
+ recommendedControl = 'increase_budget_or_manual';
349
+ forbiddenActions.push('Do not auto-dispatch because projected spend exceeds the hard budget.');
350
+ }
351
+ else if (!cheapEvidenceAction && !evidenceBacked) {
352
+ canAutoDispatch = false;
353
+ requiresHumanApproval = true;
354
+ reason = 'auto_retry_requires_material_evidence';
355
+ forbiddenActions.push('Do not auto-dispatch repair work until a deterministic artifact, changed blocker fingerprint, or material proof is recorded.');
356
+ }
357
+ else if (!cheapEvidenceAction && !expectedValuePositive) {
358
+ canAutoDispatch = false;
359
+ requiresHumanApproval = true;
360
+ reason = 'auto_retry_expected_value_not_positive';
361
+ forbiddenActions.push('Do not auto-dispatch repair work when the expected value score is below the configured minimum.');
362
+ }
363
+ else {
364
+ canAutoDispatch = true;
365
+ requiresHumanApproval = softBudgetExceeded;
366
+ reason = softBudgetExceeded
367
+ ? 'auto_retry_soft_budget_warning_requires_review'
368
+ : 'auto_retry_within_budget_allows_auto_dispatch';
369
+ if (softBudgetExceeded) {
370
+ forbiddenActions.push('Do not escalate to a more expensive model or broader repair without operator review after soft budget warning.');
371
+ }
372
+ }
373
+ return {
374
+ mode: mode,
375
+ canAutoDispatch: canAutoDispatch,
376
+ canManualDispatch: canManualDispatch,
377
+ blocked: blocked,
378
+ requiresHumanApproval: requiresHumanApproval,
379
+ reason: reason,
380
+ currentSpendUsd: currentSpendUsd,
381
+ projectedActionCostUsd: projectedActionCostUsd,
382
+ projectedSpendUsd: projectedSpendUsd,
383
+ budgetSoftUsd: budgetSoftUsd,
384
+ budgetHardUsd: budgetHardUsd,
385
+ softBudgetExceeded: softBudgetExceeded,
386
+ hardBudgetExceeded: hardBudgetExceeded,
387
+ dispatchAction: dispatchAction,
388
+ expectedValueScore: expectedValueScore,
389
+ minExpectedValueScore: minExpectedValue,
390
+ expectedValueKnown: expectedValueKnown,
391
+ expectedValueSource: expectedValueSource,
392
+ expectedValuePositive: expectedValuePositive,
393
+ evidenceStrength: evidenceStrength,
394
+ materialEvidence: materialEvidence,
395
+ newEvidence: newEvidence,
396
+ evidenceBacked: evidenceBacked,
397
+ evidenceSignals: evidenceSignals,
398
+ recommendedControl: recommendedControl,
399
+ forbiddenActions: forbiddenActions,
400
+ requiredEvidence: requiredEvidence
401
+ };
402
+ }
403
+ function cleanDateString(value) {
404
+ var date = value instanceof Date ? value : new Date(value || Date.now());
405
+ return Number.isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString();
406
+ }
117
407
  function normalizeHotfixChannel(value) {
118
408
  var normalized = cleanText(value, 120).toLowerCase().replace(/[\s-]+/g, '_');
119
409
  return HOTFIX_CHANNELS.has(normalized) ? normalized : '';
@@ -182,6 +472,11 @@ function normalizeResolveIOAIManagerHotfixEvidence(value, policy) {
182
472
  serviceRestartEvidence: cleanText(source.serviceRestartEvidence || source.service_restart_evidence, 1000),
183
473
  releaseArtifactFingerprint: cleanText(source.releaseArtifactFingerprint || source.release_artifact_fingerprint || source.artifactFingerprint || source.artifact_fingerprint, 240),
184
474
  lastReleaseArtifactFingerprint: cleanText(source.lastReleaseArtifactFingerprint || source.last_release_artifact_fingerprint || source.lastArtifactFingerprint || source.last_artifact_fingerprint, 240),
475
+ sourceCommitSha: cleanText(source.sourceCommitSha || source.source_commit_sha || source.gitCommitSha || source.git_commit_sha || source.commitSha || source.commit_sha, 120),
476
+ githubCommitUrl: cleanText(source.githubCommitUrl || source.github_commit_url || source.gitCommitUrl || source.git_commit_url || source.commitUrl || source.commit_url, 500),
477
+ gitCommitStatus: cleanText(source.gitCommitStatus || source.git_commit_status || source.githubCommitStatus || source.github_commit_status, 160),
478
+ gitPushStatus: cleanText(source.gitPushStatus || source.git_push_status || source.githubPushStatus || source.github_push_status || source.githubCommitReachableStatus || source.github_commit_reachable_status, 160),
479
+ committedAt: source.committedAt || source.committed_at,
185
480
  forceDeployReason: cleanText(source.forceDeployReason || source.force_deploy_reason, 1000),
186
481
  hotfixCannotResolveReason: cleanText(source.hotfixCannotResolveReason || source.hotfix_cannot_resolve_reason, 1000),
187
482
  fullDeployRequested: source.fullDeployRequested === true || source.full_deploy_requested === true,
@@ -197,6 +492,100 @@ function pushMissing(blockers, condition, message) {
197
492
  blockers.push(message);
198
493
  }
199
494
  }
495
+ function hotfixRequiresGithubCommit(channel) {
496
+ return /^(backend_js|static_ui|config|seed_data|release_artifact|new_artifact_deploy|artifact_build)$/.test(channel);
497
+ }
498
+ function extractGithubCommitSha(value) {
499
+ var _a;
500
+ var match = cleanText(value, 500).match(/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/commit\/([a-f0-9]{40})(?:$|[?#/])/i);
501
+ return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
502
+ }
503
+ function isFullGitCommitSha(value) {
504
+ return /^[a-f0-9]{40}$/i.test(cleanText(value, 120));
505
+ }
506
+ function isStrongRemoteChecksum(value) {
507
+ return /^[a-f0-9]{12,128}$/i.test(cleanText(value, 160));
508
+ }
509
+ function githubCommitProofBlockers(value, channel) {
510
+ var blockers = [];
511
+ var sourceCommitSha = cleanText(value.sourceCommitSha, 120);
512
+ var githubCommitUrl = cleanText(value.githubCommitUrl, 500);
513
+ var gitCommitStatus = cleanText(value.gitCommitStatus, 160);
514
+ var remoteCommitProofStatus = cleanText(value.gitPushStatus || value.gitCommitStatus, 160);
515
+ var urlCommitSha = extractGithubCommitSha(githubCommitUrl);
516
+ if (!sourceCommitSha || !githubCommitUrl) {
517
+ blockers.push("".concat(channel, " hotfix requires sourceCommitSha, githubCommitUrl, and passed gitPushStatus so live hotfixes are backed by a GitHub commit pushed before continuation."));
518
+ return blockers;
519
+ }
520
+ if (!isFullGitCommitSha(sourceCommitSha)) {
521
+ blockers.push("".concat(channel, " hotfix sourceCommitSha must be a full 40-character git SHA."));
522
+ }
523
+ if (!urlCommitSha) {
524
+ blockers.push("".concat(channel, " hotfix githubCommitUrl must be a GitHub commit URL ending in a full 40-character git SHA."));
525
+ }
526
+ else if (isFullGitCommitSha(sourceCommitSha) && urlCommitSha !== sourceCommitSha.toLowerCase()) {
527
+ blockers.push("".concat(channel, " hotfix githubCommitUrl commit SHA must match sourceCommitSha."));
528
+ }
529
+ if (!remoteCommitProofStatus) {
530
+ blockers.push("".concat(channel, " hotfix requires passed gitPushStatus or gitCommitStatus so the manager knows the hotfix commit is present on GitHub before continuation."));
531
+ }
532
+ else if (!hotfixStatusPassed(remoteCommitProofStatus)) {
533
+ blockers.push("".concat(channel, " hotfix gitPushStatus or gitCommitStatus must be passed before continuation."));
534
+ }
535
+ if (gitCommitStatus && gitCommitStatus !== remoteCommitProofStatus && !hotfixStatusPassed(gitCommitStatus)) {
536
+ blockers.push("".concat(channel, " hotfix gitCommitStatus must be passed when provided."));
537
+ }
538
+ return blockers;
539
+ }
540
+ function evaluateResolveIOAIManagerHotfixGitProof(value, channelHint) {
541
+ if (channelHint === void 0) { channelHint = ''; }
542
+ var normalized = normalizeResolveIOAIManagerHotfixEvidence(value);
543
+ var source = (normalized || cleanObject(value));
544
+ var channel = (channelHint || (normalized === null || normalized === void 0 ? void 0 : normalized.channel) || source.channel || '');
545
+ var required = hotfixRequiresGithubCommit(channel);
546
+ var sourceCommitSha = cleanText(source.sourceCommitSha || source.source_commit_sha || source.gitCommitSha || source.git_commit_sha || source.commitSha || source.commit_sha, 120);
547
+ var githubCommitUrl = cleanText(source.githubCommitUrl || source.github_commit_url || source.gitCommitUrl || source.git_commit_url || source.commitUrl || source.commit_url, 500);
548
+ var githubCommitSha = extractGithubCommitSha(githubCommitUrl);
549
+ var gitCommitStatus = cleanText(source.gitCommitStatus || source.git_commit_status || source.githubCommitStatus || source.github_commit_status, 160);
550
+ var gitPushStatus = cleanText(source.gitPushStatus || source.git_push_status || source.githubPushStatus || source.github_push_status || source.githubCommitReachableStatus || source.github_commit_reachable_status || gitCommitStatus, 160);
551
+ var blockers = required
552
+ ? githubCommitProofBlockers({
553
+ channel: channel,
554
+ target: (normalized === null || normalized === void 0 ? void 0 : normalized.target) || {},
555
+ sourceCommitSha: sourceCommitSha,
556
+ githubCommitUrl: githubCommitUrl,
557
+ gitCommitStatus: gitCommitStatus,
558
+ gitPushStatus: gitPushStatus
559
+ }, channel)
560
+ : [];
561
+ var status = !required
562
+ ? 'not_required'
563
+ : (!sourceCommitSha || !githubCommitUrl || !gitPushStatus)
564
+ ? 'missing'
565
+ : blockers.length
566
+ ? 'invalid'
567
+ : 'passed';
568
+ var passed = !required || status === 'passed';
569
+ return {
570
+ required: required,
571
+ status: status,
572
+ passed: passed,
573
+ managerMustCommitBeforeHotfix: required && !passed,
574
+ channel: channel,
575
+ sourceCommitSha: sourceCommitSha,
576
+ githubCommitUrl: githubCommitUrl,
577
+ githubCommitSha: githubCommitSha,
578
+ gitCommitStatus: gitCommitStatus,
579
+ gitPushStatus: gitPushStatus,
580
+ blockers: blockers,
581
+ nextCommands: required && !passed
582
+ ? ['commit_and_push_hotfix_to_github', 'verify_github_commit_url_and_push_status', 'record_hotfix_evidence']
583
+ : [],
584
+ requiredEvidence: required
585
+ ? ['full 40-character sourceCommitSha', 'matching GitHub commit URL', 'passed gitPushStatus or gitCommitStatus']
586
+ : []
587
+ };
588
+ }
200
589
  function validateResolveIOAIManagerHotfixEvidence(value, options) {
201
590
  var _a, _b, _c, _d;
202
591
  if (options === void 0) { options = {}; }
@@ -214,6 +603,7 @@ function validateResolveIOAIManagerHotfixEvidence(value, options) {
214
603
  hotfixSatisfied: false,
215
604
  requiredEvidence: ((_b = (_a = policy === null || policy === void 0 ? void 0 : policy.hotfixPlan) === null || _a === void 0 ? void 0 : _a.steps) === null || _b === void 0 ? void 0 : _b.flatMap(function (step) { return step.requiredEvidence; })) || [],
216
605
  successEvidence: ((_d = (_c = policy === null || policy === void 0 ? void 0 : policy.hotfixPlan) === null || _c === void 0 ? void 0 : _c.steps) === null || _d === void 0 ? void 0 : _d.flatMap(function (step) { return step.successEvidence; })) || [],
606
+ githubCommitGuard: evaluateResolveIOAIManagerHotfixGitProof(value),
217
607
  policy: policy,
218
608
  nextAction: 'record_hotfix_evidence'
219
609
  };
@@ -227,17 +617,24 @@ function validateResolveIOAIManagerHotfixEvidence(value, options) {
227
617
  var successEvidence = (step === null || step === void 0 ? void 0 : step.successEvidence) || [];
228
618
  var policyAllowsFullDeploy = (policy === null || policy === void 0 ? void 0 : policy.fullDeployAllowed) === true;
229
619
  var policyBlocksDuplicate = (policy === null || policy === void 0 ? void 0 : policy.duplicateDeployBlocked) === true || (policy === null || policy === void 0 ? void 0 : policy.duplicatePublishBlocked) === true;
620
+ var githubCommitGuard = evaluateResolveIOAIManagerHotfixGitProof(normalized, channel);
230
621
  if (normalized.fullDeployRequested && !policyAllowsFullDeploy && channel !== 'force_deploy_review' && channel !== 'new_artifact_deploy') {
231
622
  blockers.push('Full deploy requested but hotfix-first policy does not allow a full deploy for this release state.');
232
623
  }
233
624
  if (normalized.fullDeployRequested && policyBlocksDuplicate && !normalized.forceDeployReason && channel !== 'force_deploy_review') {
234
625
  blockers.push('Duplicate deploy/publish fingerprint requires force deploy/publish evidence before any full deploy.');
235
626
  }
627
+ if (githubCommitGuard.required) {
628
+ blockers.push.apply(blockers, __spreadArray([], __read(githubCommitGuard.blockers), false));
629
+ }
236
630
  if (channel === 'backend_js') {
237
631
  pushMissing(blockers, !!normalized.compiledArtifactPath, 'Backend JS hotfix requires compiledArtifactPath.');
238
632
  pushMissing(blockers, !!target.host, 'Backend JS hotfix requires target.host.');
239
633
  pushMissing(blockers, !!target.path, 'Backend JS hotfix requires target.path.');
240
634
  pushMissing(blockers, !!(normalized.remoteChecksumAfter || normalized.remoteChecksum), 'Backend JS hotfix requires remote checksum after replacement.');
635
+ if ((normalized.remoteChecksumAfter || normalized.remoteChecksum) && !isStrongRemoteChecksum(normalized.remoteChecksumAfter || normalized.remoteChecksum || '')) {
636
+ blockers.push('Backend JS hotfix remote checksum after replacement must be a strong hex checksum, not a label or placeholder.');
637
+ }
241
638
  pushMissing(blockers, !!(normalized.restartEvidence || normalized.serviceRestartEvidence), 'Backend JS hotfix requires restart evidence.');
242
639
  pushMissing(blockers, hotfixStatusPassed(normalized.healthCheckStatus), 'Backend JS hotfix requires passed healthCheckStatus.');
243
640
  pushMissing(blockers, hotfixStatusPassed(normalized.selfTestStatus), 'Backend JS hotfix requires passed selfTestStatus.');
@@ -329,21 +726,228 @@ function validateResolveIOAIManagerHotfixEvidence(value, options) {
329
726
  requiredEvidence: requiredEvidence,
330
727
  successEvidence: successEvidence,
331
728
  normalized: normalized,
729
+ githubCommitGuard: githubCommitGuard,
332
730
  policy: policy,
333
731
  nextAction: nextAction
334
732
  };
335
733
  }
336
- function decideResolveIOAIManagerHotfixContinuation(input) {
734
+ function mergeHotfixEvidenceRecordInput(input) {
735
+ var e_3, _a;
736
+ var merged = __assign({}, cleanObject(input.evidence));
737
+ var directFields = [
738
+ 'channel',
739
+ 'status',
740
+ 'target',
741
+ 'compiledArtifactPath',
742
+ 'builtDistPath',
743
+ 'remoteChecksumBefore',
744
+ 'remoteChecksumAfter',
745
+ 'remoteChecksum',
746
+ 'restartEvidence',
747
+ 'healthCheckStatus',
748
+ 'selfTestStatus',
749
+ 'releaseGateStatus',
750
+ 's3UploadResult',
751
+ 'cloudfrontInvalidationId',
752
+ 'publicProof',
753
+ 'configChangeEvidence',
754
+ 'seedDataEvidence',
755
+ 'cacheInvalidationEvidence',
756
+ 'serviceRestartEvidence',
757
+ 'releaseArtifactFingerprint',
758
+ 'lastReleaseArtifactFingerprint',
759
+ 'sourceCommitSha',
760
+ 'githubCommitUrl',
761
+ 'gitCommitStatus',
762
+ 'gitPushStatus',
763
+ 'committedAt',
764
+ 'forceDeployReason',
765
+ 'hotfixCannotResolveReason',
766
+ 'fullDeployRequested',
767
+ 'recordedAt'
768
+ ];
769
+ try {
770
+ for (var directFields_1 = __values(directFields), directFields_1_1 = directFields_1.next(); !directFields_1_1.done; directFields_1_1 = directFields_1.next()) {
771
+ var field = directFields_1_1.value;
772
+ var value = input[field];
773
+ if (value !== undefined && value !== null && value !== '') {
774
+ merged[field] = value;
775
+ }
776
+ }
777
+ }
778
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
779
+ finally {
780
+ try {
781
+ if (directFields_1_1 && !directFields_1_1.done && (_a = directFields_1.return)) _a.call(directFields_1);
782
+ }
783
+ finally { if (e_3) throw e_3.error; }
784
+ }
785
+ return merged;
786
+ }
787
+ function resolveHotfixEvidenceRequiredFields(channel) {
788
+ var fields = ['channel'];
789
+ if (hotfixRequiresGithubCommit(channel)) {
790
+ fields.push('sourceCommitSha', 'githubCommitUrl', 'gitPushStatus');
791
+ }
792
+ if (channel === 'backend_js') {
793
+ fields.push('compiledArtifactPath', 'target.host', 'target.path', 'remoteChecksumAfter', 'restartEvidence', 'healthCheckStatus', 'selfTestStatus');
794
+ }
795
+ else if (channel === 'static_ui') {
796
+ fields.push('builtDistPath', 'target.bucket', 'target.distributionId', 's3UploadResult', 'cloudfrontInvalidationId', 'publicProof');
797
+ }
798
+ else if (channel === 'config') {
799
+ fields.push('target.configKey', 'configChangeEvidence', 'releaseGateStatus');
800
+ }
801
+ else if (channel === 'seed_data') {
802
+ fields.push('target.seedKey', 'seedDataEvidence', 'releaseGateStatus');
803
+ }
804
+ else if (channel === 'cache_invalidation') {
805
+ fields.push('target.cacheKey', 'cacheInvalidationEvidence', 'releaseGateStatus_or_healthCheckStatus');
806
+ }
807
+ else if (channel === 'service_restart') {
808
+ fields.push('target.processName', 'serviceRestartEvidence', 'healthCheckStatus', 'selfTestStatus');
809
+ }
810
+ else if (channel === 'release_artifact') {
811
+ fields.push('releaseArtifactFingerprint', 'releaseGateStatus');
812
+ }
813
+ else if (channel === 'new_artifact_deploy') {
814
+ fields.push('releaseArtifactFingerprint', 'lastReleaseArtifactFingerprint', 'releaseGateStatus');
815
+ }
816
+ else if (channel === 'artifact_build') {
817
+ fields.push('releaseArtifactFingerprint');
818
+ }
819
+ else if (channel === 'force_deploy_review') {
820
+ fields.push('forceDeployReason', 'hotfixCannotResolveReason');
821
+ }
822
+ return Array.from(new Set(fields));
823
+ }
824
+ function hotfixEvidenceFieldPresent(evidence, field) {
825
+ if (!evidence) {
826
+ return false;
827
+ }
828
+ var target = evidence.target || {};
829
+ switch (field) {
830
+ case 'channel':
831
+ return !!evidence.channel;
832
+ case 'sourceCommitSha':
833
+ return isFullGitCommitSha(evidence.sourceCommitSha || '');
834
+ case 'githubCommitUrl':
835
+ return !!extractGithubCommitSha(evidence.githubCommitUrl || '');
836
+ case 'gitPushStatus':
837
+ return hotfixStatusPassed(evidence.gitPushStatus) || hotfixStatusPassed(evidence.gitCommitStatus);
838
+ case 'compiledArtifactPath':
839
+ return !!cleanText(evidence.compiledArtifactPath, 500);
840
+ case 'builtDistPath':
841
+ return !!cleanText(evidence.builtDistPath, 500);
842
+ case 'remoteChecksumAfter':
843
+ return !!cleanText(evidence.remoteChecksumAfter || evidence.remoteChecksum, 160);
844
+ case 'restartEvidence':
845
+ return !!cleanText(evidence.restartEvidence || evidence.serviceRestartEvidence, 1000);
846
+ case 'serviceRestartEvidence':
847
+ return !!cleanText(evidence.serviceRestartEvidence || evidence.restartEvidence, 1000);
848
+ case 'healthCheckStatus':
849
+ return hotfixStatusPassed(evidence.healthCheckStatus);
850
+ case 'selfTestStatus':
851
+ return hotfixStatusPassed(evidence.selfTestStatus);
852
+ case 'releaseGateStatus':
853
+ return hotfixStatusPassed(evidence.releaseGateStatus);
854
+ case 'releaseGateStatus_or_healthCheckStatus':
855
+ return hotfixStatusPassed(evidence.releaseGateStatus) || hotfixStatusPassed(evidence.healthCheckStatus);
856
+ case 's3UploadResult':
857
+ return !!cleanText(evidence.s3UploadResult, 1000);
858
+ case 'cloudfrontInvalidationId':
859
+ return !!cleanText(evidence.cloudfrontInvalidationId, 240);
860
+ case 'publicProof':
861
+ return !!cleanText(evidence.publicProof, 1000);
862
+ case 'configChangeEvidence':
863
+ return !!cleanText(evidence.configChangeEvidence, 1000);
864
+ case 'seedDataEvidence':
865
+ return !!cleanText(evidence.seedDataEvidence, 1000);
866
+ case 'cacheInvalidationEvidence':
867
+ return !!cleanText(evidence.cacheInvalidationEvidence, 1000);
868
+ case 'releaseArtifactFingerprint':
869
+ return !!cleanText(evidence.releaseArtifactFingerprint, 240);
870
+ case 'lastReleaseArtifactFingerprint':
871
+ return !!cleanText(evidence.lastReleaseArtifactFingerprint, 240);
872
+ case 'forceDeployReason':
873
+ return !!cleanText(evidence.forceDeployReason, 1000);
874
+ case 'hotfixCannotResolveReason':
875
+ return !!cleanText(evidence.hotfixCannotResolveReason, 1000);
876
+ case 'target.host':
877
+ return !!cleanText(target.host, 200);
878
+ case 'target.path':
879
+ return !!cleanText(target.path, 500);
880
+ case 'target.bucket':
881
+ return !!cleanText(target.bucket, 200);
882
+ case 'target.distributionId':
883
+ return !!cleanText(target.distributionId, 200);
884
+ case 'target.configKey':
885
+ return !!cleanText(target.configKey, 200);
886
+ case 'target.seedKey':
887
+ return !!cleanText(target.seedKey, 200);
888
+ case 'target.cacheKey':
889
+ return !!cleanText(target.cacheKey, 200);
890
+ case 'target.processName':
891
+ return !!cleanText(target.processName, 200);
892
+ default:
893
+ return false;
894
+ }
895
+ }
896
+ function buildResolveIOAIManagerHotfixEvidenceRecord(input) {
337
897
  var _a, _b;
338
898
  if (input === void 0) { input = {}; }
899
+ var merged = mergeHotfixEvidenceRecordInput(input);
900
+ var normalized = normalizeResolveIOAIManagerHotfixEvidence(merged, input.policy);
901
+ var validation = validateResolveIOAIManagerHotfixEvidence(merged, { policy: input.policy });
902
+ var channel = validation.channel || (normalized === null || normalized === void 0 ? void 0 : normalized.channel) || ((_b = (_a = input.policy) === null || _a === void 0 ? void 0 : _a.hotfixPlan) === null || _b === void 0 ? void 0 : _b.recommendedChannel) || '';
903
+ var requiredFields = resolveHotfixEvidenceRequiredFields(channel);
904
+ var missingFields = requiredFields.filter(function (field) { return !hotfixEvidenceFieldPresent(normalized, field); });
905
+ var recordedAt = cleanDateString(input.now || (normalized === null || normalized === void 0 ? void 0 : normalized.recordedAt) || new Date());
906
+ var githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(normalized || merged, channel);
907
+ return {
908
+ recordId: cleanText(input.recordId, 160) || "hotfix-".concat(fingerprintResolveIOAIManagerBlocker("".concat(channel, ":").concat(githubCommitGuard.sourceCommitSha, ":").concat(recordedAt))),
909
+ recordedAt: recordedAt,
910
+ recordedBy: cleanText(input.recordedBy || 'manager', 160),
911
+ source: cleanText(input.source || 'unknown', 80),
912
+ reason: cleanText(input.reason, 1000),
913
+ channel: channel,
914
+ status: validation.status,
915
+ nextAction: validation.nextAction,
916
+ valid: validation.valid,
917
+ hotfixSatisfied: validation.hotfixSatisfied,
918
+ fullDeployAllowed: validation.fullDeployAllowed,
919
+ fullDeployBlocked: validation.fullDeployBlocked,
920
+ requiredFields: requiredFields,
921
+ missingFields: missingFields,
922
+ evidence: normalized,
923
+ validation: validation,
924
+ sourceCommitSha: githubCommitGuard.sourceCommitSha,
925
+ githubCommitUrl: githubCommitGuard.githubCommitUrl,
926
+ gitPushStatus: githubCommitGuard.gitPushStatus,
927
+ githubCommitRequired: githubCommitGuard.required,
928
+ githubCommitProofPassed: githubCommitGuard.passed,
929
+ commitProofStatus: githubCommitGuard.status,
930
+ managerMustCommitBeforeHotfix: githubCommitGuard.managerMustCommitBeforeHotfix,
931
+ githubCommitGuard: githubCommitGuard,
932
+ readyForReleaseGate: validation.valid === true && validation.nextAction === 'rerun_release_gate',
933
+ readyForContinuation: validation.valid === true && validation.hotfixSatisfied === true && hotfixStatusPassed(normalized === null || normalized === void 0 ? void 0 : normalized.releaseGateStatus)
934
+ };
935
+ }
936
+ function decideResolveIOAIManagerHotfixContinuation(input) {
937
+ var e_4, _a;
938
+ var _b, _c;
939
+ if (input === void 0) { input = {}; }
339
940
  var validation = validateResolveIOAIManagerHotfixEvidence(input.evidence, { policy: input.policy });
340
941
  var failureClass = cleanText(input.failureClass, 120) || 'release';
341
- var blockerText = cleanText(input.blocker || validation.blockers.join('; ') || ((_a = validation.normalized) === null || _a === void 0 ? void 0 : _a.hotfixCannotResolveReason), 1000);
942
+ var blockerText = cleanText(input.blocker || validation.blockers.join('; ') || ((_b = validation.normalized) === null || _b === void 0 ? void 0 : _b.hotfixCannotResolveReason), 1000);
342
943
  var blockerFingerprint = fingerprintResolveIOAIManagerBlocker(blockerText || "".concat(validation.channel, ":").concat(validation.status, ":").concat(validation.nextAction));
343
944
  var baseBlockers = __spreadArray([], __read(validation.blockers), false);
945
+ var githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(input.evidence, validation.channel);
946
+ var needsGithubCommitProof = githubCommitGuard.managerMustCommitBeforeHotfix
947
+ || validation.blockers.some(function (blocker) { return /sourceCommitSha|githubCommitUrl|gitPushStatus|gitCommitStatus|GitHub commit|pushed/i.test(blocker); });
344
948
  var repeatedFailure = input.repeatedFailure === true;
345
949
  var releaseGatePassed = input.releaseGatePassed === true
346
- || hotfixStatusPassed((_b = validation.normalized) === null || _b === void 0 ? void 0 : _b.releaseGateStatus);
950
+ || hotfixStatusPassed((_c = validation.normalized) === null || _c === void 0 ? void 0 : _c.releaseGateStatus);
347
951
  var action = validation.nextAction;
348
952
  var canContinueRun = false;
349
953
  var canRunFullDeploy = validation.fullDeployAllowed;
@@ -351,13 +955,38 @@ function decideResolveIOAIManagerHotfixContinuation(input) {
351
955
  var shouldPark = false;
352
956
  var reason = '';
353
957
  var nextCommands = [];
354
- if (!validation.valid || validation.status === 'missing' || validation.status === 'incomplete') {
958
+ if (githubCommitGuard.managerMustCommitBeforeHotfix) {
959
+ action = 'record_hotfix_evidence';
960
+ reason = githubCommitGuard.blockers.length
961
+ ? "Manager hotfix cannot continue until the hotfix is committed and pushed: ".concat(githubCommitGuard.blockers.join('; '))
962
+ : 'Manager hotfix cannot continue until sourceCommitSha, githubCommitUrl, and passed gitPushStatus prove the hotfix is on GitHub.';
963
+ nextCommands.push.apply(nextCommands, __spreadArray(__spreadArray([], __read(githubCommitGuard.nextCommands), false), ['rerun_hotfix_evidence_validator'], false));
964
+ try {
965
+ for (var _d = __values(githubCommitGuard.blockers), _e = _d.next(); !_e.done; _e = _d.next()) {
966
+ var blocker = _e.value;
967
+ if (!baseBlockers.includes(blocker)) {
968
+ baseBlockers.push(blocker);
969
+ }
970
+ }
971
+ }
972
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
973
+ finally {
974
+ try {
975
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
976
+ }
977
+ finally { if (e_4) throw e_4.error; }
978
+ }
979
+ }
980
+ else if (!validation.valid || validation.status === 'missing' || validation.status === 'incomplete') {
355
981
  action = validation.nextAction === 'request_force_deploy_reason'
356
982
  ? 'request_force_deploy_reason'
357
983
  : 'record_hotfix_evidence';
358
984
  reason = validation.blockers.length
359
985
  ? "Hotfix evidence is not complete: ".concat(validation.blockers.join('; '))
360
986
  : 'Hotfix evidence is missing; record the hotfix target, checksum, restart, health, and self-test proof before continuing.';
987
+ if (needsGithubCommitProof) {
988
+ nextCommands.push('commit_and_push_hotfix_to_github', 'verify_github_commit_url_and_push_status');
989
+ }
361
990
  nextCommands.push('record_hotfix_evidence', 'rerun_hotfix_evidence_validator');
362
991
  }
363
992
  else if (validation.fullDeployAllowed) {
@@ -402,13 +1031,49 @@ function decideResolveIOAIManagerHotfixContinuation(input) {
402
1031
  warnings: validation.warnings,
403
1032
  requiredEvidence: validation.requiredEvidence,
404
1033
  successEvidence: validation.successEvidence,
405
- nextCommands: nextCommands,
1034
+ nextCommands: Array.from(new Set(nextCommands)),
406
1035
  failureClass: failureClass,
407
1036
  blockerFingerprint: blockerFingerprint,
1037
+ githubCommitGuard: githubCommitGuard,
408
1038
  validation: validation,
409
1039
  recordedAt: isoNow(input.now)
410
1040
  };
411
1041
  }
1042
+ function decideResolveIOAIManagerHotfixCommitGuard(input) {
1043
+ if (input === void 0) { input = {}; }
1044
+ var normalized = normalizeResolveIOAIManagerHotfixEvidence(input.evidence, input.policy);
1045
+ var validation = validateResolveIOAIManagerHotfixEvidence(input.evidence, { policy: input.policy });
1046
+ var githubCommitGuard = validation.githubCommitGuard || evaluateResolveIOAIManagerHotfixGitProof(input.evidence, validation.channel);
1047
+ var active = !!normalized || githubCommitGuard.required === true;
1048
+ var hotfixCommitBlockers = validation.blockers.filter(function (blocker) { return /sourceCommitSha|githubCommitUrl|gitPushStatus|gitCommitStatus|GitHub commit|pushed/i.test(blocker); });
1049
+ var blockers = Array.from(new Set(__spreadArray(__spreadArray([], __read((githubCommitGuard.blockers || [])), false), __read(hotfixCommitBlockers), false)));
1050
+ var blocked = active && (githubCommitGuard.managerMustCommitBeforeHotfix === true
1051
+ || (githubCommitGuard.required === true && githubCommitGuard.passed !== true)
1052
+ || hotfixCommitBlockers.length > 0);
1053
+ var nextCommands = blocked
1054
+ ? Array.from(new Set(__spreadArray(__spreadArray([], __read((githubCommitGuard.nextCommands || [])), false), [
1055
+ 'commit_and_push_hotfix_to_github',
1056
+ 'verify_github_commit_url_and_push_status',
1057
+ 'record_hotfix_evidence'
1058
+ ], false)))
1059
+ : [];
1060
+ return {
1061
+ active: active,
1062
+ blocked: blocked,
1063
+ reason: blocked
1064
+ ? (blockers.length
1065
+ ? "Hotfix continuation is blocked until GitHub commit proof is recorded: ".concat(blockers.join('; '))
1066
+ : 'Hotfix continuation is blocked until sourceCommitSha, githubCommitUrl, and passed gitPushStatus prove the hotfix is pushed to GitHub.')
1067
+ : (active ? 'Hotfix GitHub commit proof is recorded.' : 'No hotfix commit guard is active.'),
1068
+ nextAction: blocked ? 'record_hotfix_evidence' : 'continue',
1069
+ channel: validation.channel || githubCommitGuard.channel || '',
1070
+ blockers: blockers,
1071
+ nextCommands: nextCommands,
1072
+ requiredEvidence: githubCommitGuard.requiredEvidence,
1073
+ githubCommitGuard: githubCommitGuard,
1074
+ validation: validation
1075
+ };
1076
+ }
412
1077
  function releaseStatusIsBlocked(value) {
413
1078
  return /fail|error|blocked|missing|empty|not ready|not_ready|denied|invalid|timeout|stale/i.test(value);
414
1079
  }
@@ -550,17 +1215,17 @@ function buildHotfixFirstReleasePlan(input) {
550
1215
  steps: [
551
1216
  hotfixStep('static_ui_hotfix', 'Static UI hotfix', 'static_ui', {
552
1217
  safeToAutoRun: true,
553
- requiredEvidence: ['built dist path', 'target S3 bucket and CloudFront distribution', 'changed frontend bundle or asset list'],
554
- successEvidence: ['S3 upload result', 'CloudFront invalidation id', 'public bundle contains expected hotfix marker']
1218
+ requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'built dist path', 'target S3 bucket and CloudFront distribution', 'changed frontend bundle or asset list'],
1219
+ successEvidence: ['GitHub commit and push proof', 'S3 upload result', 'CloudFront invalidation id', 'public bundle contains expected hotfix marker']
555
1220
  }),
556
1221
  hotfixStep('backend_js_hotfix', 'Backend JS hotfix', 'backend_js', {
557
- requiredEvidence: ['compiled JS artifact path', 'target backend host', 'diff limited to runner/operator code'],
558
- successEvidence: ['remote file checksum', 'service restart or process reload evidence', 'self-test passes']
1222
+ requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'compiled JS artifact path', 'target backend host', 'diff limited to runner/operator code'],
1223
+ successEvidence: ['GitHub commit and push proof', 'remote file checksum', 'service restart or process reload evidence', 'self-test passes']
559
1224
  }),
560
1225
  hotfixStep('config_seed_cache_restart', 'Config, seed, cache, or restart repair', 'config', {
561
1226
  safeToAutoRun: true,
562
- requiredEvidence: ['exact config/seed/cache key or process name', 'why no product artifact changed'],
563
- successEvidence: ['release gate rerun result', 'operator console release snapshot refreshed']
1227
+ requiredEvidence: ['sourceCommitSha, githubCommitUrl, and passed gitPushStatus', 'exact config/seed/cache key or process name', 'why no product artifact changed'],
1228
+ successEvidence: ['GitHub commit and push proof', 'release gate rerun result', 'operator console release snapshot refreshed']
564
1229
  })
565
1230
  ],
566
1231
  acceptanceEvidence: commonAcceptance,
@@ -1017,6 +1682,48 @@ function recoveryActionAutoRunnable(mode, probe) {
1017
1682
  }
1018
1683
  return probe.steps.some(function (step) { return step.commandHint || step.kind === 'rerun_same_gate' || step.kind === 'release_status' || step.kind === 'diff_scope_check'; });
1019
1684
  }
1685
+ function recoveryActionCostCeilingUsd(mode) {
1686
+ var _a;
1687
+ var map = {
1688
+ advance: 0,
1689
+ collect_evidence: 0.25,
1690
+ read_only_diagnosis: 1,
1691
+ repair_infra: 0.5,
1692
+ repair_compile: 1.5,
1693
+ repair_journey_contract: 2,
1694
+ repair_business_assertion: 1.5,
1695
+ repair_release: 0.75,
1696
+ targeted_product_repair: 5,
1697
+ manual_review: 0,
1698
+ continue_gate: 0
1699
+ };
1700
+ return (_a = map[mode]) !== null && _a !== void 0 ? _a : 1;
1701
+ }
1702
+ function recoveryActionRequiredStateTransition(mode) {
1703
+ var map = {
1704
+ advance: 'current gate advances without repair after existing proof is accepted',
1705
+ collect_evidence: 'blockerFingerprint or evidenceHash changes with material proof, or run remains parked',
1706
+ read_only_diagnosis: 'diagnosis gate becomes valid or records a blocked-reproduction reason',
1707
+ repair_infra: 'infra preflight changes from failed/blocked to passed, or records a new infra blocker',
1708
+ repair_compile: 'compile/startup status changes from failed to passed, or records a new compile blocker',
1709
+ repair_journey_contract: 'journey contract validates with first/next/last workflow proof',
1710
+ repair_business_assertion: 'business assertion changes from missing/failed to passed with artifact proof',
1711
+ repair_release: 'hotfix/release gate changes from blocked to passed, rerun_release_gate, or parked manual',
1712
+ targeted_product_repair: 'owner-scoped diff is produced and deterministic proof advances to QA',
1713
+ manual_review: 'operator records one explicit decision',
1714
+ continue_gate: 'current gate records accepted proof and advances'
1715
+ };
1716
+ return map[mode] || 'runner records a validated state transition before continuing';
1717
+ }
1718
+ function recoveryActionProofRequiredBeforeContinuation(mode) {
1719
+ return mode !== 'advance' && mode !== 'continue_gate' && mode !== 'manual_review';
1720
+ }
1721
+ function recoveryActionExitCriteria(input) {
1722
+ return Array.from(new Set(__spreadArray(__spreadArray([
1723
+ "exact dispatch action ".concat(input.dispatchAction, " completed or was parked"),
1724
+ input.requiredStateTransition
1725
+ ], __read((recoveryActionProofRequiredBeforeContinuation(input.mode) ? ['do not continue runner until required proof artifact is attached'] : [])), false), __read(input.stopWhen), false).map(function (entry) { return cleanText(entry, 500); }).filter(Boolean))).slice(0, 30);
1726
+ }
1020
1727
  function buildResolveIOAIManagerRecoveryActionPacket(input) {
1021
1728
  var checkpoint = input.checkpoint;
1022
1729
  var probe = input.probe || buildResolveIOAIManagerRecoveryEvidenceProbe({
@@ -1048,6 +1755,15 @@ function buildResolveIOAIManagerRecoveryActionPacket(input) {
1048
1755
  publishStatus: checkpoint.lane
1049
1756
  })
1050
1757
  : undefined;
1758
+ var allowedDispatchAction = dispatchActionForMode(mode);
1759
+ var requiredStateTransition = recoveryActionRequiredStateTransition(mode);
1760
+ var proofRequiredBeforeContinuation = recoveryActionProofRequiredBeforeContinuation(mode);
1761
+ var exitCriteria = recoveryActionExitCriteria({
1762
+ mode: mode,
1763
+ dispatchAction: allowedDispatchAction,
1764
+ requiredStateTransition: requiredStateTransition,
1765
+ stopWhen: stopWhen
1766
+ });
1051
1767
  var now = isoNow(input.now);
1052
1768
  return __assign(__assign(__assign({ actionId: stableHash('mgr-action', {
1053
1769
  checkpointId: checkpoint.checkpointId,
@@ -1055,13 +1771,13 @@ function buildResolveIOAIManagerRecoveryActionPacket(input) {
1055
1771
  mode: mode,
1056
1772
  evidenceHash: checkpoint.evidenceHash,
1057
1773
  blockerFingerprint: checkpoint.blockerFingerprint
1058
- }), 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'
1774
+ }), checkpointId: checkpoint.checkpointId, probeId: probe.probeId, recoveryClass: checkpoint.recoveryClass, mode: mode, singleActionOnly: true, allowedDispatchAction: allowedDispatchAction, 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'
1059
1775
  ? probe.objective
1060
- : 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
1776
+ : 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', costCeilingUsd: recoveryActionCostCeilingUsd(mode), requiredStateTransition: requiredStateTransition, proofRequiredBeforeContinuation: proofRequiredBeforeContinuation, canResetLoopAfterEvidence: resetLoopWhen.length > 0, maxAttemptsBeforePark: checkpoint.maxAttemptsBeforePark, requiredArtifacts: probe.requiredArtifacts.slice(0, 20), nextCommands: releasePolicy
1061
1777
  ? Array.from(new Set(__spreadArray(__spreadArray([], __read(nextCommands), false), __read(releasePolicy.allowedActions), false))).slice(0, 12)
1062
1778
  : nextCommands, successCriteria: releasePolicy
1063
1779
  ? Array.from(new Set(__spreadArray(__spreadArray([], __read(probe.acceptanceEvidence), false), __read(releasePolicy.requiredEvidence), false))).slice(0, 20)
1064
- : probe.acceptanceEvidence.slice(0, 20), retryPolicy: {
1780
+ : probe.acceptanceEvidence.slice(0, 20), exitCriteria: exitCriteria, retryPolicy: {
1065
1781
  allowImmediateRetry: checkpoint.status !== 'parked' && mode !== 'manual_review',
1066
1782
  requireNewEvidence: requireNewEvidence,
1067
1783
  resetLoopWhen: resetLoopWhen,
@@ -1098,6 +1814,7 @@ function buildRecoveryDispatchRecord(action, dispatchAction, status, reason, cur
1098
1814
  }), actionId: action.actionId, checkpointId: action.checkpointId, probeId: action.probeId, mode: action.mode, dispatchAction: dispatchAction, status: status, evidenceHash: evidenceHash, blockerFingerprint: blockerFingerprint, productRepairAllowed: action.productRepairAllowed && dispatchAction === 'run_targeted_product_repair', expensiveModelAllowed: action.expensiveModelAllowed && dispatchAction !== 'run_evidence_probe', reason: reason, artifactPaths: artifactPaths }, (status === 'started' ? { startedAt: createdAt } : {})), (status === 'completed' ? { completedAt: createdAt } : {})), { createdAt: createdAt });
1099
1815
  }
1100
1816
  function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1817
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1101
1818
  var action = input.action;
1102
1819
  var history = Array.isArray(input.history) ? input.history.filter(Boolean).slice(-50) : [];
1103
1820
  var current = input.current || {};
@@ -1115,6 +1832,7 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1115
1832
  };
1116
1833
  }
1117
1834
  var dispatchAction = dispatchActionForMode(action.mode);
1835
+ var allowedDispatchAction = action.allowedDispatchAction || dispatchAction;
1118
1836
  var evidenceHash = current.evidenceHash || action.checkpointId || action.actionId;
1119
1837
  var blockerFingerprint = resolveResolveIOAIManagerBlockerFingerprint(current, action.objective);
1120
1838
  var sameActionHistory = history.filter(function (entry) { return entry.actionId === action.actionId; });
@@ -1124,11 +1842,20 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1124
1842
  var completedDispatch = sameEvidenceHistory.find(function (entry) { return entry.status === 'completed' || entry.status === 'failed'; });
1125
1843
  var newEvidence = sameActionHistory.length > 0 && sameEvidenceHistory.length === 0;
1126
1844
  var requiresNewEvidence = action.retryPolicy.requireNewEvidence === true;
1845
+ var sameEvidenceAlreadyAttempted = !!completedDispatch && requiresNewEvidence && !newEvidence;
1127
1846
  var manualOnly = action.mode === 'manual_review' || !action.autoRunnable;
1128
1847
  var productDispatch = dispatchAction === 'run_targeted_product_repair';
1129
1848
  var expensiveDispatch = !['run_evidence_probe', 'advance', 'continue_gate', 'park_manual'].includes(dispatchAction);
1130
- if (manualOnly && input.operatorApproved !== true) {
1131
- var record_1 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_requires_manual_review', current, input.now);
1849
+ var evidenceAssessment = assessResolveIOAIManagerEvidenceChange(undefined, current, action.objective);
1850
+ var autonomyPolicy = input.autonomyPolicy
1851
+ ? decideResolveIOAIManagerAutonomyPolicy(__assign(__assign({}, (input.autonomyPolicy || {})), { dispatchAction: dispatchAction, projectedActionCostUsd: (_b = (_a = input.autonomyPolicy) === null || _a === void 0 ? void 0 : _a.projectedActionCostUsd) !== null && _b !== void 0 ? _b : action.costCeilingUsd, newEvidence: (_d = (_c = input.autonomyPolicy) === null || _c === void 0 ? void 0 : _c.newEvidence) !== null && _d !== void 0 ? _d : newEvidence, materialEvidence: (_f = (_e = input.autonomyPolicy) === null || _e === void 0 ? void 0 : _e.materialEvidence) !== null && _f !== void 0 ? _f : evidenceAssessment.material, evidenceStrength: (_h = (_g = input.autonomyPolicy) === null || _g === void 0 ? void 0 : _g.evidenceStrength) !== null && _h !== void 0 ? _h : evidenceAssessment.strength, evidenceSignals: (_k = (_j = input.autonomyPolicy) === null || _j === void 0 ? void 0 : _j.evidenceSignals) !== null && _k !== void 0 ? _k : evidenceAssessment.signals, sameEvidenceAlreadyAttempted: (_m = (_l = input.autonomyPolicy) === null || _l === void 0 ? void 0 : _l.sameEvidenceAlreadyAttempted) !== null && _m !== void 0 ? _m : sameEvidenceAlreadyAttempted, operatorApproved: input.operatorApproved }))
1852
+ : undefined;
1853
+ var autonomyBlocksDispatch = !!autonomyPolicy && (autonomyPolicy.mode === 'monitor_only'
1854
+ || (autonomyPolicy.requiresHumanApproval === true && input.operatorApproved !== true)
1855
+ || (autonomyPolicy.canAutoDispatch !== true && input.operatorApproved !== true)
1856
+ || (autonomyPolicy.blocked === true && autonomyPolicy.canManualDispatch !== true));
1857
+ if (autonomyBlocksDispatch) {
1858
+ var record_1 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', "recovery_dispatch_blocked_by_autonomy_policy:".concat((autonomyPolicy === null || autonomyPolicy === void 0 ? void 0 : autonomyPolicy.reason) || 'unknown'), current, input.now);
1132
1859
  return {
1133
1860
  dispatchAction: 'park_manual',
1134
1861
  allowed: false,
@@ -1139,11 +1866,12 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1139
1866
  shouldRecordDispatch: true,
1140
1867
  requiresNewEvidence: requiresNewEvidence,
1141
1868
  newEvidence: newEvidence,
1869
+ autonomyPolicy: autonomyPolicy,
1142
1870
  dispatchRecord: record_1
1143
1871
  };
1144
1872
  }
1145
- if (productDispatch && action.productRepairAllowed !== true) {
1146
- var record_2 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_product_repair_not_allowed', current, input.now);
1873
+ if (action.singleActionOnly === true && allowedDispatchAction !== dispatchAction) {
1874
+ var record_2 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_action_not_in_packet_contract', current, input.now);
1147
1875
  return {
1148
1876
  dispatchAction: 'park_manual',
1149
1877
  allowed: false,
@@ -1154,9 +1882,42 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1154
1882
  shouldRecordDispatch: true,
1155
1883
  requiresNewEvidence: true,
1156
1884
  newEvidence: newEvidence,
1885
+ autonomyPolicy: autonomyPolicy,
1157
1886
  dispatchRecord: record_2
1158
1887
  };
1159
1888
  }
1889
+ if (manualOnly && input.operatorApproved !== true) {
1890
+ var record_3 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_requires_manual_review', current, input.now);
1891
+ return {
1892
+ dispatchAction: 'park_manual',
1893
+ allowed: false,
1894
+ reason: record_3.reason,
1895
+ status: 'parked',
1896
+ canRunProductRepair: false,
1897
+ canRunExpensiveModel: false,
1898
+ shouldRecordDispatch: true,
1899
+ requiresNewEvidence: requiresNewEvidence,
1900
+ newEvidence: newEvidence,
1901
+ autonomyPolicy: autonomyPolicy,
1902
+ dispatchRecord: record_3
1903
+ };
1904
+ }
1905
+ if (productDispatch && action.productRepairAllowed !== true) {
1906
+ var record_4 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_product_repair_not_allowed', current, input.now);
1907
+ return {
1908
+ dispatchAction: 'park_manual',
1909
+ allowed: false,
1910
+ reason: record_4.reason,
1911
+ status: 'parked',
1912
+ canRunProductRepair: false,
1913
+ canRunExpensiveModel: false,
1914
+ shouldRecordDispatch: true,
1915
+ requiresNewEvidence: true,
1916
+ newEvidence: newEvidence,
1917
+ autonomyPolicy: autonomyPolicy,
1918
+ dispatchRecord: record_4
1919
+ };
1920
+ }
1160
1921
  if (activeDispatch) {
1161
1922
  return {
1162
1923
  dispatchAction: 'park_manual',
@@ -1167,22 +1928,24 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1167
1928
  canRunExpensiveModel: false,
1168
1929
  shouldRecordDispatch: false,
1169
1930
  requiresNewEvidence: true,
1170
- newEvidence: false
1931
+ newEvidence: false,
1932
+ autonomyPolicy: autonomyPolicy
1171
1933
  };
1172
1934
  }
1173
- if (completedDispatch && requiresNewEvidence && !newEvidence) {
1174
- var record_3 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_same_evidence_already_attempted', current, input.now);
1935
+ if (sameEvidenceAlreadyAttempted) {
1936
+ var record_5 = buildRecoveryDispatchRecord(action, 'park_manual', 'parked', 'recovery_dispatch_same_evidence_already_attempted', current, input.now);
1175
1937
  return {
1176
1938
  dispatchAction: 'park_manual',
1177
1939
  allowed: false,
1178
- reason: record_3.reason,
1940
+ reason: record_5.reason,
1179
1941
  status: 'parked',
1180
1942
  canRunProductRepair: false,
1181
1943
  canRunExpensiveModel: false,
1182
1944
  shouldRecordDispatch: true,
1183
1945
  requiresNewEvidence: true,
1184
1946
  newEvidence: false,
1185
- dispatchRecord: record_3
1947
+ autonomyPolicy: autonomyPolicy,
1948
+ dispatchRecord: record_5
1186
1949
  };
1187
1950
  }
1188
1951
  var status = dispatchAction === 'advance' || dispatchAction === 'continue_gate'
@@ -1199,6 +1962,7 @@ function decideResolveIOAIManagerRecoveryActionDispatch(input) {
1199
1962
  shouldRecordDispatch: true,
1200
1963
  requiresNewEvidence: requiresNewEvidence,
1201
1964
  newEvidence: newEvidence,
1965
+ autonomyPolicy: autonomyPolicy,
1202
1966
  dispatchRecord: record
1203
1967
  };
1204
1968
  }
@@ -1245,6 +2009,7 @@ function buildResolveIOAIManagerRecoveryExecutionDirective(input) {
1245
2009
  var dispatchDecision = input.dispatchDecision || decideResolveIOAIManagerRecoveryActionDispatch({
1246
2010
  action: action,
1247
2011
  current: input.current,
2012
+ autonomyPolicy: input.autonomyPolicy,
1248
2013
  now: input.now
1249
2014
  });
1250
2015
  var dispatchAction = dispatchDecision.dispatchAction;
@@ -1254,17 +2019,17 @@ function buildResolveIOAIManagerRecoveryExecutionDirective(input) {
1254
2019
  var stepType = cleanText((action === null || action === void 0 ? void 0 : action.stepType) || ((_b = input.current) === null || _b === void 0 ? void 0 : _b.stepType) || '', 120);
1255
2020
  var rerunReason = recoveryDirectiveReason(action, dispatchDecision, input.current);
1256
2021
  var surface = cleanText(input.surface || 'runner', 120);
1257
- return __assign(__assign(__assign({ directiveId: stableHash('mgr-directive', {
2022
+ return __assign(__assign(__assign(__assign(__assign({ directiveId: stableHash('mgr-directive', {
1258
2023
  surface: surface,
1259
2024
  actionId: (action === null || action === void 0 ? void 0 : action.actionId) || '',
1260
2025
  dispatchAction: dispatchAction,
1261
2026
  reason: dispatchDecision.reason,
1262
2027
  evidenceHash: ((_c = dispatchDecision.dispatchRecord) === null || _c === void 0 ? void 0 : _c.evidenceHash) || ((_d = input.current) === null || _d === void 0 ? void 0 : _d.evidenceHash) || ''
1263
- }), 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
2028
+ }), surface: surface, dispatchAction: dispatchAction, phase: phase, allowed: dispatchDecision.allowed, status: dispatchDecision.status, reason: dispatchDecision.reason, singleActionOnly: (action === null || action === void 0 ? void 0 : action.singleActionOnly) !== false, allowedDispatchAction: (action === null || action === void 0 ? void 0 : action.allowedDispatchAction) || dispatchAction, 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, costCeilingUsd: Math.max(0, Number((action === null || action === void 0 ? void 0 : action.costCeilingUsd) || 0) || 0), requiredStateTransition: cleanText(action === null || action === void 0 ? void 0 : action.requiredStateTransition, 500), proofRequiredBeforeContinuation: (action === null || action === void 0 ? void 0 : action.proofRequiredBeforeContinuation) === true, canRunProductRepair: dispatchDecision.canRunProductRepair, canRunExpensiveModel: dispatchDecision.canRunExpensiveModel, canResetLoopAfterEvidence: (action === null || action === void 0 ? void 0 : action.canResetLoopAfterEvidence) === true, requiresNewEvidence: dispatchDecision.requiresNewEvidence, newEvidence: dispatchDecision.newEvidence }, (dispatchDecision.autonomyPolicy ? { autonomyPolicy: dispatchDecision.autonomyPolicy } : {})), { 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), exitCriteria: cleanList(action === null || action === void 0 ? void 0 : action.exitCriteria, 30, 500), forbiddenActions: (action === null || action === void 0 ? void 0 : action.productRepairAllowed) === true
1264
2029
  ? []
1265
2030
  : Array.from(new Set(__spreadArray([
1266
2031
  'Do not run product-code repair from this directive unless canRunProductRepair is true.'
1267
- ], __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 });
2032
+ ], __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 });
1268
2033
  }
1269
2034
  function newListEntries(current, previous) {
1270
2035
  var existing = new Set(previous.map(function (entry) { return cleanText(entry, 500); }).filter(Boolean));