@resolveio/server-lib 22.3.158 → 22.3.160

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,14 +47,17 @@ 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.selectResolveIOSupportSimilarCaseHints = selectResolveIOSupportSimilarCaseHints;
50
51
  exports.evaluateResolveIOSupportDiagnosisEvidenceQuality = evaluateResolveIOSupportDiagnosisEvidenceQuality;
51
52
  exports.normalizeResolveIOSupportDiagnosisGate = normalizeResolveIOSupportDiagnosisGate;
52
53
  exports.extractResolveIOSupportDiagnosisGateFromText = extractResolveIOSupportDiagnosisGateFromText;
53
54
  exports.validateResolveIOSupportDiagnosisGate = validateResolveIOSupportDiagnosisGate;
55
+ exports.evaluateResolveIOSupportBusinessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness;
54
56
  exports.decideResolveIOSupportCustomerReplyPolicy = decideResolveIOSupportCustomerReplyPolicy;
55
57
  exports.buildResolveIOSupportIssueClassProbes = buildResolveIOSupportIssueClassProbes;
56
58
  exports.hashResolveIOSupportV5Evidence = hashResolveIOSupportV5Evidence;
57
59
  exports.decideResolveIOSupportV5RepeatedFailureStop = decideResolveIOSupportV5RepeatedFailureStop;
60
+ exports.evaluateResolveIOSupportEvidenceFreshness = evaluateResolveIOSupportEvidenceFreshness;
58
61
  exports.changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles = changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles;
59
62
  exports.decideResolveIOSupportV5RepairGate = decideResolveIOSupportV5RepairGate;
60
63
  exports.applyResolveIOSupportDiagnosisGateToMicrotasks = applyResolveIOSupportDiagnosisGateToMicrotasks;
@@ -228,6 +231,199 @@ function normalizeSupportDiagnosisHints(values) {
228
231
  .filter(function (entry) { return entry.id || entry.ticketNumber || entry.title || entry.reason || entry.commitSha; })
229
232
  .slice(0, 8);
230
233
  }
