@vypdev/copilot 3.9.2 → 3.9.3

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.
@@ -38089,6 +38089,55 @@ function runAtApplicationErrorBoundary(operation) {
38089
38089
  }
38090
38090
 
38091
38091
 
38092
+ /***/ }),
38093
+
38094
+ /***/ 50519:
38095
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
38096
+
38097
+ "use strict";
38098
+
38099
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
38100
+ exports.approvalEvidenceError = approvalEvidenceError;
38101
+ exports.approvalDiagnosticError = approvalDiagnosticError;
38102
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
38103
+ const application_error_1 = __nccwpck_require__(75999);
38104
+ function approvalEvidenceError(operation, reason) {
38105
+ return approvalDiagnosticError({ operation, reason, permission: approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS[operation] });
38106
+ }
38107
+ function approvalDiagnosticError(diagnostic) {
38108
+ const reason = diagnostic.reason;
38109
+ const code = reason === 'http-401' ? 'authorization.credential-invalid'
38110
+ : reason === 'http-403' ? 'authorization.denied'
38111
+ : reason === 'http-404' ? 'provider.not-found'
38112
+ : reason === 'http-429' ? 'provider.rate-limited'
38113
+ : reason === 'http-409' ? 'provider.conflict'
38114
+ : reason === 'transport-unavailable' || /^http-50[0234]$/u.test(reason) ? 'provider.unavailable'
38115
+ : 'provider.contract-invalid';
38116
+ return new application_error_1.ApplicationError(code, `Approval evidence read failed (${diagnostic.operation}, ${reason}).`, {
38117
+ recovery: { id: 'approval-evidence-read-failed', variables: diagnostic },
38118
+ });
38119
+ }
38120
+
38121
+
38122
+ /***/ }),
38123
+
38124
+ /***/ 12183:
38125
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
38126
+
38127
+ "use strict";
38128
+
38129
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
38130
+ exports.closedIssueReconciliationError = closedIssueReconciliationError;
38131
+ const application_error_1 = __nccwpck_require__(75999);
38132
+ /** Public diagnostic is fixed native facts and counters, never label/card/provider text. */
38133
+ function closedIssueReconciliationError(cause, facts) {
38134
+ return new application_error_1.ApplicationError(cause instanceof application_error_1.ApplicationError ? cause.code : 'provider.unavailable', 'Closed issue reconciliation remains unverified.', {
38135
+ ...(cause instanceof application_error_1.ApplicationError ? { correlationId: cause.correlationId, retryable: cause.retryable } : {}),
38136
+ recovery: { id: 'closed-issue-reconciliation-failed', variables: facts },
38137
+ });
38138
+ }
38139
+
38140
+
38092
38141
  /***/ }),
38093
38142
 
38094
38143
  /***/ 72594:
@@ -39265,6 +39314,16 @@ const SPANISH_CONTENT = Object.freeze({
39265
39314
  }),
39266
39315
  });
