@resolveio/server-lib 22.3.127 → 22.3.128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,6 +47,14 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
47
47
  return to.concat(ar || Array.prototype.slice.call(from));
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.normalizeResolveIOSupportDiagnosisGate = normalizeResolveIOSupportDiagnosisGate;
51
+ exports.extractResolveIOSupportDiagnosisGateFromText = extractResolveIOSupportDiagnosisGateFromText;
52
+ exports.validateResolveIOSupportDiagnosisGate = validateResolveIOSupportDiagnosisGate;
53
+ exports.buildResolveIOSupportIssueClassProbes = buildResolveIOSupportIssueClassProbes;
54
+ exports.hashResolveIOSupportV5Evidence = hashResolveIOSupportV5Evidence;
55
+ exports.decideResolveIOSupportV5RepeatedFailureStop = decideResolveIOSupportV5RepeatedFailureStop;
56
+ exports.changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles = changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles;
57
+ exports.applyResolveIOSupportDiagnosisGateToMicrotasks = applyResolveIOSupportDiagnosisGateToMicrotasks;
50
58
  exports.fingerprintResolveIOSupportV5Blocker = fingerprintResolveIOSupportV5Blocker;
51
59
  exports.buildResolveIOSupportV5Budget = buildResolveIOSupportV5Budget;
52
60
  exports.buildResolveIOSupportV5PromptBudget = buildResolveIOSupportV5PromptBudget;
@@ -104,6 +112,470 @@ function cleanList(values, limit, max) {
104
112
  }
105
113
  return result;
106
114
  }