234
+ function normalizeSupportSimilarCaseSource(value) {
235
+ var normalized = cleanText(value, 80).toLowerCase();
236
+ if (/commit|git/.test(normalized)) {
237
+ return 'git_commit';
238
+ }
239
+ if (/ticket/.test(normalized)) {
240
+ return 'support_ticket';
241
+ }
242
+ if (/airun|ai_run|run/.test(normalized)) {
243
+ return 'airun';
244
+ }
245
+ return 'manual';
246
+ }
247
+ function supportOutcomeLooksAccepted(value) {
248
+ return /^(accepted|pass|passed|complete|completed|merged|released)$/i.test(cleanText(value, 80));
249
+ }
250
+ function supportOutcomeLooksRejected(value) {
251
+ return /\b(reject|rejected|false_pass|failed|failure|blocked|unknown|stopped|manual_handoff)\b/i.test(cleanText(value, 120));
252
+ }
253
+ function supportOwnerFileDirectory(value) {
254
+ var normalized = normalizeOwnerFilePath(value);
255
+ var parts = normalized.split('/').filter(Boolean);
256
+ return parts.length > 1 ? parts.slice(0, -1).join('/') : normalized;
257
+ }
258
+ function supportTextTokens(value) {
259
+ var stop = new Set(['the', 'and', 'for', 'with', 'that', 'this', 'from', 'into', 'when', 'where', 'what', 'have', 'has', 'had', 'not', 'but', 'are', 'was', 'were', 'issue', 'ticket', 'support']);
260
+ return Array.from(new Set(cleanText(value, 3000).toLowerCase()
261
+ .split(/[^a-z0-9_/-]+/g)
262
+ .map(function (token) { return token.trim(); })
263
+ .filter(function (token) { return token.length >= 3 && !stop.has(token); })))
264
+ .slice(0, 80);
265
+ }
266
+ function normalizeResolveIOSupportSimilarCaseCandidate(value) {
267
+ var _a, _b;
268
+ var source = cleanObject(value);
269
+ if (!Object.keys(source).length && typeof value !== 'string') {
270
+ return undefined;
271
+ }
272
+ if (typeof value === 'string') {
273
+ var summary = cleanText(value, 500);
274
+ return summary ? {
275
+ source: 'manual',
276
+ title: summary,
277
+ summary: summary,
278
+ keywords: supportTextTokens(summary)
279
+ } : undefined;
280
+ }
281
+ var metadata = cleanObject(source.metadata);
282
+ var runSourceIds = cleanObject(source.sourceIds || source.source_ids || metadata.sourceIds || metadata.source_ids);
283
+ var diagnosis = cleanObject(source.diagnosisGate || source.diagnosis_gate || source.supportV5DiagnosisGate || source.support_v5_diagnosis_gate || metadata.diagnosisGate || metadata.diagnosis_gate);
284
+ var sourceIds = cleanObject(source.sourceIds || source.source_ids || metadata.sourceIds || metadata.source_ids);
285
+ var sourceText = [
286
+ source.title,
287
+ source.summary,
288
+ source.message,
289
+ source.description,
290
+ source.reason,
291
+ source.commitMessage,
292
+ source.commit_message,
293
+ (_a = diagnosis.issue_case) === null || _a === void 0 ? void 0 : _a.customer_complaint,
294
+ (_b = diagnosis.accepted_hypothesis) === null || _b === void 0 ? void 0 : _b.statement
295
+ ].filter(Boolean).join(' ');
296
+ var ownerFiles = cleanList(source.ownerFiles
297
+ || source.owner_files
298
+ || source.files
299
+ || diagnosis.owner_files
300
+ || diagnosis.ownerFiles
301
+ || metadata.ownerFiles
302
+ || metadata.owner_files, 12, 300).map(normalizeOwnerFilePath).filter(Boolean);
303
+ var issueClass = cleanText(source.issueClass
304
+ || source.issue_class
305
+ || diagnosis.issue_class
306
+ || diagnosis.issueClass
307
+ || metadata.issueClass
308
+ || metadata.issue_class, 80);
309
+ var candidate = {
310
+ source: normalizeSupportSimilarCaseSource(source.source || source.type || source.runSource),
311
+ id: cleanText(source.id || source._id || source.runId || source.run_id, 160),
312
+ ticketNumber: cleanText(source.ticketNumber || source.ticket_number || source.sourceTicketNumber || sourceIds.ticketNumber || runSourceIds.ticketNumber, 80),
313
+ title: cleanText(source.title || source.name || source.summary, 300),
314
+ outcome: cleanText(source.outcome || source.status || source.outcomeLabel || source.outcome_label, 80),
315
+ issueClass: normalizeIssueClass(issueClass) || cleanText(issueClass, 80),
316
+ ownerFiles: ownerFiles,
317
+ commitSha: cleanText(source.commitSha || source.commit_sha || source.sha || source.sourceCommitSha || source.source_commit_sha, 80),
318
+ commitMessage: cleanText(source.commitMessage || source.commit_message || source.message, 300),
319
+ reason: cleanText(source.reason || source.matchReason || source.match_reason, 500),
320
+ summary: cleanText(source.summary || source.description || source.message || sourceText, 800),
321
+ keywords: cleanList(source.keywords || source.tags, 20, 80).concat(supportTextTokens(sourceText)).slice(0, 40),
322
+ updatedAt: isoNow(source.updatedAt || source.updated_at || source.recordedAt || source.recorded_at || source.createdAt || source.created_at),
323
+ metadata: metadata
324
+ };
325
+ if (!candidate.id && !candidate.ticketNumber && !candidate.title && !candidate.commitSha && !candidate.summary) {
326
+ return undefined;
327
+ }
328
+ if (candidate.commitSha && candidate.source === 'manual') {
329
+ candidate.source = 'git_commit';
330
+ }
331
+ if (candidate.ticketNumber && candidate.source === 'manual') {
332
+ candidate.source = 'support_ticket';
333
+ }
334
+ return candidate;
335
+ }
336
+ function selectResolveIOSupportSimilarCaseHints(input) {
337
+ var e_3, _a;
338
+ if (input === void 0) { input = {}; }
339
+ var issueClass = normalizeIssueClass(input.issueClass);
340
+ var ownerFiles = cleanList(input.ownerFiles, 12, 300).map(normalizeOwnerFilePath).filter(Boolean);
341
+ var ownerFileSet = new Set(ownerFiles);
342
+ var ownerDirs = new Set(ownerFiles.map(supportOwnerFileDirectory).filter(Boolean));
343
+ var textTokens = new Set(supportTextTokens(input.text));
344
+ var limit = Math.max(1, Math.min(12, Number(input.limit || 6)));
345
+ var normalized = (Array.isArray(input.candidates) ? input.candidates : [])
346
+ .map(normalizeResolveIOSupportSimilarCaseCandidate)
347
+ .filter(function (candidate) { return !!candidate; });
348
+ var ranked = [];
349
+ var ignoredCount = 0;
350
+ try {
351
+ for (var normalized_1 = __values(normalized), normalized_1_1 = normalized_1.next(); !normalized_1_1.done; normalized_1_1 = normalized_1.next()) {
352
+ var candidate = normalized_1_1.value;
353
+ var outcomeKnown = !!candidate.outcome;
354
+ var accepted = supportOutcomeLooksAccepted(candidate.outcome) || (!!candidate.commitSha && !supportOutcomeLooksRejected(candidate.outcome));
355
+ if (outcomeKnown && !accepted) {
356
+ ignoredCount += 1;
357
+ continue;
358
+ }
359
+ var signals = [];
360
+ var score = accepted ? 100 : 40;
361
+ var candidateIssueClass = normalizeIssueClass(candidate.issueClass);
362
+ if (issueClass && candidateIssueClass && issueClass === candidateIssueClass) {
363
+ score += 45;
364
+ signals.push("issue_class:".concat(issueClass));
365
+ }
366
+ var exactOwnerOverlap = candidate.ownerFiles.filter(function (file) { return ownerFileSet.has(normalizeOwnerFilePath(file)); });
367
+ if (exactOwnerOverlap.length) {
368
+ score += 35 + Math.min(20, exactOwnerOverlap.length * 5);
369
+ signals.push("owner_file_overlap:".concat(exactOwnerOverlap.slice(0, 3).join(',')));
370
+ }
371
+ var directoryOverlap = candidate.ownerFiles
372
+ .map(supportOwnerFileDirectory)
373
+ .filter(function (dir) { return dir && ownerDirs.has(dir); });
374
+ if (directoryOverlap.length && !exactOwnerOverlap.length) {
375
+ score += 18;
376
+ signals.push("owner_dir_overlap:".concat(Array.from(new Set(directoryOverlap)).slice(0, 2).join(',')));
377
+ }
378
+ var candidateTokens = new Set(__spreadArray(__spreadArray([], __read((candidate.keywords || [])), false), __read(supportTextTokens([candidate.title, candidate.summary, candidate.commitMessage].join(' '))), false));
379
+ var tokenOverlap = Array.from(candidateTokens).filter(function (token) { return textTokens.has(token); }).slice(0, 6);
380
+ if (tokenOverlap.length) {
381
+ score += Math.min(18, tokenOverlap.length * 3);
382
+ signals.push("weak_text_overlap:".concat(tokenOverlap.join(',')));
383
+ }
384
+ if (candidate.commitSha) {
385
+ score += 8;
386
+ signals.push('commit_linked');
387
+ }
388
+ if (!signals.length && score < 100) {
389
+ ignoredCount += 1;
390
+ continue;
391
+ }
392
+ ranked.push({
393
+ id: candidate.id,
394
+ ticketNumber: candidate.ticketNumber,
395
+ title: candidate.title,
396
+ outcome: candidate.outcome || (candidate.commitSha ? 'commit_hint' : ''),
397
+ issueClass: candidateIssueClass || candidate.issueClass,
398
+ ownerFiles: candidate.ownerFiles.slice(0, 8),
399
+ commitSha: candidate.commitSha,
400
+ commitMessage: candidate.commitMessage,
401
+ reason: signals.join('; '),
402
+ source: candidate.source,
403
+ score: score,
404
+ structuredSignals: signals,
405
+ advisoryOnly: true
406
+ });
407
+ }
408
+ }
409
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
410
+ finally {
411
+ try {
412
+ if (normalized_1_1 && !normalized_1_1.done && (_a = normalized_1.return)) _a.call(normalized_1);
413
+ }
414
+ finally { if (e_3) throw e_3.error; }
415
+ }
416
+ var sorted = ranked
417
+ .sort(function (a, b) { return b.score - a.score || String(b.ticketNumber || b.commitSha || '').localeCompare(String(a.ticketNumber || a.commitSha || '')); })
418
+ .slice(0, limit);
419
+ return {
420
+ ranked: sorted,
421
+ similarTickets: sorted.filter(function (hint) { return hint.source !== 'git_commit'; }).slice(0, limit),
422
+ similarCommits: sorted.filter(function (hint) { return hint.source === 'git_commit' || !!hint.commitSha; }).slice(0, limit),
423
+ ignoredCount: ignoredCount,
424
+ generatedAt: isoNow(input.now)
425
+ };
426
+ }
231
427
  function normalizeSupportDiagnosisBusinessProofContract(value, issueClassHint) {
232
428
  var source = cleanObject(value);
233
429
  if (!Object.keys(source).length) {
@@ -408,7 +604,7 @@ function normalizeResolveIOSupportDiagnosisGate(value, now) {
408
604
  return gate;
409
605
  }
410
606
  function extractResolveIOSupportDiagnosisGateFromText(value, now) {
411
- var e_3, _a;
607
+ var e_4, _a;
412
608
  var text = String(value || '').trim();
413
609
  if (!text) {
414
610
  return undefined;
@@ -436,12 +632,12 @@ function extractResolveIOSupportDiagnosisGateFromText(value, now) {
436
632
  }
437
633
  }
438
634
  }
439
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
635
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
440
636
  finally {
441
637
  try {
442
638
  if (candidates_1_1 && !candidates_1_1.done && (_a = candidates_1.return)) _a.call(candidates_1);
443
639
  }
444
- finally { if (e_3) throw e_3.error; }
640
+ finally { if (e_4) throw e_4.error; }
445
641
  }
446
642
  return undefined;
447
643
  }
@@ -584,11 +780,289 @@ function normalizeSupportConfidenceLevel(value) {
584
780
  return normalized ? 'unknown' : 'unknown';
585
781
  }
586
782
  function supportReleaseLooksBlocked(value) {
587
- return /\b(fail|failed|error|blocked|missing|empty|stale|denied|timeout|pending_manual|requested|in_progress|queued)\b/i.test(cleanText(value, 200));
783
+ return /\b(fail|failed|error|blocked|missing|empty|stale|denied|timeout)\b/i.test(cleanText(value, 200));
588
784
  }
589
785
  function supportBusinessAssertionPassed(value) {
590
786
  return /^(pass|passed|accepted|business_assertion_passed)$/i.test(cleanText(value, 80));
591
787
  }
788
+ function supportBusinessAssertionFailed(value) {
789
+ return /^(fail|failed|blocked|needs_repair|business_assertion_failed)$/i.test(cleanText(value, 80));
790
+ }
791
+ function supportBusinessAssertionRouteOnly(value) {
792
+ return /^(route_probe_pass|route_only_pass|route_pass|route_probe|route_loaded)$/i.test(cleanText(value, 80));
793
+ }
794
+ function normalizeSupportBusinessProofAssertions(input) {
795
+ var e_5, _a;
796
+ var gate = normalizeResolveIOSupportDiagnosisGate(input.diagnosisGate);
797
+ var proofPlan = gate === null || gate === void 0 ? void 0 : gate.proof_plan;
798
+ var proofContract = proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.business_proof_contract;
799
+ var artifactPaths = cleanList(input.businessProofArtifacts, 30, 500);
800
+ var assertions = [];
801
+ try {
802
+ for (var _b = __values(Array.isArray(input.businessAssertions) ? input.businessAssertions : []), _c = _b.next(); !_c.done; _c = _b.next()) {
803
+ var entry = _c.value;
804
+ var source = cleanObject(entry);
805
+ if (!Object.keys(source).length) {
806
+ continue;
807
+ }
808
+ var metadata = cleanObject(source.metadata);
809
+ assertions.push({
810
+ assertion: pickText(source, ['assertion', 'name', 'workflow', 'expected'], 1000) || (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.business_assertion) || 'business assertion',
811
+ status: cleanText(source.status || source.outcome || source.result, 80),
812
+ workflow: pickText(source, ['workflow', 'workflowName'], 400),
813
+ route: pickText(source, ['route', 'url'], 500),
814
+ before: pickText(source, ['before', 'before_state', 'beforeState'], 1000),
815
+ action: pickText(source, ['action', 'action_under_test', 'actionUnderTest'], 1000),
816
+ expected: pickText(source, ['expected', 'expected_business_state_change', 'expectedBusinessStateChange'], 1000),
817
+ after: pickText(source, ['after', 'after_state', 'afterState'], 1000),
818
+ observed: pickText(source, ['observed', 'actual'], 1000),
819
+ dataProof: pickText(source, ['dataProof', 'data_proof', 'proof', 'domProof', 'dom_proof'], 1400),
820
+ mongoDelta: cleanObject(source.mongoDelta || source.mongo_delta),
821
+ artifactPaths: cleanList(source.artifactPaths || source.artifact_paths || source.artifacts, 30, 500),
822
+ message: pickText(source, ['message', 'reason', 'summary'], 1000),
823
+ acceptanceBlocked: source.acceptanceBlocked === true || source.acceptance_blocked === true || metadata.acceptanceBlocked === true || metadata.acceptance_blocked === true,
824
+ routeOnly: source.routeOnly === true || source.route_only === true || source.outcome === 'route_only_pass' || source.status === 'route_probe_pass' || metadata.routeOnly === true || metadata.route_only === true,
825
+ metadata: metadata
826
+ });
827
+ }
828
+ }
829
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
830
+ finally {
831
+ try {
832
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
833
+ }
834
+ finally { if (e_5) throw e_5.error; }
835
+ }
836
+ var status = cleanText(input.businessAssertionStatus || input.outcomeLabel, 80);
837
+ if (!assertions.length && (supportBusinessAssertionPassed(status) || supportBusinessAssertionRouteOnly(status) || supportBusinessAssertionFailed(status))) {
838
+ assertions.push({
839
+ assertion: (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.business_assertion) || (proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion) || 'business assertion',
840
+ status: status,
841
+ workflow: proofContract === null || proofContract === void 0 ? void 0 : proofContract.action_under_test,
842
+ route: (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.route) || (gate === null || gate === void 0 ? void 0 : gate.issue_case.route_module),
843
+ before: proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.before,
844
+ action: (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.action) || (proofContract === null || proofContract === void 0 ? void 0 : proofContract.action_under_test),
845
+ expected: (proofContract === null || proofContract === void 0 ? void 0 : proofContract.expected_business_state_change) || (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.after),
846
+ after: proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.after,
847
+ observed: '',
848
+ dataProof: (proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion) || (proofPlan === null || proofPlan === void 0 ? void 0 : proofPlan.data_assertion) || '',
849
+ mongoDelta: {},
850
+ artifactPaths: artifactPaths,
851
+ message: '',
852
+ acceptanceBlocked: supportBusinessAssertionRouteOnly(status),
853
+ routeOnly: supportBusinessAssertionRouteOnly(status),
854
+ metadata: {}
855
+ });
856
+ }
857
+ return assertions;
858
+ }
859
+ function supportBusinessProofAssertionText(assertion) {
860
+ return cleanText([
861
+ assertion.assertion,
862
+ assertion.workflow,
863
+ assertion.before,
864
+ assertion.action,
865
+ assertion.expected,
866
+ assertion.after,
867
+ assertion.observed,
868
+ assertion.dataProof,
869
+ assertion.message
870
+ ].filter(Boolean).join(' '), 4000);
871
+ }
872
+ function supportBusinessProofAssertionMatchesContract(assertion, gate) {
873
+ var e_6, _a;
874
+ if (!gate) {
875
+ return false;
876
+ }
877
+ var metadata = assertion.metadata || {};
878
+ if (metadata.supportDiagnosisProof === true
879
+ || metadata.support_diagnosis_proof === true
880
+ || metadata.diagnosisProofPlanMatched === true
881
+ || metadata.diagnosis_proof_plan_matched === true
882
+ || metadata.proofPlanMatched === true
883
+ || metadata.proof_plan_matched === true) {
884
+ return true;
885
+ }
886
+ var proofPlan = gate.proof_plan;
887
+ var proofContract = proofPlan.business_proof_contract;
888
+ var requiredParts = [
889
+ proofPlan.business_assertion,
890
+ proofPlan.after,
891
+ proofPlan.data_assertion,
892
+ proofContract === null || proofContract === void 0 ? void 0 : proofContract.expected_business_state_change,
893
+ proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion
894
+ ].map(function (part) { return cleanText(part, 1000).toLowerCase(); }).filter(function (part) { return part.length >= 12; });
895
+ var assertionText = supportBusinessProofAssertionText(assertion).toLowerCase();
896
+ if (!requiredParts.length) {
897
+ return false;
898
+ }
899
+ if (requiredParts.some(function (part) { return assertionText.includes(part); })) {
900
+ return true;
901
+ }
902
+ var proofWords = new Set(requiredParts.join(' ').split(/[^a-z0-9]+/g).filter(function (word) { return word.length >= 5; }));
903
+ var assertionWords = new Set(assertionText.split(/[^a-z0-9]+/g).filter(function (word) { return word.length >= 5; }));
904
+ var overlap = 0;
905
+ try {
906
+ for (var proofWords_1 = __values(proofWords), proofWords_1_1 = proofWords_1.next(); !proofWords_1_1.done; proofWords_1_1 = proofWords_1.next()) {
907
+ var word = proofWords_1_1.value;
908
+ if (assertionWords.has(word)) {
909
+ overlap += 1;
910
+ }
911
+ }
912
+ }
913
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
914
+ finally {
915
+ try {
916
+ if (proofWords_1_1 && !proofWords_1_1.done && (_a = proofWords_1.return)) _a.call(proofWords_1);
917
+ }
918
+ finally { if (e_6) throw e_6.error; }
919
+ }
920
+ return overlap >= Math.min(5, Math.max(3, Math.ceil(proofWords.size * 0.45)));
921
+ }
922
+ function supportBusinessProofArtifactFingerprint(paths) {
923
+ var normalized = cleanList(paths, 80, 500).sort();
924
+ return normalized.length ? hashResolveIOSupportV5Evidence({ artifacts: normalized }) : '';
925
+ }
926
+ function supportBusinessProofFingerprint(gate, assertion, artifactPaths) {
927
+ var _a;
928
+ if (artifactPaths === void 0) { artifactPaths = []; }
929
+ if (!gate || !assertion) {
930
+ return '';
931
+ }
932
+ var proofPlan = gate.proof_plan;
933
+ var proofContract = proofPlan.business_proof_contract;
934
+ return hashResolveIOSupportV5Evidence({
935
+ issueClass: gate.issue_class,
936
+ proofPlan: {
937
+ before: proofPlan.before,
938
+ action: proofPlan.action,
939
+ after: proofPlan.after,
940
+ businessAssertion: proofPlan.business_assertion,
941
+ dataAssertion: proofPlan.data_assertion,
942
+ contractAction: proofContract === null || proofContract === void 0 ? void 0 : proofContract.action_under_test,
943
+ contractExpected: proofContract === null || proofContract === void 0 ? void 0 : proofContract.expected_business_state_change,
944
+ contractAssertion: proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion
945
+ },
946
+ assertion: {
947
+ assertion: assertion.assertion,
948
+ status: assertion.status,
949
+ workflow: assertion.workflow,
950
+ route: assertion.route,
951
+ before: assertion.before,
952
+ action: assertion.action,
953
+ expected: assertion.expected,
954
+ after: assertion.after,
955
+ observed: assertion.observed,
956
+ dataProof: assertion.dataProof,
957
+ mongoDelta: assertion.mongoDelta
958
+ },
959
+ artifacts: cleanList(((_a = assertion.artifactPaths) === null || _a === void 0 ? void 0 : _a.length) ? assertion.artifactPaths : artifactPaths, 80, 500).sort()
960
+ });
961
+ }
962
+ function defaultSupportBusinessProofReadinessFields(values) {
963
+ if (values === void 0) { values = {}; }
964
+ var artifactPaths = cleanList(values.artifactPaths, 80, 500);
965
+ return {
966
+ artifactPaths: artifactPaths,
967
+ proofFingerprint: cleanText(values.proofFingerprint, 160),
968
+ artifactFingerprint: cleanText(values.artifactFingerprint, 160) || supportBusinessProofArtifactFingerprint(artifactPaths),
969
+ proofFreshness: values.proofFreshness || (artifactPaths.length ? 'unknown' : 'missing')
970
+ };
971
+ }
972
+ function evaluateResolveIOSupportBusinessProofReadiness(input) {
973
+ if (input === void 0) { input = {}; }
974
+ var diagnosisValidation = validateResolveIOSupportDiagnosisGate(input.diagnosisGate);
975
+ var gate = diagnosisValidation.normalized;
976
+ var requiredEvidence = [
977
+ 'valid SupportDiagnosisGate proof_plan',
978
+ 'AIQaBusinessAssertion status=pass',
979
+ 'business assertion maps to diagnosis proof_plan/business_proof_contract',
980
+ 'artifact path for browser/data proof',
981
+ 'DOM/data proof or Mongo delta for the expected business state change'
982
+ ];
983
+ if (!diagnosisValidation.valid || !gate) {
984
+ return __assign({ ready: false, status: 'blocked', reason: 'support_business_proof_waiting_on_valid_diagnosis', blockers: diagnosisValidation.blockers.length ? diagnosisValidation.blockers : ['Valid SupportDiagnosisGate is required before business proof can accept the ticket.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields());
985
+ }
986
+ var assertions = normalizeSupportBusinessProofAssertions(input);
987
+ if (!assertions.length) {
988
+ return __assign({ ready: false, status: 'missing', reason: 'support_business_proof_missing_aiqa_business_assertion', blockers: ['No AIQaBusinessAssertion was recorded for the diagnosis proof_plan.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields());
989
+ }
990
+ var artifactPaths = Array.from(new Set(assertions.flatMap(function (assertion) { return assertion.artifactPaths || []; })));
991
+ var artifactFingerprint = supportBusinessProofArtifactFingerprint(artifactPaths);
992
+ var failed = assertions.find(function (assertion) { return supportBusinessAssertionFailed(assertion.status); });
993
+ if (failed) {
994
+ var failedFingerprint = supportBusinessProofFingerprint(gate, failed, artifactPaths);
995
+ return __assign(__assign({ ready: false, status: 'failed', reason: 'support_business_proof_assertion_failed', blockers: [failed.message || failed.observed || failed.assertion || 'Business assertion failed.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
996
+ artifactPaths: artifactPaths,
997
+ proofFingerprint: failedFingerprint,
998
+ artifactFingerprint: artifactFingerprint,
999
+ proofFreshness: 'fresh'
1000
+ })), { matchedAssertion: failed });
1001
+ }
1002
+ var routeOnly = assertions.find(function (assertion) { return assertion.routeOnly || assertion.acceptanceBlocked || supportBusinessAssertionRouteOnly(assertion.status) || proofPlanLooksRouteOnly({
1003
+ before: assertion.before || '',
1004
+ action: assertion.action || '',
1005
+ after: assertion.after || assertion.expected || '',
1006
+ business_assertion: assertion.assertion || assertion.message || '',
1007
+ data_assertion: assertion.dataProof || '',
1008
+ artifact_expectation: assertion.artifactPaths.join(', ')
1009
+ }); });
1010
+ if (routeOnly) {
1011
+ var routeOnlyFingerprint = supportBusinessProofFingerprint(gate, routeOnly, artifactPaths);
1012
+ return __assign(__assign({ ready: false, status: 'route_only', reason: 'support_business_proof_route_only_or_acceptance_blocked', blockers: ['Route probe, shell/screenshot, or acceptance_blocked proof cannot accept the ticket. Run the issue-class business assertion.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
1013
+ artifactPaths: artifactPaths,
1014
+ proofFingerprint: routeOnlyFingerprint,
1015
+ artifactFingerprint: artifactFingerprint,
1016
+ proofFreshness: 'fresh'
1017
+ })), { matchedAssertion: routeOnly });
1018
+ }
1019
+ var passedAssertions = assertions.filter(function (assertion) { return supportBusinessAssertionPassed(assertion.status); });
1020
+ var matched = passedAssertions.find(function (assertion) { return supportBusinessProofAssertionMatchesContract(assertion, gate); });
1021
+ if (!matched) {
1022
+ return __assign({ ready: false, status: 'weak', reason: 'support_business_proof_does_not_match_diagnosis_contract', blockers: ['A passed assertion exists, but it does not map to the diagnosis proof_plan/business_proof_contract.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({ artifactPaths: artifactPaths, artifactFingerprint: artifactFingerprint }));
1023
+ }
1024
+ var proofArtifactPaths = matched.artifactPaths.length ? matched.artifactPaths : artifactPaths;
1025
+ var proofFingerprint = supportBusinessProofFingerprint(gate, matched, proofArtifactPaths);
1026
+ var matchedArtifactFingerprint = supportBusinessProofArtifactFingerprint(proofArtifactPaths);
1027
+ var previousProofFingerprint = cleanText(input.previousProofFingerprint, 160);
1028
+ var previousArtifactFingerprint = cleanText(input.previousArtifactFingerprint, 160);
1029
+ var sameProofAsPrevious = !!previousProofFingerprint && previousProofFingerprint === proofFingerprint;
1030
+ var sameArtifactAsPrevious = !!previousArtifactFingerprint && previousArtifactFingerprint === matchedArtifactFingerprint;
1031
+ if (sameProofAsPrevious || sameArtifactAsPrevious) {
1032
+ return __assign(__assign({ ready: false, status: 'stale', reason: sameProofAsPrevious
1033
+ ? 'support_business_proof_same_fingerprint_as_previous'
1034
+ : 'support_business_proof_same_artifact_fingerprint_as_previous', blockers: ['Business proof did not produce a new proof/artifact fingerprint for this run. Rerun the issue-specific assertion and record fresh artifact proof.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
1035
+ artifactPaths: proofArtifactPaths,
1036
+ proofFingerprint: proofFingerprint,
1037
+ artifactFingerprint: matchedArtifactFingerprint,
1038
+ proofFreshness: sameProofAsPrevious ? 'same_as_previous' : 'stale_artifact'
1039
+ })), { matchedAssertion: matched });
1040
+ }
1041
+ if (!matched.artifactPaths.length && !artifactPaths.length) {
1042
+ return __assign(__assign({ ready: false, status: 'weak', reason: 'support_business_proof_missing_artifact_path', blockers: ['Business proof must include at least one artifact path.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
1043
+ artifactPaths: artifactPaths,
1044
+ proofFingerprint: proofFingerprint,
1045
+ artifactFingerprint: matchedArtifactFingerprint
1046
+ })), { matchedAssertion: matched });
1047
+ }
1048
+ var hasDataOrDomProof = !!cleanText(matched.dataProof, 50)
1049
+ || Object.keys(cleanObject(matched.mongoDelta)).length > 0
1050
+ || !!cleanText(matched.observed, 80)
1051
+ || !!cleanText(matched.after, 80);
1052
+ if (!hasDataOrDomProof) {
1053
+ return __assign(__assign({ ready: false, status: 'weak', reason: 'support_business_proof_missing_dom_or_data_proof', blockers: ['Business proof must include DOM/data proof, observed result, after-state, or Mongo delta.'], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
1054
+ artifactPaths: proofArtifactPaths,
1055
+ proofFingerprint: proofFingerprint,
1056
+ artifactFingerprint: matchedArtifactFingerprint
1057
+ })), { matchedAssertion: matched });
1058
+ }
1059
+ return __assign(__assign({ ready: true, status: 'passed', reason: 'support_business_proof_ready', blockers: [], requiredEvidence: requiredEvidence }, defaultSupportBusinessProofReadinessFields({
1060
+ artifactPaths: proofArtifactPaths,
1061
+ proofFingerprint: proofFingerprint,
1062
+ artifactFingerprint: matchedArtifactFingerprint,
1063
+ proofFreshness: 'fresh'
1064
+ })), { matchedAssertion: matched });
1065
+ }
592
1066
  function buildSupportClarificationQuestion(gate, blockers) {
593
1067
  if ((gate === null || gate === void 0 ? void 0 : gate.issue_case.reproduction_status) === 'blocked' && gate.issue_case.reproduction_blocker) {
594
1068
  return "Can you provide the missing detail needed to reproduce this issue: ".concat(gate.issue_case.reproduction_blocker, "?");
@@ -604,6 +1078,53 @@ function buildSupportClarificationQuestion(gate, blockers) {
604
1078
  }
605
1079
  return 'Can you send one concrete example record, screen, or action path where this issue still occurs?';
606
1080
  }
1081
+ function buildResolveIOSupportHumanReviewPacket(input) {
1082
+ return {
1083
+ reviewType: input.reviewType,
1084
+ title: cleanText(input.title, 200) || 'Review Support Runner Action',
1085
+ summary: cleanText(input.summary || input.reason, 1000),
1086
+ primaryAction: cleanText(input.primaryAction, 160) || 'review_support_runner_action',
1087
+ question: cleanText(input.question, 1000) || undefined,
1088
+ customerFacingDraftAllowed: input.customerFacingDraftAllowed === true,
1089
+ customerSendAllowed: false,
1090
+ requiresHumanApproval: input.requiresHumanApproval !== false,
1091
+ safety: input.safety || 'internal_hold',
1092
+ reason: cleanText(input.reason, 500),
1093
+ blockers: cleanList(input.blockers, 20, 500),
1094
+ requiredEvidence: cleanList(input.requiredEvidence, 30, 500),
1095
+ evidenceRefs: cleanList(input.evidenceRefs, 40, 500),
1096
+ nextCommands: cleanList(input.nextCommands, 20, 200),
1097
+ forbiddenActions: Array.from(new Set(__spreadArray([
1098
+ 'Do not send customer email without explicit human approval.'
1099
+ ], __read(cleanList(input.forbiddenActions, 20, 500)), false))),
1100
+ costRisk: input.costRisk || 'small_model_or_qa',
1101
+ createdAt: isoNow(input.now)
1102
+ };
1103
+ }
1104
+ function supportAutonomousReviewTypeForAction(action) {
1105
+ switch (action) {
1106
+ case 'run_diagnosis_gate':
1107
+ return 'diagnosis_gate';
1108
+ case 'ask_customer_clarification':
1109
+ return 'customer_clarification';
1110
+ case 'run_owner_scoped_repair':
1111
+ case 'revise_diagnosis_scope':
1112
+ return 'owner_scoped_repair';
1113
+ case 'run_business_proof_qa':
1114
+ return 'business_proof_qa';
1115
+ case 'repair_release_hotfix_first':
1116
+ case 'ready_for_release_gate':
1117
+ return 'release_hotfix';
1118
+ case 'draft_customer_reply':
1119
+ return 'customer_resolution_reply';
1120
+ case 'collect_new_evidence':
1121
+ return 'new_evidence';
1122
+ case 'repair_infra_only':
1123
+ case 'park_manual':
1124
+ default:
1125
+ return 'internal_hold';
1126
+ }
1127
+ }
607
1128
  function decideResolveIOSupportCustomerReplyPolicy(input) {
608
1129
  var _a, _b, _c;
609
1130
  if (input === void 0) { input = {}; }
@@ -613,10 +1134,16 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
613
1134
  var shouldBlockConfidence = ((_a = input.confidence) === null || _a === void 0 ? void 0 : _a.shouldBlockPr) === true
614
1135
  || ((_b = input.confidence) === null || _b === void 0 ? void 0 : _b.should_block_pr) === true
615
1136
  || ((_c = input.confidence) === null || _c === void 0 ? void 0 : _c.blocked) === true;
616
- var outcomeLabel = cleanText(input.outcomeLabel, 120).toLowerCase();
617
- var businessPassed = supportBusinessAssertionPassed(input.businessAssertionStatus)
618
- || outcomeLabel === 'accepted';
619
- var artifactCount = cleanList(input.businessProofArtifacts, 20, 500).length;
1137
+ var businessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
1138
+ diagnosisGate: input.diagnosisGate,
1139
+ outcomeLabel: input.outcomeLabel,
1140
+ businessAssertionStatus: input.businessAssertionStatus,
1141
+ businessAssertions: input.businessAssertions,
1142
+ businessProofArtifacts: input.businessProofArtifacts,
1143
+ previousProofFingerprint: input.previousProofFingerprint,
1144
+ previousArtifactFingerprint: input.previousArtifactFingerprint
1145
+ });
1146
+ var artifactCount = businessProofReadiness.artifactPaths.length;
620
1147
  var unresolvedBlockers = cleanList(input.unresolvedBlockers, 20, 500);
621
1148
  var releaseBlocked = supportReleaseLooksBlocked(input.releaseStatus);
622
1149
  var requiredEvidence = [
@@ -630,6 +1157,7 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
630
1157
  var canAskCustomer = (gate === null || gate === void 0 ? void 0 : gate.issue_case.reproduction_status) === 'blocked'
631
1158
  || diagnosisValidation.blockers.some(function (blocker) { return /expected_result|observed_result|account_customer_context/.test(blocker); });
632
1159
  if (canAskCustomer) {
1160
+ var clarificationQuestion = buildSupportClarificationQuestion(gate, diagnosisValidation.blockers);
633
1161
  return {
634
1162
  action: 'ask_clarification',
635
1163
  canDraftCustomerReply: true,
@@ -638,7 +1166,22 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
638
1166
  safety: 'needs_clarification',
639
1167
  reason: 'support_reply_waiting_on_customer_reproduction_detail',
640
1168
  requiredEvidence: requiredEvidence,
641
- clarificationQuestion: buildSupportClarificationQuestion(gate, diagnosisValidation.blockers)
1169
+ clarificationQuestion: clarificationQuestion,
1170
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1171
+ reviewType: 'customer_clarification',
1172
+ title: 'Review Customer Clarification',
1173
+ summary: 'The runner needs one customer detail before it can reproduce or continue safely.',
1174
+ primaryAction: 'review_customer_clarification',
1175
+ question: clarificationQuestion,
1176
+ customerFacingDraftAllowed: true,
1177
+ safety: 'needs_clarification',
1178
+ reason: 'support_reply_waiting_on_customer_reproduction_detail',
1179
+ blockers: diagnosisValidation.blockers,
1180
+ requiredEvidence: requiredEvidence,
1181
+ nextCommands: ['edit_clarification_question', 'send_after_human_review', 'park_ticket_until_customer_reply'],
1182
+ forbiddenActions: ['Do not run repair from a guessed reproduction path.'],
1183
+ costRisk: 'release_or_customer_send'
1184
+ })
642
1185
  };
643
1186
  }
644
1187
  return {
@@ -648,7 +1191,20 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
648
1191
  confidenceLevel: confidenceLevel,
649
1192
  safety: 'internal_hold',
650
1193
  reason: 'support_reply_blocked_until_diagnosis_gate_validates',
651
- requiredEvidence: requiredEvidence
1194
+ requiredEvidence: requiredEvidence,
1195
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1196
+ reviewType: 'internal_hold',
1197
+ title: 'Complete Diagnosis Gate',
1198
+ summary: 'Customer reply is blocked until the diagnosis gate validates.',
1199
+ primaryAction: 'run_support_v5_read_only_diagnosis_gate',
1200
+ safety: 'internal_hold',
1201
+ reason: 'support_reply_blocked_until_diagnosis_gate_validates',
1202
+ blockers: diagnosisValidation.blockers,
1203
+ requiredEvidence: requiredEvidence,
1204
+ nextCommands: ['retrieve_similar_tickets_and_commits', 'run_reproduction_or_classification_probe', 'write_support_diagnosis_gate_json'],
1205
+ forbiddenActions: ['Do not draft a resolution reply before root-cause diagnosis validates.'],
1206
+ costRisk: 'expensive_model'
1207
+ })
652
1208
  };
653
1209
  }
654
1210
  if (shouldBlockConfidence || confidenceLevel !== 'high') {
@@ -659,7 +1215,19 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
659
1215
  confidenceLevel: confidenceLevel,
660
1216
  safety: 'internal_hold',
661
1217
  reason: shouldBlockConfidence ? 'support_reply_blocked_by_confidence_gate' : 'support_reply_requires_high_confidence',
662
- requiredEvidence: requiredEvidence
1218
+ requiredEvidence: requiredEvidence,
1219
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1220
+ reviewType: 'internal_hold',
1221
+ title: 'Review Confidence Gate',
1222
+ summary: 'Customer reply is blocked until confidence is high and the confidence gate is not blocking.',
1223
+ primaryAction: 'review_support_confidence_evidence',
1224
+ safety: 'internal_hold',
1225
+ reason: shouldBlockConfidence ? 'support_reply_blocked_by_confidence_gate' : 'support_reply_requires_high_confidence',
1226
+ requiredEvidence: requiredEvidence,
1227
+ nextCommands: ['inspect_confidence_basis', 'collect_missing_business_or_release_evidence'],
1228
+ forbiddenActions: ['Do not draft a customer resolution from medium or low confidence.'],
1229
+ costRisk: 'free_or_deterministic'
1230
+ })
663
1231
  };
664
1232
  }
665
1233
  if (unresolvedBlockers.length) {
@@ -670,7 +1238,20 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
670
1238
  confidenceLevel: confidenceLevel,
671
1239
  safety: 'internal_hold',
672
1240
  reason: 'support_reply_blocked_by_unresolved_runner_blocker',
673
- requiredEvidence: requiredEvidence
1241
+ requiredEvidence: requiredEvidence,
1242
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1243
+ reviewType: 'internal_hold',
1244
+ title: 'Resolve Runner Blocker',
1245
+ summary: 'Customer reply is blocked by unresolved runner blockers.',
1246
+ primaryAction: 'review_support_runner_blocker',
1247
+ safety: 'internal_hold',
1248
+ reason: 'support_reply_blocked_by_unresolved_runner_blocker',
1249
+ blockers: unresolvedBlockers,
1250
+ requiredEvidence: requiredEvidence,
1251
+ nextCommands: ['classify_blocker', 'collect_new_evidence_or_repair_smallest_gate'],
1252
+ forbiddenActions: ['Do not send customer status as resolved while blockers remain.'],
1253
+ costRisk: 'small_model_or_qa'
1254
+ })
674
1255
  };