39267
39316
  const ENGLISH_RECOVERY_CONTENT = Object.freeze({
39317
+ 'closed-issue-reconciliation-failed': Object.freeze({
39318
+ impact: 'Issue cleanup remains unverified: {resultId}, phase {phase}; native state {state}, reason {reason}.',
39319
+ action: 'Inspect the closed-issue or merged-PR run and current native state. Retry cleanup only if the issue is still closed with the intended reason; leave reopened work open. Resolve the named failure before retrying.',
39320
+ retainedState: 'Native state was preserved. Confirmed changes: {changed}; remaining required changes in the last verified plan: {remaining}. Unavailable reads do not establish completion.',
39321
+ }),
39322
+ 'approval-evidence-read-failed': Object.freeze({
39323
+ impact: 'Approval evidence could not be verified: {operation} ({reason}).',
39324
+ action: 'Inspect {permission} access for the runtime credential and the named evidence response. Correct authorization, missing-resource or schema errors before rerunning; retry transient failures only after provider recovery or rate-limit reset.',
39325
+ retainedState: 'No approval is authorized by this failed read. Existing PRs, reviews and completed external effects were preserved.',
39326
+ }),
39268
39327
  'workspace-changes-rejected': Object.freeze({
39269
39328
  impact: 'The changes were rejected during workspace verification.',
39270
39329
  action: 'Inspect the rejected paths in the workflow and correct the workspace before retrying.',
@@ -39332,6 +39391,16 @@ const ENGLISH_RECOVERY_CONTENT = Object.freeze({
39332
39391
  }),
39333
39392
  });
39334
39393
  const SPANISH_RECOVERY_CONTENT = Object.freeze({
39394
+ 'closed-issue-reconciliation-failed': Object.freeze({
39395
+ impact: 'La limpieza de la issue sigue sin verificarse: {resultId}, fase {phase}; estado nativo {state}, motivo {reason}.',
39396
+ action: 'Revisa la ejecución de cierre o de la PR fusionada y el estado nativo actual. Reintenta la limpieza solo si la issue sigue cerrada con el motivo previsto; conserva abierto el trabajo reabierto. Resuelve el fallo indicado antes de reintentar.',
39397
+ retainedState: 'Se conservó el estado nativo. Cambios confirmados: {changed}; cambios pendientes en el último plan verificado: {remaining}. Una lectura no disponible no demuestra que haya finalizado.',
39398
+ }),
39399
+ 'approval-evidence-read-failed': Object.freeze({
39400
+ impact: 'No se pudo verificar la evidencia de aprobación: {operation} ({reason}).',
39401
+ action: 'Comprueba el acceso {permission} de la credencial de ejecución y la respuesta indicada. Corrige los errores de autorización, recurso o esquema antes de reejecutar; reintenta los fallos transitorios solo tras recuperarse el proveedor o restablecerse el límite.',
39402
+ retainedState: 'Esta lectura fallida no autoriza ninguna aprobación. Se conservaron las PRs, revisiones y efectos externos completados.',
39403
+ }),
39335
39404
  'workspace-changes-rejected': Object.freeze({
39336
39405
  impact: 'Los cambios se rechazaron durante la verificación del espacio de trabajo.',
39337
39406
  action: 'Revisa las rutas rechazadas en la ejecución y corrige el espacio de trabajo antes de reintentar.',
@@ -56396,8 +56465,9 @@ function failure(taskId, message, code) {
56396
56465
  Object.defineProperty(exports, "__esModule", ({ value: true }));
56397
56466
  exports.ReconcileClosedIssueMetadataUseCase = void 0;
56398
56467
  const result_1 = __nccwpck_require__(73817);
56399
- const issue_completion_1 = __nccwpck_require__(31147);
56400
56468
  const application_error_1 = __nccwpck_require__(75999);
56469
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
56470
+ const reconcile_issue_completion_metadata_1 = __nccwpck_require__(70614);
56401
56471
  /** Selective completion repair from fresh native facts; no native state authority. */
56402
56472
  class ReconcileClosedIssueMetadataUseCase {
56403
56473
  constructor(issues) {
@@ -56405,50 +56475,22 @@ class ReconcileClosedIssueMetadataUseCase {
56405
56475
  this.taskId = 'ReconcileClosedIssueMetadataUseCase';
56406
56476
  }
56407
56477
  async invoke(context) {
56408
- let labelsChanged = 0;
56409
- let nativeClosed = false;
56478
+ const progress = (0, reconcile_issue_completion_metadata_1.completionRepairProgress)();
56410
56479
  try {
56411
56480
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1)
56412
56481
  throw stale();
56413
56482
  const target = this.issues.forIssue(context.issueNumber);
56414
- const initial = await target.read();
56415
- nativeClosed = initial.number === context.issueNumber && initial.state === 'closed';
56416
- const initialPlan = (0, issue_completion_1.planIssueCompletion)(initial, context.completionLabels);
56417
- if (!nativeClosed || initialPlan.kind !== 'closed')
56418
- throw stale();
56419
- const fence = async () => {
56420
- const current = await target.read();
56421
- nativeClosed = current.number === context.issueNumber && current.state === 'closed';
56422
- const plan = (0, issue_completion_1.planIssueCompletion)(current, context.completionLabels);
56423
- if (!nativeClosed || plan.kind !== 'closed' || current.reason !== initial.reason)
56424
- throw stale();
56425
- return plan;
56426
- };
56427
- for (const label of initialPlan.remove) {
56428
- if ((await fence()).remove.includes(label)) {
56429
- await target.removeLabel(label);
56430
- labelsChanged++;
56431
- }
56432
- }
56433
- for (const label of initialPlan.add) {
56434
- if ((await fence()).add.includes(label)) {
56435
- await target.addLabel(label);
56436
- labelsChanged++;
56437
- }
56438
- }
56439
- const final = await fence();
56440
- if (final.remove.length || final.add.length)
56441
- throw stale();
56442
- return [new result_1.Result({ id: this.taskId, success: true, executed: labelsChanged > 0,
56483
+ const repaired = await (0, reconcile_issue_completion_metadata_1.reconcileIssueCompletionMetadata)(target, context.issueNumber, context.completionLabels, progress);
56484
+ return [new result_1.Result({ id: this.taskId, success: true, executed: progress.labelsChanged > 0,
56443
56485
  steps: ['The closed issue completion metadata was verified; historical and unrelated labels were retained.'],
56444
- payload: { issueCompletion: { nativeClosed, labelsChanged, reconciled: true, reason: initial.reason } } })];
56486
+ payload: { issueCompletion: { nativeClosed: progress.nativeClosed, labelsChanged: progress.labelsChanged, reconciled: true, ...repaired } } })];
56445
56487
  }
56446
56488
  catch (cause) {
56447
- return [new result_1.Result({ id: this.taskId, success: false, executed: labelsChanged > 0,
56448
- steps: [nativeClosed ? 'The issue remains closed; completion metadata could not be fully verified. Inspect and retry the closed-issue workflow.'
56489
+ return [new result_1.Result({ id: this.taskId, success: false, executed: progress.labelsChanged > 0,
56490
+ steps: [progress.nativeClosed ? 'The issue remains closed; completion metadata could not be fully verified. Inspect and retry the closed-issue workflow.'
56449
56491
  : 'Current native issue closure could not be verified; completion repair stopped without changing native state.'],
56450
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Closed issue completion metadata remains unverified.')],
56451
- payload: { issueCompletion: { nativeClosed, labelsChanged, reconciled: false } } })];
56492
+ errors: [(0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(cause, { resultId: this.taskId, phase: progress.phase, state: progress.state, reason: progress.reason, changed: progress.labelsChanged, remaining: progress.remaining })],
56493
+ payload: { issueCompletion: { nativeClosed: progress.nativeClosed, labelsChanged: progress.labelsChanged, reconciled: false } } })];
56452
56494
  }
56453
56495
  }
56454
56496
  }
@@ -56481,6 +56523,84 @@ class ReconcileClosedIssueUseCase {
56481
56523
  exports.ReconcileClosedIssueUseCase = ReconcileClosedIssueUseCase;
56482
56524
 
56483
56525
 
56526
+ /***/ }),
56527
+
56528
+ /***/ 70614:
56529
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
56530
+
56531
+ "use strict";
56532
+
56533
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
56534
+ exports.completionRepairProgress = void 0;
56535
+ exports.reconcileIssueCompletionMetadata = reconcileIssueCompletionMetadata;
56536
+ const application_error_1 = __nccwpck_require__(75999);
56537
+ const issue_completion_1 = __nccwpck_require__(31147);
56538
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
56539
+ const completionRepairProgress = () => ({ nativeClosed: false, labelsChanged: 0,
56540
+ phase: 'identity', state: 'unavailable', reason: 'unavailable', remaining: 0 });
56541
+ exports.completionRepairProgress = completionRepairProgress;
56542
+ /** At most two bounded selective passes, with fresh native fences before every write. */
56543
+ async function reconcileIssueCompletionMetadata(target, number, configuration, progress, initial) {
56544
+ let expectedReason;
56545
+ let superseded = false;
56546
+ const read = async (phase, snapshot) => {
56547
+ progress.phase = phase;
56548
+ progress.state = 'unavailable';
56549
+ progress.reason = 'unavailable';
56550
+ const facts = snapshot ?? await target.read();
56551
+ progress.nativeClosed = facts?.number === number && facts.state === 'closed';
56552
+ progress.state = facts?.number === number && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
56553
+ progress.reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(reason => reason === facts?.reason) ? facts.reason : 'unavailable';
56554
+ const plan = (0, issue_completion_1.planIssueCompletion)(facts, configuration);
56555
+ if (!progress.nativeClosed || plan.kind !== 'closed' || expectedReason !== undefined && facts.reason !== expectedReason) {
56556
+ throw new application_error_1.ApplicationError('workflow.stale', 'The native issue identity, state or closure reason changed.');
56557
+ }
56558
+ expectedReason = facts.reason;
56559
+ progress.remaining = plan.remove.length + plan.add.length;
56560
+ return plan;
56561
+ };
56562
+ let plan = await read('read', initial);
56563
+ for (let pass = 0; pass < 2; pass++) {
56564
+ for (const label of plan.remove) {
56565
+ if (!(await read('label-remove')).remove.includes(label)) {
56566
+ superseded = true;
56567
+ continue;
56568
+ }
56569
+ try {
56570
+ await target.removeLabel(label);
56571
+ progress.labelsChanged++;
56572
+ }
56573
+ catch (cause) {
56574
+ if ((await read('label-remove')).remove.includes(label))
56575
+ throw cause;
56576
+ superseded = true;
56577
+ }
56578
+ }
56579
+ for (const label of plan.add) {
56580
+ if (!(await read('label-add')).add.includes(label)) {
56581
+ superseded = true;
56582
+ continue;
56583
+ }
56584
+ try {
56585
+ await target.addLabel(label);
56586
+ progress.labelsChanged++;
56587
+ }
56588
+ catch (cause) {
56589
+ if ((await read('label-add')).add.includes(label))
56590
+ throw cause;
56591
+ superseded = true;
56592
+ }
56593
+ }
56594
+ plan = await read('metadata-verification');
56595
+ if (!progress.remaining)
56596
+ return { reason: plan.reason,
56597
+ outcome: progress.labelsChanged ? 'reconciled' : 'already-converged', superseded };
56598
+ superseded = true;
56599
+ }
56600
+ throw new application_error_1.ApplicationError('provider.conflict', 'Completion metadata remains incomplete after two bounded passes.');
56601
+ }
56602
+
56603
+
56484
56604
  /***/ }),
56485
56605
 
56486
56606
  /***/ 74111:
@@ -56490,7 +56610,9 @@ exports.ReconcileClosedIssueUseCase = ReconcileClosedIssueUseCase;
56490
56610
 
56491
56611
  Object.defineProperty(exports, "__esModule", ({ value: true }));
56492
56612
  exports.RetireBranchSyncUseCase = void 0;
56493
- const reply_request_digest_1 = __nccwpck_require__(47971);
56613
+ const verify_closed_issue_card_retirement_1 = __nccwpck_require__(78813);
56614
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
56615
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
56494
56616
  const result_1 = __nccwpck_require__(73817);
56495
56617
  const github_user_policy_1 = __nccwpck_require__(84403);
56496
56618
  const publication_identity_policy_1 = __nccwpck_require__(45403);
@@ -56508,17 +56630,33 @@ class RetireBranchSyncUseCase {
56508
56630
  async invoke(context) {
56509
56631
  let retired = 0;
56510
56632
  let nativeClosed = false;
56633
+ let phase = 'identity';
56634
+ let state = 'unavailable';
56635
+ let reason = 'unavailable';
56636
+ let remaining = 0;
56637
+ let superseded = false;
56511
56638
  try {
56512
56639
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1 || !context.trustedBotLogin) {
56513
56640
  return [this.result(retired, nativeClosed)];
56514
56641
  }
56515
- const initial = await this.issues.readIssue(context.issueNumber);
56516
- if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state))
56642
+ const read = async () => {
56643
+ state = 'unavailable';
56644
+ reason = 'unavailable';
56645
+ const facts = await this.issues.readIssue(context.issueNumber);
56646
+ nativeClosed = facts?.number === context.issueNumber && facts.state === 'closed';
56647
+ state = facts?.number === context.issueNumber && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
56648
+ reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === facts?.reason) ? facts.reason : 'unavailable';
56649
+ return facts;
56650
+ };
56651
+ phase = 'read';
56652
+ const initial = await read();
56653
+ if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state) || initial.state === 'closed' && !['completed', 'not_planned', 'duplicate', 'unknown'].includes(initial.reason))
56517
56654
  throw stale();
56518
56655
  if (initial.state !== 'closed')
56519
56656
  return [this.result(retired, nativeClosed)];
56520
56657
  nativeClosed = true;
56521
56658
  const messages = (0, branch_sync_message_catalog_1.resolveStaticBranchSyncCatalog)(context.locale);
56659
+ phase = 'card-inventory';
56522
56660
  const cards = (await this.comments.listIssueComments(context.issueNumber)).filter(comment => {
56523
56661
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(comment.body);
56524
56662
  return marker?.identity.topic === 'branch-sync' && marker.identity.target.kind === 'issue'
@@ -56527,38 +56665,40 @@ class RetireBranchSyncUseCase {
56527
56665
  && Number.isSafeInteger(comment.id) && comment.id > 0
56528
56666
  && Boolean(comment.user?.login && (0, github_user_policy_1.githubUsersMatch)(context.trustedBotLogin, comment.user.login));
56529
56667
  });
56668
+ remaining = Math.min(cards.length, 3000);
56530
56669
  if (cards.length > 20)
56531
56670
  throw stale();
56671
+ const verifyClosed = async () => {
56672
+ phase = 'native-fence';
56673
+ const fresh = await read();
56674
+ if (!nativeClosed || fresh.reason !== initial.reason)
56675
+ throw stale();
56676
+ };
56532
56677
  for (const card of cards) {
56533
56678
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(card.body);
56534
56679
  const body = (0, branch_sync_notification_policy_1.buildRetiredBranchSyncComment)(marker.identity, messages);
56535
- if (card.body === body)
56536
- continue;
56537
- const witness = Object.freeze({ surface: 'issue-comment', commentId: card.id,
56538
- author: context.trustedBotLogin, target: Object.freeze({ kind: 'issue', number: context.issueNumber }),
56539
- bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(card.body ?? '') });
56540
- if (!await this.cardFreshness.matches(witness))
56541
- throw stale();
56542
- const fresh = await this.issues.readIssue(context.issueNumber);
56543
- nativeClosed = fresh.number === context.issueNumber && fresh.state === 'closed';
56544
- if (!nativeClosed || fresh.reason !== initial.reason)
56545
- throw stale();
56546
- await this.comments.updateComment(context.issueNumber, card.id, body);
56547
- retired++;
56680
+ const verified = await (0, verify_closed_issue_card_retirement_1.verifyClosedIssueCardRetirement)({ issueNumber: context.issueNumber,
56681
+ commentId: card.id, author: context.trustedBotLogin, originalBody: card.body ?? '', finalBody: body,
56682
+ freshness: this.cardFreshness, verifyClosed, onUpdated: () => { retired++; }, phase: value => { phase = value; },
56683
+ update: () => this.comments.updateComment(context.issueNumber, card.id, body) });
56684
+ superseded || (superseded = verified.superseded);
56685
+ remaining--;
56548
56686
  }
56549
- return [this.result(retired, nativeClosed)];
56687
+ await verifyClosed();
56688
+ return [this.result(retired, nativeClosed, superseded)];
56550
56689
  }
56551
56690
  catch (cause) {
56552
56691
  return [new result_1.Result({ id: this.taskId, success: false, executed: retired > 0,
56553
56692
  steps: [nativeClosed ? 'Issue closure is retained; synchronization recommendation retirement could not be fully verified. Retry the closed-issue event or merged-PR operation.' : 'Native issue closure could not be verified; synchronization recommendations were left unchanged.'],
56554
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Unable to retire synchronization recommendations safely.')],
56693
+ errors: [(0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(cause, { resultId: this.taskId, phase, state, reason, changed: retired, remaining })],
56555
56694
  payload: { nativeClosed, retiredCards: retired, reconciled: false } })];
56556
56695
  }
56557
56696
  }
56558
- result(retired, nativeClosed) {
56697
+ result(retired, nativeClosed, superseded = false) {
56559
56698
  return new result_1.Result({ id: this.taskId, success: true, executed: retired > 0,
56560
56699
  steps: [retired ? 'The closed issue’s synchronization recommendations were retired.' : 'No synchronization recommendation required retirement.'],
56561
- payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true } });
56700
+ payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true, superseded,
56701
+ outcome: nativeClosed ? retired ? 'reconciled' : 'already-converged' : 'not-applicable' } });
56562
56702
  }