115
+ function cleanObject(value) {
116
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
117
+ }
118
+ function pickText(source, fields, max) {
119
+ var e_2, _a;
120
+ if (max === void 0) { max = 1000; }
121
+ try {
122
+ for (var fields_1 = __values(fields), fields_1_1 = fields_1.next(); !fields_1_1.done; fields_1_1 = fields_1.next()) {
123
+ var field = fields_1_1.value;
124
+ var normalized = cleanText(source === null || source === void 0 ? void 0 : source[field], max);
125
+ if (normalized) {
126
+ return normalized;
127
+ }
128
+ }
129
+ }
130
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
131
+ finally {
132
+ try {
133
+ if (fields_1_1 && !fields_1_1.done && (_a = fields_1.return)) _a.call(fields_1);
134
+ }
135
+ finally { if (e_2) throw e_2.error; }
136
+ }
137
+ return '';
138
+ }
139
+ function normalizeIssueClass(value) {
140
+ var normalized = cleanText(value, 80)
141
+ .toLowerCase()
142
+ .replace(/[\s-]+/g, '_');
143
+ var aliases = {
144
+ no_op: 'no_op_submit',
145
+ no_op_submit: 'no_op_submit',
146
+ submit_noop: 'no_op_submit',
147
+ missing_data: 'missing_wrong_data',
148
+ wrong_data: 'missing_wrong_data',
149
+ missing_wrong_data: 'missing_wrong_data',
150
+ filter_mismatch: 'filter_query_mismatch',
151
+ query_mismatch: 'filter_query_mismatch',
152
+ filter_query_mismatch: 'filter_query_mismatch',
153
+ invoice_pdf_export: 'invoice_pdf_export',
154
+ pdf_export: 'invoice_pdf_export',
155
+ export: 'invoice_pdf_export',
156
+ upload: 'upload_import',
157
+ import: 'upload_import',
158
+ upload_import: 'upload_import',
159
+ route_auth: 'route_auth_hydration',
160
+ auth_hydration: 'route_auth_hydration',
161
+ route_auth_hydration: 'route_auth_hydration',
162
+ slow_query: 'slow_query_performance',
163
+ performance: 'slow_query_performance',
164
+ slow_query_performance: 'slow_query_performance'
165
+ };
166
+ return aliases[normalized] || '';
167
+ }
168
+ function normalizeReproductionStatus(value) {
169
+ var normalized = cleanText(value, 80).toLowerCase();
170
+ if (/blocked|unable|cannot/.test(normalized)) {
171
+ return 'blocked';
172
+ }
173
+ if (/classif|triag|inferred/.test(normalized)) {
174
+ return 'classified';
175
+ }
176
+ return 'reproduced';
177
+ }
178
+ function normalizeSupportDiagnosisEvidence(values) {
179
+ var allowed = new Set(['ticket', 'browser', 'mongo', 'log', 'code', 'commit', 'qa', 'other']);
180
+ if (!Array.isArray(values)) {
181
+ var summary = cleanText(values, 1200);
182
+ return summary ? [{ type: 'other', summary: summary }] : [];
183
+ }
184
+ var stringEvidence = values
185
+ .filter(function (entry) { return typeof entry === 'string'; })
186
+ .map(function (entry) { return ({
187
+ type: 'other',
188
+ summary: cleanText(entry, 1200)
189
+ }); });
190
+ var objectEvidence = values
191
+ .filter(function (entry) { return entry && typeof entry === 'object' && !Array.isArray(entry); })
192
+ .map(function (entry) {
193
+ var type = cleanText(entry.type, 80).toLowerCase();
194
+ return {
195
+ type: (allowed.has(type) ? type : 'other'),
196
+ summary: cleanText(entry.summary || entry.message || entry.evidence || entry.reason, 1200),
197
+ artifactPath: cleanText(entry.artifactPath || entry.path || entry.file, 500)
198
+ };
199
+ });
200
+ return stringEvidence.concat(objectEvidence)
201
+ .filter(function (entry) { return entry.summary; })
202
+ .slice(0, 20);
203
+ }
204
+ function normalizeSupportDiagnosisHints(values) {
205
+ return (Array.isArray(values) ? values : [])
206
+ .map(function (entry) {
207
+ if (typeof entry === 'string') {
208
+ return { reason: cleanText(entry, 500) };
209
+ }
210
+ var value = cleanObject(entry);
211
+ return {
212
+ id: cleanText(value.id || value._id, 160),
213
+ ticketNumber: cleanText(value.ticketNumber || value.ticket_number || value.sourceTicketNumber, 80),
214
+ title: cleanText(value.title || value.summary, 300),
215
+ outcome: cleanText(value.outcome || value.status, 80),
216
+ issueClass: cleanText(value.issueClass || value.issue_class, 80),
217
+ ownerFiles: cleanList(value.ownerFiles || value.owner_files || value.files, 8, 240),
218
+ commitSha: cleanText(value.commitSha || value.commit_sha, 80),
219
+ commitMessage: cleanText(value.commitMessage || value.commit_message, 300),
220
+ reason: cleanText(value.reason || value.matchReason || value.match_reason, 500)
221
+ };
222
+ })
223
+ .filter(function (entry) { return entry.id || entry.ticketNumber || entry.title || entry.reason || entry.commitSha; })
224
+ .slice(0, 8);
225
+ }
226
+ function normalizeOwnerFilePath(value) {
227
+ return cleanText(value, 500)
228
+ .replace(/\\/g, '/')
229
+ .replace(/^\.\/+/, '')
230
+ .replace(/^\/+/, '')
231
+ .replace(/\s+$/g, '');
232
+ }
233
+ function ownerFileLooksBroad(value) {
234
+ var normalized = normalizeOwnerFilePath(value);
235
+ return !normalized
236
+ || normalized.includes('*')
237
+ || normalized.endsWith('/')
238
+ || !/\.[a-z0-9]+$/i.test(normalized)
239
+ || /^(\.|src|server|angular|client|app|lib|packages?)$/i.test(normalized)
240
+ || /(^|\/)(node_modules|dist|build|coverage|\.git)(\/|$)/i.test(normalized);
241
+ }
242
+ function normalizeResolveIOSupportDiagnosisGate(value, now) {
243
+ var source = cleanObject(value);
244
+ if (!Object.keys(source).length) {
245
+ return undefined;
246
+ }
247
+ var issueCaseSource = cleanObject(source.issue_case || source.issueCase);
248
+ var hypothesisSource = cleanObject(source.accepted_hypothesis || source.acceptedHypothesis);
249
+ var failingPathSource = cleanObject(source.failing_path || source.failingPath);
250
+ var proofPlanSource = cleanObject(source.proof_plan || source.proofPlan);
251
+ var issueClass = normalizeIssueClass(source.issue_class || source.issueClass);
252
+ var ownerFiles = cleanList(source.owner_files || source.ownerFiles, 20, 500)
253
+ .map(normalizeOwnerFilePath)
254
+ .filter(Boolean);
255
+ var gate = {
256
+ issue_case: {
257
+ customer_complaint: pickText(issueCaseSource, ['customer_complaint', 'customerComplaint', 'complaint', 'request', 'summary'], 1200),
258
+ expected_result: pickText(issueCaseSource, ['expected_result', 'expectedResult', 'expected'], 1000),
259
+ observed_result: pickText(issueCaseSource, ['observed_result', 'observedResult', 'observed', 'actual'], 1000),
260
+ route_module: pickText(issueCaseSource, ['route_module', 'routeModule', 'route', 'module', 'screen'], 500),
261
+ account_customer_context: pickText(issueCaseSource, ['account_customer_context', 'accountCustomerContext', 'account', 'customer', 'user', 'context'], 800),
262
+ reproduction_status: normalizeReproductionStatus(issueCaseSource.reproduction_status || issueCaseSource.reproductionStatus || source.reproduction_status),
263
+ reproduction_blocker: pickText(issueCaseSource, ['reproduction_blocker', 'reproductionBlocker', 'blocked_reason', 'blockedReason'], 1000)
264
+ },
265
+ issue_class: issueClass || 'missing_wrong_data',
266
+ accepted_hypothesis: {
267
+ statement: pickText(hypothesisSource, ['statement', 'hypothesis', 'root_cause', 'rootCause', 'summary'], 1200)
268
+ || (typeof source.accepted_hypothesis === 'string' ? cleanText(source.accepted_hypothesis, 1200) : ''),
269
+ falsifiable_test: pickText(hypothesisSource, ['falsifiable_test', 'falsifiableTest', 'test', 'proof', 'falsifier'], 1000),
270
+ evidence: cleanList(hypothesisSource.evidence || source.hypothesis_evidence || source.hypothesisEvidence, 10, 800)
271
+ },
272
+ rejected_alternatives: cleanList(source.rejected_alternatives || source.rejectedAlternatives, 10, 700),
273
+ failing_path: {
274
+ frontend: pickText(failingPathSource, ['frontend', 'frontend_path', 'frontendPath', 'eventPath'], 700),
275
+ backend: pickText(failingPathSource, ['backend', 'backend_path', 'backendPath', 'methodPublicationPath'], 700),
276
+ shared_library: pickText(failingPathSource, ['shared_library', 'sharedLibrary', 'library', 'lib'], 700),
277
+ data_query: pickText(failingPathSource, ['data_query', 'dataQuery', 'query'], 700),
278
+ description: pickText(failingPathSource, ['description', 'path', 'summary'], 1200) || cleanText(source.failing_path, 1200)
279
+ },
280
+ owner_files: ownerFiles,
281
+ proof_plan: {
282
+ before: pickText(proofPlanSource, ['before', 'before_state', 'beforeState', 'precondition'], 1000),
283
+ before_state_unavailable_reason: pickText(proofPlanSource, ['before_state_unavailable_reason', 'beforeStateUnavailableReason', 'before_unavailable_reason'], 1000),
284
+ action: pickText(proofPlanSource, ['action', 'browser_action', 'browserAction', 'steps'], 1000),
285
+ after: pickText(proofPlanSource, ['after', 'after_state', 'afterState', 'expected_after'], 1000),
286
+ business_assertion: pickText(proofPlanSource, ['business_assertion', 'businessAssertion', 'assertion'], 1000),
287
+ route: pickText(proofPlanSource, ['route', 'url'], 500),
288
+ data_assertion: pickText(proofPlanSource, ['data_assertion', 'dataAssertion', 'mongo_delta', 'mongoDelta'], 1000),
289
+ artifact_expectation: pickText(proofPlanSource, ['artifact_expectation', 'artifactExpectation', 'artifact', 'screenshot'], 1000)
290
+ },
291
+ similar_tickets: normalizeSupportDiagnosisHints(source.similar_tickets || source.similarTickets),
292
+ similar_commits: normalizeSupportDiagnosisHints(source.similar_commits || source.similarCommits),
293
+ evidence: normalizeSupportDiagnosisEvidence(source.evidence),
294
+ status: cleanText(source.status, 80).toLowerCase() || 'incomplete',
295
+ updatedAt: isoNow(now || source.updatedAt || source.updated_at)
296
+ };
297
+ if (!['missing', 'incomplete', 'blocked', 'passed'].includes(gate.status)) {
298
+ gate.status = 'incomplete';
299
+ }
300
+ return gate;
301
+ }
302
+ function extractResolveIOSupportDiagnosisGateFromText(value, now) {
303
+ var e_3, _a;
304
+ var text = String(value || '').trim();
305
+ if (!text) {
306
+ return undefined;
307
+ }
308
+ var candidates = [];
309
+ var fenced = Array.from(text.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi)).map(function (match) { return match[1]; });
310
+ candidates.push.apply(candidates, __spreadArray([], __read(fenced), false));
311
+ var jsonBlock = text.match(/\{[\s\S]*\}/);
312
+ if (jsonBlock) {
313
+ candidates.push(jsonBlock[0]);
314
+ }
315
+ try {
316
+ for (var candidates_1 = __values(candidates), candidates_1_1 = candidates_1.next(); !candidates_1_1.done; candidates_1_1 = candidates_1.next()) {
317
+ var candidate = candidates_1_1.value;
318
+ try {
319
+ var parsed = JSON.parse(candidate);
320
+ var wrapped = (parsed === null || parsed === void 0 ? void 0 : parsed.support_diagnosis_gate) || (parsed === null || parsed === void 0 ? void 0 : parsed.supportDiagnosisGate) || (parsed === null || parsed === void 0 ? void 0 : parsed.diagnosis_gate) || (parsed === null || parsed === void 0 ? void 0 : parsed.diagnosisGate) || parsed;
321
+ var normalized = normalizeResolveIOSupportDiagnosisGate(wrapped, now);
322
+ if (normalized) {
323
+ return normalized;
324
+ }
325
+ }
326
+ catch (_b) {
327
+ // Continue trying less exact JSON candidates.
328
+ }
329
+ }
330
+ }
331
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
332
+ finally {
333
+ try {
334
+ if (candidates_1_1 && !candidates_1_1.done && (_a = candidates_1.return)) _a.call(candidates_1);
335
+ }
336
+ finally { if (e_3) throw e_3.error; }
337
+ }
338
+ return undefined;
339
+ }
340
+ function validateResolveIOSupportDiagnosisGate(value, options) {
341
+ if (options === void 0) { options = {}; }
342
+ var normalized = normalizeResolveIOSupportDiagnosisGate(value);
343
+ if (!normalized) {
344
+ return { valid: false, status: 'missing', blockers: ['SupportDiagnosisGate is missing.'] };
345
+ }
346
+ var blockers = [];
347
+ var maxOwnerFiles = Math.max(1, Number(options.maxOwnerFiles || 8) || 8);
348
+ if (!normalized.issue_case.customer_complaint) {
349
+ blockers.push('Diagnosis issue_case.customer_complaint is required.');
350
+ }
351
+ if (!normalized.issue_case.expected_result) {
352
+ blockers.push('Diagnosis issue_case.expected_result is required.');
353
+ }
354
+ if (!normalized.issue_case.observed_result) {
355
+ blockers.push('Diagnosis issue_case.observed_result is required.');
356
+ }
357
+ if (!normalized.issue_case.route_module) {
358
+ blockers.push('Diagnosis issue_case.route_module is required.');
359
+ }
360
+ if (!normalized.issue_case.account_customer_context) {
361
+ blockers.push('Diagnosis issue_case.account_customer_context is required.');
362
+ }
363
+ if (normalized.issue_case.reproduction_status === 'blocked' && !normalized.issue_case.reproduction_blocker) {
364
+ blockers.push('Diagnosis blocked reproduction requires issue_case.reproduction_blocker.');
365
+ }
366
+ if (!normalizeIssueClass(normalized.issue_class)) {
367
+ blockers.push('Diagnosis issue_class must be one of the supported issue-class probes.');
368
+ }
369
+ if (!normalized.accepted_hypothesis.statement) {
370
+ blockers.push('Diagnosis accepted_hypothesis.statement is required.');
371
+ }
372
+ if (!normalized.accepted_hypothesis.falsifiable_test) {
373
+ blockers.push('Diagnosis accepted_hypothesis.falsifiable_test is required.');
374
+ }
375
+ if (!normalized.accepted_hypothesis.evidence.length) {
376
+ blockers.push('Diagnosis accepted_hypothesis.evidence must cite proof.');
377
+ }
378
+ if (!normalized.rejected_alternatives.length) {
379
+ blockers.push('Diagnosis rejected_alternatives must include at least one rejected theory.');
380
+ }
381
+ if (!normalized.failing_path.description
382
+ && !normalized.failing_path.frontend
383
+ && !normalized.failing_path.backend
384
+ && !normalized.failing_path.shared_library
385
+ && !normalized.failing_path.data_query) {
386
+ blockers.push('Diagnosis failing_path must identify frontend, backend, query, shared library, or path description.');
387
+ }
388
+ if (!normalized.owner_files.length) {
389
+ blockers.push('Diagnosis owner_files must contain the small editable file set.');
390
+ }
391
+ if (normalized.owner_files.length > maxOwnerFiles) {
392
+ blockers.push("Diagnosis owner_files has ".concat(normalized.owner_files.length, " entries; maximum is ").concat(maxOwnerFiles, "."));
393
+ }
394
+ var broadFiles = normalized.owner_files.filter(ownerFileLooksBroad);
395
+ if (broadFiles.length) {
396
+ blockers.push("Diagnosis owner_files contains broad or unsafe path(s): ".concat(broadFiles.join(', '), "."));
397
+ }
398
+ if (!normalized.proof_plan.before && !normalized.proof_plan.before_state_unavailable_reason) {
399
+ blockers.push('Diagnosis proof_plan.before is required unless proof_plan.before_state_unavailable_reason explains why before-state proof is impossible.');
400
+ }
401
+ if (!normalized.proof_plan.action) {
402
+ blockers.push('Diagnosis proof_plan.action is required.');
403
+ }
404
+ if (!normalized.proof_plan.after) {
405
+ blockers.push('Diagnosis proof_plan.after is required.');
406
+ }
407
+ if (!normalized.proof_plan.business_assertion) {
408
+ blockers.push('Diagnosis proof_plan.business_assertion is required.');
409
+ }
410
+ if (!normalized.evidence.length) {
411
+ blockers.push('Diagnosis evidence must include ticket/code/browser/log/Mongo proof.');
412
+ }
413
+ normalized.status = blockers.length ? 'incomplete' : 'passed';
414
+ return {
415
+ valid: blockers.length === 0,
416
+ status: normalized.status,
417
+ blockers: blockers,
418
+ normalized: normalized
419
+ };
420
+ }
421
+ function buildResolveIOSupportIssueClassProbes(value) {
422
+ var validation = validateResolveIOSupportDiagnosisGate(value);
423
+ var gate = validation.normalized || normalizeResolveIOSupportDiagnosisGate(value);
424
+ if (!gate) {
425
+ return [];
426
+ }
427
+ var route = gate.proof_plan.route || gate.issue_case.route_module;
428
+ var proof = gate.proof_plan.business_assertion;
429
+ var common = {
430
+ issue_class: gate.issue_class,
431
+ probe_type: 'issue_class_probe',
432
+ route: route,
433
+ blocks_acceptance_without_business_assertion: true
434
+ };
435
+ var map = {
436
+ no_op_submit: {
437
+ action: "Submit the customer action on ".concat(route || 'the affected screen', " and assert a persisted state change or explicit validation message."),
438
+ expected: proof || 'Before/action/after proof shows the submit is no longer a no-op.'
439
+ },
440
+ missing_wrong_data: {
441
+ action: "Load the named record/list on ".concat(route || 'the affected route', " and compare visible data to the expected persisted source."),
442
+ expected: proof || 'Visible data and persisted data match the customer expectation.'
443
+ },
444
+ filter_query_mismatch: {
445
+ action: 'Apply the reported filter/query inputs and assert the returned rows/counts match the expected dataset.',
446
+ expected: proof || 'Filter/query output contains the correct included rows and excludes the wrong rows.'
447
+ },
448
+ invoice_pdf_export: {
449
+ action: 'Generate the invoice/PDF/export from the affected route and inspect the downloaded/generated artifact.',
450
+ expected: proof || 'Generated artifact contains the expected customer-visible rows, totals, or fields.'
451
+ },
452
+ upload_import: {
453
+ action: 'Run the upload/import workflow with a representative file and assert parsed plus persisted results.',
454
+ expected: proof || 'Import shows a success result and persisted rows/counts changed as expected.'
455
+ },
456
+ route_auth_hydration: {
457
+ action: 'Open the route as the affected user and assert authenticated hydration reaches the functional screen, not a shell.',
458
+ expected: proof || 'Route hydrates with the required controls/data for the affected account.'
459
+ },
460
+ slow_query_performance: {
461
+ action: 'Run the reported query/workflow with timing/log evidence before and after the fix.',
462
+ expected: proof || 'Performance evidence shows the slow path improved without changing results.'
463
+ }
464
+ };
465
+ var selected = map[gate.issue_class];
466
+ return [__assign(__assign({}, common), { objective: "Issue-class probe for ".concat(gate.issue_class, ": ").concat(gate.issue_case.customer_complaint), action: selected.action, expected_evidence: selected.expected })];
467
+ }
468
+ function hashResolveIOSupportV5Evidence(value) {
469
+ var raw = typeof value === 'string' ? value : JSON.stringify(value || {});
470
+ var normalized = cleanText(raw, 8000)
471
+ .toLowerCase()
472
+ .replace(/[a-f0-9]{16,}/g, '<id>')
473
+ .replace(/\b\d{2,}\b/g, '<n>');
474
+ var hash = 0;
475
+ for (var index = 0; index < normalized.length; index += 1) {
476
+ hash = ((hash << 5) - hash + normalized.charCodeAt(index)) | 0;
477
+ }
478
+ return "ev-".concat(Math.abs(hash).toString(36) || '0');
479
+ }
480
+ function decideResolveIOSupportV5RepeatedFailureStop(input) {
481
+ var failureClass = cleanText(input.failureClass || 'unknown', 80).toLowerCase();
482
+ var blockerFingerprint = fingerprintResolveIOSupportV5Blocker(input.blocker || '');
483
+ var evidenceHash = cleanText(input.evidenceHash, 80) || hashResolveIOSupportV5Evidence(input.evidence || input.blocker || '');
484
+ var limit = Math.max(1, Number(input.limit || 2) || 2);
485
+ if (input.ignoreInfra !== false && /^(infra|compile)$/.test(failureClass)) {
486
+ return {
487
+ shouldStop: false,
488
+ repeatedCount: 0,
489
+ failureClass: failureClass,
490
+ blockerFingerprint: blockerFingerprint,
491
+ evidenceHash: evidenceHash,
492
+ reason: 'support_v5_infra_failures_do_not_count_as_product_repair_loops'
493
+ };
494
+ }
495
+ var repeatedCount = 0;
496
+ for (var index = (input.history || []).length - 1; index >= 0; index -= 1) {
497
+ var item = input.history[index];
498
+ if (item.outcome === 'pass' || item.outcome === 'ready_for_merge') {
499
+ break;
500
+ }
501
+ var itemFailureClass = cleanText(item.failureClass || 'unknown', 80).toLowerCase();
502
+ var itemBlockerFingerprint = cleanText(item.blockerFingerprint, 80)
503
+ || fingerprintResolveIOSupportV5Blocker(item.blocker || item.summary || '');
504
+ var itemEvidenceHash = cleanText(item.evidenceHash, 80)
505
+ || hashResolveIOSupportV5Evidence(item.artifactPaths || item.blocker || item.summary || '');
506
+ if (itemFailureClass !== failureClass || itemBlockerFingerprint !== blockerFingerprint || itemEvidenceHash !== evidenceHash) {
507
+ break;
508
+ }
509
+ repeatedCount += 1;
510
+ }
511
+ return {
512
+ shouldStop: repeatedCount >= limit,
513
+ repeatedCount: repeatedCount,
514
+ failureClass: failureClass,
515
+ blockerFingerprint: blockerFingerprint,
516
+ evidenceHash: evidenceHash,
517
+ reason: repeatedCount >= limit
518
+ ? 'support_v5_same_failure_class_without_new_evidence'
519
+ : 'support_v5_retry_allowed_new_or_below_repeat_limit'
520
+ };
521
+ }
522
+ function changedFilesOutsideResolveIOSupportDiagnosisOwnerFiles(diagnosisGate, changedFiles, options) {
523
+ if (options === void 0) { options = {}; }
524
+ var validation = validateResolveIOSupportDiagnosisGate(diagnosisGate);
525
+ if (!validation.valid || !validation.normalized) {
526
+ return cleanList(changedFiles, 80, 500);
527
+ }
528
+ var owners = new Set(validation.normalized.owner_files.map(normalizeOwnerFilePath));
529
+ return cleanList(changedFiles, 120, 500)
530
+ .map(normalizeOwnerFilePath)
531
+ .filter(function (filePath) {
532
+ if (!filePath) {
533
+ return false;
534
+ }
535
+ if (owners.has(filePath)) {
536
+ return false;
537
+ }
538
+ if (options.allowTests && /(^|\/)(tests?|spec|__tests__)(\/|$)|\.(?:spec|test)\.[jt]sx?$/i.test(filePath)) {
539
+ return false;
540
+ }
541
+ return true;
542
+ });
543
+ }
544
+ function applyResolveIOSupportDiagnosisGateToMicrotasks(bundle, diagnosisGate) {
545
+ var validation = validateResolveIOSupportDiagnosisGate(diagnosisGate);
546
+ var gate = validation.normalized;
547
+ if (!gate) {
548
+ return bundle;
549
+ }
550
+ var ownerFiles = gate.owner_files;
551
+ var probes = buildResolveIOSupportIssueClassProbes(gate);
552
+ var now = isoNow();
553
+ var ledger = (bundle.supportV5MicrotaskLedger || []).map(function (task) {
554
+ var _a;
555
+ if (task.type === 'diagnosis_gate') {
556
+ return __assign(__assign({}, task), { status: validation.valid ? 'pass' : 'needs_repair', blocker: validation.valid ? '' : validation.blockers.join(' | '), updatedAt: now });
557
+ }
558
+ if (task.lane === 'build' && /repair|product_repair|build_repair/i.test(String(task.type || ''))) {
559
+ return __assign(__assign({}, task), { targetFiles: ownerFiles, contextRefs: Array.from(new Set(__spreadArray(__spreadArray([], __read((task.contextRefs || [])), false), ['supportV5DiagnosisGate', 'owner_files'], false))), selfGate: "Repair only the diagnosed owner files unless you revise the diagnosis with new evidence. Owner files: ".concat(ownerFiles.join(', '), "."), acceptanceProof: gate.proof_plan.business_assertion, dependsOn: Array.from(new Set(__spreadArray(__spreadArray([], __read((task.dependsOn || [])), false), [stableIdFromText('diagnosis', bundle.supportV5ScopeDigest || gate.issue_case.customer_complaint)], false))), updatedAt: now });
560
+ }
561
+ if (task.lane === 'qa' && task.type === 'qa_row') {
562
+ return __assign(__assign({}, task), { contextRefs: Array.from(new Set(__spreadArray(__spreadArray([], __read((task.contextRefs || [])), false), ['supportV5DiagnosisGate', 'proof_plan'], false))), selfGate: ((_a = probes[0]) === null || _a === void 0 ? void 0 : _a.action) || task.selfGate, acceptanceProof: gate.proof_plan.business_assertion || task.acceptanceProof, targetFiles: ownerFiles, updatedAt: now });
563
+ }
564
+ return task;
565
+ });
566
+ var nextActive = selectResolveIOSupportV5ActiveMicrotask(ledger, bundle.supportV5ActiveMicrotaskId);
567
+ return __assign(__assign({}, bundle), { supportV5DiagnosisGate: gate, supportV5MicrotaskLedger: ledger, supportV5ActiveMicrotaskId: nextActive === null || nextActive === void 0 ? void 0 : nextActive.microtaskId, supportV5LaneMemory: __assign(__assign({}, bundle.supportV5LaneMemory), { build: __assign(__assign({}, bundle.supportV5LaneMemory.build), { changedFiles: ownerFiles, scopeSummary: [
568
+ bundle.supportV5LaneMemory.build.scopeSummary,
569
+ "Diagnosis issue class: ".concat(gate.issue_class),
570
+ "Accepted hypothesis: ".concat(gate.accepted_hypothesis.statement),
571
+ "Owner files: ".concat(ownerFiles.join(', '))
572
+ ].filter(Boolean).join(' | '), updatedAt: now }), qa: __assign(__assign({}, bundle.supportV5LaneMemory.qa), { activeQaRow: {
573
+ workflow: gate.proof_plan.business_assertion,
574
+ route: gate.proof_plan.route || gate.issue_case.route_module,
575
+ assertion: gate.proof_plan.after,
576
+ status: 'pending'
577
+ }, updatedAt: now }) }) });
578
+ }
107
579
  function stableIdFromText(prefix, value) {
108
580
  var text = cleanText(value, 2000).toLowerCase();
109
581
  var hash = 0;
@@ -166,7 +638,7 @@ function buildResolveIOSupportV5ScopeDigest(input) {
166
638
  return raw.slice(0, maxChars);
167
639
  }
168
640
  function buildResolveIOSupportV5MicrotaskLedger(input) {
169
- var e_2, _a;
641
+ var e_4, _a;
170
642
  var existing = Array.isArray(input.existing) ? input.existing : [];
171
643
  var completedByObjective = new Map();
172
644
  try {
@@ -177,12 +649,12 @@ function buildResolveIOSupportV5MicrotaskLedger(input) {
177
649
  }
178
650
  }
179
651
  }
180
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
652
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
181
653
  finally {
182
654
  try {
183
655
  if (existing_1_1 && !existing_1_1.done && (_a = existing_1.return)) _a.call(existing_1);
184
656
  }
185
- finally { if (e_2) throw e_2.error; }
657
+ finally { if (e_4) throw e_4.error; }
186
658
  }
187
659
  var now = isoNow(input.now);
188
660
  var requirements = cleanList(input.requirements, 30, 240);
@@ -190,6 +662,28 @@ function buildResolveIOSupportV5MicrotaskLedger(input) {
190
662
  ? requirements
191
663
  : cleanText(input.scopeDigest, 1000).split(/\s+\|\s+|\r?\n/g).map(function (line) { return line.trim(); }).filter(Boolean).slice(0, 12);
192
664
  var ledger = [];
665
+ var diagnosisObjective = "Root-cause-first diagnosis gate for: ".concat(cleanText(input.scopeDigest, 360) || 'support ticket');
666
+ var diagnosisId = stableIdFromText('diagnosis', diagnosisObjective);
667
+ var existingDiagnosis = existing.find(function (task) { return (task === null || task === void 0 ? void 0 : task.type) === 'diagnosis_gate' || (task === null || task === void 0 ? void 0 : task.microtaskId) === diagnosisId; });
668
+ ledger.push(existingDiagnosis && (existingDiagnosis.status === 'pass' || existingDiagnosis.status === 'parked') ? existingDiagnosis : {
669
+ microtaskId: diagnosisId,
670
+ lane: 'build',
671
+ type: 'diagnosis_gate',
672
+ status: (existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.status) || 'pending',
673
+ objective: diagnosisObjective,
674
+ targetFiles: [],
675
+ contextRefs: ['scope_digest', 'support_context', 'similar_tickets', 'similar_commits'],
676
+ selfGate: 'Read-only diagnosis only: reproduce or explicitly classify the issue, accept one falsifiable root-cause hypothesis, reject alternatives, identify the failing path, cap owner_files, and define before/action/after business proof.',
677
+ acceptanceProof: 'Valid ResolveIOSupportDiagnosisGate JSON with issue_case, issue_class, accepted_hypothesis, rejected_alternatives, failing_path, owner_files, proof_plan, evidence, and status=passed.',
678
+ threadKey: input.buildThreadKey,
679
+ promptTokenEstimate: existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.promptTokenEstimate,
680
+ attempts: (existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.attempts) || 0,
681
+ dependsOn: [],
682
+ parentScopeId: stableIdFromText('scope', diagnosisObjective),
683
+ blocker: existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.blocker,
684
+ createdAt: (existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.createdAt) || now,
685
+ updatedAt: (existingDiagnosis === null || existingDiagnosis === void 0 ? void 0 : existingDiagnosis.updatedAt) || now
686
+ });
193
687
  sourceRequirements.forEach(function (requirement, index) {
194
688
  var objective = cleanText(requirement, 240);
195
689
  if (!objective) {
@@ -210,7 +704,7 @@ function buildResolveIOSupportV5MicrotaskLedger(input) {
210
704
  acceptanceProof: 'Concrete code/data proof for this behavior, with changed files listed.',
211
705
  threadKey: input.buildThreadKey,
212
706
  attempts: 0,
213
- dependsOn: [],
707
+ dependsOn: [diagnosisId],
214
708
  parentScopeId: stableIdFromText('scope', objective),
215
709
  createdAt: now,
216
710
  updatedAt: now
@@ -227,7 +721,7 @@ function buildResolveIOSupportV5MicrotaskLedger(input) {
227
721
  acceptanceProof: 'QA matrix row pass with route/data assertion, screenshot/caption artifact, and persisted before/after row/count/value proof for data-changing workflows.',
228
722
  threadKey: input.qaThreadKey,
229
723
  attempts: 0,
230
- dependsOn: [buildId],
724
+ dependsOn: [diagnosisId, buildId],
231
725
  parentScopeId: stableIdFromText('scope', objective),
232
726
  createdAt: now,
233
727
  updatedAt: now
@@ -248,7 +742,7 @@ function selectResolveIOSupportV5ActiveMicrotask(ledger, preferredId) {
248
742
  || ledger.find(function (task) { return task.status === 'blocked'; });
249
743
  }
250
744
  function initializeResolveIOSupportV5State(input) {
251
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
745
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
252
746
  var now = isoNow(input.now);
253
747
  var existing = input.existing || {};
254
748
  var existingSupervisor = existing.supportV5SupervisorState;
@@ -260,6 +754,8 @@ function initializeResolveIOSupportV5State(input) {
260
754
  var buildThreadKey = cleanText(input.buildThreadKey || ((_a = existingLaneMemory.build) === null || _a === void 0 ? void 0 : _a.threadKey) || "support:".concat(input.ticketId || input.jobId, ":job:").concat(input.jobId, ":build"), 240);
261
755
  var qaThreadKey = cleanText(input.qaThreadKey || ((_b = existingLaneMemory.qa) === null || _b === void 0 ? void 0 : _b.threadKey) || "support:".concat(input.ticketId || input.jobId, ":job:").concat(input.jobId, ":qa"), 240);
262
756
  var budget = buildResolveIOSupportV5Budget(existing.supportV5Budget);
757
+ var existingDiagnosisGate = normalizeResolveIOSupportDiagnosisGate(existing.supportV5DiagnosisGate);
758
+ var diagnosisValidation = validateResolveIOSupportDiagnosisGate(existingDiagnosisGate);
263
759
  var scopeDigest = cleanText(existing.supportV5ScopeDigest, 4000) || buildResolveIOSupportV5ScopeDigest({
264
760
  goal: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.currentGoal) || "Resolve support ticket ".concat(ticketLabel),
265
761
  approvedScope: scope,
@@ -274,7 +770,7 @@ function initializeResolveIOSupportV5State(input) {
274
770
  existing: existing.supportV5MicrotaskLedger
275
771
  });
276
772
  var activeMicrotask = selectResolveIOSupportV5ActiveMicrotask(ledger, existing.supportV5ActiveMicrotaskId);
277
- return {
773
+ var initialized = {
278
774
  supportWorkflowVersion: 'v5',
279
775
  supportV5SupervisorState: {
280
776
  version: 'v5',
@@ -282,8 +778,8 @@ function initializeResolveIOSupportV5State(input) {
282
778
  currentGoal: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.currentGoal) || "Resolve support ticket ".concat(ticketLabel),
283
779
  approvedScope: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.approvedScope) || scope,
284
780
  prBranch: cleanText(input.prBranch || (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.prBranch) || '', 240),
285
- activeStep: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.activeStep) || 'compile_check',
286
- activeBlocker: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.activeBlocker) || '',
781
+ activeStep: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.activeStep) || (diagnosisValidation.valid ? 'compile_check' : 'diagnosis_gate'),
782
+ activeBlocker: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.activeBlocker) || (diagnosisValidation.valid ? '' : 'SupportDiagnosisGate required before product-code repair.'),
287
783
  lastGoodCheckpoint: (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.lastGoodCheckpoint) || 'v5_initialized',
288
784
  currentQaRow: existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.currentQaRow,
289
785
  processLease: input.processLease || (existingSupervisor === null || existingSupervisor === void 0 ? void 0 : existingSupervisor.processLease),
@@ -291,6 +787,7 @@ function initializeResolveIOSupportV5State(input) {
291
787
  noEmailUnlessApproved: true,
292
788
  updatedAt: now
293
789
  },
790
+ supportV5DiagnosisGate: diagnosisValidation.valid ? diagnosisValidation.normalized : existingDiagnosisGate,
294
791
  supportV5LaneMemory: {
295
792
  build: {
296
793
  lane: 'build',
@@ -325,24 +822,43 @@ function initializeResolveIOSupportV5State(input) {
325
822
  ? existing.supportV5RunnerIncidents.slice(-80)
326
823
  : [],
327
824
  supportV5MicrotaskLedger: ledger,
328
- supportV5ActiveMicrotaskId: activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.microtaskId,
825
+ supportV5ActiveMicrotaskId: diagnosisValidation.valid
826
+ ? activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.microtaskId
827
+ : ((_u = ledger.find(function (task) { return task.type === 'diagnosis_gate' && !['pass', 'parked'].includes(task.status); })) === null || _u === void 0 ? void 0 : _u.microtaskId) || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.microtaskId),
329
828
  supportV5ScopeDigest: scopeDigest,
330
829
  supportV5MicrotaskUsageHistory: Array.isArray(existing.supportV5MicrotaskUsageHistory)
331
830
  ? existing.supportV5MicrotaskUsageHistory.slice(-200)
831
+ : [],
832
+ supportV5FailureFingerprints: Array.isArray(existing.supportV5FailureFingerprints)
833
+ ? existing.supportV5FailureFingerprints.slice(-200)
332
834
  : []
333
835
  };
836
+ return diagnosisValidation.valid && diagnosisValidation.normalized
837
+ ? applyResolveIOSupportDiagnosisGateToMicrotasks(initialized, diagnosisValidation.normalized)
838
+ : initialized;
334
839
  }
335
840
  function recordResolveIOSupportV5Step(bundle, step) {
336
- var _a, _b;
841
+ var _a, _b, _c, _d;
337
842
  var now = isoNow(step.now);
338
843
  var promptTokens = Math.max(0, Number(step.promptTokenEstimate || 0) || 0);
339
844
  var runtimeMs = Math.max(0, Number(step.runtimeMs || 0) || 0);
340
845
  var microtaskId = cleanText(step.microtaskId || bundle.supportV5ActiveMicrotaskId, 160);
341
- var record = __assign(__assign({}, (microtaskId ? { microtaskId: microtaskId } : {})), { stepType: step.stepType, outcome: step.outcome, lane: step.lane, model: cleanText(step.model, 80), threadKey: cleanText(step.threadKey, 240), promptTokenEstimate: promptTokens || undefined, runtimeMs: runtimeMs || undefined, summary: cleanText(step.summary || step.blocker || step.outcome, 1200), blocker: cleanText(step.blocker, 1200), changedFiles: cleanList(step.changedFiles, 80, 500), artifactPaths: cleanList(step.artifactPaths, 80, 500), recordedAt: now });
846
+ var normalizedDiagnosisGate = step.diagnosisGate
847
+ ? normalizeResolveIOSupportDiagnosisGate(step.diagnosisGate, now)
848
+ : undefined;
849
+ var blockerFingerprint = step.blocker || step.summary
850
+ ? fingerprintResolveIOSupportV5Blocker(step.blocker || step.summary || '')
851
+ : undefined;
852
+ var evidenceHash = cleanText(step.evidenceHash, 80)
853
+ || (((_a = step.artifactPaths) === null || _a === void 0 ? void 0 : _a.length) || step.blocker || step.summary
854
+ ? hashResolveIOSupportV5Evidence(((_b = step.artifactPaths) === null || _b === void 0 ? void 0 : _b.length) ? step.artifactPaths : "".concat(step.blocker || '', "\n").concat(step.summary || ''))
855
+ : undefined);
856
+ var record = __assign(__assign({}, (microtaskId ? { microtaskId: microtaskId } : {})), { stepType: step.stepType, outcome: step.outcome, lane: step.lane, model: cleanText(step.model, 80), threadKey: cleanText(step.threadKey, 240), promptTokenEstimate: promptTokens || undefined, runtimeMs: runtimeMs || undefined, summary: cleanText(step.summary || step.blocker || step.outcome, 1200), blocker: cleanText(step.blocker, 1200), changedFiles: cleanList(step.changedFiles, 80, 500), artifactPaths: cleanList(step.artifactPaths, 80, 500), diagnosisGate: normalizedDiagnosisGate, failureClass: cleanText(step.failureClass, 80) || undefined, blockerFingerprint: blockerFingerprint, evidenceHash: evidenceHash, recordedAt: now });
857
+ var diagnosisGate = normalizedDiagnosisGate || bundle.supportV5DiagnosisGate;
342
858
  var laneMemory = __assign({}, bundle.supportV5LaneMemory);
343
859
  if (step.lane === 'build' || step.lane === 'qa') {
344
860
  var previous = laneMemory[step.lane];
345
- laneMemory[step.lane] = __assign(__assign({}, previous), { activeBlocker: record.blocker || previous.activeBlocker || '', activeQaRow: step.activeQaRow || previous.activeQaRow, changedFiles: ((_a = record.changedFiles) === null || _a === void 0 ? void 0 : _a.length) ? record.changedFiles : previous.changedFiles, artifactPaths: ((_b = record.artifactPaths) === null || _b === void 0 ? void 0 : _b.length) ? record.artifactPaths : previous.artifactPaths, latestPromptTokenEstimate: promptTokens || previous.latestPromptTokenEstimate, updatedAt: now });
861
+ laneMemory[step.lane] = __assign(__assign({}, previous), { activeBlocker: record.blocker || previous.activeBlocker || '', activeQaRow: step.activeQaRow || previous.activeQaRow, changedFiles: ((_c = record.changedFiles) === null || _c === void 0 ? void 0 : _c.length) ? record.changedFiles : previous.changedFiles, artifactPaths: ((_d = record.artifactPaths) === null || _d === void 0 ? void 0 : _d.length) ? record.artifactPaths : previous.artifactPaths, latestPromptTokenEstimate: promptTokens || previous.latestPromptTokenEstimate, updatedAt: now });
346
862
  }
347
863
  var supervisor = __assign(__assign({}, bundle.supportV5SupervisorState), { status: step.outcome === 'ready_for_merge' ? 'complete' : (step.outcome === 'park_manual' || step.outcome === 'budget_stop' ? 'parked' : 'active'), activeStep: step.stepType, activeBlocker: record.blocker || '', currentQaRow: step.activeQaRow || bundle.supportV5SupervisorState.currentQaRow, lastGoodCheckpoint: step.outcome === 'pass' || step.outcome === 'ready_for_merge'
348
864
  ? step.stepType
@@ -360,8 +876,23 @@ function recordResolveIOSupportV5Step(bundle, step) {
360
876
  : 'in_progress';
361
877
  return __assign(__assign({}, task), { status: status, blocker: record.blocker || task.blocker, promptTokenEstimate: promptTokens || task.promptTokenEstimate, attempts: task.attempts + (step.outcome === 'pass' || step.outcome === 'ready_for_merge' ? 0 : 1), updatedAt: now });
362
878
  });
879
+ var failureFingerprint = record.failureClass && record.blockerFingerprint && record.evidenceHash
880
+ ? {
881
+ stepType: record.stepType,
882
+ failureClass: cleanText(record.failureClass, 80),
883
+ blockerFingerprint: record.blockerFingerprint,
884
+ evidenceHash: record.evidenceHash,
885
+ recordedAt: now
886
+ }
887
+ : undefined;
363
888
  var nextMicrotask = selectResolveIOSupportV5ActiveMicrotask(ledger, bundle.supportV5ActiveMicrotaskId);
364
- return __assign(__assign({}, bundle), { supportV5SupervisorState: supervisor, supportV5LaneMemory: laneMemory, supportV5StepHistory: __spreadArray(__spreadArray([], __read(bundle.supportV5StepHistory), false), [record], false).slice(-100), supportV5Budget: __assign(__assign({}, bundle.supportV5Budget), { totalPromptTokenEstimate: bundle.supportV5Budget.totalPromptTokenEstimate + promptTokens, totalRuntimeMs: bundle.supportV5Budget.totalRuntimeMs + runtimeMs, loopCount: bundle.supportV5Budget.loopCount + 1 }), supportV5MicrotaskLedger: ledger, supportV5ActiveMicrotaskId: nextMicrotask === null || nextMicrotask === void 0 ? void 0 : nextMicrotask.microtaskId, supportV5MicrotaskUsageHistory: bundle.supportV5MicrotaskUsageHistory || [] });
889
+ var nextBundle = __assign(__assign({}, bundle), { supportV5SupervisorState: supervisor, supportV5DiagnosisGate: diagnosisGate, supportV5LaneMemory: laneMemory, supportV5StepHistory: __spreadArray(__spreadArray([], __read(bundle.supportV5StepHistory), false), [record], false).slice(-100), supportV5Budget: __assign(__assign({}, bundle.supportV5Budget), { totalPromptTokenEstimate: bundle.supportV5Budget.totalPromptTokenEstimate + promptTokens, totalRuntimeMs: bundle.supportV5Budget.totalRuntimeMs + runtimeMs, loopCount: bundle.supportV5Budget.loopCount + 1 }), supportV5MicrotaskLedger: ledger, supportV5ActiveMicrotaskId: nextMicrotask === null || nextMicrotask === void 0 ? void 0 : nextMicrotask.microtaskId, supportV5MicrotaskUsageHistory: bundle.supportV5MicrotaskUsageHistory || [], supportV5FailureFingerprints: failureFingerprint
890
+ ? __spreadArray(__spreadArray([], __read((bundle.supportV5FailureFingerprints || [])), false), [failureFingerprint], false).slice(-200)
891
+ : (bundle.supportV5FailureFingerprints || []) });
892
+ if (normalizedDiagnosisGate && validateResolveIOSupportDiagnosisGate(normalizedDiagnosisGate).valid) {
893
+ return applyResolveIOSupportDiagnosisGateToMicrotasks(nextBundle, normalizedDiagnosisGate);
894
+ }
895
+ return nextBundle;
365
896
  }
366
897
  function recordResolveIOSupportV5MicrotaskUsage(bundle, usage) {
367
898
  var record = __assign(__assign({}, usage), { microtaskId: cleanText(usage.microtaskId, 160), threadKey: cleanText(usage.threadKey, 240), model: cleanText(usage.model, 80), promptTokenEstimate: Math.max(0, Number(usage.promptTokenEstimate || 0) || 0), promptSections: Array.isArray(usage.promptSections)
@@ -400,6 +931,14 @@ function decideResolveIOSupportV5Continuation(bundle) {
400
931
  }
401
932
  var budgetExceeded = budget.loopCount >= budget.maxLoopsPerTicket
402
933
  || ((last === null || last === void 0 ? void 0 : last.promptTokenEstimate) || 0) > budget.maxPromptTokensPerNonInitialStep;
934
+ var repeatedFailure = last ? decideResolveIOSupportV5RepeatedFailureStop({
935
+ history: history,
936
+ failureClass: last.failureClass,
937
+ blocker: last.blocker || last.summary,
938
+ evidenceHash: last.evidenceHash,
939
+ limit: budget.maxRepeatedNoProgress,
940
+ ignoreInfra: true
941
+ }) : null;
403
942
  if (budgetExceeded) {
404
943
  return {
405
944
  action: 'park',
@@ -418,6 +957,15 @@ function decideResolveIOSupportV5Continuation(bundle) {
418
957
  budgetExceeded: budgetExceeded
419
958
  };
420
959
  }
960
+ if (repeatedFailure === null || repeatedFailure === void 0 ? void 0 : repeatedFailure.shouldStop) {
961
+ return {
962
+ action: 'park',
963
+ reason: repeatedFailure.reason,
964
+ nextStep: (last === null || last === void 0 ? void 0 : last.stepType) || 'cleanup',
965
+ repeatedNoProgressCount: repeatedFailure.repeatedCount,
966
+ budgetExceeded: budgetExceeded
967
+ };
968
+ }
421
969
  return {
422
970
  action: 'continue',
423
971
  reason: 'support_v5_continue',
@@ -451,6 +999,9 @@ function buildResolveIOSupportV5DiagnoseFirstPrompt(lines) {
451
999
  function buildResolveIOSupportV5MicrotaskPrompt(input) {
452
1000
  var _a, _b, _c;
453
1001
  var activeMicrotask = selectResolveIOSupportV5ActiveMicrotask(input.bundle.supportV5MicrotaskLedger || [], input.bundle.supportV5ActiveMicrotaskId);
1002
+ var diagnosisValidation = validateResolveIOSupportDiagnosisGate(input.bundle.supportV5DiagnosisGate);
1003
+ var diagnosisGate = diagnosisValidation.normalized || input.bundle.supportV5DiagnosisGate;
1004
+ var diagnosisActive = (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.type) === 'diagnosis_gate';
454
1005
  var budget = buildResolveIOSupportV5PromptBudget();
455
1006
  var repairLike = Boolean(input.failureText || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.status) === 'needs_repair' || /repair/i.test(String(input.stage || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.type) || '')));
456
1007
  var cap = repairLike
@@ -469,7 +1020,9 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
469
1020
  var artifactPaths = cleanList(((_b = input.artifactPaths) === null || _b === void 0 ? void 0 : _b.length) ? input.artifactPaths : laneMemory.artifactPaths, 8, 200);
470
1021
  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);
471
1022
  var contextSnippets = cleanList(input.contextSnippets, 5, 360);
472
- var qaRow = input.activeQaRow || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) === 'qa' ? input.activeQaRow || laneMemory.activeQaRow || input.bundle.supportV5SupervisorState.currentQaRow : undefined;
1023
+ var qaRow = input.activeQaRow || (activeMicrotask === null || activeMicrotask === void 0 ? void 0 : activeMicrotask.lane) === 'qa'
1024
+ ? input.activeQaRow || laneMemory.activeQaRow || input.bundle.supportV5SupervisorState.currentQaRow
1025
+ : undefined;
473
1026
  var sections = [
474
1027
  {
475
1028
  name: 'microtask_contract',
@@ -479,11 +1032,33 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
479
1032
  'Do exactly one microtask and one proof gate. Do not replay ticket triage, prior plans, attachments, broad QA checklist, or unrelated scope.',
480
1033
  'If context is insufficient, request the smallest missing file/log/artifact by path and park this microtask; do not broaden the prompt.',
481
1034
  'Do not send customer email. Do not deploy. Do not spawn duplicate server/client/Mongo/browser/Codex processes.',
482
- input.lane === 'qa'
483
- ? 'QA lane hard boundary: the platform preflight owns compile, dependency install, Mongo/server/client startup, and Angular startup. This lane owns only browser/data proof and QA artifacts unless the prompt explicitly says preflight was skipped.'
484
- : 'Build lane hard boundary: do not run `npm run build-dev`, `ng build`, `npm run server`, `npm run client`, `ng serve`, `run-local-qa.sh`, browser automation, or any watch/long-lived command. If a broad compile gate is needed, run only `npm run build-prod -- --watch=false`; otherwise use a targeted finite check or return the precise blocker.'
1035
+ diagnosisActive
1036
+ ? 'Diagnosis gate hard boundary: read-only investigation only. Do not edit source, generated files, package files, tests, fixtures, QA artifacts, or local data. Return the diagnosis JSON contract only.'
1037
+ : input.lane === 'qa'
1038
+ ? 'QA lane hard boundary: the platform preflight owns compile, dependency install, Mongo/server/client startup, and Angular startup. This lane owns only browser/data proof and QA artifacts unless the prompt explicitly says preflight was skipped.'
1039
+ : 'Build lane hard boundary: do not run `npm run build-dev`, `ng build`, `npm run server`, `npm run client`, `ng serve`, `run-local-qa.sh`, browser automation, or any watch/long-lived command. If a broad compile gate is needed, run only `npm run build-prod -- --watch=false`; otherwise use a targeted finite check or return the precise blocker.'
485
1040
  ].join('\n')
486
1041
  },
1042
+ diagnosisActive ? {
1043
+ name: 'root_cause_first_diagnosis_contract',
1044
+ text: [
1045
+ 'Before any product-code repair, produce strict JSON only:',
1046
+ '{"support_diagnosis_gate":{"issue_case":{"customer_complaint":"","expected_result":"","observed_result":"","route_module":"","account_customer_context":"","reproduction_status":"reproduced|blocked|classified","reproduction_blocker":""},"issue_class":"no_op_submit|missing_wrong_data|filter_query_mismatch|invoice_pdf_export|upload_import|route_auth_hydration|slow_query_performance","accepted_hypothesis":{"statement":"","falsifiable_test":"","evidence":[""]},"rejected_alternatives":[""],"failing_path":{"frontend":"","backend":"","shared_library":"","data_query":"","description":""},"owner_files":["small/exact/file.ts"],"proof_plan":{"before":"","before_state_unavailable_reason":"","action":"","after":"","business_assertion":"","route":"","data_assertion":"","artifact_expectation":""},"similar_tickets":[],"similar_commits":[],"evidence":[{"type":"ticket|browser|mongo|log|code|commit|qa|other","summary":"","artifactPath":""}],"status":"passed"}}',
1047
+ 'Owner files must be a small exact editable set, not directories, globs, generated wrappers, or broad repo areas.',
1048
+ 'The accepted hypothesis must be falsifiable and backed by evidence; prior similar tickets or commits are hints only and cannot bypass fresh diagnosis.',
1049
+ 'The proof plan must be before/action/after business proof. If before-state proof is impossible, explain exactly why in before_state_unavailable_reason.'
1050
+ ].join('\n')
1051
+ } : {
1052
+ name: 'diagnosis_gate_context',
1053
+ text: diagnosisValidation.valid && diagnosisGate ? [
1054
+ "Diagnosis issue class: ".concat(diagnosisGate.issue_class),
1055
+ "Accepted hypothesis: ".concat(diagnosisGate.accepted_hypothesis.statement),
1056
+ "Failing path: ".concat(diagnosisGate.failing_path.description || diagnosisGate.failing_path.frontend || diagnosisGate.failing_path.backend || ''),
1057
+ "Owner files: ".concat(diagnosisGate.owner_files.join(', ')),
1058
+ "Business proof required: ".concat(diagnosisGate.proof_plan.business_assertion),
1059
+ "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)
1060
+ ].filter(Boolean).join('\n') : 'SupportDiagnosisGate is not valid. Park instead of editing product code.'
1061
+ },
487
1062
  {
488
1063
  name: 'scope_digest',
489
1064
  text: cleanText(input.bundle.supportV5ScopeDigest || input.bundle.supportV5SupervisorState.approvedScope, 900)
@@ -542,9 +1117,11 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
542
1117
  },
543
1118
  {
544
1119
  name: 'return_contract',
545
- text: input.lane === 'qa'
546
- ? 'Return strict JSON only: {"status":"pass"|"needs-fix"|"blocked","microtaskId":"","summary":"","evidence":[""],"artifacts":[""],"next_actions":[""]}.'
547
- : 'Return concise Markdown with: Microtask Result, Root Cause, Changes, Self-Gate, Acceptance Proof, Residual Risk.'
1120
+ text: diagnosisActive
1121
+ ? 'Return strict JSON only with support_diagnosis_gate. Do not include Markdown and do not edit files.'
1122
+ : input.lane === 'qa'
1123
+ ? 'Return strict JSON only: {"status":"pass"|"needs-fix"|"blocked","microtaskId":"","summary":"","evidence":[""],"artifacts":[""],"next_actions":[""]}.'
1124
+ : 'Return concise Markdown with: Microtask Result, Root Cause, Changes, Self-Gate, Acceptance Proof, Residual Risk.'
548
1125
  }
549
1126
  ].filter(function (section) { return cleanText(section.text, 20); });
550
1127
  var promptSections = sections.map(function (section) { return ({
@@ -570,7 +1147,7 @@ function buildResolveIOSupportV5MicrotaskPrompt(input) {
570
1147
  };
571
1148
  }
572
1149
  function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
573
- var e_3, _a, e_4, _b;
1150
+ var e_5, _a, e_6, _b;
574
1151
  var byMicrotask = new Map();
575
1152
  var bySection = new Map();
576
1153
  var totalPromptTokenEstimate = 0;
@@ -585,17 +1162,17 @@ function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
585
1162
  existing.calls += 1;
586
1163
  byMicrotask.set(usage.microtaskId, existing);
587
1164
  try {
588
- for (var _e = (e_4 = void 0, __values(usage.promptSections || [])), _f = _e.next(); !_f.done; _f = _e.next()) {
1165
+ for (var _e = (e_6 = void 0, __values(usage.promptSections || [])), _f = _e.next(); !_f.done; _f = _e.next()) {
589
1166
  var section = _f.value;
590
1167
  bySection.set(section.name, (bySection.get(section.name) || 0) + section.tokenEstimate);
591
1168
  }
592
1169
  }
593
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
1170
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
594
1171
  finally {
595
1172
  try {
596
1173
  if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
597
1174
  }
598
- finally { if (e_4) throw e_4.error; }
1175
+ finally { if (e_6) throw e_6.error; }
599
1176
  }
600
1177
  var hardCap = usage.lane === 'qa' ? promptBudget.qaMicrotaskHardCap : promptBudget.buildMicrotaskHardCap;
601
1178
  if ((usage.promptTokenEstimate || 0) > hardCap) {
@@ -603,12 +1180,12 @@ function summarizeResolveIOSupportV5MicrotaskUsage(bundle) {
603
1180
  }
604
1181
  }
605
1182
  }
606
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
1183
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
607
1184
  finally {
608
1185
  try {
609
1186
  if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
610
1187
  }
611
- finally { if (e_3) throw e_3.error; }
1188
+ finally { if (e_5) throw e_5.error; }
612
1189
  }
613
1190
  return {
614
1191
  totalPromptTokenEstimate: totalPromptTokenEstimate,