675
1256
  }
676
1257
  if (releaseBlocked) {
@@ -681,18 +1262,49 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
681
1262
  confidenceLevel: confidenceLevel,
682
1263
  safety: 'internal_hold',
683
1264
  reason: 'support_reply_blocked_until_release_or_hotfix_gate_finishes',
684
- requiredEvidence: requiredEvidence
1265
+ requiredEvidence: requiredEvidence,
1266
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1267
+ reviewType: 'release_hotfix',
1268
+ title: 'Finish Release Or Hotfix Gate',
1269
+ summary: 'Business proof exists, but the customer reply is blocked until release or hotfix evidence is complete.',
1270
+ primaryAction: 'repair_release_hotfix_first',
1271
+ safety: 'internal_hold',
1272
+ reason: 'support_reply_blocked_until_release_or_hotfix_gate_finishes',
1273
+ blockers: [input.releaseStatus],
1274
+ requiredEvidence: requiredEvidence,
1275
+ nextCommands: ['record_hotfix_evidence', 'commit_and_push_hotfix_to_github', 'rerun_release_gate_once'],
1276
+ forbiddenActions: ['Do not tell the customer the fix is live before release evidence passes.'],
1277
+ costRisk: 'release_or_customer_send'
1278
+ })
685
1279
  };