56563
56703
  }
56564
56704
  exports.RetireBranchSyncUseCase = RetireBranchSyncUseCase;
@@ -56599,7 +56739,9 @@ exports.RetireClosedIssueCardsUseCase = RetireClosedIssueCardsUseCase;
56599
56739
 
56600
56740
  Object.defineProperty(exports, "__esModule", ({ value: true }));
56601
56741
  exports.RetireProgressUseCase = void 0;
56602
- const reply_request_digest_1 = __nccwpck_require__(47971);
56742
+ const verify_closed_issue_card_retirement_1 = __nccwpck_require__(78813);
56743
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
56744
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
56603
56745
  const result_1 = __nccwpck_require__(73817);
56604
56746
  const github_user_policy_1 = __nccwpck_require__(84403);
56605
56747
  const publication_identity_policy_1 = __nccwpck_require__(45403);
@@ -56616,16 +56758,32 @@ class RetireProgressUseCase {
56616
56758
  async invoke(context) {
56617
56759
  let retired = 0;
56618
56760
  let nativeClosed = false;
56761
+ let phase = 'identity';
56762
+ let state = 'unavailable';
56763
+ let reason = 'unavailable';
56764
+ let remaining = 0;
56765
+ let superseded = false;
56619
56766
  try {
56620
56767
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1 || !context.trustedBotLogin) {
56621
56768
  return [this.result(retired, nativeClosed)];
56622
56769
  }
56623
- const initial = await this.issues.readIssue(context.issueNumber);
56770
+ const read = async () => {
56771
+ state = 'unavailable';
56772
+ reason = 'unavailable';
56773
+ const facts = await this.issues.readIssue(context.issueNumber);
56774
+ nativeClosed = facts?.number === context.issueNumber && facts.state === 'closed';
56775
+ state = facts?.number === context.issueNumber && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
56776
+ reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === facts?.reason) ? facts.reason : 'unavailable';
56777
+ return facts;
56778
+ };
56779
+ phase = 'read';
56780
+ const initial = await read();
56624
56781
  if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state) || initial.state === 'closed' && !['completed', 'not_planned', 'duplicate', 'unknown'].includes(initial.reason))
56625
56782
  throw stale();
56626
56783
  if (initial.state !== 'closed')
56627
56784
  return [this.result(retired, nativeClosed)];
56628
56785
  nativeClosed = true;