686
1280
  }
687
- if (!businessPassed) {
1281
+ if (!businessProofReadiness.ready) {
1282
+ var replyReason = businessProofReadiness.reason === 'support_business_proof_route_only_or_acceptance_blocked'
1283
+ ? 'support_reply_rejects_route_only_business_proof'
1284
+ : 'support_reply_requires_business_assertion_pass';
1285
+ var proofRequiredEvidence = Array.from(new Set(__spreadArray(__spreadArray([], __read(requiredEvidence), false), __read(businessProofReadiness.requiredEvidence), false)));
688
1286
  return {
689
1287
  action: 'hold_internal',
690
1288
  canDraftCustomerReply: false,
691
1289
  canSendCustomerReply: false,
692
1290
  confidenceLevel: confidenceLevel,
693
1291
  safety: 'internal_hold',
694
- reason: 'support_reply_requires_business_assertion_pass',
695
- requiredEvidence: requiredEvidence
1292
+ reason: replyReason,
1293
+ requiredEvidence: proofRequiredEvidence,
1294
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1295
+ reviewType: 'business_proof_qa',
1296
+ title: 'Run Business Proof QA',
1297
+ summary: 'Customer reply is blocked until the issue-specific before/action/after assertion passes.',
1298
+ primaryAction: 'run_support_v5_business_proof_qa_row',
1299
+ safety: 'internal_hold',
1300
+ reason: replyReason,
1301
+ blockers: businessProofReadiness.blockers,
1302
+ requiredEvidence: proofRequiredEvidence,
1303
+ evidenceRefs: businessProofReadiness.artifactPaths,
1304
+ nextCommands: ['execute_issue_class_probe', 'record_before_action_after_artifacts', 'write_aiqa_business_assertion'],
1305
+ forbiddenActions: ['Route probe pass remains route evidence only.'],
1306
+ costRisk: 'small_model_or_qa'
1307
+ })
696
1308
  };
697
1309
  }
698
1310
  if (!(gate === null || gate === void 0 ? void 0 : gate.proof_plan.business_proof_contract) || artifactCount < 1) {
@@ -703,7 +1315,20 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
703
1315
  confidenceLevel: confidenceLevel,
704
1316
  safety: 'internal_hold',
705
1317
  reason: 'support_reply_requires_business_proof_contract_artifact',
706
- requiredEvidence: requiredEvidence
1318
+ requiredEvidence: requiredEvidence,
1319
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1320
+ reviewType: 'business_proof_qa',
1321
+ title: 'Attach Business Proof Artifact',
1322
+ summary: 'Customer reply is blocked until the business proof contract and artifact are attached.',
1323
+ primaryAction: 'record_business_proof_artifact',
1324
+ safety: 'internal_hold',
1325
+ reason: 'support_reply_requires_business_proof_contract_artifact',
1326
+ requiredEvidence: requiredEvidence,
1327
+ evidenceRefs: businessProofReadiness.artifactPaths,
1328
+ nextCommands: ['attach_business_proof_contract', 'attach_artifact_path', 'rerun_reply_policy'],
1329
+ forbiddenActions: ['Do not draft a resolution reply without a proof artifact.'],
1330
+ costRisk: 'free_or_deterministic'
1331
+ })
707
1332
  };
708
1333
  }
709
1334
  return {
@@ -714,6 +1339,20 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
714
1339
  safety: 'safe_to_draft',
715
1340
  reason: 'support_reply_resolution_draft_allowed_after_business_proof',
716
1341
  requiredEvidence: requiredEvidence,
1342
+ humanReviewPacket: buildResolveIOSupportHumanReviewPacket({
1343
+ reviewType: 'customer_resolution_reply',
1344
+ title: 'Review Customer Resolution Reply',
1345
+ summary: "Business proof is ready for ".concat(gate.issue_class, "; draft a customer-facing resolution for human review."),
1346
+ primaryAction: 'review_customer_reply',
1347
+ customerFacingDraftAllowed: true,
1348
+ safety: 'safe_to_draft',
1349
+ reason: 'support_reply_resolution_draft_allowed_after_business_proof',
1350
+ requiredEvidence: requiredEvidence,
1351
+ evidenceRefs: businessProofReadiness.artifactPaths,
1352
+ nextCommands: ['summarize_business_proof', 'draft_customer_reply_for_human_review'],
1353
+ forbiddenActions: ['Draft only; do not send automatically.'],
1354
+ costRisk: 'release_or_customer_send'
1355
+ }),
717
1356
  draftBasis: {
718
1357
  issueClass: gate.issue_class,
719
1358
  businessProof: gate.proof_plan.business_proof_contract.expected_business_state_change,
@@ -722,7 +1361,6 @@ function decideResolveIOSupportCustomerReplyPolicy(input) {
722
1361
  };
723
1362
  }
724
1363
  function buildResolveIOSupportIssueClassProbes(value) {
725
- var _a;
726
1364
  var validation = validateResolveIOSupportDiagnosisGate(value);
727
1365
  var gate = validation.normalized || normalizeResolveIOSupportDiagnosisGate(value);
728
1366
  if (!gate) {
@@ -732,49 +1370,69 @@ function buildResolveIOSupportIssueClassProbes(value) {
732
1370
  var proofContract = gate.proof_plan.business_proof_contract;
733
1371
  var proof = (proofContract === null || proofContract === void 0 ? void 0 : proofContract.expected_business_state_change) || gate.proof_plan.business_assertion;
734
1372
  var assertion = (proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion) || gate.proof_plan.data_assertion || gate.proof_plan.after;
735
- var artifacts = ((_a = proofContract === null || proofContract === void 0 ? void 0 : proofContract.proof_artifacts) === null || _a === void 0 ? void 0 : _a.length)
736
- ? " Required artifacts: ".concat(proofContract.proof_artifacts.join(', '), ".")
737
- : '';
1373
+ var contractArtifacts = cleanList(proofContract === null || proofContract === void 0 ? void 0 : proofContract.proof_artifacts, 8, 160);
738
1374
  var common = {
739
1375
  issue_class: gate.issue_class,
740
1376
  probe_type: 'issue_class_probe',
741
1377
  route: route,
1378
+ state_transition: {
1379
+ before: gate.proof_plan.before || gate.proof_plan.before_state_unavailable_reason || (proofContract === null || proofContract === void 0 ? void 0 : proofContract.setup_state) || '',
1380
+ action: gate.proof_plan.action || (proofContract === null || proofContract === void 0 ? void 0 : proofContract.action_under_test) || '',
1381
+ after: gate.proof_plan.after || (proofContract === null || proofContract === void 0 ? void 0 : proofContract.expected_business_state_change) || '',
1382
+ assertion: assertion || gate.proof_plan.business_assertion || ''
1383
+ },
1384
+ acceptance_gate: 'aiqa_business_assertion',
742
1385
  blocks_acceptance_without_business_assertion: true
743
1386
  };
744
1387
  var map = {
745
1388
  no_op_submit: {
746
1389
  action: "Submit the customer action on ".concat(route || 'the affected screen', " and assert a persisted state change or explicit validation message."),
747
- expected: proof || 'Before/action/after proof shows the submit is no longer a no-op.'
1390
+ expected: proof || 'Before/action/after proof shows the submit is no longer a no-op.',
1391
+ failureClass: 'business',
1392
+ requiredArtifacts: ['before form state', 'submit action trace', 'post-submit DOM/data proof', 'method/network result']
748
1393
  },
749
1394
  missing_wrong_data: {
750
1395
  action: "Load the named record/list on ".concat(route || 'the affected route', " and compare visible data to the expected persisted source."),
751
- expected: proof || 'Visible data and persisted data match the customer expectation.'
1396
+ expected: proof || 'Visible data and persisted data match the customer expectation.',
1397
+ failureClass: 'business',
1398
+ requiredArtifacts: ['expected source record', 'visible DOM/data snapshot', 'persisted data comparison']
752
1399
  },
753
1400
  filter_query_mismatch: {
754
1401
  action: 'Apply the reported filter/query inputs and assert the returned rows/counts match the expected dataset.',
755
- expected: proof || 'Filter/query output contains the correct included rows and excludes the wrong rows.'
1402
+ expected: proof || 'Filter/query output contains the correct included rows and excludes the wrong rows.',
1403
+ failureClass: 'business',
1404
+ requiredArtifacts: ['filter input trace', 'included/excluded row proof', 'query or publication result proof']
756
1405
  },
757
1406
  invoice_pdf_export: {
758
1407
  action: 'Generate the invoice/PDF/export from the affected route and inspect the downloaded/generated artifact.',
759
- expected: proof || 'Generated artifact contains the expected customer-visible rows, totals, or fields.'
1408
+ expected: proof || 'Generated artifact contains the expected customer-visible rows, totals, or fields.',
1409
+ failureClass: 'business',
1410
+ requiredArtifacts: ['export trigger trace', 'generated file artifact path', 'parsed artifact content proof']
760
1411
  },
761
1412
  upload_import: {
762
1413
  action: 'Run the upload/import workflow with a representative file and assert parsed plus persisted results.',
763
- expected: proof || 'Import shows a success result and persisted rows/counts changed as expected.'
1414
+ expected: proof || 'Import shows a success result and persisted rows/counts changed as expected.',
1415
+ failureClass: 'business',
1416
+ requiredArtifacts: ['input file fixture', 'import result message', 'persisted row/count delta']
764
1417
  },
765
1418
  route_auth_hydration: {
766
1419
  action: 'Open the route as the affected user and assert authenticated hydration reaches the functional screen, not a shell.',
767
- expected: proof || 'Route hydrates with the required controls/data for the affected account.'
1420
+ expected: proof || 'Route hydrates with the required controls/data for the affected account.',
1421
+ failureClass: 'route',
1422
+ requiredArtifacts: ['auth context', 'hydrated DOM proof', 'console/network error log']
768
1423
  },
769
1424
  slow_query_performance: {
770
1425
  action: 'Run the reported query/workflow with timing/log evidence before and after the fix.',
771
- expected: proof || 'Performance evidence shows the slow path improved without changing results.'
1426
+ expected: proof || 'Performance evidence shows the slow path improved without changing results.',
1427
+ failureClass: 'business',
1428
+ requiredArtifacts: ['before timing', 'after timing', 'result equivalence proof', 'query/log trace']
772
1429
  }
773
1430
  };
774
1431
  var selected = map[gate.issue_class];
775
- return [__assign(__assign({}, common), { objective: "Issue-class probe for ".concat(gate.issue_class, ": ").concat(gate.issue_case.customer_complaint), action: proofContract
1432
+ var requiredArtifacts = Array.from(new Set(__spreadArray(__spreadArray([], __read(contractArtifacts), false), __read(selected.requiredArtifacts), false)));
1433
+ return [__assign(__assign({}, common), { failure_class: selected.failureClass, objective: "Issue-class probe for ".concat(gate.issue_class, ": ").concat(gate.issue_case.customer_complaint), action: proofContract
776
1434
  ? "".concat(proofContract.action_under_test, " ").concat(selected.action)
777
- : selected.action, expected_evidence: "".concat(selected.expected).concat(assertion ? " Assertion: ".concat(assertion, ".") : '').concat(artifacts).trim() })];
1435
+ : selected.action, expected_evidence: "".concat(selected.expected).concat(assertion ? " Assertion: ".concat(assertion, ".") : '').concat(requiredArtifacts.length ? " Required artifacts: ".concat(requiredArtifacts.join(', '), ".") : '').trim(), expected_business_proof: proof || gate.proof_plan.business_assertion, required_artifacts: requiredArtifacts })];
778
1436
  }
779
1437
  function hashResolveIOSupportV5Evidence(value) {
780
1438
  var raw = typeof value === 'string' ? value : JSON.stringify(value || {});
@@ -811,7 +1469,6 @@ function decideResolveIOSupportV5RepeatedFailureStop(input) {
811
1469
  stepType: entry.stepType,
812
1470
  failureClass: entry.failureClass,
813
1471
  blocker: entry.blocker || entry.summary,
814
- blockerFingerprint: entry.blockerFingerprint,
815
1472
  evidenceHash: entry.evidenceHash,
816
1473
  changedFiles: entry.changedFiles,
817
1474
  artifactPaths: entry.artifactPaths,
@@ -824,8 +1481,10 @@ function decideResolveIOSupportV5RepeatedFailureStop(input) {
824
1481
  stepType: latestRecord === null || latestRecord === void 0 ? void 0 : latestRecord.stepType,
825
1482
  failureClass: failureClass,
826
1483
  blocker: input.blocker,
827
- blockerFingerprint: blockerFingerprint,
828
- evidenceHash: evidenceHash
1484
+ evidenceHash: evidenceHash,
1485
+ changedFiles: cleanList(input.changedFiles, 40, 500),
1486
+ artifactPaths: cleanList(input.artifactPaths, 40, 500),
1487
+ summary: input.blocker
829
1488
  },
830
1489
  maxSameFailureRepeats: limit,
831
1490
  maxPingPongTransitions: 3,
@@ -848,17 +1507,39 @@ function decideResolveIOSupportV5RepeatedFailureStop(input) {
848
1507
  failureClass: failureClass,
849
1508
  blockerFingerprint: blockerFingerprint,
850
1509
  evidenceHash: evidenceHash,
1510
+ newEvidence: managerDecision.newEvidence,
1511
+ materialEvidence: managerDecision.materialEvidence,
1512
+ evidenceStrength: managerDecision.evidenceStrength,
1513
+ evidenceSignals: managerDecision.evidenceSignals,
851
1514
  reason: 'support_v5_ping_pong_failure_loop'
852
1515
  };
853
1516
  }
854
- if (managerDecision.action === 'park_repeated_failure' && managerDecision.newEvidence) {
1517
+ if (managerDecision.action === 'park_repeated_failure' && managerDecision.newEvidence && !managerDecision.materialEvidence) {
1518
+ return {
1519
+ shouldStop: true,
1520
+ repeatedCount: managerDecision.sameFailureCount,
1521
+ failureClass: failureClass,
1522
+ blockerFingerprint: blockerFingerprint,
1523
+ evidenceHash: evidenceHash,
1524
+ newEvidence: managerDecision.newEvidence,
1525
+ materialEvidence: managerDecision.materialEvidence,
1526
+ evidenceStrength: managerDecision.evidenceStrength,
1527
+ evidenceSignals: managerDecision.evidenceSignals,
1528
+ reason: 'support_v5_same_failure_with_weak_evidence'
1529
+ };
1530
+ }
1531
+ if (managerDecision.newEvidence && managerDecision.materialEvidence) {
855
1532
  return {
856
1533
  shouldStop: false,
857
1534
  repeatedCount: managerDecision.sameFailureCount,
858
1535
  failureClass: failureClass,
859
1536
  blockerFingerprint: blockerFingerprint,
860
1537
  evidenceHash: evidenceHash,
861
- reason: 'support_v5_retry_allowed_changed_evidence_hash'
1538
+ newEvidence: managerDecision.newEvidence,
1539
+ materialEvidence: managerDecision.materialEvidence,
1540
+ evidenceStrength: managerDecision.evidenceStrength,
1541
+ evidenceSignals: managerDecision.evidenceSignals,
1542
+ reason: 'support_v5_retry_allowed_material_evidence'
862
1543
  };
863
1544
  }
864
1545
  if (managerDecision.action === 'park_repeated_failure') {
@@ -868,6 +1549,10 @@ function decideResolveIOSupportV5RepeatedFailureStop(input) {
868
1549
  failureClass: failureClass,
869
1550
  blockerFingerprint: blockerFingerprint,
870
1551
  evidenceHash: evidenceHash,
1552
+ newEvidence: managerDecision.newEvidence,
1553
+ materialEvidence: managerDecision.materialEvidence,
1554
+ evidenceStrength: managerDecision.evidenceStrength,
1555
+ evidenceSignals: managerDecision.evidenceSignals,
871
1556
  reason: 'support_v5_same_failure_class_without_new_evidence'
872
1557
  };
873
1558
  }
@@ -893,11 +1578,125 @@ function decideResolveIOSupportV5RepeatedFailureStop(input) {
893
1578
  failureClass: failureClass,
894
1579
  blockerFingerprint: blockerFingerprint,
895
1580
  evidenceHash: evidenceHash,
1581
+ newEvidence: managerDecision.newEvidence,
1582
+ materialEvidence: managerDecision.materialEvidence,
1583
+ evidenceStrength: managerDecision.evidenceStrength,
1584
+ evidenceSignals: managerDecision.evidenceSignals,
896
1585
  reason: repeatedCount >= limit
897
1586
  ? 'support_v5_same_failure_class_without_new_evidence'
898
1587
  : 'support_v5_retry_allowed_new_or_below_repeat_limit'
899
1588
  };
900
1589
  }
1590
+ function evaluateResolveIOSupportEvidenceFreshness(input) {
1591
+ var _a;
1592
+ var history = Array.isArray(input.history) ? input.history : [];
1593
+ var latest = history.length ? history[history.length - 1] : undefined;
1594
+ var rawFailureClass = cleanText(input.failureClass || (latest === null || latest === void 0 ? void 0 : latest.failureClass), 80).toLowerCase();
1595
+ var failureClass = rawFailureClass || 'unknown';
1596
+ var blocker = cleanText(input.blocker || (latest === null || latest === void 0 ? void 0 : latest.blocker) || (latest === null || latest === void 0 ? void 0 : latest.summary), 1200);
1597
+ var changedFiles = cleanList(input.changedFiles || (latest === null || latest === void 0 ? void 0 : latest.changedFiles), 40, 500);
1598
+ var artifactPaths = cleanList(input.artifactPaths || (latest === null || latest === void 0 ? void 0 : latest.artifactPaths), 40, 500);
1599
+ var explicitEvidenceHash = cleanText(input.evidenceHash || (latest === null || latest === void 0 ? void 0 : latest.evidenceHash), 120);
1600
+ var evidenceHash = explicitEvidenceHash || hashResolveIOSupportV5Evidence(input.evidence || artifactPaths || blocker || (latest === null || latest === void 0 ? void 0 : latest.summary) || '');
1601
+ var blockerFingerprint = fingerprintResolveIOSupportV5Blocker(blocker || (latest === null || latest === void 0 ? void 0 : latest.summary) || '');
1602
+ var missing = !history.length && !rawFailureClass && !blocker && !explicitEvidenceHash && !artifactPaths.length && !changedFiles.length;
1603
+ if (missing) {
1604
+ return {
1605
+ status: 'missing',
1606
+ failureClass: '',
1607
+ blockerFingerprint: '',
1608
+ evidenceHash: '',
1609
+ sameFailureCount: 0,
1610
+ pingPongCount: 0,
1611
+ newEvidence: false,
1612
+ materialEvidence: false,
1613
+ evidenceStrength: 'none',
1614
+ evidenceSignals: [],
1615
+ loopBudgetShouldReset: false,
1616
+ canRetry: true,
1617
+ mustCollectNewEvidence: false,
1618
+ productRepairFailure: false,
1619
+ reason: 'support_evidence_freshness_waiting_for_first_failure_or_proof',
1620
+ requiredResetEvidence: [
1621
+ 'current blocker fingerprint',
1622
+ 'evidence hash',
1623
+ 'artifact path when available'
1624
+ ],
1625
+ changedFiles: changedFiles,
1626
+ artifactPaths: artifactPaths
1627
+ };
1628
+ }
1629
+ var current = {
1630
+ outcome: 'needs_repair',
1631
+ lane: cleanText(input.lane || (latest === null || latest === void 0 ? void 0 : latest.lane), 80) || (latest === null || latest === void 0 ? void 0 : latest.lane),
1632
+ stepType: cleanText(input.stepType || (latest === null || latest === void 0 ? void 0 : latest.stepType), 80) || (latest === null || latest === void 0 ? void 0 : latest.stepType),
1633
+ failureClass: failureClass,
1634
+ blocker: blocker,
1635
+ evidenceHash: evidenceHash,
1636
+ changedFiles: changedFiles,
1637
+ artifactPaths: artifactPaths,
1638
+ summary: blocker
1639
+ };
1640
+ var policy = (0, ai_runner_manager_policy_1.decideResolveIOAIManagerPolicy)({
1641
+ history: history.map(function (entry) { return ({
1642
+ outcome: entry.outcome,
1643
+ lane: entry.lane,
1644
+ stepType: entry.stepType,
1645
+ failureClass: entry.failureClass,
1646
+ blocker: entry.blocker || entry.summary,
1647
+ evidenceHash: entry.evidenceHash,
1648
+ changedFiles: entry.changedFiles,
1649
+ artifactPaths: entry.artifactPaths,
1650
+ summary: entry.summary,
1651
+ recordedAt: entry.recordedAt
1652
+ }); }),
1653
+ current: current,
1654
+ maxSameFailureRepeats: Math.max(1, Number(input.limit || 2) || 2),
1655
+ maxPingPongTransitions: 3,
1656
+ infraFailureClasses: input.ignoreInfra !== false ? ['infra', 'compile'] : []
1657
+ });
1658
+ var status = 'retry_below_limit';
1659
+ if (policy.action === 'retry_infra') {
1660
+ status = 'infra_ignored';
1661
+ }
1662
+ else if (policy.action === 'park_ping_pong') {
1663
+ status = 'ping_pong';
1664
+ }
1665
+ else if (policy.action === 'park_repeated_failure') {
1666
+ status = policy.newEvidence ? 'weak_evidence' : 'stale_repeated';
1667
+ }
1668
+ else if (policy.newEvidence && policy.materialEvidence) {
1669
+ status = 'material_evidence';
1670
+ }
1671
+ else if (policy.newEvidence && !policy.materialEvidence) {
1672
+ status = 'weak_evidence';
1673
+ }
1674
+ else if (policy.loopBudgetShouldReset || policy.reason === 'manager_policy_new_failure_or_lane') {
1675
+ status = 'fresh';
1676
+ }
1677
+ var mustCollectNewEvidence = policy.action === 'park_repeated_failure'
1678
+ || policy.action === 'park_ping_pong';
1679
+ return {
1680
+ status: status,
1681
+ failureClass: policy.failureClass,
1682
+ blockerFingerprint: blockerFingerprint || policy.blockerFingerprint,
1683
+ evidenceHash: policy.evidenceHash,
1684
+ sameFailureCount: policy.sameFailureCount,
1685
+ pingPongCount: policy.pingPongCount,
1686
+ newEvidence: policy.newEvidence,
1687
+ materialEvidence: policy.materialEvidence,
1688
+ evidenceStrength: policy.evidenceStrength,
1689
+ evidenceSignals: policy.evidenceSignals,
1690
+ loopBudgetShouldReset: policy.loopBudgetShouldReset,
1691
+ canRetry: !mustCollectNewEvidence,
1692
+ mustCollectNewEvidence: mustCollectNewEvidence,
1693
+ productRepairFailure: policy.productRepairFailure,
1694
+ reason: policy.reason,
1695
+ requiredResetEvidence: Array.from(new Set(__spreadArray(__spreadArray([], __read(cleanList(policy.recoveryPlan.loopResetEvidence, 12, 500)), false), __read(cleanList((_a = policy.recoveryAction) === null || _a === void 0 ? void 0 : _a.successCriteria, 12, 500)), false))).slice(0, 18),
1696
+ changedFiles: changedFiles,
1697
+ artifactPaths: artifactPaths
1698
+ };
1699
+ }
901
1700
  function changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(diagnosisGate, changedFiles, options) {
902
1701
  if (options === void 0) { options = {}; }
903
1702
  var validation = validateResolveIOSupportDiagnosisGate(diagnosisGate);
@@ -935,6 +1734,8 @@ function decideResolveIOSupportV5RepairGate(input) {
935
1734
  blocker: input.blocker,
936
1735
  evidence: input.evidence,
937
1736
  evidenceHash: input.evidenceHash,
1737
+ changedFiles: input.changedFiles,
1738
+ artifactPaths: input.artifactPaths,
938
1739
  limit: Math.max(1, Number(input.maxRepeatedNoProgress || 2) || 2),
939
1740
  ignoreInfra: true
940
1741
  }) : undefined;
@@ -1125,7 +1926,7 @@ function buildResolveIOSupportV5ScopeDigest(input) {
1125
1926
  return raw.slice(0, maxChars);
1126
1927
  }
1127
1928
  function buildResolveIOSupportV5MicrotaskLedger(input) {
1128
- var e_4, _a;
1929
+ var e_7, _a;
1129
1930
  var existing = Array.isArray(input.existing) ? input.existing : [];
1130
1931
  var completedByObjective = new Map();
1131
1932
  try {
@@ -1136,12 +1937,12 @@ function buildResolveIOSupportV5MicrotaskLedger(input) {
1136
1937
  }
1137
1938
  }
1138
1939
  }
1139
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1940
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
1140
1941
  finally {
1141
1942
  try {
1142
1943
  if (existing_1_1 && !existing_1_1.done && (_a = existing_1.return)) _a.call(existing_1);
1143
1944
  }
1144
- finally { if (e_4) throw e_4.error; }
1945
+ finally { if (e_7) throw e_7.error; }
1145
1946
  }
1146
1947
  var now = isoNow(input.now);
1147
1948
  var requirements = cleanList(input.requirements, 30, 240);
@@ -1452,6 +2253,8 @@ function decideResolveIOSupportV5Continuation(bundle) {
1452
2253
  failureClass: last.failureClass,
1453
2254
  blocker: last.blocker || last.summary,
1454
2255
  evidenceHash: last.evidenceHash,
2256
+ changedFiles: last.changedFiles,
2257
+ artifactPaths: last.artifactPaths,
1455
2258
  limit: budget.maxRepeatedNoProgress,
1456
2259
  ignoreInfra: true
1457
2260
  }) : null;
@@ -1488,6 +2291,9 @@ function decideResolveIOSupportV5Continuation(bundle) {
1488
2291
  return __assign({ action: 'park', reason: 'support_v5_budget_guard', nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup', repeatedNoProgressCount: repeatedNoProgressCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_5));
1489
2292
  }
1490
2293
  var lastFailureClass = cleanText(last === null || last === void 0 ? void 0 : last.failureClass, 80).toLowerCase();
2294
+ var materialEvidenceRetryAllowed = (repeatedFailure === null || repeatedFailure === void 0 ? void 0 : repeatedFailure.shouldStop) === false
2295
+ && repeatedFailure.newEvidence === true
2296
+ && repeatedFailure.materialEvidence === true;
1491
2297
  if (repeatedNoProgressCount > budget.maxRepeatedNoProgress && /^(infra|compile)$/.test(lastFailureClass)) {
1492
2298
  var recoveryPlan_6 = recoveryPlanFor('retry_infra', 'support_v5_infra_or_compile_repair_required', {
1493
2299
  failureClass: lastFailureClass,
@@ -1495,24 +2301,24 @@ function decideResolveIOSupportV5Continuation(bundle) {
1495
2301
  });
1496
2302
  return __assign({ action: 'continue', reason: 'support_v5_infra_or_compile_repair_required', nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'compile_check', repeatedNoProgressCount: repeatedNoProgressCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_6));
1497
2303
  }
1498
- if (repeatedNoProgressCount > budget.maxRepeatedNoProgress) {
1499
- var recoveryPlan_7 = recoveryPlanFor('park_repeated_failure', 'support_v5_repeated_no_progress', {
2304
+ if (repeatedFailure === null || repeatedFailure === void 0 ? void 0 : repeatedFailure.shouldStop) {
2305
+ var recoveryPlan_7 = recoveryPlanFor(repeatedFailure.reason === 'support_v5_ping_pong_failure_loop' ? 'park_ping_pong' : 'park_repeated_failure', repeatedFailure.reason, {
2306
+ failureClass: repeatedFailure.failureClass,
1500
2307
  productRepairFailure: true
1501
2308
  });
1502
- return __assign({ action: 'park', reason: 'support_v5_repeated_no_progress', nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup', repeatedNoProgressCount: repeatedNoProgressCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_7));
2309
+ return __assign({ action: 'park', reason: repeatedFailure.reason, nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup', repeatedNoProgressCount: repeatedFailure.repeatedCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_7));
1503
2310
  }
1504
- if (repeatedFailure === null || repeatedFailure === void 0 ? void 0 : repeatedFailure.shouldStop) {
1505
- var recoveryPlan_8 = recoveryPlanFor(repeatedFailure.reason === 'support_v5_ping_pong_failure_loop' ? 'park_ping_pong' : 'park_repeated_failure', repeatedFailure.reason, {
1506
- failureClass: repeatedFailure.failureClass,
2311
+ if (repeatedNoProgressCount > budget.maxRepeatedNoProgress && !materialEvidenceRetryAllowed) {
2312
+ var recoveryPlan_8 = recoveryPlanFor('park_repeated_failure', 'support_v5_repeated_no_progress', {
1507
2313
  productRepairFailure: true
1508
2314
  });
1509
- return __assign({ action: 'park', reason: repeatedFailure.reason, nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup', repeatedNoProgressCount: repeatedFailure.repeatedCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_8));
2315
+ return __assign({ action: 'park', reason: 'support_v5_repeated_no_progress', nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup', repeatedNoProgressCount: repeatedNoProgressCount, budgetExceeded: budgetExceeded }, recoveryFieldsFor(recoveryPlan_8));
1510
2316
  }
1511
2317
  var recoveryPlan = recoveryPlanFor('continue', 'support_v5_continue');
1512
2318
  return __assign({ action: 'continue', reason: 'support_v5_continue', nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || bundle.supportV5SupervisorState.activeStep, repeatedNoProgressCount: repeatedNoProgressCount, budgetExceeded: false }, recoveryFieldsFor(recoveryPlan));
1513
2319
  }
1514
2320
  function decideResolveIOSupportV5AutonomousNextAction(input) {
1515
- var _a, _b, _c;
2321
+ var _a, _b, _c, _d, _e;
1516
2322
  var bundle = input.bundle;
1517
2323
  var activeMicrotask = selectResolveIOSupportV5ActiveMicrotask(bundle.supportV5MicrotaskLedger || [], bundle.supportV5ActiveMicrotaskId);
1518
2324
  var diagnosisValidation = validateResolveIOSupportDiagnosisGate(bundle.supportV5DiagnosisGate, {
@@ -1528,6 +2334,7 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1528
2334
  evidence: input.evidence,
1529
2335
  evidenceHash: input.evidenceHash,
1530
2336
  history: bundle.supportV5StepHistory,
2337
+ artifactPaths: input.artifactPaths,
1531
2338
  maxOwnerFiles: input.maxOwnerFiles,
1532
2339
  allowTestsOutsideOwnerFiles: true
1533
2340
  });
@@ -1537,19 +2344,153 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1537
2344
  outcomeLabel: input.outcomeLabel,
1538
2345
  confidence: input.confidence,
1539
2346
  businessAssertionStatus: input.businessAssertionStatus,
2347
+ businessAssertions: input.businessAssertions,
1540
2348
  businessProofArtifacts: input.businessProofArtifacts,
2349
+ previousProofFingerprint: input.previousProofFingerprint,
2350
+ previousArtifactFingerprint: input.previousArtifactFingerprint,
1541
2351
  unresolvedBlockers: input.unresolvedBlockers,
1542
2352
  releaseStatus: input.releaseStatus
1543
2353
  });
2354
+ var businessProofReadiness = evaluateResolveIOSupportBusinessProofReadiness({
2355
+ diagnosisGate: bundle.supportV5DiagnosisGate,
2356
+ outcomeLabel: input.outcomeLabel,
2357
+ businessAssertionStatus: input.businessAssertionStatus,
2358
+ businessAssertions: input.businessAssertions,
2359
+ businessProofArtifacts: input.businessProofArtifacts,
2360
+ previousProofFingerprint: input.previousProofFingerprint,
2361
+ previousArtifactFingerprint: input.previousArtifactFingerprint
2362
+ });
2363
+ var evidenceFreshness = evaluateResolveIOSupportEvidenceFreshness({
2364
+ history: bundle.supportV5StepHistory,
2365
+ failureClass: input.failureClass,
2366
+ blocker: input.blocker,
2367
+ evidence: input.evidence,
2368
+ evidenceHash: input.evidenceHash,
2369
+ changedFiles: input.changedFiles,
2370
+ artifactPaths: input.artifactPaths || input.businessProofArtifacts,
2371
+ lane: (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) || 'supervisor',
2372
+ stepType: activeStepType,
2373
+ limit: buildResolveIOSupportV5Budget(bundle.supportV5Budget).maxRepeatedNoProgress,
2374
+ ignoreInfra: true
2375
+ });
1544
2376
  var ownerFiles = ((_a = diagnosisValidation.normalized) === null || _a === void 0 ? void 0 : _a.owner_files) || repairGate.ownerFiles || [];
1545
2377
  var proofContract = (_b = diagnosisValidation.normalized) === null || _b === void 0 ? void 0 : _b.proof_plan.business_proof_contract;
1546
2378
  var expectedProof = (proofContract === null || proofContract === void 0 ? void 0 : proofContract.data_or_dom_assertion)
1547
2379
  || ((_c = diagnosisValidation.normalized) === null || _c === void 0 ? void 0 : _c.proof_plan.business_assertion)
1548
2380
  || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.acceptanceProof)
1549
2381
  || '';
2382
+ var issueClassProbes = diagnosisValidation.valid && diagnosisValidation.normalized
2383
+ ? buildResolveIOSupportIssueClassProbes(diagnosisValidation.normalized)
2384
+ : [];
2385
+ var activeIssueClassProbe = issueClassProbes[0];
2386
+ var issueClassProbeEvidence = activeIssueClassProbe
2387
+ ? Array.from(new Set(__spreadArray([
2388
+ "AIQaBusinessAssertion mapped to ".concat(activeIssueClassProbe.issue_class),
2389
+ "state transition: ".concat(activeIssueClassProbe.state_transition.before || 'before', " -> ").concat(activeIssueClassProbe.state_transition.action || activeIssueClassProbe.action, " -> ").concat(activeIssueClassProbe.state_transition.after || 'after')
2390
+ ], __read(activeIssueClassProbe.required_artifacts.map(function (artifact) { return "artifact: ".concat(artifact); })), false)))
2391
+ : [];
2392
+ var buildRootCauseReadiness = function (action, reason, fields, primaryCommand, nextCommands, blockers) {
2393
+ var _a;
2394
+ var statusByAction = {
2395
+ run_diagnosis_gate: 'diagnosis_required',
2396
+ ask_customer_clarification: 'customer_clarification_required',
2397
+ repair_infra_only: 'infra_repair_only',
2398
+ revise_diagnosis_scope: 'scope_revision_required',
2399
+ run_owner_scoped_repair: 'owner_scoped_repair_ready',
2400
+ run_business_proof_qa: 'business_proof_required',
2401
+ repair_release_hotfix_first: 'release_hotfix_required',
2402
+ collect_new_evidence: 'collect_new_evidence',
2403
+ draft_customer_reply: 'customer_reply_draft_ready',
2404
+ ready_for_release_gate: 'release_gate_ready',
2405
+ park_manual: 'parked'
2406
+ };
2407
+ var nextGateByStatus = {
2408
+ diagnosis_required: 'diagnosis',
2409
+ customer_clarification_required: 'customer_reply',
2410
+ infra_repair_only: 'infra',
2411
+ scope_revision_required: 'scope',
2412
+ owner_scoped_repair_ready: 'repair',
2413
+ business_proof_required: 'business_proof',
2414
+ release_hotfix_required: 'release',
2415
+ release_gate_ready: 'release',
2416
+ customer_reply_draft_ready: 'customer_reply',
2417
+ collect_new_evidence: 'evidence',
2418
+ parked: 'manual'
2419
+ };
2420
+ var status = statusByAction[action] || 'parked';
2421
+ var diagnosisValid = diagnosisValidation.valid === true;
2422
+ var ownerFilesReady = diagnosisValid && ownerFiles.length > 0;
2423
+ var proofPlanReady = diagnosisValid && !!proofContract;
2424
+ var rootCauseFirstSatisfied = diagnosisValid && ownerFilesReady && proofPlanReady;
2425
+ var sameFailureParked = evidenceFreshness.mustCollectNewEvidence === true
2426
+ || (action === 'collect_new_evidence' && /repeated|no_progress|ping_pong|same failure|same evidence/i.test(reason));
2427
+ return {
2428
+ status: status,
2429
+ nextGate: nextGateByStatus[status],
2430
+ nextCommand: primaryCommand || nextCommands[0] || action,
2431
+ rootCauseFirstSatisfied: rootCauseFirstSatisfied,
2432
+ diagnosisValid: diagnosisValid,
2433
+ ownerFilesReady: ownerFilesReady,
2434
+ proofPlanReady: proofPlanReady,
2435
+ businessProofReady: businessProofReadiness.ready === true,
2436
+ infraOnly: action === 'repair_infra_only',
2437
+ sameFailureParked: sameFailureParked,
2438
+ canEditProductCode: fields.canEditProductCode === true && rootCauseFirstSatisfied,
2439
+ canRunIssueClassProbe: rootCauseFirstSatisfied && (action === 'run_business_proof_qa' || status === 'business_proof_required'),
2440
+ canRunBusinessProofQa: rootCauseFirstSatisfied && (action === 'run_business_proof_qa' || !businessProofReadiness.ready),
2441
+ canRelease: action === 'ready_for_release_gate' && businessProofReadiness.ready === true,
2442
+ canDraftCustomerReply: (action === 'draft_customer_reply' && businessProofReadiness.ready === true)
2443
+ || action === 'ask_customer_clarification',
2444
+ requiresHumanDecision: action === 'park_manual' || fields.canRunAutonomously !== true,
2445
+ reason: reason,
2446
+ blockers: blockers,
2447
+ ownerFiles: ownerFiles,
2448
+ issueClass: ((_a = diagnosisValidation.normalized) === null || _a === void 0 ? void 0 : _a.issue_class) || repairGate.issueClass,
2449
+ expectedProof: expectedProof,
2450
+ issueClassProbes: issueClassProbes,
2451
+ businessProofStatus: businessProofReadiness.status,
2452
+ proofFingerprint: businessProofReadiness.proofFingerprint,
2453
+ artifactFingerprint: businessProofReadiness.artifactFingerprint,
2454
+ proofFreshness: businessProofReadiness.proofFreshness
2455
+ };
2456
+ };
1550
2457
  var makeDecision = function (action, label, reason, fields) {
1551
2458
  var _a;
1552
- return ({
2459
+ var primaryCommand = fields.primaryCommand || action;
2460
+ var nextCommands = fields.nextCommands || [primaryCommand];
2461
+ var requiredEvidence = fields.requiredEvidence || [];
2462
+ var blockers = fields.blockers || [];
2463
+ var rootCauseReadiness = buildRootCauseReadiness(action, reason, fields, primaryCommand, nextCommands, blockers);
2464
+ var forbiddenActions = Array.from(new Set(__spreadArray([
2465
+ 'Do not send customer email without explicit human approval.',
2466
+ 'Do not broaden owner_files without revised diagnosis evidence.',
2467
+ 'Do not accept route-load, screenshot-only, scorecard-only, or model-claim proof.'
2468
+ ], __read((fields.forbiddenActions || [])), false)));
2469
+ var humanReviewPacket = fields.humanReviewPacket
2470
+ || (action === 'draft_customer_reply' && customerReplyPolicy.humanReviewPacket ? customerReplyPolicy.humanReviewPacket : undefined)
2471
+ || buildResolveIOSupportHumanReviewPacket({
2472
+ reviewType: supportAutonomousReviewTypeForAction(action),
2473
+ title: label,
2474
+ summary: reason,
2475
+ primaryAction: primaryCommand,
2476
+ customerFacingDraftAllowed: fields.canDraftCustomerReply === true,
2477
+ safety: action === 'draft_customer_reply' ? 'safe_to_draft' : 'internal_hold',
2478
+ reason: reason,
2479
+ blockers: blockers,
2480
+ requiredEvidence: requiredEvidence,
2481
+ evidenceRefs: businessProofReadiness.artifactPaths,
2482
+ nextCommands: nextCommands,
2483
+ forbiddenActions: forbiddenActions,
2484
+ costRisk: fields.canHotfixBackend === true || action === 'draft_customer_reply'
2485
+ ? 'release_or_customer_send'
2486
+ : fields.canRunModel === true
2487
+ ? 'expensive_model'
2488
+ : fields.canRunQa === true
2489
+ ? 'small_model_or_qa'
2490
+ : 'free_or_deterministic',
2491
+ now: input.now
2492
+ });
2493
+ return {
1553
2494
  action: action,
1554
2495
  label: label,
1555
2496
  reason: reason,
@@ -1557,31 +2498,35 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1557
2498
  canEditProductCode: fields.canEditProductCode === true,
1558
2499
  canRunModel: fields.canRunModel === true,
1559
2500
  canRunQa: fields.canRunQa === true,
2501
+ canPrepareHotfixPatch: fields.canPrepareHotfixPatch === true,
1560
2502
  canHotfixBackend: fields.canHotfixBackend === true,
2503
+ liveHotfixBlockedUntilCommit: fields.liveHotfixBlockedUntilCommit === true,
1561
2504
  canDraftCustomerReply: fields.canDraftCustomerReply === true,
1562
2505
  canSendCustomerReply: false,
1563
2506
  lane: fields.lane || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) || 'supervisor',
1564
2507
  stepType: fields.stepType || activeStepType,
1565
2508
  microtaskId: activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.microtaskId,
1566
- primaryCommand: fields.primaryCommand || action,
1567
- nextCommands: fields.nextCommands || [fields.primaryCommand || action],
1568
- requiredEvidence: fields.requiredEvidence || [],
1569
- forbiddenActions: Array.from(new Set(__spreadArray([
1570
- 'Do not send customer email without explicit human approval.',
1571
- 'Do not broaden owner_files without revised diagnosis evidence.',
1572
- 'Do not accept route-load, screenshot-only, scorecard-only, or model-claim proof.'
1573
- ], __read((fields.forbiddenActions || [])), false))),
1574
- blockers: fields.blockers || [],
2509
+ primaryCommand: primaryCommand,
2510
+ nextCommands: nextCommands,
2511
+ requiredEvidence: requiredEvidence,
2512
+ forbiddenActions: forbiddenActions,
2513
+ blockers: blockers,
1575
2514
  ownerFiles: ownerFiles,
1576
2515
  issueClass: ((_a = diagnosisValidation.normalized) === null || _a === void 0 ? void 0 : _a.issue_class) || repairGate.issueClass,
1577
2516
  expectedProof: expectedProof,
2517
+ issueClassProbes: issueClassProbes,
1578
2518
  activeMicrotask: activeMicrotask,
1579
2519
  diagnosisValidation: diagnosisValidation,
1580
2520
  repairGate: repairGate,
1581
2521
  continuation: continuation,
1582
2522
  customerReplyPolicy: customerReplyPolicy,
2523
+ businessProofReadiness: businessProofReadiness,
2524
+ evidenceFreshness: evidenceFreshness,
2525
+ rootCauseReadiness: rootCauseReadiness,
2526
+ humanReviewPacket: humanReviewPacket,
2527
+ hotfixContinuation: fields.hotfixContinuation,
1583
2528
  recordedAt: isoNow(input.now)
1584
- });
2529
+ };
1585
2530
  };
1586
2531
  if (continuation.action === 'park' && continuation.reason === 'support_v5_budget_guard') {
1587
2532
  return makeDecision('park_manual', 'Park Manual', continuation.reason, {
@@ -1607,6 +2552,25 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1607
2552
  forbiddenActions: ['Do not run another product-code repair until blockerFingerprint or evidenceHash changes.']
1608
2553
  });
1609
2554
  }
2555
+ if (customerReplyPolicy.action === 'ask_clarification') {
2556
+ return makeDecision('ask_customer_clarification', 'Ask Customer Clarification', customerReplyPolicy.reason, {
2557
+ canRunAutonomously: false,
2558
+ canRunModel: false,
2559
+ canEditProductCode: false,
2560
+ canDraftCustomerReply: true,
2561
+ lane: 'customer',
2562
+ stepType: 'customer_reply',
2563
+ primaryCommand: ((_d = customerReplyPolicy.humanReviewPacket) === null || _d === void 0 ? void 0 : _d.primaryAction) || 'review_customer_clarification',
2564
+ nextCommands: ((_e = customerReplyPolicy.humanReviewPacket) === null || _e === void 0 ? void 0 : _e.nextCommands) || ['edit_clarification_question', 'send_after_human_review', 'park_ticket_until_customer_reply'],
2565
+ requiredEvidence: customerReplyPolicy.requiredEvidence,
2566
+ blockers: diagnosisValidation.blockers,
2567
+ forbiddenActions: [
2568
+ 'Do not run repair from a guessed reproduction path.',
2569
+ 'Do not send customer email without explicit human approval.'
2570
+ ],
2571
+ humanReviewPacket: customerReplyPolicy.humanReviewPacket
2572
+ });
2573
+ }
1610
2574
  if (repairGate.action === 'diagnose_only' || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.type) === 'diagnosis_gate' || !diagnosisValidation.valid) {
1611
2575
  return makeDecision('run_diagnosis_gate', 'Run Diagnosis Gate', 'support_v5_root_cause_first_required', {
1612
2576
  canRunAutonomously: true,
@@ -1672,17 +2636,66 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1672
2636
  });
1673
2637
  }
1674
2638
  if (supportReleaseLooksBlocked(input.releaseStatus)) {
2639
+ var releaseBlocker = cleanList(input.unresolvedBlockers, 20, 500).join('; ')
2640
+ || cleanText(input.blocker || input.releaseStatus, 1000)
2641
+ || 'Support release is blocked.';
2642
+ var hotfixContinuation = (0, ai_runner_manager_policy_1.decideResolveIOAIManagerHotfixContinuation)({
2643
+ evidence: input.hotfixEvidence,
2644
+ policy: input.releasePolicy,
2645
+ releaseGatePassed: input.releaseGatePassed,
2646
+ failureClass: 'release',
2647
+ blocker: releaseBlocker,
2648
+ now: input.now
2649
+ });
2650
+ var hotfixPrimaryCommandByAction = {
2651
+ record_hotfix_evidence: 'record_hotfix_evidence',
2652
+ request_force_deploy_reason: 'request_force_deploy_reason',
2653
+ rerun_release_gate: 'rerun_support_release_gate_once',
2654
+ continue_runner: 'continue_support_runner_after_committed_hotfix',
2655
+ allow_one_full_deploy: 'execute_one_full_deploy_with_force_evidence',
2656
+ park_manual: 'park_support_release_manual'
2657
+ };
2658
+ var hotfixNextCommands = Array.from(new Set(__spreadArray(__spreadArray([
2659
+ 'classify_release_blocker',
2660
+ 'prepare_hotfix_patch_without_live_apply',
2661
+ 'commit_and_push_hotfix_to_github',
2662
+ 'record_github_commit_for_hotfix'
2663
+ ], __read(hotfixContinuation.nextCommands), false), [
2664
+ 'apply_backend_hotfix_only_after_commit_proof',
2665
+ 'rerun_release_gate_once'
2666
+ ], false)));
2667
+ var hotfixRequiredEvidence = Array.from(new Set(__spreadArray([
2668
+ 'hotfix evidence',
2669
+ 'full sourceCommitSha, githubCommitUrl, and passed gitPushStatus for the exact pushed GitHub commit',
2670
+ 'checksum before/after',
2671
+ 'health/self-test pass',
2672
+ 'release gate result'
2673
+ ], __read(hotfixContinuation.requiredEvidence), false)));
2674
+ var hotfixForbiddenActions = [
2675
+ 'Do not apply a live hotfix before the exact diff is committed and pushed to GitHub.',
2676
+ 'Do not mark a hotfix durable without sourceCommitSha, githubCommitUrl, and passed gitPushStatus.',
2677
+ 'Do not run a full deploy to clear a duplicate release loop unless force evidence explicitly allows one.'
2678
+ ];
2679
+ var hotfixGitGuard = hotfixContinuation.githubCommitGuard;
2680
+ var liveHotfixBlockedUntilCommit = hotfixContinuation.action === 'record_hotfix_evidence'
2681
+ || (hotfixGitGuard === null || hotfixGitGuard === void 0 ? void 0 : hotfixGitGuard.managerMustCommitBeforeHotfix) === true
2682
+ || ((hotfixGitGuard === null || hotfixGitGuard === void 0 ? void 0 : hotfixGitGuard.required) === true && (hotfixGitGuard === null || hotfixGitGuard === void 0 ? void 0 : hotfixGitGuard.passed) !== true);
2683
+ var canPrepareHotfixPatch = hotfixContinuation.action !== 'park_manual';
1675
2684
  return makeDecision('repair_release_hotfix_first', 'Hotfix Release', 'support_v5_release_blocked_hotfix_first', {
1676
- canRunAutonomously: true,
2685
+ canRunAutonomously: hotfixContinuation.action !== 'park_manual',
1677
2686
  canRunModel: false,
1678
2687
  canEditProductCode: false,
1679
- canHotfixBackend: true,
2688
+ canPrepareHotfixPatch: canPrepareHotfixPatch,
2689
+ canHotfixBackend: canPrepareHotfixPatch && !liveHotfixBlockedUntilCommit,
2690
+ liveHotfixBlockedUntilCommit: liveHotfixBlockedUntilCommit,
1680
2691
  lane: 'release',
1681
2692
  stepType: 'release_gate',
1682
- primaryCommand: 'run_support_release_hotfix_first_gate',
1683
- nextCommands: ['classify_release_blocker', 'hotfix_backend_or_release_artifact', 'record_github_commit_for_hotfix', 'rerun_release_gate_once'],
1684
- requiredEvidence: ['hotfix evidence', 'sourceCommitSha and githubCommitUrl', 'health/self-test pass', 'release gate result'],
1685
- blockers: cleanList(input.unresolvedBlockers, 20, 500)
2693
+ primaryCommand: hotfixPrimaryCommandByAction[hotfixContinuation.action] || 'record_hotfix_evidence',
2694
+ nextCommands: hotfixNextCommands,
2695
+ requiredEvidence: hotfixRequiredEvidence,
2696
+ blockers: Array.from(new Set(__spreadArray(__spreadArray([], __read(cleanList(input.unresolvedBlockers, 20, 500)), false), __read(hotfixContinuation.blockers), false))),
2697
+ forbiddenActions: hotfixForbiddenActions,
2698
+ hotfixContinuation: hotfixContinuation
1686
2699
  });
1687
2700
  }
1688
2701
  if ((activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) === 'qa' || /^(qa_row|qa_retest|business_proof|route_probe|issue_class_probe)$/.test(activeStepType)) {
@@ -1695,7 +2708,7 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1695
2708
  stepType: /^(qa_row|qa_retest|business_proof|route_probe|issue_class_probe)$/.test(activeStepType) ? activeStepType : 'qa_row',
1696
2709
  primaryCommand: 'run_support_v5_business_proof_qa_row',
1697
2710
  nextCommands: ['start_local_stack_if_needed', 'execute_issue_class_probe', 'record_before_action_after_artifacts', 'write_aiqa_business_assertion'],
1698
- requiredEvidence: ['AIQaBusinessAssertion pass', 'DOM/data proof', 'artifact path', 'Mongo delta when data-changing'],
2711
+ requiredEvidence: Array.from(new Set(__spreadArray(['AIQaBusinessAssertion pass', 'DOM/data proof', 'artifact path', 'Mongo delta when data-changing'], __read(issueClassProbeEvidence), false))),
1699
2712
  forbiddenActions: ['Route probe pass alone remains route evidence only.']
1700
2713
  });
1701
2714
  }
@@ -1712,7 +2725,22 @@ function decideResolveIOSupportV5AutonomousNextAction(input) {
1712
2725
  forbiddenActions: ['Draft only; do not send.']
1713
2726
  });
1714
2727
  }
1715
- if (!activeMicrotask && supportBusinessAssertionPassed(input.businessAssertionStatus || input.outcomeLabel)) {
2728
+ if (!activeMicrotask && diagnosisValidation.valid && !businessProofReadiness.ready) {
2729
+ return makeDecision('run_business_proof_qa', 'Run Business Proof QA', businessProofReadiness.reason, {
2730
+ canRunAutonomously: true,
2731
+ canRunQa: true,
2732
+ canRunModel: false,
2733
+ canEditProductCode: false,
2734
+ lane: 'qa',
2735
+ stepType: 'business_proof',
2736
+ primaryCommand: 'run_support_v5_business_proof_qa_row',
2737
+ nextCommands: ['execute_issue_class_probe', 'record_before_action_after_artifacts', 'write_aiqa_business_assertion'],
2738
+ requiredEvidence: Array.from(new Set(__spreadArray(__spreadArray([], __read(businessProofReadiness.requiredEvidence), false), __read(issueClassProbeEvidence), false))),
2739
+ blockers: businessProofReadiness.blockers,
2740
+ forbiddenActions: ['Do not run release or customer reply until businessProofReadiness.ready=true.']
2741
+ });
2742
+ }
2743
+ if (!activeMicrotask && businessProofReadiness.ready) {
1716
2744
  return makeDecision('ready_for_release_gate', 'Run Release Gate', 'support_v5_business_proof_complete_release_gate_ready', {
1717
2745
  canRunAutonomously: true,
1718
2746
  canRunQa: true,
@@ -1758,7 +2786,7 @@ function buildResolveIOSupportV5DiagnoseFirstPrompt(lines) {
1758
2786
  ].filter(Boolean);
1759
2787
  }
1760
2788
  function buildResolveIOSupportV5MicrotaskPrompt(input) {
1761
- var _a, _b, _c, _d, _e, _f;
2789
+ var _a, _b, _c, _d, _e, _f, _g;
1762
2790
  var activeMicrotask = selectResolveIOSupportV5ActiveMicrotask(input.bundle.supportV5MicrotaskLedger || [], input.bundle.supportV5ActiveMicrotaskId);
1763
2791
  var diagnosisValidation = validateResolveIOSupportDiagnosisGate(input.bundle.supportV5DiagnosisGate);
1764
2792
  var diagnosisGate = diagnosisValidation.normalized || input.bundle.supportV5DiagnosisGate;
@@ -1781,6 +2809,39 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
1781
2809
  var artifactPaths = cleanList(((_b = input.artifactPaths) === null || _b === void 0 ? void 0 : _b.length) ? input.artifactPaths : laneMemory.artifactPaths, 8, 200);
1782
2810
  var targetFiles = cleanList(((_c = input.targetFiles) === null || _c === void 0 ? void 0 : _c.length) ? input.targetFiles : activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.targetFiles, 5, 160);
1783
2811
  var contextSnippets = cleanList(input.contextSnippets, 5, 360);
2812
+ var similarCaseSelection = Array.isArray(input.similarCaseHints)
2813
+ ? selectResolveIOSupportSimilarCaseHints({
2814
+ candidates: input.similarCaseHints,
2815
+ issueClass: diagnosisGate === null || diagnosisGate === void 0 ? void 0 : diagnosisGate.issue_class,
2816
+ ownerFiles: diagnosisGate === null || diagnosisGate === void 0 ? void 0 : diagnosisGate.owner_files,
2817
+ text: input.bundle.supportV5ScopeDigest || input.bundle.supportV5SupervisorState.currentGoal,
2818
+ limit: 5
2819
+ })
2820
+ : input.similarCaseHints && typeof input.similarCaseHints === 'object'
2821
+ ? {
2822
+ ranked: cleanObject(input.similarCaseHints).ranked || [],
2823
+ similarTickets: cleanObject(input.similarCaseHints).similarTickets || cleanObject(input.similarCaseHints).similar_tickets || [],
2824
+ similarCommits: cleanObject(input.similarCaseHints).similarCommits || cleanObject(input.similarCaseHints).similar_commits || [],
2825
+ ignoredCount: Number(cleanObject(input.similarCaseHints).ignoredCount || cleanObject(input.similarCaseHints).ignored_count || 0),
2826
+ generatedAt: cleanText(cleanObject(input.similarCaseHints).generatedAt || cleanObject(input.similarCaseHints).generated_at, 120)
2827
+ }
2828
+ : undefined;
2829
+ var similarCaseHintsText = ((_d = similarCaseSelection === null || similarCaseSelection === void 0 ? void 0 : similarCaseSelection.ranked) === null || _d === void 0 ? void 0 : _d.length)
2830
+ ? __spreadArray([
2831
+ 'Similar accepted fix hints. Advisory only: use these to prioritize inspection paths, not as proof.'
2832
+ ], __read(similarCaseSelection.ranked.slice(0, 5).map(function (hint, index) {
2833
+ var _a;
2834
+ return [
2835
+ "".concat(index + 1, ". source=").concat(hint.source || 'unknown', " score=").concat(Number(hint.score || 0)),
2836
+ hint.ticketNumber ? "ticket=".concat(hint.ticketNumber) : '',
2837
+ hint.commitSha ? "commit=".concat(hint.commitSha) : '',
2838
+ hint.issueClass ? "issue_class=".concat(hint.issueClass) : '',
2839
+ ((_a = hint.ownerFiles) === null || _a === void 0 ? void 0 : _a.length) ? "owner_files=".concat(hint.ownerFiles.slice(0, 4).join(', ')) : '',
2840
+ hint.reason ? "signals=".concat(hint.reason) : '',
2841
+ hint.title ? "title=".concat(cleanText(hint.title, 180)) : ''
2842
+ ].filter(Boolean).join(' | ');
2843
+ })), false).join('\n')
2844
+ : '';
1784
2845
  var qaRow = input.activeQaRow || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) === 'qa'
1785
2846
  ? input.activeQaRow || laneMemory.activeQaRow || input.bundle.supportV5SupervisorState.currentQaRow
1786
2847
  : undefined;
@@ -1822,11 +2883,15 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
1822
2883
  "Owner files: ".concat(diagnosisGate.owner_files.join(', ')),
1823
2884
  "Business proof required: ".concat(diagnosisGate.proof_plan.business_assertion),
1824
2885
  diagnosisGate.proof_plan.business_proof_contract
1825
- ? "Business proof contract: ".concat((_d = diagnosisGate.proof_plan.business_proof_contract) === null || _d === void 0 ? void 0 : _d.action_under_test, " -> ").concat((_e = diagnosisGate.proof_plan.business_proof_contract) === null || _e === void 0 ? void 0 : _e.expected_business_state_change, "; assertion ").concat((_f = diagnosisGate.proof_plan.business_proof_contract) === null || _f === void 0 ? void 0 : _f.data_or_dom_assertion)
2886
+ ? "Business proof contract: ".concat((_e = diagnosisGate.proof_plan.business_proof_contract) === null || _e === void 0 ? void 0 : _e.action_under_test, " -> ").concat((_f = diagnosisGate.proof_plan.business_proof_contract) === null || _f === void 0 ? void 0 : _f.expected_business_state_change, "; assertion ").concat((_g = diagnosisGate.proof_plan.business_proof_contract) === null || _g === void 0 ? void 0 : _g.data_or_dom_assertion)
1826
2887
  : '',
1827
2888
  "Before/action/after: ".concat(diagnosisGate.proof_plan.before || diagnosisGate.proof_plan.before_state_unavailable_reason, " -> ").concat(diagnosisGate.proof_plan.action, " -> ").concat(diagnosisGate.proof_plan.after)
1828
2889
  ].filter(Boolean).join('\n') : 'SupportDiagnosisGate is not valid. Park instead of editing product code.'
1829
2890
  },
2891
+ diagnosisActive && similarCaseHintsText ? {
2892
+ name: 'similar_accepted_fix_hints',
2893
+ text: similarCaseHintsText
2894
+ } : undefined,
1830
2895
  {
1831
2896
  name: 'scope_digest',
1832
2897
  text: cleanText(input.bundle.supportV5ScopeDigest || input.bundle.supportV5SupervisorState.approvedScope, 900)
@@ -1872,6 +2937,7 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
1872
2937
  'Launch Puppeteer from `PUPPETEER_EXECUTABLE_PATH || CHROME_BIN`, seed localStorage from `qa-artifacts/auth-bootstrap-storage-state.json`, then navigate to the active QA route.',
1873
2938
  'Before passing, confirm `qa-artifacts/auth-bootstrap-result.json` used the ticket reporter or named affected user when available. If it used generic admin/dev while `qa-live-data-seed-result.json.selected.qa_user_context` names a reporter/affected user, rerun auth as that user or return needs-fix.',
1874
2939
  'Drive the visible customer workflow for this one row. Capture a new customer-facing screenshot/caption and update `qa-artifacts/qa-coverage-matrix.json` with workflow, route, data id/name, assertion, screenshot path, caption, and pass/failed/blocked status.',
2940
+ 'Also write `qa-artifacts/aiqa-business-assertion.json` with one AIQaBusinessAssertion object: assertion, status, workflow, route, before, action, expected, after/observed, dataProof or mongoDelta, artifactPaths, and metadata.supportDiagnosisProof=true only when it maps to the active SupportDiagnosisGate proof_plan.',
1875
2941
  'After selecting any From/To, source/target, dropdown, combobox, rio-select, filter, item, customer, yard, chemical, or treatment-plan value, read the visible control text back from the DOM. If it still contains placeholder text such as Select Chemical, Select Yard, Select Customer, Select Treatment Plan Type, Loading..., empty, null, or undefined, do not click the action button and do not mark pass. Capture the selected-state blocker screenshot/DOM text and return needs-fix.',
1876
2942
  'If the row needs persisted data proof, create that proof yourself in the same bounded QA command by querying localhost QA Mongo through process.env.MONGO_URL after the visible UI action. Write the result under qa-artifacts/ as JSON and reference that path in the matrix. Do not ask for a missing post-action DB artifact while the local QA stack is running; either write it or return needs-fix with the exact query/script error. For update-interchangeables rows, the JSON must include concrete non-placeholder fromText and toText, before/after counts or sample documents for treatment plans and tank/interchangeable records, and must fail if either selected value is only an arrow/placeholder or if the persisted collections are empty.',
1877
2943
  'If the row names multiple concrete entities such as assets, units, BOLs, invoices, chemicals, customers, yards, or numbered records, prove every named entity. Do not pass by proving only one representative record.',
@@ -1891,7 +2957,7 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
1891
2957
  ? 'Return strict JSON only: {"status":"pass"|"needs-fix"|"blocked","microtaskId":"","summary":"","evidence":[""],"artifacts":[""],"next_actions":[""]}.'
1892
2958
  : 'Return concise Markdown with: Microtask Result, Root Cause, Changes, Self-Gate, Acceptance Proof, Residual Risk.'
1893
2959
  }
1894
- ].filter(function (section) { return cleanText(section.text, 20); });
2960
+ ].filter(function (section) { return !!section && !!cleanText(section.text, 20); });
1895
2961
  var promptSections = sections.map(function (section) { return ({
1896
2962
  name: section.name,
1897
2963
  tokenEstimate: estimateTextTokens(section.text)
@@ -1915,7 +2981,7 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
1915
2981
  };
1916
2982
  }
1917
2983
  function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
1918
- var e_5, _a, e_6, _b;
2984
+ var e_8, _a, e_9, _b;
1919
2985
  var byMicrotask = new Map();
1920
2986
  var bySection = new Map();
1921
2987
  var totalPromptTokenEstimate = 0;
@@ -1930,17 +2996,17 @@ function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
1930
2996
  existing.calls += 1;
1931
2997
  byMicrotask.set(usage.microtaskId, existing);
1932
2998
  try {
1933
- for (var _e = (e_6 = void 0, __values(usage.promptSections || [])), _f = _e.next(); !_f.done; _f = _e.next()) {
2999
+ for (var _e = (e_9 = void 0, __values(usage.promptSections || [])), _f = _e.next(); !_f.done; _f = _e.next()) {
1934
3000
  var section = _f.value;
1935
3001
  bySection.set(section.name, (bySection.get(section.name) || 0) + section.tokenEstimate);
1936
3002
  }
1937
3003
  }
1938
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
3004
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
1939
3005
  finally {
1940
3006
  try {
1941
3007
  if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
1942
3008
  }
1943
- finally { if (e_6) throw e_6.error; }
3009
+ finally { if (e_9) throw e_9.error; }
1944
3010
  }
1945
3011
  var hardCap = usage.lane === 'qa' ? promptBudget.qaMicrotaskHardCap : promptBudget.buildMicrotaskHardCap;
1946
3012
  if ((usage.promptTokenEstimate || 0) > hardCap) {
@@ -1948,12 +3014,12 @@ function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
1948
3014
  }
1949
3015
  }
1950
3016
  }
1951
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
3017
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
1952
3018
  finally {
1953
3019
  try {
1954
3020
  if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
1955
3021
  }
1956
- finally { if (e_5) throw e_5.error; }
3022
+ finally { if (e_8) throw e_8.error; }
1957
3023
  }
1958
3024
  return {
1959
3025
  totalPromptTokenEstimate: totalPromptTokenEstimate,