56786
+ phase = 'card-inventory';
56629
56787
  const cards = (await this.comments.listIssueComments(context.issueNumber)).filter(comment => {
56630
56788
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(comment.body);
56631
56789
  return marker?.identity.topic === 'progress' && marker.identity.target.kind === 'issue'
@@ -56634,37 +56792,39 @@ class RetireProgressUseCase {
56634
56792
  && Number.isSafeInteger(comment.id) && comment.id > 0
56635
56793
  && Boolean(comment.user?.login && (0, github_user_policy_1.githubUsersMatch)(context.trustedBotLogin, comment.user.login));
56636
56794
  });
56795
+ remaining = Math.min(cards.length, 3000);
56637
56796
  if (cards.length > 20)
56638
56797
  throw stale();
56639
- for (const card of cards) {
56640
- const body = (0, retired_progress_presentation_policy_1.buildRetiredProgressComment)(card.body ?? '', context.locale, initial.reason);
56641
- if (card.body === body)
56642
- continue;
56643
- const witness = Object.freeze({ surface: 'issue-comment', commentId: card.id,
56644
- author: context.trustedBotLogin, target: Object.freeze({ kind: 'issue', number: context.issueNumber }),
56645
- bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(card.body ?? '') });
56646
- if (!await this.cardFreshness.matches(witness))
56647
- throw stale();
56648
- const fresh = await this.issues.readIssue(context.issueNumber);
56649
- nativeClosed = fresh.number === context.issueNumber && fresh.state === 'closed';
56798
+ const verifyClosed = async () => {
56799
+ phase = 'native-fence';
56800
+ const fresh = await read();
56650
56801
  if (!nativeClosed || fresh.reason !== initial.reason)
56651
56802
  throw stale();
56652
- await this.comments.updateComment(context.issueNumber, card.id, body);
56653
- retired++;
56803
+ };
56804
+ for (const card of cards) {
56805
+ const body = (0, retired_progress_presentation_policy_1.buildRetiredProgressComment)(card.body ?? '', context.locale, initial.reason);
56806
+ const verified = await (0, verify_closed_issue_card_retirement_1.verifyClosedIssueCardRetirement)({ issueNumber: context.issueNumber,
56807
+ commentId: card.id, author: context.trustedBotLogin, originalBody: card.body ?? '', finalBody: body,
56808
+ freshness: this.cardFreshness, verifyClosed, onUpdated: () => { retired++; }, phase: value => { phase = value; },
56809
+ update: () => this.comments.updateComment(context.issueNumber, card.id, body) });
56810
+ superseded || (superseded = verified.superseded);
56811
+ remaining--;
56654
56812
  }
56655
- return [this.result(retired, nativeClosed)];
56813
+ await verifyClosed();
56814
+ return [this.result(retired, nativeClosed, superseded)];
56656
56815
  }
56657
56816
  catch (cause) {
56658
56817
  return [new result_1.Result({ id: this.taskId, success: false, executed: retired > 0,
56659
56818
  steps: [nativeClosed ? 'Issue closure is retained; progress card retirement could not be fully verified. Retry the closed-issue event or merged-PR operation.' : 'Native issue closure could not be verified; progress cards were left unchanged.'],
56660
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Unable to retire progress cards safely.')],
56819
+ errors: [(0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(cause, { resultId: this.taskId, phase, state, reason, changed: retired, remaining })],
56661
56820
  payload: { nativeClosed, retiredCards: retired, reconciled: false } })];
56662
56821
  }
56663
56822
  }
56664
- result(retired, nativeClosed) {
56823
+ result(retired, nativeClosed, superseded = false) {
56665
56824
  return new result_1.Result({ id: this.taskId, success: true, executed: retired > 0,
56666
56825
  steps: [retired ? 'The closed issue’s progress cards were retired.' : 'No progress card required retirement.'],
56667
- payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true } });
56826
+ payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true, superseded,
56827
+ outcome: nativeClosed ? retired ? 'reconciled' : 'already-converged' : 'not-applicable' } });
56668
56828
  }
56669
56829
  }
56670
56830
  exports.RetireProgressUseCase = RetireProgressUseCase;
@@ -57055,6 +57215,48 @@ function sameLabels(left, right) {
57055
57215
  }
57056
57216
 
57057
57217
 
57218
+ /***/ }),
57219
+
57220
+ /***/ 78813:
57221
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
57222
+
57223
+ "use strict";
57224
+
57225
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
57226
+ exports.verifyClosedIssueCardRetirement = verifyClosedIssueCardRetirement;
57227
+ const reply_request_digest_1 = __nccwpck_require__(47971);
57228
+ const application_error_1 = __nccwpck_require__(75999);
57229
+ /** Adopt another invocation only after the exact final owned body and native closure are fresh. */
57230
+ async function verifyClosedIssueCardRetirement(input) {
57231
+ const witness = (body) => Object.freeze({ surface: 'issue-comment',
57232
+ commentId: input.commentId, author: input.author,
57233
+ target: Object.freeze({ kind: 'issue', number: input.issueNumber }), bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(body) });
57234
+ let updated = false;
57235
+ let failedWrite;
57236
+ if (input.originalBody !== input.finalBody) {
57237
+ input.phase('card-freshness');
57238
+ if (await input.freshness.matches(witness(input.originalBody))) {
57239
+ await input.verifyClosed();
57240
+ input.phase('card-update');
57241
+ try {
57242
+ await input.update();
57243
+ updated = true;
57244
+ input.onUpdated();
57245
+ }
57246
+ catch (cause) {
57247
+ failedWrite = cause;
57248
+ }
57249
+ }
57250
+ }
57251
+ input.phase('card-verification');
57252
+ if (!await input.freshness.matches(witness(input.finalBody))) {
57253
+ throw failedWrite ?? new application_error_1.ApplicationError('workflow.stale', 'The required final owned card could not be verified.');
57254
+ }
57255
+ await input.verifyClosed();
57256
+ return { updated, superseded: !updated && input.originalBody !== input.finalBody };
57257
+ }
57258
+
57259
+
57058
57260
  /***/ }),
57059
57261
 
57060
57262
  /***/ 4643:
@@ -70739,6 +70941,8 @@ exports.CheckPriorityIssueSizeUseCase = CheckPriorityIssueSizeUseCase;
70739
70941
 
70740
70942
  Object.defineProperty(exports, "__esModule", ({ value: true }));
70741
70943
  exports.CloseIssueAfterMergingUseCase = void 0;
70944
+ const reconcile_issue_completion_metadata_1 = __nccwpck_require__(70614);
70945
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
70742
70946
  const result_1 = __nccwpck_require__(73817);
70743
70947
  const issue_completion_1 = __nccwpck_require__(31147);
70744
70948
  const application_error_1 = __nccwpck_require__(75999);
@@ -70757,7 +70961,7 @@ class CloseIssueAfterMergingUseCase {
70757
70961
  steps: ['No separate safe linked issue and pull request were found; issue closure was skipped.'] })];
70758
70962
  let closedThisRun = false;
70759
70963
  let nativeClosed = false;
70760
- let labelsChanged = 0;
70964
+ const progress = (0, reconcile_issue_completion_metadata_1.completionRepairProgress)();
70761
70965
  try {
70762
70966
  const pull = await this.issues.readPullRequest(pullNumber);
70763
70967
  if (pull.number !== pullNumber || pull.state !== 'closed' || pull.merged !== true)
@@ -70781,44 +70985,21 @@ class CloseIssueAfterMergingUseCase {
70781
70985
  nativeClosed = facts.state === 'closed';
70782
70986
  if (!nativeClosed || closedThisRun && facts.reason !== 'completed')
70783
70987
  throw stale();
70784
- const fence = async () => {
70785
- const freshFacts = await read();
70786
- const freshPlan = (0, issue_completion_1.planIssueCompletion)(freshFacts, param.completionLabels);
70787
- if (freshPlan.kind !== 'closed' || freshFacts.reason !== facts.reason)
70788
- throw stale();
70789
- return freshPlan;
70790
- };
70791
- const plan = (0, issue_completion_1.planIssueCompletion)(facts, param.completionLabels);
70792
- if (plan.kind !== 'closed')
70793
- throw stale();
70794
- for (const label of plan.remove) {
70795
- const fresh = await fence();
70796
- if (fresh.remove.includes(label)) {
70797
- await target.removeLabel(label);
70798
- labelsChanged++;
70799
- }
70800
- }
70801
- for (const label of plan.add) {
70802
- const fresh = await fence();
70803
- if (fresh.add.includes(label)) {
70804
- await target.addLabel(label);
70805
- labelsChanged++;
70806
- }
70807
- }
70808
- const final = await fence();
70809
- if (final.remove.length || final.add.length)
70810
- throw stale();
70811
- return [new result_1.Result({ id: this.taskId, success: true, executed: closedThisRun || labelsChanged > 0,
70988
+ const repaired = await (0, reconcile_issue_completion_metadata_1.reconcileIssueCompletionMetadata)(target, number, param.completionLabels, progress, facts);
70989
+ nativeClosed = progress.nativeClosed;
70990
+ return [new result_1.Result({ id: this.taskId, success: true, executed: closedThisRun || progress.labelsChanged > 0,
70812
70991
  steps: [`Issue #${number} is closed and its completion metadata was verified after PR #${pullNumber} merged.`],
70813
- payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged, reason: facts.reason, reconciled: true }) } })];
70992
+ payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged: progress.labelsChanged, ...repaired, reconciled: true }) } })];
70814
70993
  }
70815
70994
  catch (error) {
70816
- const semanticError = (0, application_error_1.toApplicationError)(error, 'provider.unavailable', nativeClosed
70817
- ? 'The issue is closed, but completion metadata could not be fully verified. Retry the verified merged-PR operation.'
70818
- : 'Native issue completion could not be verified. Inspect the linked run before retrying.');
70819
- return [new result_1.Result({ id: this.taskId, success: false, executed: closedThisRun || labelsChanged > 0,
70995
+ if (progress.phase !== 'identity')
70996
+ nativeClosed = progress.nativeClosed;
70997
+ const semanticError = (0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(error, { resultId: this.taskId,
70998
+ phase: progress.phase, state: progress.state, reason: progress.reason,
70999
+ changed: progress.labelsChanged, remaining: progress.remaining });
71000
+ return [new result_1.Result({ id: this.taskId, success: false, executed: closedThisRun || progress.labelsChanged > 0,
70820
71001
  steps: [nativeClosed ? 'The native issue closure was retained; completion metadata remains unverified.' : 'Native issue completion remains unverified.'],
70821
- errors: [semanticError], payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged, reconciled: false }) } })];
71002
+ errors: [semanticError], payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged: progress.labelsChanged, reconciled: false }) } })];
70822
71003
  }
70823
71004
  }
70824
71005
  }
@@ -78173,6 +78354,8 @@ var _ApplicationError_cause;
78173
78354
  Object.defineProperty(exports, "__esModule", ({ value: true }));
78174
78355
  exports.ApplicationError = exports.APPLICATION_ERROR_METADATA = exports.APPLICATION_ERROR_RECOVERY_IDS = void 0;
78175
78356
  exports.isApplicationErrorCorrelationId = isApplicationErrorCorrelationId;
78357
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
78358
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
78176
78359
  const branch_sync_validation_1 = __nccwpck_require__(90981);
78177
78360
  exports.APPLICATION_ERROR_RECOVERY_IDS = Object.freeze([
78178
78361
  'pull-request-link-restored',
@@ -78188,6 +78371,8 @@ exports.APPLICATION_ERROR_RECOVERY_IDS = Object.freeze([
78188
78371
  'sdd-draft-size-exceeded',
78189
78372
  'sdd-draft-format-invalid',
78190
78373
  'workspace-changes-rejected',
78374
+ 'closed-issue-reconciliation-failed',
78375
+ 'approval-evidence-read-failed',
78191
78376
  ]);
78192
78377
  const PRESERVED_STATE = 'Existing persisted state and completed external effects were preserved.';
78193
78378
  const UNCHANGED_STATE = 'No new state or external effect was created.';
@@ -78379,6 +78564,8 @@ class ApplicationError extends Error {
78379
78564
  exports.ApplicationError = ApplicationError;
78380
78565
  _ApplicationError_cause = new WeakMap();
78381
78566
  const RECOVERY_VARIABLE_KEYS = Object.freeze({
78567
+ 'closed-issue-reconciliation-failed': Object.freeze(['resultId', 'phase', 'state', 'reason', 'changed', 'remaining']),
78568
+ 'approval-evidence-read-failed': Object.freeze(['operation', 'reason', 'permission']),
78382
78569
  'pull-request-link-restored': Object.freeze([]),
78383
78570
  'pull-request-link-base-retained': Object.freeze([]),
78384
78571
  'pull-request-link-reference-retained': Object.freeze([]),
@@ -78431,6 +78618,20 @@ function normalizeApplicationErrorRecovery(recovery) {
78431
78618
  && !branch_sync_validation_1.BRANCH_SYNC_VALIDATION_CODES.some(code => code === variables.validationCode)) {
78432
78619
  throw new TypeError('Application error recovery synchronization validation code is invalid.');
78433
78620
  }
78621
+ if (recovery.id === 'closed-issue-reconciliation-failed'
78622
+ && (!closed_issue_reconciliation_1.CLOSED_ISSUE_RESULTS.some(value => value === variables.resultId)
78623
+ || !closed_issue_reconciliation_1.CLOSED_ISSUE_PHASES.some(value => value === variables.phase)
78624
+ || !['open', 'closed', 'unavailable'].includes(variables.state)
78625
+ || !closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === variables.reason)
78626
+ || ![variables.changed, variables.remaining].every(value => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && value <= 3000))) {
78627
+ throw new TypeError('Closed issue reconciliation diagnostic is invalid.');
78628
+ }
78629
+ if (recovery.id === 'approval-evidence-read-failed'
78630
+ && (!Object.prototype.hasOwnProperty.call(approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS, variables.operation)
78631
+ || !approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_REASONS.some(reason => reason === variables.reason)
78632
+ || approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS[variables.operation] !== variables.permission)) {
78633
+ throw new TypeError('Approval evidence diagnostic is invalid.');
78634
+ }
78434
78635
  return Object.freeze({
78435
78636
  id: recovery.id,
78436
78637
  variables: Object.freeze({ ...variables }),
@@ -86219,6 +86420,41 @@ function parseApprovalCoverageZip(value) {
86219
86420
  }
86220
86421
 
86221
86422
 
86423
+ /***/ }),
86424
+
86425
+ /***/ 9958:
86426
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
86427
+
86428
+ "use strict";
86429
+
86430
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
86431
+ exports.readApprovalEvidence = readApprovalEvidence;
86432
+ const application_error_1 = __nccwpck_require__(75999);
86433
+ const approval_evidence_error_1 = __nccwpck_require__(50519);
86434
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
86435
+ /** Terminates provider exceptions; even unknown statuses never interpolate provider text. */
86436
+ async function readApprovalEvidence(operation, read) {
86437
+ try {
86438
+ return await read();
86439
+ }
86440
+ catch (cause) {
86441
+ if (cause instanceof application_error_1.ApplicationError && cause.recovery?.id === 'approval-evidence-read-failed')
86442
+ throw cause;
86443
+ const error = cause && typeof cause === 'object' ? cause : undefined;
86444
+ const response = error?.response;
86445
+ const status = error?.status;
86446
+ const rateLimited = status === 403 && (response?.headers?.['x-ratelimit-remaining'] === '0'
86447
+ || typeof response?.headers?.['retry-after'] === 'string' && /^\d{1,8}$/u.test(response.headers['retry-after']));
86448
+ const http = typeof status === 'number' ? `http-${status}` : undefined;
86449
+ const reason = rateLimited ? 'http-429'
86450
+ : approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_REASONS.some(value => value === http) ? http
86451
+ : cause instanceof application_error_1.ApplicationError && cause.code === 'provider.contract-invalid' ? 'invalid-response'
86452
+ : status === undefined ? 'transport-unavailable' : 'invalid-response';
86453
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, reason);
86454
+ }
86455
+ }
86456
+
86457
+
86222
86458
  /***/ }),
86223
86459
 
86224
86460
  /***/ 55165:
@@ -86253,21 +86489,22 @@ exports.BugbotPullRequestRepository = BugbotPullRequestRepository;
86253
86489
  /***/ }),
86254
86490
 
86255
86491
  /***/ 68742:
86256
- /***/ ((__unused_webpack_module, exports) => {
86492
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
86257
86493
 
86258
86494
  "use strict";
86259
86495
 
86260
86496
  Object.defineProperty(exports, "__esModule", ({ value: true }));
86261
86497
  exports.readCurrentBaseRevision = readCurrentBaseRevision;
86498
+ const application_error_1 = __nccwpck_require__(75999);
86262
86499
  /** PR base metadata can lag a parent push; read the exact native branch ref. */
86263
86500
  async function readCurrentBaseRevision(port, target, baseRef) {
86264
86501
  if (typeof baseRef !== 'string' || baseRef.length === 0 || baseRef.length > 1024
86265
86502
  || baseRef.includes('\u0000') || /[\r\n]/u.test(baseRef))
86266
- throw new Error('The PR base ref is invalid.');
86503
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'The PR base ref is invalid.');
86267
86504
  const { data } = await port.getRef({ owner: target.owner, repo: target.repository, ref: `heads/${baseRef}` });
86268
86505
  if (data?.ref !== `refs/heads/${baseRef}` || data.object?.type !== 'commit' || typeof data.object.sha !== 'string'
86269
86506
  || !/^[a-f0-9]{40}$/u.test(data.object.sha))
86270
- throw new Error('The PR base ref cannot be verified.');
86507
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'The PR base ref cannot be verified.');
86271
86508
  return data.object.sha;
86272
86509
  }
86273
86510
 
@@ -86315,6 +86552,9 @@ var __importStar = (this && this.__importStar) || (function () {
86315
86552
  Object.defineProperty(exports, "__esModule", ({ value: true }));
86316
86553
  exports.PullRequestApprovalRepository = void 0;
86317
86554
  exports.readEffectiveApprovalPolicy = readEffectiveApprovalPolicy;
86555
+ const approval_evidence_read_1 = __nccwpck_require__(9958);
86556
+ const approval_evidence_error_1 = __nccwpck_require__(50519);
86557
+ const application_error_1 = __nccwpck_require__(75999);
86318
86558
  const current_base_revision_query_1 = __nccwpck_require__(68742);
86319
86559
  const logging_ports_1 = __nccwpck_require__(6152);
86320
86560
  const github = __importStar(__nccwpck_require__(78227));
@@ -86341,40 +86581,64 @@ class PullRequestApprovalRepository {
86341
86581
  }
86342
86582
  }
86343
86583
  async readPolicy(target) {
86344
- return (await this.readEffectivePolicy(target))?.value;
86584
+ return (0, approval_evidence_read_1.readApprovalEvidence)('policy', async () => (await this.readEffectivePolicy(target))?.value);
86345
86585
  }
86346
86586
  async readEffectivePolicy(target) {
86347
86587
  return readEffectiveApprovalPolicy(this.octokit, target);
86348
86588
  }
86349
86589
  async loadEvidence(target) {
86350
- const { data: pull } = await this.octokit.rest.pulls.get({
86351
- owner: target.owner, repo: target.repository, pull_number: target.pullNumber,
86590
+ const diagnostics = [];
86591
+ const { data: pull } = await (0, approval_evidence_read_1.readApprovalEvidence)('pull-request', async () => {
86592
+ const response = await this.octokit.rest.pulls.get({ owner: target.owner, repo: target.repository, pull_number: target.pullNumber });
86593
+ if (!response.data?.head || !response.data?.base || typeof response.data.head.sha !== 'string'
86594
+ || typeof response.data.head.ref !== 'string' || !Number.isSafeInteger(response.data.number)) {
86595
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('pull-request', 'invalid-response');
86596
+ }
86597
+ return response;
86352
86598
  });
86353
86599
  const headSha = pull.head.sha;
86354
86600
  const baseSha = await this.readCurrentBaseSha(target, pull.base.ref);
86355
- const policy = (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(await this.readPolicy(target));
86601
+ const policy = await (0, approval_evidence_read_1.readApprovalEvidence)('policy', async () => {
86602
+ const raw = await this.readPolicy(target);
86603
+ try {
86604
+ return (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(raw);
86605
+ }
86606
+ catch {
86607
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('policy', 'invalid-response');
86608
+ }
86609
+ });
86356
86610
  let testedMergeSha;
86357
86611
  if (pull.mergeable === true && /^[a-f0-9]{40}$/iu.test(pull.merge_commit_sha ?? '')) {
86358
86612
  try {
86359
- const merge = await this.octokit.rest.git.getCommit({
86360
- owner: target.owner, repo: target.repository, commit_sha: pull.merge_commit_sha,
86613
+ const merge = await (0, approval_evidence_read_1.readApprovalEvidence)('merge-commit', async () => {
86614
+ const response = await this.octokit.rest.git.getCommit({ owner: target.owner, repo: target.repository, commit_sha: pull.merge_commit_sha });
86615
+ if (!Array.isArray(response.data?.parents))
86616
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('merge-commit', 'invalid-response');
86617
+ return response;
86361
86618
  });
86362
86619
  if (merge.data.parents[0]?.sha === baseSha && merge.data.parents[1]?.sha === headSha
86363
86620
  && merge.data.parents.length === 2)
86364
86621
  testedMergeSha = pull.merge_commit_sha;
86365
86622
  }
86366
- catch { /* Missing or unreadable merge parentage is non-authorizing. */ }
86623
+ catch (cause) {
86624
+ collectDiagnostic(diagnostics, cause);
86625
+ }
86367
86626
  }
86368
86627
  const [files, reviews, comments, headChecks, mergeChecks, headRuns, mergeRuns, rules, user] = await Promise.all([
86369
- this.allPages(this.octokit.rest.pulls.listFiles, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
86370
- this.allPages(this.octokit.rest.pulls.listReviews, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
86371
- this.allPages(this.octokit.rest.issues.listComments, { owner: target.owner, repo: target.repository, issue_number: target.pullNumber }),
86372
- this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: headSha, filter: 'all' }),
86373
- testedMergeSha ? this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: testedMergeSha, filter: 'all' }) : Promise.resolve([]),
86374
- this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: headSha }),
86375
- testedMergeSha ? this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: testedMergeSha }) : Promise.resolve([]),
86376
- this.effectiveRules(target.owner, target.repository, pull.base.ref),
86377
- this.octokit.rest.users.getAuthenticated(),
86628
+ this.allPages('files', this.octokit.rest.pulls.listFiles, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
86629
+ this.allPages('reviews', this.octokit.rest.pulls.listReviews, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
86630
+ this.allPages('comments', this.octokit.rest.issues.listComments, { owner: target.owner, repo: target.repository, issue_number: target.pullNumber }),
86631
+ this.allPages('head-checks', this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: headSha, filter: 'all' }),
86632
+ testedMergeSha ? this.allPages('merge-checks', this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: testedMergeSha, filter: 'all' }) : Promise.resolve([]),
86633
+ this.allPages('head-runs', this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: headSha }),
86634
+ testedMergeSha ? this.allPages('merge-runs', this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: testedMergeSha }) : Promise.resolve([]),
86635
+ this.effectiveRules(target.owner, target.repository, pull.base.ref, diagnostics),
86636
+ (0, approval_evidence_read_1.readApprovalEvidence)('authenticated-user', async () => {
86637
+ const response = await this.octokit.rest.users.getAuthenticated();
86638
+ if (!Number.isSafeInteger(response.data?.id) || response.data.id < 1)
86639
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('authenticated-user', 'invalid-response');
86640
+ return response;
86641
+ }),
86378
86642
  ]);
86379
86643
  const branch = Object.entries(this.settings.branchPrefixes)
86380
86644
  .find(([, prefix]) => pull.head.ref.startsWith(`${prefix}/`));
@@ -86395,7 +86659,7 @@ class PullRequestApprovalRepository {
86395
86659
  });
86396
86660
  const currentJobs = await Promise.all(latestRuns.map(async (run) => ({
86397
86661
  run,
86398
- jobs: await this.allPages(this.octokit.rest.actions.listJobsForWorkflowRunAttempt, {
86662
+ jobs: await this.allPages('jobs', this.octokit.rest.actions.listJobsForWorkflowRunAttempt, {
86399
86663
  owner: target.owner, repo: target.repository, run_id: run.id, attempt_number: run.run_attempt,
86400
86664
  }),
86401
86665
  })));
@@ -86426,9 +86690,10 @@ class PullRequestApprovalRepository {
86426
86690
  submittedAt: review.submitted_at ?? '',
86427
86691
  }));
86428
86692
  const numericCoverage = policy.coverage.mode === 'numeric'
86429
- ? await this.numericCoverage(target, policy.coverage.artifactWorkflowName, headSha, baseSha, testedMergeSha, workflowRuns)
86693
+ ? await this.numericCoverage(target, policy.coverage.artifactWorkflowName, headSha, baseSha, testedMergeSha, workflowRuns, diagnostics)
86430
86694
  : undefined;
86431
86695
  return {
86696
+ ...(diagnostics.length ? { diagnostics: Object.freeze(diagnostics) } : {}),
86432
86697
  repositoryId: pull.base.repo?.id ?? 0,
86433
86698
  pullNumber: pull.number,
86434
86699
  headSha,
@@ -86462,7 +86727,7 @@ class PullRequestApprovalRepository {
86462
86727
  ...(numericCoverage ? { numericCoverage } : {}),
86463
86728
  };
86464
86729
  }
86465
- async numericCoverage(target, workflowName, headSha, baseSha, testedMergeSha, workflowRuns) {
86730
+ async numericCoverage(target, workflowName, headSha, baseSha, testedMergeSha, workflowRuns, diagnostics) {
86466
86731
  const candidates = workflowRuns.filter(run => run.name === workflowName
86467
86732
  && (run.head_sha === headSha || run.head_sha === testedMergeSha));
86468
86733
  const mergeCandidates = candidates.filter(run => run.head_sha === testedMergeSha);
@@ -86471,25 +86736,27 @@ class PullRequestApprovalRepository {
86471
86736
  if (!run || run.conclusion !== 'success' || run.status !== 'completed')
86472
86737
  return undefined;
86473
86738
  try {
86474
- const artifacts = await this.allPages(this.octokit.rest.actions.listWorkflowRunArtifacts, {
86739
+ const artifacts = await this.allPages('coverage-artifacts', this.octokit.rest.actions.listWorkflowRunArtifacts, {
86475
86740
  owner: target.owner, repo: target.repository, run_id: run.id,
86476
86741
  });
86477
86742
  const matches = artifacts.filter(artifact => artifact.name === 'copilot-diff-coverage-v1' && !artifact.expired);
86478
86743
  if (matches.length !== 1 || matches[0].size_in_bytes > 64 * 1024)
86479
- return undefined;
86480
- const response = await this.octokit.rest.actions.downloadArtifact({
86744
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-artifacts', 'artifact-unavailable');
86745
+ const response = await (0, approval_evidence_read_1.readApprovalEvidence)('coverage-download', async () => this.octokit.rest.actions.downloadArtifact({
86481
86746
  owner: target.owner, repo: target.repository, artifact_id: matches[0].id, archive_format: 'zip',
86482
- });
86747
+ }));
86483
86748
  const data = response.data;
86484
86749
  const bytes = data instanceof ArrayBuffer ? new Uint8Array(data)
86485
86750
  : data instanceof Uint8Array ? data
86486
86751
  : typeof data === 'string' ? Buffer.from(data, 'binary') : undefined;
86487
86752
  const attestation = bytes ? (0, approval_coverage_artifact_1.parseApprovalCoverageZip)(bytes) : undefined;
86488
- if (!attestation || attestation.repositoryId !== target.repositoryId
86753
+ if (!attestation)
86754
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-parse', 'artifact-invalid');
86755
+ if (attestation.repositoryId !== target.repositoryId
86489
86756
  || attestation.pullNumber !== target.pullNumber || attestation.headSha !== headSha
86490
86757
  || attestation.baseSha !== baseSha || attestation.workflowRunId !== run.id
86491
86758
  || attestation.workflowRunAttempt !== run.run_attempt)
86492
- return undefined;
86759
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-parse', 'artifact-mismatch');
86493
86760
  return {
86494
86761
  coveredChangedLines: attestation.coveredChangedLines,
86495
86762
  totalChangedLines: attestation.totalChangedLines,
@@ -86497,7 +86764,8 @@ class PullRequestApprovalRepository {
86497
86764
  baseSha,
86498
86765
  };
86499
86766
  }
86500
- catch {
86767
+ catch (cause) {
86768
+ collectDiagnostic(diagnostics, cause);
86501
86769
  return undefined;
86502
86770
  }
86503
86771
  }
@@ -86523,7 +86791,7 @@ class PullRequestApprovalRepository {
86523
86791
  throw new Error('PR head or base changed before assessment publication.');
86524
86792
  }
86525
86793
  const user = await this.octokit.rest.users.getAuthenticated();
86526
- const comments = await this.allPages(this.octokit.rest.issues.listComments, {
86794
+ const comments = await this.allPages('comments', this.octokit.rest.issues.listComments, {
86527
86795
  owner: target.owner, repo: target.repository, issue_number: target.pullNumber,
86528
86796
  });
86529
86797
  const card = [...comments].reverse().find(comment => comment.user?.id === user.data.id
@@ -86569,24 +86837,26 @@ class PullRequestApprovalRepository {
86569
86837
  }
86570
86838
  }
86571
86839
  async readCurrentBaseSha(target, baseRef) {
86572
- return (0, current_base_revision_query_1.readCurrentBaseRevision)(this.octokit.rest.git, target, baseRef);
86840
+ return (0, approval_evidence_read_1.readApprovalEvidence)('base-ref', () => (0, current_base_revision_query_1.readCurrentBaseRevision)(this.octokit.rest.git, target, baseRef));
86573
86841
  }
86574
- async effectiveRules(owner, repo, branch) {
86842
+ async effectiveRules(owner, repo, branch, diagnostics) {
86575
86843
  try {
86576
- const rulesResponse = await this.octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { owner, repo, branch });
86844
+ const rulesResponse = await (0, approval_evidence_read_1.readApprovalEvidence)('rules', async () => this.octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { owner, repo, branch }));
86577
86845
  if (!Array.isArray(rulesResponse.data) || rulesResponse.data.some((rule) => !record(rule)
86578
86846
  || typeof record(rule)?.type !== 'string' || record(rule)?.type === '')) {
86579
- throw new Error('GitHub returned an invalid effective rules inventory.');
86847
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('rules', 'invalid-response');
86580
86848
  }
86581
86849
  const rules = rulesResponse.data;
86582
86850
  let classic;
86583
86851
  try {
86584
86852
  const response = await this.octokit.rest.repos.getBranchProtection({ owner, repo, branch });
86853
+ if (!record(response.data))
86854
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('classic-protection', 'invalid-response');
86585
86855
  classic = response.data;
86586
86856
  }
86587
86857
  catch (error) {
86588
86858
  if (statusOf(error) !== 404)
86589
- throw error;
86859
+ await (0, approval_evidence_read_1.readApprovalEvidence)('classic-protection', async () => { throw error; });
86590
86860
  }
86591
86861
  const reviewRules = rules.filter(rule => rule.type === 'pull_request');
86592
86862
  const classicReview = classic?.required_pull_request_reviews;
@@ -86612,23 +86882,26 @@ class PullRequestApprovalRepository {
86612
86882
  }
86613
86883
  return { readable: true, dismissesStaleReviews: hasStaleRule && allStale, requiredChecks: [...requiredChecks] };
86614
86884
  }
86615
- catch {
86885
+ catch (cause) {
86886
+ collectDiagnostic(diagnostics, cause);
86616
86887
  return { readable: false, dismissesStaleReviews: false, requiredChecks: [] };
86617
86888
  }
86618
86889
  }
86619
- async allPages(method, params) {
86620
- const result = [];
86621
- for await (const response of this.octokit.paginate.iterator(method, { ...params, per_page: 100 })) {
86622
- const page = Array.isArray(response.data) ? response.data
86623
- : record(response.data)?.check_runs ?? record(response.data)?.workflow_runs
86624
- ?? record(response.data)?.artifacts ?? record(response.data)?.jobs;
86625
- if (!Array.isArray(page))
86626
- throw new Error('GitHub pagination returned an invalid page.');
86627
- result.push(...page);
86628
- if (result.length > 3000)
86629
- throw new Error('Approval evidence pagination exceeded the safe bound.');
86630
- }
86631
- return result;
86890
+ async allPages(operation, method, params) {
86891
+ return (0, approval_evidence_read_1.readApprovalEvidence)(operation, async () => {
86892
+ const result = [];
86893
+ for await (const response of this.octokit.paginate.iterator(method, { ...params, per_page: 100 })) {
86894
+ const page = Array.isArray(response.data) ? response.data
86895
+ : record(response.data)?.check_runs ?? record(response.data)?.workflow_runs
86896
+ ?? record(response.data)?.artifacts ?? record(response.data)?.jobs;
86897
+ if (!Array.isArray(page))
86898
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, 'invalid-page');
86899
+ result.push(...page);
86900
+ if (result.length > 3000)
86901
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, 'page-limit');
86902
+ }
86903
+ return result;
86904
+ });
86632
86905
  }
86633
86906
  }
86634
86907
  exports.PullRequestApprovalRepository = PullRequestApprovalRepository;
@@ -86665,7 +86938,7 @@ function array(value) { return Array.isArray(value) ? value : []; }
86665
86938
  async function readEffectiveApprovalPolicy(octokit, target) {
86666
86939
  const admit = (source, value) => {
86667
86940
  if (typeof value !== 'string' || value.length > 65536)
86668
- throw new Error('Approval policy response is malformed or unbounded.');
86941
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'Approval policy response is malformed or unbounded.');
86669
86942
  return { source, value };
86670
86943
  };
86671
86944
  try {
@@ -86689,6 +86962,14 @@ async function readEffectiveApprovalPolicy(octokit, target) {
86689
86962
  throw error;
86690
86963
  }
86691
86964
  }
86965
+ function collectDiagnostic(diagnostics, cause) {
86966
+ if (cause instanceof application_error_1.ApplicationError && cause.recovery?.id === 'approval-evidence-read-failed') {
86967
+ diagnostics.push(cause.recovery.variables);
86968
+ }
86969
+ else {
86970
+ diagnostics.push((0, approval_evidence_error_1.approvalEvidenceError)('evidence', 'invalid-response').recovery.variables);
86971
+ }
86972
+ }
86692
86973
 
86693
86974
 
86694
86975
  /***/ }),
@@ -88929,6 +89210,44 @@ class AgentResponseLengthFailure extends Error {
88929
89210
  exports.AgentResponseLengthFailure = AgentResponseLengthFailure;
88930
89211
 
88931
89212
 
89213
+ /***/ }),
89214
+
89215
+ /***/ 98205:
89216
+ /***/ ((__unused_webpack_module, exports) => {
89217
+
89218
+ "use strict";
89219
+
89220
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
89221
+ exports.APPROVAL_EVIDENCE_REASONS = exports.APPROVAL_EVIDENCE_PERMISSIONS = void 0;
89222
+ /** Closed vocabulary: no provider text, URLs, headers, policy values or credentials. */
89223
+ exports.APPROVAL_EVIDENCE_PERMISSIONS = Object.freeze({
89224
+ 'evidence': 'repository-read',
89225
+ 'pull-request': 'pull-requests-read',
89226
+ 'base-ref': 'contents-read',
89227
+ 'merge-commit': 'contents-read',
89228
+ 'policy': 'actions-variables-read',
89229
+ 'files': 'pull-requests-read',
89230
+ 'reviews': 'pull-requests-read',
89231
+ 'comments': 'issues-read',
89232
+ 'head-checks': 'checks-read',
89233
+ 'merge-checks': 'checks-read',
89234
+ 'head-runs': 'actions-read',
89235
+ 'merge-runs': 'actions-read',
89236
+ 'jobs': 'actions-read',
89237
+ 'rules': 'metadata-read',
89238
+ 'classic-protection': 'administration-read',
89239
+ 'authenticated-user': 'authenticated-user-read',
89240
+ 'coverage-artifacts': 'actions-read',
89241
+ 'coverage-download': 'actions-read',
89242
+ 'coverage-parse': 'actions-read',
89243
+ });
89244
+ exports.APPROVAL_EVIDENCE_REASONS = Object.freeze([
89245
+ 'http-400', 'http-401', 'http-403', 'http-404', 'http-409', 'http-422', 'http-429',
89246
+ 'http-500', 'http-502', 'http-503', 'http-504', 'transport-unavailable',
89247
+ 'invalid-response', 'invalid-page', 'page-limit', 'artifact-unavailable', 'artifact-invalid', 'artifact-mismatch',
89248
+ ]);
89249
+
89250
+
88932
89251
  /***/ }),
88933
89252
 
88934
89253
  /***/ 51114:
@@ -89557,6 +89876,25 @@ function isNewerCliVersion(installedVersion, publishedVersion) {
89557
89876
  }
89558
89877
 
89559
89878
 
89879
+ /***/ }),
89880
+
89881
+ /***/ 21565:
89882
+ /***/ ((__unused_webpack_module, exports) => {
89883
+
89884
+ "use strict";
89885
+
89886
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
89887
+ exports.CLOSED_ISSUE_REASONS = exports.CLOSED_ISSUE_PHASES = exports.CLOSED_ISSUE_RESULTS = void 0;
89888
+ exports.CLOSED_ISSUE_RESULTS = Object.freeze([
89889
+ 'ReconcileClosedIssueMetadataUseCase', 'CloseIssueAfterMergingUseCase', 'RetireBranchSyncUseCase', 'RetireProgressUseCase',
89890
+ ]);
89891
+ exports.CLOSED_ISSUE_PHASES = Object.freeze([
89892
+ 'identity', 'read', 'native-fence', 'label-remove', 'label-add', 'metadata-verification',
89893
+ 'card-inventory', 'card-freshness', 'card-update', 'card-verification',
89894
+ ]);
89895
+ exports.CLOSED_ISSUE_REASONS = Object.freeze(['completed', 'not_planned', 'duplicate', 'reopened', 'unknown', 'unavailable']);
89896
+
89897
+
89560
89898
  /***/ }),
89561
89899
 
89562
89900
  /***/ 77454: