@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.
@@ -38189,6 +38189,8 @@ var __importStar = (this && this.__importStar) || (function () {
38189
38189
  })();
38190
38190
  Object.defineProperty(exports, "__esModule", ({ value: true }));
38191
38191
  exports.runPullRequestApprovalAction = runPullRequestApprovalAction;
38192
+ const application_error_presentation_policy_1 = __nccwpck_require__(95067);
38193
+ const application_error_message_catalog_1 = __nccwpck_require__(64809);
38192
38194
  const core = __importStar(__nccwpck_require__(75855));
38193
38195
  const github = __importStar(__nccwpck_require__(78227));
38194
38196
  const observe_pull_request_approval_use_case_1 = __nccwpck_require__(36169);
@@ -38238,15 +38240,22 @@ async function runPullRequestApprovalAction(token) {
38238
38240
  });
38239
38241
  const useCase = new observe_pull_request_approval_use_case_1.ObservePullRequestApprovalUseCase(port);
38240
38242
  const failedPulls = [];
38243
+ let firstFailure;
38241
38244
  for (const pullNumber of targetNumbers) {
38242
38245
  const target = { owner, repository: repo, repositoryId, pullNumber };
38243
38246
  const result = await useCase.execute(target);
38247
+ if (result.failure) {
38248
+ firstFailure ?? (firstFailure = result.failure);
38249
+ const diagnostic = (0, application_error_presentation_policy_1.renderApplicationErrorText)(result.failure, (0, application_error_message_catalog_1.resolveStaticApplicationErrorCatalog)(locale).message);
38250
+ core.info(diagnostic);
38251
+ core.summary.addRaw(`<pre>${diagnostic.replace(/&/gu, '&amp;').replace(/</gu, '&lt;').replace(/>/gu, '&gt;')}</pre>\n`);
38252
+ }
38244
38253
  const safeCode = result.decision.code.replace(/[^a-z0-9-]/gu, '').slice(0, 60);
38245
38254
  core.info(`PR #${pullNumber}: ${result.decision.status} (${safeCode}); assessment ${result.publication}.`);
38246
38255
  if (result.publication === 'failed' || [
38247
38256
  'invalid-policy', 'policy-unavailable', 'evidence-unavailable', 'target-mismatch',
38248
38257
  'pre-submit-unavailable', 'publication-unknown',
38249
- ].includes(result.decision.code))
38258
+ ].includes(result.decision.code) || result.failure)
38250
38259
  failedPulls.push(pullNumber);
38251
38260
  if (result.evidence) {
38252
38261
  core.summary.addRaw((0, pull_request_approval_presentation_policy_1.renderApprovalRunSummary)({
@@ -38267,6 +38276,8 @@ async function runPullRequestApprovalAction(token) {
38267
38276
  }
38268
38277
  }
38269
38278
  await core.summary.write();
38279
+ if (firstFailure)
38280
+ throw firstFailure;
38270
38281
  if (failedPulls.length > 0) {
38271
38282
  throw new Error(`Approval observation needs attention for PR(s) ${failedPulls.join(', ')}; inspect the Job Summary and rerun after recovery.`);
38272
38283
  }
@@ -38816,6 +38827,55 @@ function runAtApplicationErrorBoundary(operation) {
38816
38827
  }
38817
38828
 
38818
38829
 
38830
+ /***/ }),
38831
+
38832
+ /***/ 50519:
38833
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
38834
+
38835
+ "use strict";
38836
+
38837
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
38838
+ exports.approvalEvidenceError = approvalEvidenceError;
38839
+ exports.approvalDiagnosticError = approvalDiagnosticError;
38840
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
38841
+ const application_error_1 = __nccwpck_require__(75999);
38842
+ function approvalEvidenceError(operation, reason) {
38843
+ return approvalDiagnosticError({ operation, reason, permission: approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS[operation] });
38844
+ }
38845
+ function approvalDiagnosticError(diagnostic) {
38846
+ const reason = diagnostic.reason;
38847
+ const code = reason === 'http-401' ? 'authorization.credential-invalid'
38848
+ : reason === 'http-403' ? 'authorization.denied'
38849
+ : reason === 'http-404' ? 'provider.not-found'
38850
+ : reason === 'http-429' ? 'provider.rate-limited'
38851
+ : reason === 'http-409' ? 'provider.conflict'
38852
+ : reason === 'transport-unavailable' || /^http-50[0234]$/u.test(reason) ? 'provider.unavailable'
38853
+ : 'provider.contract-invalid';
38854
+ return new application_error_1.ApplicationError(code, `Approval evidence read failed (${diagnostic.operation}, ${reason}).`, {
38855
+ recovery: { id: 'approval-evidence-read-failed', variables: diagnostic },
38856
+ });
38857
+ }
38858
+
38859
+
38860
+ /***/ }),
38861
+
38862
+ /***/ 12183:
38863
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
38864
+
38865
+ "use strict";
38866
+
38867
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
38868
+ exports.closedIssueReconciliationError = closedIssueReconciliationError;
38869
+ const application_error_1 = __nccwpck_require__(75999);
38870
+ /** Public diagnostic is fixed native facts and counters, never label/card/provider text. */
38871
+ function closedIssueReconciliationError(cause, facts) {
38872
+ return new application_error_1.ApplicationError(cause instanceof application_error_1.ApplicationError ? cause.code : 'provider.unavailable', 'Closed issue reconciliation remains unverified.', {
38873
+ ...(cause instanceof application_error_1.ApplicationError ? { correlationId: cause.correlationId, retryable: cause.retryable } : {}),
38874
+ recovery: { id: 'closed-issue-reconciliation-failed', variables: facts },
38875
+ });
38876
+ }
38877
+
38878
+
38819
38879
  /***/ }),
38820
38880
 
38821
38881
  /***/ 72594:
@@ -38854,7 +38914,7 @@ const application_error_message_catalog_1 = __nccwpck_require__(64809);
38854
38914
  const SIMPLE_MESSAGE_KEYS = Object.freeze([
38855
38915
  'heading', 'repository', 'property', 'value', 'status', 'event', 'target',
38856
38916
  'lifecycle', 'descriptionPolicy', 'results', 'findingStates', 'bugbotReview',
38857
- 'inactivity', 'unavailable',
38917
+ 'inactivity', 'unavailable', 'issueCleanup',
38858
38918
  'sourceFreshness', 'staleSourceSuppressed',
38859
38919
  'duplicateCleanup', 'actionNotifications', 'actionNotificationCreated',
38860
38920
  'actionNotificationReused',
@@ -38876,6 +38936,7 @@ const TEMPLATE_MESSAGE_IDS = Object.freeze([
38876
38936
  'summary.duplicateCleanup.bounded',
38877
38937
  'summary.actionNotification.entry',
38878
38938
  'summary.inactivityCounts',
38939
+ 'summary.issueCleanupCounts',
38879
38940
  ]);
38880
38941
  const FINDING_STATE_KEYS = Object.freeze([
38881
38942
  'open', 'reopened', 'fixed', 'obsolete', 'dismissed',
@@ -38899,6 +38960,7 @@ const ENGLISH_SIMPLE = Object.freeze({
38899
38960
  descriptionPolicy: 'PR description policy',
38900
38961
  results: 'Results',
38901
38962
  inactivity: 'Issue inactivity',
38963
+ issueCleanup: 'Issue cleanup',
38902
38964
  unavailable: 'unavailable',
38903
38965
  findingStates: 'Finding states',
38904
38966
  bugbotReview: 'Bugbot review',
@@ -38948,6 +39010,7 @@ const SPANISH_SIMPLE = Object.freeze({
38948
39010
  descriptionPolicy: 'Política de descripción de PR',
38949
39011
  results: 'Resultados',
38950
39012
  inactivity: 'Inactividad de issues',
39013
+ issueCleanup: 'Limpieza de la issue',
38951
39014
  unavailable: 'no disponible',
38952
39015
  findingStates: 'Estados de los hallazgos',
38953
39016
  bugbotReview: 'Revisión de Bugbot',
@@ -38995,6 +39058,7 @@ const ENGLISH_TEMPLATES = Object.freeze({
38995
39058
  'summary.duplicateCleanup.bounded': 'Deletion was forbidden; retained compact pointers for {count} comments (first {reported} IDs: {ids})',
38996
39059
  'summary.actionNotification.entry': '{topic} on {target}: {effect} (fingerprint {fingerprint})',
38997
39060
  'summary.inactivityCounts': 'Scanned: {scanned} · Closed: {closed} · Skipped: {skipped}',
39061
+ 'summary.issueCleanupCounts': 'Reconciled: {reconciled} · Already converged: {converged}. Required cleanup was freshly verified.',
38998
39062
  });
38999
39063
  const SPANISH_TEMPLATES = Object.freeze({
39000
39064
  'summary.target.pullRequest': 'PR n.º {number}',
@@ -39006,6 +39070,7 @@ const SPANISH_TEMPLATES = Object.freeze({
39006
39070
  'summary.duplicateCleanup.bounded': 'Se denegó el borrado; se conservaron enlaces compactos para {count} comentarios (primeros {reported} ID: {ids})',
39007
39071
  'summary.actionNotification.entry': '{topic} en {target}: {effect} (huella {fingerprint})',
39008
39072
  'summary.inactivityCounts': 'Examinadas: {scanned} · Cerradas: {closed} · Omitidas: {skipped}',
39073
+ 'summary.issueCleanupCounts': 'Reconciliadas: {reconciled} · Ya convergidas: {converged}. Se verificó de nuevo la limpieza necesaria.',
39009
39074
  });
39010
39075
  const ENGLISH_FINDING_STATES = Object.freeze({
39011
39076
  open: 'open',
@@ -39068,6 +39133,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
39068
39133
  exports.buildActionSummary = buildActionSummary;
39069
39134
  exports.actionSummaryLocalizationLabels = actionSummaryLocalizationLabels;
39070
39135
  exports.renderLocalizationSummarySection = renderLocalizationSummarySection;
39136
+ const closed_issue_reconciliation_outcome_policy_1 = __nccwpck_require__(85462);
39071
39137
  const inactivity_result_counts_projection_policy_1 = __nccwpck_require__(14960);
39072
39138
  const github_comment_publication_policy_1 = __nccwpck_require__(72712);
39073
39139
  const bugbot_telemetry_projection_policy_1 = __nccwpck_require__(43244);
@@ -39117,6 +39183,7 @@ function buildActionSummary(context, catalog = (0, action_summary_message_catalo
39117
39183
  `| ${catalogText(catalog, 'summary.descriptionPolicy')} | ${escapeTable(context.pullRequestDescriptionMode ?? '—')} |`,
39118
39184
  `| ${catalogText(catalog, 'summary.results')} | ${formatResultCounts(context.results, catalog)} |`,
39119
39185
  ...inactivitySummaryRows(context.results, catalog),
39186
+ ...closedIssueCleanupRows(context.results, catalog),
39120
39187
  `| ${catalogText(catalog, 'summary.findingStates')} | ${formatFindingStates(findingStateProjection, catalog)} |`,
39121
39188
  `| ${catalogText(catalog, 'summary.bugbotReview')} | ${formatBugbotTelemetry(telemetryProjection, catalog)} |`,
39122
39189
  ...(staleSourceSuppressed ? [
@@ -39147,6 +39214,14 @@ function buildActionSummary(context, catalog = (0, action_summary_message_catalo
39147
39214
  ...(localization ? ['', localization] : []),
39148
39215
  ].join('\n');
39149
39216
  }
39217
+ function closedIssueCleanupRows(results, catalog) {
39218
+ const projection = (0, closed_issue_reconciliation_outcome_policy_1.projectClosedIssueCleanupCounts)(results);
39219
+ if (projection.status === 'absent')
39220
+ return [];
39221
+ const value = projection.status === 'valid' ? catalogText(catalog, 'summary.issueCleanupCounts', projection.counts)
39222
+ : catalogText(catalog, 'summary.unavailable');
39223
+ return [`| ${catalogText(catalog, 'summary.issueCleanup')} | ${value} |`];
39224
+ }
39150
39225
  function inactivitySummaryRows(results, catalog) {
39151
39226
  const projection = (0, inactivity_result_counts_projection_policy_1.projectInactivityResultCounts)(results);
39152
39227
  if (projection.status === 'absent')
@@ -40592,6 +40667,16 @@ const SPANISH_CONTENT = Object.freeze({
40592
40667
  }),
40593
40668
  });
40594
40669
  const ENGLISH_RECOVERY_CONTENT = Object.freeze({
40670
+ 'closed-issue-reconciliation-failed': Object.freeze({
40671
+ impact: 'Issue cleanup remains unverified: {resultId}, phase {phase}; native state {state}, reason {reason}.',
40672
+ 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.',
40673
+ retainedState: 'Native state was preserved. Confirmed changes: {changed}; remaining required changes in the last verified plan: {remaining}. Unavailable reads do not establish completion.',
40674
+ }),
40675
+ 'approval-evidence-read-failed': Object.freeze({
40676
+ impact: 'Approval evidence could not be verified: {operation} ({reason}).',
40677
+ 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.',
40678
+ retainedState: 'No approval is authorized by this failed read. Existing PRs, reviews and completed external effects were preserved.',
40679
+ }),
40595
40680
  'workspace-changes-rejected': Object.freeze({
40596
40681
  impact: 'The changes were rejected during workspace verification.',
40597
40682
  action: 'Inspect the rejected paths in the workflow and correct the workspace before retrying.',
@@ -40659,6 +40744,16 @@ const ENGLISH_RECOVERY_CONTENT = Object.freeze({
40659
40744
  }),
40660
40745
  });
40661
40746
  const SPANISH_RECOVERY_CONTENT = Object.freeze({
40747
+ 'closed-issue-reconciliation-failed': Object.freeze({
40748
+ impact: 'La limpieza de la issue sigue sin verificarse: {resultId}, fase {phase}; estado nativo {state}, motivo {reason}.',
40749
+ 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.',
40750
+ 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.',
40751
+ }),
40752
+ 'approval-evidence-read-failed': Object.freeze({
40753
+ impact: 'No se pudo verificar la evidencia de aprobación: {operation} ({reason}).',
40754
+ 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.',
40755
+ retainedState: 'Esta lectura fallida no autoriza ninguna aprobación. Se conservaron las PRs, revisiones y efectos externos completados.',
40756
+ }),
40662
40757
  'workspace-changes-rejected': Object.freeze({
40663
40758
  impact: 'Los cambios se rechazaron durante la verificación del espacio de trabajo.',
40664
40759
  action: 'Revisa las rutas rechazadas en la ejecución y corrige el espacio de trabajo antes de reintentar.',
@@ -42877,6 +42972,51 @@ function hasBugbotTelemetryField(value) {
42877
42972
  }
42878
42973
 
42879
42974
 
42975
+ /***/ }),
42976
+
42977
+ /***/ 85462:
42978
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
42979
+
42980
+ "use strict";
42981
+
42982
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
42983
+ exports.projectClosedIssueCleanupCounts = projectClosedIssueCleanupCounts;
42984
+ const result_1 = __nccwpck_require__(73817);
42985
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
42986
+ /** Summary consumes semantic counts; internal result identifiers never become presentation. */
42987
+ function projectClosedIssueCleanupCounts(results) {
42988
+ let reconciled = 0;
42989
+ let converged = 0;
42990
+ let invalid = false;
42991
+ for (const result of results.filter(result => closed_issue_reconciliation_1.CLOSED_ISSUE_RESULTS.some(id => id === result.id))) {
42992
+ const payload = (0, result_1.getResultPayload)(result.payload);
42993
+ if (!result.success || result.errors?.length) {
42994
+ invalid = true;
42995
+ continue;
42996
+ }
42997
+ if (!payload)
42998
+ continue;
42999
+ const completion = result.id === 'CloseIssueAfterMergingUseCase' || result.id === 'ReconcileClosedIssueMetadataUseCase'
43000
+ ? (0, result_1.getResultPayload)(payload.issueCompletion) : payload;
43001
+ if (completion?.outcome === 'not-applicable' && completion.nativeClosed === false)
43002
+ continue;
43003
+ if (completion?.nativeClosed !== true || completion.reconciled !== true) {
43004
+ invalid = true;
43005
+ continue;
43006
+ }
43007
+ if (completion.outcome === 'already-converged')
43008
+ converged++;
43009
+ else if (completion.outcome === 'reconciled')
43010
+ reconciled++;
43011
+ else
43012
+ invalid = true;
43013
+ }
43014
+ if (!invalid && !reconciled && !converged)
43015
+ return { status: 'absent' };
43016
+ return invalid ? { status: 'invalid' } : { status: 'valid', counts: { reconciled, converged } };
43017
+ }
43018
+
43019
+
42880
43020
  /***/ }),
42881
43021
 
42882
43022
  /***/ 27150:
@@ -54062,8 +54202,9 @@ function failure(taskId, message, code) {
54062
54202
  Object.defineProperty(exports, "__esModule", ({ value: true }));
54063
54203
  exports.ReconcileClosedIssueMetadataUseCase = void 0;
54064
54204
  const result_1 = __nccwpck_require__(73817);
54065
- const issue_completion_1 = __nccwpck_require__(31147);
54066
54205
  const application_error_1 = __nccwpck_require__(75999);
54206
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
54207
+ const reconcile_issue_completion_metadata_1 = __nccwpck_require__(70614);
54067
54208
  /** Selective completion repair from fresh native facts; no native state authority. */
54068
54209
  class ReconcileClosedIssueMetadataUseCase {
54069
54210
  constructor(issues) {
@@ -54071,50 +54212,22 @@ class ReconcileClosedIssueMetadataUseCase {
54071
54212
  this.taskId = 'ReconcileClosedIssueMetadataUseCase';
54072
54213
  }
54073
54214
  async invoke(context) {
54074
- let labelsChanged = 0;
54075
- let nativeClosed = false;
54215
+ const progress = (0, reconcile_issue_completion_metadata_1.completionRepairProgress)();
54076
54216
  try {
54077
54217
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1)
54078
54218
  throw stale();
54079
54219
  const target = this.issues.forIssue(context.issueNumber);
54080
- const initial = await target.read();
54081
- nativeClosed = initial.number === context.issueNumber && initial.state === 'closed';
54082
- const initialPlan = (0, issue_completion_1.planIssueCompletion)(initial, context.completionLabels);
54083
- if (!nativeClosed || initialPlan.kind !== 'closed')
54084
- throw stale();
54085
- const fence = async () => {
54086
- const current = await target.read();
54087
- nativeClosed = current.number === context.issueNumber && current.state === 'closed';
54088
- const plan = (0, issue_completion_1.planIssueCompletion)(current, context.completionLabels);
54089
- if (!nativeClosed || plan.kind !== 'closed' || current.reason !== initial.reason)
54090
- throw stale();
54091
- return plan;
54092
- };
54093
- for (const label of initialPlan.remove) {
54094
- if ((await fence()).remove.includes(label)) {
54095
- await target.removeLabel(label);
54096
- labelsChanged++;
54097
- }
54098
- }
54099
- for (const label of initialPlan.add) {
54100
- if ((await fence()).add.includes(label)) {
54101
- await target.addLabel(label);
54102
- labelsChanged++;
54103
- }
54104
- }
54105
- const final = await fence();
54106
- if (final.remove.length || final.add.length)
54107
- throw stale();
54108
- return [new result_1.Result({ id: this.taskId, success: true, executed: labelsChanged > 0,
54220
+ const repaired = await (0, reconcile_issue_completion_metadata_1.reconcileIssueCompletionMetadata)(target, context.issueNumber, context.completionLabels, progress);
54221
+ return [new result_1.Result({ id: this.taskId, success: true, executed: progress.labelsChanged > 0,
54109
54222
  steps: ['The closed issue completion metadata was verified; historical and unrelated labels were retained.'],
54110
- payload: { issueCompletion: { nativeClosed, labelsChanged, reconciled: true, reason: initial.reason } } })];
54223
+ payload: { issueCompletion: { nativeClosed: progress.nativeClosed, labelsChanged: progress.labelsChanged, reconciled: true, ...repaired } } })];
54111
54224
  }
54112
54225
  catch (cause) {
54113
- return [new result_1.Result({ id: this.taskId, success: false, executed: labelsChanged > 0,
54114
- steps: [nativeClosed ? 'The issue remains closed; completion metadata could not be fully verified. Inspect and retry the closed-issue workflow.'
54226
+ return [new result_1.Result({ id: this.taskId, success: false, executed: progress.labelsChanged > 0,
54227
+ steps: [progress.nativeClosed ? 'The issue remains closed; completion metadata could not be fully verified. Inspect and retry the closed-issue workflow.'
54115
54228
  : 'Current native issue closure could not be verified; completion repair stopped without changing native state.'],
54116
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Closed issue completion metadata remains unverified.')],
54117
- payload: { issueCompletion: { nativeClosed, labelsChanged, reconciled: false } } })];
54229
+ 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 })],
54230
+ payload: { issueCompletion: { nativeClosed: progress.nativeClosed, labelsChanged: progress.labelsChanged, reconciled: false } } })];
54118
54231
  }
54119
54232
  }
54120
54233
  }
@@ -54147,6 +54260,84 @@ class ReconcileClosedIssueUseCase {
54147
54260
  exports.ReconcileClosedIssueUseCase = ReconcileClosedIssueUseCase;
54148
54261
 
54149
54262
 
54263
+ /***/ }),
54264
+
54265
+ /***/ 70614:
54266
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
54267
+
54268
+ "use strict";
54269
+
54270
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
54271
+ exports.completionRepairProgress = void 0;
54272
+ exports.reconcileIssueCompletionMetadata = reconcileIssueCompletionMetadata;
54273
+ const application_error_1 = __nccwpck_require__(75999);
54274
+ const issue_completion_1 = __nccwpck_require__(31147);
54275
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
54276
+ const completionRepairProgress = () => ({ nativeClosed: false, labelsChanged: 0,
54277
+ phase: 'identity', state: 'unavailable', reason: 'unavailable', remaining: 0 });
54278
+ exports.completionRepairProgress = completionRepairProgress;
54279
+ /** At most two bounded selective passes, with fresh native fences before every write. */
54280
+ async function reconcileIssueCompletionMetadata(target, number, configuration, progress, initial) {
54281
+ let expectedReason;
54282
+ let superseded = false;
54283
+ const read = async (phase, snapshot) => {
54284
+ progress.phase = phase;
54285
+ progress.state = 'unavailable';
54286
+ progress.reason = 'unavailable';
54287
+ const facts = snapshot ?? await target.read();
54288
+ progress.nativeClosed = facts?.number === number && facts.state === 'closed';
54289
+ progress.state = facts?.number === number && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
54290
+ progress.reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(reason => reason === facts?.reason) ? facts.reason : 'unavailable';
54291
+ const plan = (0, issue_completion_1.planIssueCompletion)(facts, configuration);
54292
+ if (!progress.nativeClosed || plan.kind !== 'closed' || expectedReason !== undefined && facts.reason !== expectedReason) {
54293
+ throw new application_error_1.ApplicationError('workflow.stale', 'The native issue identity, state or closure reason changed.');
54294
+ }
54295
+ expectedReason = facts.reason;
54296
+ progress.remaining = plan.remove.length + plan.add.length;
54297
+ return plan;
54298
+ };
54299
+ let plan = await read('read', initial);
54300
+ for (let pass = 0; pass < 2; pass++) {
54301
+ for (const label of plan.remove) {
54302
+ if (!(await read('label-remove')).remove.includes(label)) {
54303
+ superseded = true;
54304
+ continue;
54305
+ }
54306
+ try {
54307
+ await target.removeLabel(label);
54308
+ progress.labelsChanged++;
54309
+ }
54310
+ catch (cause) {
54311
+ if ((await read('label-remove')).remove.includes(label))
54312
+ throw cause;
54313
+ superseded = true;
54314
+ }
54315
+ }
54316
+ for (const label of plan.add) {
54317
+ if (!(await read('label-add')).add.includes(label)) {
54318
+ superseded = true;
54319
+ continue;
54320
+ }
54321
+ try {
54322
+ await target.addLabel(label);
54323
+ progress.labelsChanged++;
54324
+ }
54325
+ catch (cause) {
54326
+ if ((await read('label-add')).add.includes(label))
54327
+ throw cause;
54328
+ superseded = true;
54329
+ }
54330
+ }
54331
+ plan = await read('metadata-verification');
54332
+ if (!progress.remaining)
54333
+ return { reason: plan.reason,
54334
+ outcome: progress.labelsChanged ? 'reconciled' : 'already-converged', superseded };
54335
+ superseded = true;
54336
+ }
54337
+ throw new application_error_1.ApplicationError('provider.conflict', 'Completion metadata remains incomplete after two bounded passes.');
54338
+ }
54339
+
54340
+
54150
54341
  /***/ }),
54151
54342
 
54152
54343
  /***/ 74111:
@@ -54156,7 +54347,9 @@ exports.ReconcileClosedIssueUseCase = ReconcileClosedIssueUseCase;
54156
54347
 
54157
54348
  Object.defineProperty(exports, "__esModule", ({ value: true }));
54158
54349
  exports.RetireBranchSyncUseCase = void 0;
54159
- const reply_request_digest_1 = __nccwpck_require__(47971);
54350
+ const verify_closed_issue_card_retirement_1 = __nccwpck_require__(78813);
54351
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
54352
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
54160
54353
  const result_1 = __nccwpck_require__(73817);
54161
54354
  const github_user_policy_1 = __nccwpck_require__(84403);
54162
54355
  const publication_identity_policy_1 = __nccwpck_require__(45403);
@@ -54174,17 +54367,33 @@ class RetireBranchSyncUseCase {
54174
54367
  async invoke(context) {
54175
54368
  let retired = 0;
54176
54369
  let nativeClosed = false;
54370
+ let phase = 'identity';
54371
+ let state = 'unavailable';
54372
+ let reason = 'unavailable';
54373
+ let remaining = 0;
54374
+ let superseded = false;
54177
54375
  try {
54178
54376
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1 || !context.trustedBotLogin) {
54179
54377
  return [this.result(retired, nativeClosed)];
54180
54378
  }
54181
- const initial = await this.issues.readIssue(context.issueNumber);
54182
- if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state))
54379
+ const read = async () => {
54380
+ state = 'unavailable';
54381
+ reason = 'unavailable';
54382
+ const facts = await this.issues.readIssue(context.issueNumber);
54383
+ nativeClosed = facts?.number === context.issueNumber && facts.state === 'closed';
54384
+ state = facts?.number === context.issueNumber && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
54385
+ reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === facts?.reason) ? facts.reason : 'unavailable';
54386
+ return facts;
54387
+ };
54388
+ phase = 'read';
54389
+ const initial = await read();
54390
+ if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state) || initial.state === 'closed' && !['completed', 'not_planned', 'duplicate', 'unknown'].includes(initial.reason))
54183
54391
  throw stale();
54184
54392
  if (initial.state !== 'closed')
54185
54393
  return [this.result(retired, nativeClosed)];
54186
54394
  nativeClosed = true;
54187
54395
  const messages = (0, branch_sync_message_catalog_1.resolveStaticBranchSyncCatalog)(context.locale);
54396
+ phase = 'card-inventory';
54188
54397
  const cards = (await this.comments.listIssueComments(context.issueNumber)).filter(comment => {
54189
54398
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(comment.body);
54190
54399
  return marker?.identity.topic === 'branch-sync' && marker.identity.target.kind === 'issue'
@@ -54193,38 +54402,40 @@ class RetireBranchSyncUseCase {
54193
54402
  && Number.isSafeInteger(comment.id) && comment.id > 0
54194
54403
  && Boolean(comment.user?.login && (0, github_user_policy_1.githubUsersMatch)(context.trustedBotLogin, comment.user.login));
54195
54404
  });
54405
+ remaining = Math.min(cards.length, 3000);
54196
54406
  if (cards.length > 20)
54197
54407
  throw stale();
54408
+ const verifyClosed = async () => {
54409
+ phase = 'native-fence';
54410
+ const fresh = await read();
54411
+ if (!nativeClosed || fresh.reason !== initial.reason)
54412
+ throw stale();
54413
+ };
54198
54414
  for (const card of cards) {
54199
54415
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(card.body);
54200
54416
  const body = (0, branch_sync_notification_policy_1.buildRetiredBranchSyncComment)(marker.identity, messages);
54201
- if (card.body === body)
54202
- continue;
54203
- const witness = Object.freeze({ surface: 'issue-comment', commentId: card.id,
54204
- author: context.trustedBotLogin, target: Object.freeze({ kind: 'issue', number: context.issueNumber }),
54205
- bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(card.body ?? '') });
54206
- if (!await this.cardFreshness.matches(witness))
54207
- throw stale();
54208
- const fresh = await this.issues.readIssue(context.issueNumber);
54209
- nativeClosed = fresh.number === context.issueNumber && fresh.state === 'closed';
54210
- if (!nativeClosed || fresh.reason !== initial.reason)
54211
- throw stale();
54212
- await this.comments.updateComment(context.issueNumber, card.id, body);
54213
- retired++;
54417
+ const verified = await (0, verify_closed_issue_card_retirement_1.verifyClosedIssueCardRetirement)({ issueNumber: context.issueNumber,
54418
+ commentId: card.id, author: context.trustedBotLogin, originalBody: card.body ?? '', finalBody: body,
54419
+ freshness: this.cardFreshness, verifyClosed, onUpdated: () => { retired++; }, phase: value => { phase = value; },
54420
+ update: () => this.comments.updateComment(context.issueNumber, card.id, body) });
54421
+ superseded || (superseded = verified.superseded);
54422
+ remaining--;
54214
54423
  }
54215
- return [this.result(retired, nativeClosed)];
54424
+ await verifyClosed();
54425
+ return [this.result(retired, nativeClosed, superseded)];
54216
54426
  }
54217
54427
  catch (cause) {
54218
54428
  return [new result_1.Result({ id: this.taskId, success: false, executed: retired > 0,
54219
54429
  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.'],
54220
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Unable to retire synchronization recommendations safely.')],
54430
+ errors: [(0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(cause, { resultId: this.taskId, phase, state, reason, changed: retired, remaining })],
54221
54431
  payload: { nativeClosed, retiredCards: retired, reconciled: false } })];
54222
54432
  }
54223
54433
  }
54224
- result(retired, nativeClosed) {
54434
+ result(retired, nativeClosed, superseded = false) {
54225
54435
  return new result_1.Result({ id: this.taskId, success: true, executed: retired > 0,
54226
54436
  steps: [retired ? 'The closed issue’s synchronization recommendations were retired.' : 'No synchronization recommendation required retirement.'],
54227
- payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true } });
54437
+ payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true, superseded,
54438
+ outcome: nativeClosed ? retired ? 'reconciled' : 'already-converged' : 'not-applicable' } });
54228
54439
  }
54229
54440
  }
54230
54441
  exports.RetireBranchSyncUseCase = RetireBranchSyncUseCase;
@@ -54265,7 +54476,9 @@ exports.RetireClosedIssueCardsUseCase = RetireClosedIssueCardsUseCase;
54265
54476
 
54266
54477
  Object.defineProperty(exports, "__esModule", ({ value: true }));
54267
54478
  exports.RetireProgressUseCase = void 0;
54268
- const reply_request_digest_1 = __nccwpck_require__(47971);
54479
+ const verify_closed_issue_card_retirement_1 = __nccwpck_require__(78813);
54480
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
54481
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
54269
54482
  const result_1 = __nccwpck_require__(73817);
54270
54483
  const github_user_policy_1 = __nccwpck_require__(84403);
54271
54484
  const publication_identity_policy_1 = __nccwpck_require__(45403);
@@ -54282,16 +54495,32 @@ class RetireProgressUseCase {
54282
54495
  async invoke(context) {
54283
54496
  let retired = 0;
54284
54497
  let nativeClosed = false;
54498
+ let phase = 'identity';
54499
+ let state = 'unavailable';
54500
+ let reason = 'unavailable';
54501
+ let remaining = 0;
54502
+ let superseded = false;
54285
54503
  try {
54286
54504
  if (!Number.isSafeInteger(context.issueNumber) || context.issueNumber < 1 || !context.trustedBotLogin) {
54287
54505
  return [this.result(retired, nativeClosed)];
54288
54506
  }
54289
- const initial = await this.issues.readIssue(context.issueNumber);
54507
+ const read = async () => {
54508
+ state = 'unavailable';
54509
+ reason = 'unavailable';
54510
+ const facts = await this.issues.readIssue(context.issueNumber);
54511
+ nativeClosed = facts?.number === context.issueNumber && facts.state === 'closed';
54512
+ state = facts?.number === context.issueNumber && ['open', 'closed'].includes(facts.state) ? facts.state : 'unavailable';
54513
+ reason = closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === facts?.reason) ? facts.reason : 'unavailable';
54514
+ return facts;
54515
+ };
54516
+ phase = 'read';
54517
+ const initial = await read();
54290
54518
  if (initial.number !== context.issueNumber || !['open', 'closed'].includes(initial.state) || initial.state === 'closed' && !['completed', 'not_planned', 'duplicate', 'unknown'].includes(initial.reason))
54291
54519
  throw stale();
54292
54520
  if (initial.state !== 'closed')
54293
54521
  return [this.result(retired, nativeClosed)];
54294
54522
  nativeClosed = true;
54523
+ phase = 'card-inventory';
54295
54524
  const cards = (await this.comments.listIssueComments(context.issueNumber)).filter(comment => {
54296
54525
  const marker = (0, publication_identity_policy_1.parsePublicationMarker)(comment.body);
54297
54526
  return marker?.identity.topic === 'progress' && marker.identity.target.kind === 'issue'
@@ -54300,37 +54529,39 @@ class RetireProgressUseCase {
54300
54529
  && Number.isSafeInteger(comment.id) && comment.id > 0
54301
54530
  && Boolean(comment.user?.login && (0, github_user_policy_1.githubUsersMatch)(context.trustedBotLogin, comment.user.login));
54302
54531
  });
54532
+ remaining = Math.min(cards.length, 3000);
54303
54533
  if (cards.length > 20)
54304
54534
  throw stale();
54305
- for (const card of cards) {
54306
- const body = (0, retired_progress_presentation_policy_1.buildRetiredProgressComment)(card.body ?? '', context.locale, initial.reason);
54307
- if (card.body === body)
54308
- continue;
54309
- const witness = Object.freeze({ surface: 'issue-comment', commentId: card.id,
54310
- author: context.trustedBotLogin, target: Object.freeze({ kind: 'issue', number: context.issueNumber }),
54311
- bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(card.body ?? '') });
54312
- if (!await this.cardFreshness.matches(witness))
54313
- throw stale();
54314
- const fresh = await this.issues.readIssue(context.issueNumber);
54315
- nativeClosed = fresh.number === context.issueNumber && fresh.state === 'closed';
54535
+ const verifyClosed = async () => {
54536
+ phase = 'native-fence';
54537
+ const fresh = await read();
54316
54538
  if (!nativeClosed || fresh.reason !== initial.reason)
54317
54539
  throw stale();
54318
- await this.comments.updateComment(context.issueNumber, card.id, body);
54319
- retired++;
54540
+ };
54541
+ for (const card of cards) {
54542
+ const body = (0, retired_progress_presentation_policy_1.buildRetiredProgressComment)(card.body ?? '', context.locale, initial.reason);
54543
+ const verified = await (0, verify_closed_issue_card_retirement_1.verifyClosedIssueCardRetirement)({ issueNumber: context.issueNumber,
54544
+ commentId: card.id, author: context.trustedBotLogin, originalBody: card.body ?? '', finalBody: body,
54545
+ freshness: this.cardFreshness, verifyClosed, onUpdated: () => { retired++; }, phase: value => { phase = value; },
54546
+ update: () => this.comments.updateComment(context.issueNumber, card.id, body) });
54547
+ superseded || (superseded = verified.superseded);
54548
+ remaining--;
54320
54549
  }
54321
- return [this.result(retired, nativeClosed)];
54550
+ await verifyClosed();
54551
+ return [this.result(retired, nativeClosed, superseded)];
54322
54552
  }
54323
54553
  catch (cause) {
54324
54554
  return [new result_1.Result({ id: this.taskId, success: false, executed: retired > 0,
54325
54555
  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.'],
54326
- errors: [(0, application_error_1.toApplicationError)(cause, 'provider.unavailable', 'Unable to retire progress cards safely.')],
54556
+ errors: [(0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(cause, { resultId: this.taskId, phase, state, reason, changed: retired, remaining })],
54327
54557
  payload: { nativeClosed, retiredCards: retired, reconciled: false } })];
54328
54558
  }
54329
54559
  }
54330
- result(retired, nativeClosed) {
54560
+ result(retired, nativeClosed, superseded = false) {
54331
54561
  return new result_1.Result({ id: this.taskId, success: true, executed: retired > 0,
54332
54562
  steps: [retired ? 'The closed issue’s progress cards were retired.' : 'No progress card required retirement.'],
54333
- payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true } });
54563
+ payload: { ...(nativeClosed ? { state: 'closed' } : {}), nativeClosed, retiredCards: retired, reconciled: true, superseded,
54564
+ outcome: nativeClosed ? retired ? 'reconciled' : 'already-converged' : 'not-applicable' } });
54334
54565
  }
54335
54566
  }
54336
54567
  exports.RetireProgressUseCase = RetireProgressUseCase;
@@ -54853,6 +55084,48 @@ function sameLabels(left, right) {
54853
55084
  }
54854
55085
 
54855
55086
 
55087
+ /***/ }),
55088
+
55089
+ /***/ 78813:
55090
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
55091
+
55092
+ "use strict";
55093
+
55094
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
55095
+ exports.verifyClosedIssueCardRetirement = verifyClosedIssueCardRetirement;
55096
+ const reply_request_digest_1 = __nccwpck_require__(47971);
55097
+ const application_error_1 = __nccwpck_require__(75999);
55098
+ /** Adopt another invocation only after the exact final owned body and native closure are fresh. */
55099
+ async function verifyClosedIssueCardRetirement(input) {
55100
+ const witness = (body) => Object.freeze({ surface: 'issue-comment',
55101
+ commentId: input.commentId, author: input.author,
55102
+ target: Object.freeze({ kind: 'issue', number: input.issueNumber }), bodyDigest: (0, reply_request_digest_1.replyRequestBodyDigest)(body) });
55103
+ let updated = false;
55104
+ let failedWrite;
55105
+ if (input.originalBody !== input.finalBody) {
55106
+ input.phase('card-freshness');
55107
+ if (await input.freshness.matches(witness(input.originalBody))) {
55108
+ await input.verifyClosed();
55109
+ input.phase('card-update');
55110
+ try {
55111
+ await input.update();
55112
+ updated = true;
55113
+ input.onUpdated();
55114
+ }
55115
+ catch (cause) {
55116
+ failedWrite = cause;
55117
+ }
55118
+ }
55119
+ }
55120
+ input.phase('card-verification');
55121
+ if (!await input.freshness.matches(witness(input.finalBody))) {
55122
+ throw failedWrite ?? new application_error_1.ApplicationError('workflow.stale', 'The required final owned card could not be verified.');
55123
+ }
55124
+ await input.verifyClosed();
55125
+ return { updated, superseded: !updated && input.originalBody !== input.finalBody };
55126
+ }
55127
+
55128
+
54856
55129
  /***/ }),
54857
55130
 
54858
55131
  /***/ 4643:
@@ -57197,6 +57470,8 @@ function buildCatalogResponseSchema(source, ids, targetLocale = 'en-US') {
57197
57470
 
57198
57471
  Object.defineProperty(exports, "__esModule", ({ value: true }));
57199
57472
  exports.ObservePullRequestApprovalUseCase = void 0;
57473
+ const application_error_1 = __nccwpck_require__(75999);
57474
+ const approval_evidence_error_1 = __nccwpck_require__(50519);
57200
57475
  const pull_request_approval_1 = __nccwpck_require__(8024);
57201
57476
  const pull_request_approval_policy_1 = __nccwpck_require__(98820);
57202
57477
  /** Read-first replay and exact-revision guard around the non-atomic GitHub review API. */
@@ -57210,8 +57485,9 @@ class ObservePullRequestApprovalUseCase {
57210
57485
  try {
57211
57486
  rawPolicy = await this.port.readPolicy(target);
57212
57487
  }
57213
- catch {
57488
+ catch (cause) {
57214
57489
  return {
57490
+ failure: evidenceFailure(cause, 'policy'),
57215
57491
  decision: { status: 'blocked', code: 'policy-unavailable', detail: 'The installed PR approval policy could not be read from GitHub. Verify the runtime PAT can read Actions Variables, then rerun the observer. Existing PRs and reviews are unchanged.' },
57216
57492
  publication: 'not-applicable',
57217
57493
  };
@@ -57232,8 +57508,9 @@ class ObservePullRequestApprovalUseCase {
57232
57508
  try {
57233
57509
  initial = await this.port.loadEvidence(target);
57234
57510
  }
57235
- catch {
57511
+ catch (cause) {
57236
57512
  return {
57513
+ failure: evidenceFailure(cause),
57237
57514
  decision: { status: 'blocked', code: 'evidence-unavailable', detail: 'GitHub approval evidence could not be read; rerun this observer.' },
57238
57515
  publication: 'not-applicable',
57239
57516
  };
@@ -57241,18 +57518,30 @@ class ObservePullRequestApprovalUseCase {
57241
57518
  if (initial.repositoryId !== target.repositoryId || initial.pullNumber !== target.pullNumber) {
57242
57519
  return { decision: { status: 'blocked', code: 'target-mismatch', detail: 'The resolved PR does not match the bound repository and number.' }, publication: 'not-applicable' };
57243
57520
  }
57244
- let decision = (0, pull_request_approval_1.decidePullRequestApproval)(policy, initial);
57521
+ let failure = initial.diagnostics?.[0] ? (0, approval_evidence_error_1.approvalDiagnosticError)(initial.diagnostics[0]) : undefined;
57522
+ let decision = failure
57523
+ ? { status: 'blocked', code: 'evidence-unavailable', detail: 'Approval evidence is unavailable; inspect the causal diagnostic in the observer Job Summary.' }
57524
+ : (0, pull_request_approval_1.decidePullRequestApproval)(policy, initial);
57245
57525
  let reviewId;
57246
57526
  if (decision.status === 'eligible') {
57247
57527
  let current;
57248
57528
  let currentPolicy;
57529
+ let readOperation = 'evidence';
57249
57530
  try {
57250
57531
  current = await this.port.loadEvidence(target);
57532
+ readOperation = 'policy';
57251
57533
  currentPolicy = await this.port.readPolicy(target);
57252
57534
  }
57253
- catch {
57535
+ catch (cause) {
57536
+ failure = evidenceFailure(cause, readOperation);
57537
+ current = undefined;
57254
57538
  decision = { status: 'blocked', code: 'pre-submit-unavailable', detail: 'Current approval evidence could not be reread before submission.' };
57255
57539
  }
57540
+ if (current?.diagnostics?.length) {
57541
+ failure = (0, approval_evidence_error_1.approvalDiagnosticError)(current.diagnostics[0]);
57542
+ current = undefined;
57543
+ decision = { status: 'blocked', code: 'pre-submit-unavailable', detail: 'Current evidence is unavailable; inspect the observer diagnostic.' };
57544
+ }
57256
57545
  if (current && currentPolicy !== rawPolicy) {
57257
57546
  decision = { status: 'pending', code: 'superseded', detail: 'The approval policy changed before submission; recheck the current head.' };
57258
57547
  }
@@ -57274,7 +57563,11 @@ class ObservePullRequestApprovalUseCase {
57274
57563
  const botReviews = readback.reviews.filter(review => review.userId === current.botUserId)
57275
57564
  .sort((left, right) => right.submittedAt.localeCompare(left.submittedAt) || right.id - left.id);
57276
57565
  const observed = botReviews.find(review => review.state === 'APPROVED' && review.commitId === current.headSha);
57277
- if (observed && observed.userId === current.botUserId
57566
+ if (readback.diagnostics?.length) {
57567
+ failure = (0, approval_evidence_error_1.approvalDiagnosticError)(readback.diagnostics[0]);
57568
+ decision = { status: 'pending', code: 'publication-unknown', detail: 'GitHub may have accepted the review; its final evidence is unavailable. Inspect the observer diagnostic before rechecking.' };
57569
+ }
57570
+ else if (observed && observed.userId === current.botUserId
57278
57571
  && observed.state === 'APPROVED' && observed.commitId === current.headSha
57279
57572
  && !botReviews.some(review => review.state === 'DISMISSED' && review.commitId === current.headSha)
57280
57573
  && readback.headSha === current.headSha && readback.baseSha === current.baseSha) {
@@ -57285,7 +57578,8 @@ class ObservePullRequestApprovalUseCase {
57285
57578
  decision = { status: 'pending', code: 'publication-unknown', detail: 'GitHub may have accepted the review; rerun to read it before any new submission.' };
57286
57579
  }
57287
57580
  }
57288
- catch {
57581
+ catch (cause) {
57582
+ failure = evidenceFailure(cause);
57289
57583
  decision = { status: 'pending', code: 'publication-unknown', detail: 'GitHub may have accepted the review; rerun to read it before any new submission.' };
57290
57584
  }
57291
57585
  }
@@ -57294,11 +57588,11 @@ class ObservePullRequestApprovalUseCase {
57294
57588
  try {
57295
57589
  // The adapter guards the current head again before editing the single card.
57296
57590
  await this.port.publishAssessment(target, initial, decision, reviewId);
57297
- return { decision, headSha: initial.headSha, evidence: initial, mode: policy.mode,
57591
+ return { decision, ...(failure ? { failure } : {}), headSha: initial.headSha, evidence: initial, mode: policy.mode,
57298
57592
  ...(reviewId ? { reviewId } : {}), publication: 'published' };
57299
57593
  }
57300
57594
  catch {
57301
- return { decision, headSha: initial.headSha, evidence: initial, mode: policy.mode,
57595
+ return { decision, ...(failure ? { failure } : {}), headSha: initial.headSha, evidence: initial, mode: policy.mode,
57302
57596
  ...(reviewId ? { reviewId } : {}), publication: 'failed' };
57303
57597
  }
57304
57598
  }
@@ -57314,6 +57608,10 @@ function sameRevisionAndEvidence(left, right) {
57314
57608
  function approvalReviewBody(headSha) {
57315
57609
  return `<!-- copilot:guarded-approval:v1 head="${headSha}" -->\nCopilot approved this revision (${headSha.slice(0, 7)}) after its configured CI and coverage gates passed and Bugbot reported no blocking findings. GitHub still applies branch rules. Configure a separate human-review requirement if needed; a bot approval may count toward a generic approval minimum.`;
57316
57610
  }
57611
+ function evidenceFailure(cause, operation = 'evidence') {
57612
+ return cause instanceof application_error_1.ApplicationError && cause.recovery?.id === 'approval-evidence-read-failed'
57613
+ ? cause : (0, approval_evidence_error_1.approvalEvidenceError)(operation, 'transport-unavailable');
57614
+ }
57317
57615
 
57318
57616
 
57319
57617
  /***/ }),
@@ -66398,6 +66696,8 @@ exports.CheckPriorityIssueSizeUseCase = CheckPriorityIssueSizeUseCase;
66398
66696
 
66399
66697
  Object.defineProperty(exports, "__esModule", ({ value: true }));
66400
66698
  exports.CloseIssueAfterMergingUseCase = void 0;
66699
+ const reconcile_issue_completion_metadata_1 = __nccwpck_require__(70614);
66700
+ const closed_issue_reconciliation_error_1 = __nccwpck_require__(12183);
66401
66701
  const result_1 = __nccwpck_require__(73817);
66402
66702
  const issue_completion_1 = __nccwpck_require__(31147);
66403
66703
  const application_error_1 = __nccwpck_require__(75999);
@@ -66416,7 +66716,7 @@ class CloseIssueAfterMergingUseCase {
66416
66716
  steps: ['No separate safe linked issue and pull request were found; issue closure was skipped.'] })];
66417
66717
  let closedThisRun = false;
66418
66718
  let nativeClosed = false;
66419
- let labelsChanged = 0;
66719
+ const progress = (0, reconcile_issue_completion_metadata_1.completionRepairProgress)();
66420
66720
  try {
66421
66721
  const pull = await this.issues.readPullRequest(pullNumber);
66422
66722
  if (pull.number !== pullNumber || pull.state !== 'closed' || pull.merged !== true)
@@ -66440,44 +66740,21 @@ class CloseIssueAfterMergingUseCase {
66440
66740
  nativeClosed = facts.state === 'closed';
66441
66741
  if (!nativeClosed || closedThisRun && facts.reason !== 'completed')
66442
66742
  throw stale();
66443
- const fence = async () => {
66444
- const freshFacts = await read();
66445
- const freshPlan = (0, issue_completion_1.planIssueCompletion)(freshFacts, param.completionLabels);
66446
- if (freshPlan.kind !== 'closed' || freshFacts.reason !== facts.reason)
66447
- throw stale();
66448
- return freshPlan;
66449
- };
66450
- const plan = (0, issue_completion_1.planIssueCompletion)(facts, param.completionLabels);
66451
- if (plan.kind !== 'closed')
66452
- throw stale();
66453
- for (const label of plan.remove) {
66454
- const fresh = await fence();
66455
- if (fresh.remove.includes(label)) {
66456
- await target.removeLabel(label);
66457
- labelsChanged++;
66458
- }
66459
- }
66460
- for (const label of plan.add) {
66461
- const fresh = await fence();
66462
- if (fresh.add.includes(label)) {
66463
- await target.addLabel(label);
66464
- labelsChanged++;
66465
- }
66466
- }
66467
- const final = await fence();
66468
- if (final.remove.length || final.add.length)
66469
- throw stale();
66470
- return [new result_1.Result({ id: this.taskId, success: true, executed: closedThisRun || labelsChanged > 0,
66743
+ const repaired = await (0, reconcile_issue_completion_metadata_1.reconcileIssueCompletionMetadata)(target, number, param.completionLabels, progress, facts);
66744
+ nativeClosed = progress.nativeClosed;
66745
+ return [new result_1.Result({ id: this.taskId, success: true, executed: closedThisRun || progress.labelsChanged > 0,
66471
66746
  steps: [`Issue #${number} is closed and its completion metadata was verified after PR #${pullNumber} merged.`],
66472
- payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged, reason: facts.reason, reconciled: true }) } })];
66747
+ payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged: progress.labelsChanged, ...repaired, reconciled: true }) } })];
66473
66748
  }
66474
66749
  catch (error) {
66475
- const semanticError = (0, application_error_1.toApplicationError)(error, 'provider.unavailable', nativeClosed
66476
- ? 'The issue is closed, but completion metadata could not be fully verified. Retry the verified merged-PR operation.'
66477
- : 'Native issue completion could not be verified. Inspect the linked run before retrying.');
66478
- return [new result_1.Result({ id: this.taskId, success: false, executed: closedThisRun || labelsChanged > 0,
66750
+ if (progress.phase !== 'identity')
66751
+ nativeClosed = progress.nativeClosed;
66752
+ const semanticError = (0, closed_issue_reconciliation_error_1.closedIssueReconciliationError)(error, { resultId: this.taskId,
66753
+ phase: progress.phase, state: progress.state, reason: progress.reason,
66754
+ changed: progress.labelsChanged, remaining: progress.remaining });
66755
+ return [new result_1.Result({ id: this.taskId, success: false, executed: closedThisRun || progress.labelsChanged > 0,
66479
66756
  steps: [nativeClosed ? 'The native issue closure was retained; completion metadata remains unverified.' : 'Native issue completion remains unverified.'],
66480
- errors: [semanticError], payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged, reconciled: false }) } })];
66757
+ errors: [semanticError], payload: { issueCompletion: Object.freeze({ number, nativeClosed, closedThisRun, labelsChanged: progress.labelsChanged, reconciled: false }) } })];
66481
66758
  }
66482
66759
  }
66483
66760
  }
@@ -68492,6 +68769,8 @@ var _ApplicationError_cause;
68492
68769
  Object.defineProperty(exports, "__esModule", ({ value: true }));
68493
68770
  exports.ApplicationError = exports.APPLICATION_ERROR_METADATA = exports.APPLICATION_ERROR_RECOVERY_IDS = void 0;
68494
68771
  exports.isApplicationErrorCorrelationId = isApplicationErrorCorrelationId;
68772
+ const closed_issue_reconciliation_1 = __nccwpck_require__(21565);
68773
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
68495
68774
  const branch_sync_validation_1 = __nccwpck_require__(90981);
68496
68775
  exports.APPLICATION_ERROR_RECOVERY_IDS = Object.freeze([
68497
68776
  'pull-request-link-restored',
@@ -68507,6 +68786,8 @@ exports.APPLICATION_ERROR_RECOVERY_IDS = Object.freeze([
68507
68786
  'sdd-draft-size-exceeded',
68508
68787
  'sdd-draft-format-invalid',
68509
68788
  'workspace-changes-rejected',
68789
+ 'closed-issue-reconciliation-failed',
68790
+ 'approval-evidence-read-failed',
68510
68791
  ]);
68511
68792
  const PRESERVED_STATE = 'Existing persisted state and completed external effects were preserved.';
68512
68793
  const UNCHANGED_STATE = 'No new state or external effect was created.';
@@ -68698,6 +68979,8 @@ class ApplicationError extends Error {
68698
68979
  exports.ApplicationError = ApplicationError;
68699
68980
  _ApplicationError_cause = new WeakMap();
68700
68981
  const RECOVERY_VARIABLE_KEYS = Object.freeze({
68982
+ 'closed-issue-reconciliation-failed': Object.freeze(['resultId', 'phase', 'state', 'reason', 'changed', 'remaining']),
68983
+ 'approval-evidence-read-failed': Object.freeze(['operation', 'reason', 'permission']),
68701
68984
  'pull-request-link-restored': Object.freeze([]),
68702
68985
  'pull-request-link-base-retained': Object.freeze([]),
68703
68986
  'pull-request-link-reference-retained': Object.freeze([]),
@@ -68750,6 +69033,20 @@ function normalizeApplicationErrorRecovery(recovery) {
68750
69033
  && !branch_sync_validation_1.BRANCH_SYNC_VALIDATION_CODES.some(code => code === variables.validationCode)) {
68751
69034
  throw new TypeError('Application error recovery synchronization validation code is invalid.');
68752
69035
  }
69036
+ if (recovery.id === 'closed-issue-reconciliation-failed'
69037
+ && (!closed_issue_reconciliation_1.CLOSED_ISSUE_RESULTS.some(value => value === variables.resultId)
69038
+ || !closed_issue_reconciliation_1.CLOSED_ISSUE_PHASES.some(value => value === variables.phase)
69039
+ || !['open', 'closed', 'unavailable'].includes(variables.state)
69040
+ || !closed_issue_reconciliation_1.CLOSED_ISSUE_REASONS.some(value => value === variables.reason)
69041
+ || ![variables.changed, variables.remaining].every(value => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && value <= 3000))) {
69042
+ throw new TypeError('Closed issue reconciliation diagnostic is invalid.');
69043
+ }
69044
+ if (recovery.id === 'approval-evidence-read-failed'
69045
+ && (!Object.prototype.hasOwnProperty.call(approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS, variables.operation)
69046
+ || !approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_REASONS.some(reason => reason === variables.reason)
69047
+ || approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_PERMISSIONS[variables.operation] !== variables.permission)) {
69048
+ throw new TypeError('Approval evidence diagnostic is invalid.');
69049
+ }
68753
69050
  return Object.freeze({
68754
69051
  id: recovery.id,
68755
69052
  variables: Object.freeze({ ...variables }),
@@ -76675,6 +76972,41 @@ function parseApprovalCoverageZip(value) {
76675
76972
  }
76676
76973
 
76677
76974
 
76975
+ /***/ }),
76976
+
76977
+ /***/ 9958:
76978
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
76979
+
76980
+ "use strict";
76981
+
76982
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
76983
+ exports.readApprovalEvidence = readApprovalEvidence;
76984
+ const application_error_1 = __nccwpck_require__(75999);
76985
+ const approval_evidence_error_1 = __nccwpck_require__(50519);
76986
+ const approval_evidence_diagnostic_1 = __nccwpck_require__(98205);
76987
+ /** Terminates provider exceptions; even unknown statuses never interpolate provider text. */
76988
+ async function readApprovalEvidence(operation, read) {
76989
+ try {
76990
+ return await read();
76991
+ }
76992
+ catch (cause) {
76993
+ if (cause instanceof application_error_1.ApplicationError && cause.recovery?.id === 'approval-evidence-read-failed')
76994
+ throw cause;
76995
+ const error = cause && typeof cause === 'object' ? cause : undefined;
76996
+ const response = error?.response;
76997
+ const status = error?.status;
76998
+ const rateLimited = status === 403 && (response?.headers?.['x-ratelimit-remaining'] === '0'
76999
+ || typeof response?.headers?.['retry-after'] === 'string' && /^\d{1,8}$/u.test(response.headers['retry-after']));
77000
+ const http = typeof status === 'number' ? `http-${status}` : undefined;
77001
+ const reason = rateLimited ? 'http-429'
77002
+ : approval_evidence_diagnostic_1.APPROVAL_EVIDENCE_REASONS.some(value => value === http) ? http
77003
+ : cause instanceof application_error_1.ApplicationError && cause.code === 'provider.contract-invalid' ? 'invalid-response'
77004
+ : status === undefined ? 'transport-unavailable' : 'invalid-response';
77005
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, reason);
77006
+ }
77007
+ }
77008
+
77009
+
76678
77010
  /***/ }),
76679
77011
 
76680
77012
  /***/ 55165:
@@ -76709,21 +77041,22 @@ exports.BugbotPullRequestRepository = BugbotPullRequestRepository;
76709
77041
  /***/ }),
76710
77042
 
76711
77043
  /***/ 68742:
76712
- /***/ ((__unused_webpack_module, exports) => {
77044
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
76713
77045
 
76714
77046
  "use strict";
76715
77047
 
76716
77048
  Object.defineProperty(exports, "__esModule", ({ value: true }));
76717
77049
  exports.readCurrentBaseRevision = readCurrentBaseRevision;
77050
+ const application_error_1 = __nccwpck_require__(75999);
76718
77051
  /** PR base metadata can lag a parent push; read the exact native branch ref. */
76719
77052
  async function readCurrentBaseRevision(port, target, baseRef) {
76720
77053
  if (typeof baseRef !== 'string' || baseRef.length === 0 || baseRef.length > 1024
76721
77054
  || baseRef.includes('\u0000') || /[\r\n]/u.test(baseRef))
76722
- throw new Error('The PR base ref is invalid.');
77055
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'The PR base ref is invalid.');
76723
77056
  const { data } = await port.getRef({ owner: target.owner, repo: target.repository, ref: `heads/${baseRef}` });
76724
77057
  if (data?.ref !== `refs/heads/${baseRef}` || data.object?.type !== 'commit' || typeof data.object.sha !== 'string'
76725
77058
  || !/^[a-f0-9]{40}$/u.test(data.object.sha))
76726
- throw new Error('The PR base ref cannot be verified.');
77059
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'The PR base ref cannot be verified.');
76727
77060
  return data.object.sha;
76728
77061
  }
76729
77062
 
@@ -76771,6 +77104,9 @@ var __importStar = (this && this.__importStar) || (function () {
76771
77104
  Object.defineProperty(exports, "__esModule", ({ value: true }));
76772
77105
  exports.PullRequestApprovalRepository = void 0;
76773
77106
  exports.readEffectiveApprovalPolicy = readEffectiveApprovalPolicy;
77107
+ const approval_evidence_read_1 = __nccwpck_require__(9958);
77108
+ const approval_evidence_error_1 = __nccwpck_require__(50519);
77109
+ const application_error_1 = __nccwpck_require__(75999);
76774
77110
  const current_base_revision_query_1 = __nccwpck_require__(68742);
76775
77111
  const logging_ports_1 = __nccwpck_require__(6152);
76776
77112
  const github = __importStar(__nccwpck_require__(78227));
@@ -76797,40 +77133,64 @@ class PullRequestApprovalRepository {
76797
77133
  }
76798
77134
  }
76799
77135
  async readPolicy(target) {
76800
- return (await this.readEffectivePolicy(target))?.value;
77136
+ return (0, approval_evidence_read_1.readApprovalEvidence)('policy', async () => (await this.readEffectivePolicy(target))?.value);
76801
77137
  }
76802
77138
  async readEffectivePolicy(target) {
76803
77139
  return readEffectiveApprovalPolicy(this.octokit, target);
76804
77140
  }
76805
77141
  async loadEvidence(target) {
76806
- const { data: pull } = await this.octokit.rest.pulls.get({
76807
- owner: target.owner, repo: target.repository, pull_number: target.pullNumber,
77142
+ const diagnostics = [];
77143
+ const { data: pull } = await (0, approval_evidence_read_1.readApprovalEvidence)('pull-request', async () => {
77144
+ const response = await this.octokit.rest.pulls.get({ owner: target.owner, repo: target.repository, pull_number: target.pullNumber });
77145
+ if (!response.data?.head || !response.data?.base || typeof response.data.head.sha !== 'string'
77146
+ || typeof response.data.head.ref !== 'string' || !Number.isSafeInteger(response.data.number)) {
77147
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('pull-request', 'invalid-response');
77148
+ }
77149
+ return response;
76808
77150
  });
76809
77151
  const headSha = pull.head.sha;
76810
77152
  const baseSha = await this.readCurrentBaseSha(target, pull.base.ref);
76811
- const policy = (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(await this.readPolicy(target));
77153
+ const policy = await (0, approval_evidence_read_1.readApprovalEvidence)('policy', async () => {
77154
+ const raw = await this.readPolicy(target);
77155
+ try {
77156
+ return (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(raw);
77157
+ }
77158
+ catch {
77159
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('policy', 'invalid-response');
77160
+ }
77161
+ });
76812
77162
  let testedMergeSha;
76813
77163
  if (pull.mergeable === true && /^[a-f0-9]{40}$/iu.test(pull.merge_commit_sha ?? '')) {
76814
77164
  try {
76815
- const merge = await this.octokit.rest.git.getCommit({
76816
- owner: target.owner, repo: target.repository, commit_sha: pull.merge_commit_sha,
77165
+ const merge = await (0, approval_evidence_read_1.readApprovalEvidence)('merge-commit', async () => {
77166
+ const response = await this.octokit.rest.git.getCommit({ owner: target.owner, repo: target.repository, commit_sha: pull.merge_commit_sha });
77167
+ if (!Array.isArray(response.data?.parents))
77168
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('merge-commit', 'invalid-response');
77169
+ return response;
76817
77170
  });
76818
77171
  if (merge.data.parents[0]?.sha === baseSha && merge.data.parents[1]?.sha === headSha
76819
77172
  && merge.data.parents.length === 2)
76820
77173
  testedMergeSha = pull.merge_commit_sha;
76821
77174
  }
76822
- catch { /* Missing or unreadable merge parentage is non-authorizing. */ }
77175
+ catch (cause) {
77176
+ collectDiagnostic(diagnostics, cause);
77177
+ }
76823
77178
  }
76824
77179
  const [files, reviews, comments, headChecks, mergeChecks, headRuns, mergeRuns, rules, user] = await Promise.all([
76825
- this.allPages(this.octokit.rest.pulls.listFiles, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
76826
- this.allPages(this.octokit.rest.pulls.listReviews, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
76827
- this.allPages(this.octokit.rest.issues.listComments, { owner: target.owner, repo: target.repository, issue_number: target.pullNumber }),
76828
- this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: headSha, filter: 'all' }),
76829
- testedMergeSha ? this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: testedMergeSha, filter: 'all' }) : Promise.resolve([]),
76830
- this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: headSha }),
76831
- testedMergeSha ? this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: testedMergeSha }) : Promise.resolve([]),
76832
- this.effectiveRules(target.owner, target.repository, pull.base.ref),
76833
- this.octokit.rest.users.getAuthenticated(),
77180
+ this.allPages('files', this.octokit.rest.pulls.listFiles, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
77181
+ this.allPages('reviews', this.octokit.rest.pulls.listReviews, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
77182
+ this.allPages('comments', this.octokit.rest.issues.listComments, { owner: target.owner, repo: target.repository, issue_number: target.pullNumber }),
77183
+ this.allPages('head-checks', this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: headSha, filter: 'all' }),
77184
+ testedMergeSha ? this.allPages('merge-checks', this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: testedMergeSha, filter: 'all' }) : Promise.resolve([]),
77185
+ this.allPages('head-runs', this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: headSha }),
77186
+ testedMergeSha ? this.allPages('merge-runs', this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: testedMergeSha }) : Promise.resolve([]),
77187
+ this.effectiveRules(target.owner, target.repository, pull.base.ref, diagnostics),
77188
+ (0, approval_evidence_read_1.readApprovalEvidence)('authenticated-user', async () => {
77189
+ const response = await this.octokit.rest.users.getAuthenticated();
77190
+ if (!Number.isSafeInteger(response.data?.id) || response.data.id < 1)
77191
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('authenticated-user', 'invalid-response');
77192
+ return response;
77193
+ }),
76834
77194
  ]);
76835
77195
  const branch = Object.entries(this.settings.branchPrefixes)
76836
77196
  .find(([, prefix]) => pull.head.ref.startsWith(`${prefix}/`));
@@ -76851,7 +77211,7 @@ class PullRequestApprovalRepository {
76851
77211
  });
76852
77212
  const currentJobs = await Promise.all(latestRuns.map(async (run) => ({
76853
77213
  run,
76854
- jobs: await this.allPages(this.octokit.rest.actions.listJobsForWorkflowRunAttempt, {
77214
+ jobs: await this.allPages('jobs', this.octokit.rest.actions.listJobsForWorkflowRunAttempt, {
76855
77215
  owner: target.owner, repo: target.repository, run_id: run.id, attempt_number: run.run_attempt,
76856
77216
  }),
76857
77217
  })));
@@ -76882,9 +77242,10 @@ class PullRequestApprovalRepository {
76882
77242
  submittedAt: review.submitted_at ?? '',
76883
77243
  }));
76884
77244
  const numericCoverage = policy.coverage.mode === 'numeric'
76885
- ? await this.numericCoverage(target, policy.coverage.artifactWorkflowName, headSha, baseSha, testedMergeSha, workflowRuns)
77245
+ ? await this.numericCoverage(target, policy.coverage.artifactWorkflowName, headSha, baseSha, testedMergeSha, workflowRuns, diagnostics)
76886
77246
  : undefined;
76887
77247
  return {
77248
+ ...(diagnostics.length ? { diagnostics: Object.freeze(diagnostics) } : {}),
76888
77249
  repositoryId: pull.base.repo?.id ?? 0,
76889
77250
  pullNumber: pull.number,
76890
77251
  headSha,
@@ -76918,7 +77279,7 @@ class PullRequestApprovalRepository {
76918
77279
  ...(numericCoverage ? { numericCoverage } : {}),
76919
77280
  };
76920
77281
  }
76921
- async numericCoverage(target, workflowName, headSha, baseSha, testedMergeSha, workflowRuns) {
77282
+ async numericCoverage(target, workflowName, headSha, baseSha, testedMergeSha, workflowRuns, diagnostics) {
76922
77283
  const candidates = workflowRuns.filter(run => run.name === workflowName
76923
77284
  && (run.head_sha === headSha || run.head_sha === testedMergeSha));
76924
77285
  const mergeCandidates = candidates.filter(run => run.head_sha === testedMergeSha);
@@ -76927,25 +77288,27 @@ class PullRequestApprovalRepository {
76927
77288
  if (!run || run.conclusion !== 'success' || run.status !== 'completed')
76928
77289
  return undefined;
76929
77290
  try {
76930
- const artifacts = await this.allPages(this.octokit.rest.actions.listWorkflowRunArtifacts, {
77291
+ const artifacts = await this.allPages('coverage-artifacts', this.octokit.rest.actions.listWorkflowRunArtifacts, {
76931
77292
  owner: target.owner, repo: target.repository, run_id: run.id,
76932
77293
  });
76933
77294
  const matches = artifacts.filter(artifact => artifact.name === 'copilot-diff-coverage-v1' && !artifact.expired);
76934
77295
  if (matches.length !== 1 || matches[0].size_in_bytes > 64 * 1024)
76935
- return undefined;
76936
- const response = await this.octokit.rest.actions.downloadArtifact({
77296
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-artifacts', 'artifact-unavailable');
77297
+ const response = await (0, approval_evidence_read_1.readApprovalEvidence)('coverage-download', async () => this.octokit.rest.actions.downloadArtifact({
76937
77298
  owner: target.owner, repo: target.repository, artifact_id: matches[0].id, archive_format: 'zip',
76938
- });
77299
+ }));
76939
77300
  const data = response.data;
76940
77301
  const bytes = data instanceof ArrayBuffer ? new Uint8Array(data)
76941
77302
  : data instanceof Uint8Array ? data
76942
77303
  : typeof data === 'string' ? Buffer.from(data, 'binary') : undefined;
76943
77304
  const attestation = bytes ? (0, approval_coverage_artifact_1.parseApprovalCoverageZip)(bytes) : undefined;
76944
- if (!attestation || attestation.repositoryId !== target.repositoryId
77305
+ if (!attestation)
77306
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-parse', 'artifact-invalid');
77307
+ if (attestation.repositoryId !== target.repositoryId
76945
77308
  || attestation.pullNumber !== target.pullNumber || attestation.headSha !== headSha
76946
77309
  || attestation.baseSha !== baseSha || attestation.workflowRunId !== run.id
76947
77310
  || attestation.workflowRunAttempt !== run.run_attempt)
76948
- return undefined;
77311
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('coverage-parse', 'artifact-mismatch');
76949
77312
  return {
76950
77313
  coveredChangedLines: attestation.coveredChangedLines,
76951
77314
  totalChangedLines: attestation.totalChangedLines,
@@ -76953,7 +77316,8 @@ class PullRequestApprovalRepository {
76953
77316
  baseSha,
76954
77317
  };
76955
77318
  }
76956
- catch {
77319
+ catch (cause) {
77320
+ collectDiagnostic(diagnostics, cause);
76957
77321
  return undefined;
76958
77322
  }
76959
77323
  }
@@ -76979,7 +77343,7 @@ class PullRequestApprovalRepository {
76979
77343
  throw new Error('PR head or base changed before assessment publication.');
76980
77344
  }
76981
77345
  const user = await this.octokit.rest.users.getAuthenticated();
76982
- const comments = await this.allPages(this.octokit.rest.issues.listComments, {
77346
+ const comments = await this.allPages('comments', this.octokit.rest.issues.listComments, {
76983
77347
  owner: target.owner, repo: target.repository, issue_number: target.pullNumber,
76984
77348
  });
76985
77349
  const card = [...comments].reverse().find(comment => comment.user?.id === user.data.id
@@ -77025,24 +77389,26 @@ class PullRequestApprovalRepository {
77025
77389
  }
77026
77390
  }
77027
77391
  async readCurrentBaseSha(target, baseRef) {
77028
- return (0, current_base_revision_query_1.readCurrentBaseRevision)(this.octokit.rest.git, target, baseRef);
77392
+ return (0, approval_evidence_read_1.readApprovalEvidence)('base-ref', () => (0, current_base_revision_query_1.readCurrentBaseRevision)(this.octokit.rest.git, target, baseRef));
77029
77393
  }
77030
- async effectiveRules(owner, repo, branch) {
77394
+ async effectiveRules(owner, repo, branch, diagnostics) {
77031
77395
  try {
77032
- const rulesResponse = await this.octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { owner, repo, branch });
77396
+ const rulesResponse = await (0, approval_evidence_read_1.readApprovalEvidence)('rules', async () => this.octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { owner, repo, branch }));
77033
77397
  if (!Array.isArray(rulesResponse.data) || rulesResponse.data.some((rule) => !record(rule)
77034
77398
  || typeof record(rule)?.type !== 'string' || record(rule)?.type === '')) {
77035
- throw new Error('GitHub returned an invalid effective rules inventory.');
77399
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('rules', 'invalid-response');
77036
77400
  }
77037
77401
  const rules = rulesResponse.data;
77038
77402
  let classic;
77039
77403
  try {
77040
77404
  const response = await this.octokit.rest.repos.getBranchProtection({ owner, repo, branch });
77405
+ if (!record(response.data))
77406
+ throw (0, approval_evidence_error_1.approvalEvidenceError)('classic-protection', 'invalid-response');
77041
77407
  classic = response.data;
77042
77408
  }
77043
77409
  catch (error) {
77044
77410
  if (statusOf(error) !== 404)
77045
- throw error;
77411
+ await (0, approval_evidence_read_1.readApprovalEvidence)('classic-protection', async () => { throw error; });
77046
77412
  }
77047
77413
  const reviewRules = rules.filter(rule => rule.type === 'pull_request');
77048
77414
  const classicReview = classic?.required_pull_request_reviews;
@@ -77068,23 +77434,26 @@ class PullRequestApprovalRepository {
77068
77434
  }
77069
77435
  return { readable: true, dismissesStaleReviews: hasStaleRule && allStale, requiredChecks: [...requiredChecks] };
77070
77436
  }
77071
- catch {
77437
+ catch (cause) {
77438
+ collectDiagnostic(diagnostics, cause);
77072
77439
  return { readable: false, dismissesStaleReviews: false, requiredChecks: [] };
77073
77440
  }
77074
77441
  }
77075
- async allPages(method, params) {
77076
- const result = [];
77077
- for await (const response of this.octokit.paginate.iterator(method, { ...params, per_page: 100 })) {
77078
- const page = Array.isArray(response.data) ? response.data
77079
- : record(response.data)?.check_runs ?? record(response.data)?.workflow_runs
77080
- ?? record(response.data)?.artifacts ?? record(response.data)?.jobs;
77081
- if (!Array.isArray(page))
77082
- throw new Error('GitHub pagination returned an invalid page.');
77083
- result.push(...page);
77084
- if (result.length > 3000)
77085
- throw new Error('Approval evidence pagination exceeded the safe bound.');
77086
- }
77087
- return result;
77442
+ async allPages(operation, method, params) {
77443
+ return (0, approval_evidence_read_1.readApprovalEvidence)(operation, async () => {
77444
+ const result = [];
77445
+ for await (const response of this.octokit.paginate.iterator(method, { ...params, per_page: 100 })) {
77446
+ const page = Array.isArray(response.data) ? response.data
77447
+ : record(response.data)?.check_runs ?? record(response.data)?.workflow_runs
77448
+ ?? record(response.data)?.artifacts ?? record(response.data)?.jobs;
77449
+ if (!Array.isArray(page))
77450
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, 'invalid-page');
77451
+ result.push(...page);
77452
+ if (result.length > 3000)
77453
+ throw (0, approval_evidence_error_1.approvalEvidenceError)(operation, 'page-limit');
77454
+ }
77455
+ return result;
77456
+ });
77088
77457
  }
77089
77458
  }
77090
77459
  exports.PullRequestApprovalRepository = PullRequestApprovalRepository;
@@ -77121,7 +77490,7 @@ function array(value) { return Array.isArray(value) ? value : []; }
77121
77490
  async function readEffectiveApprovalPolicy(octokit, target) {
77122
77491
  const admit = (source, value) => {
77123
77492
  if (typeof value !== 'string' || value.length > 65536)
77124
- throw new Error('Approval policy response is malformed or unbounded.');
77493
+ throw new application_error_1.ApplicationError('provider.contract-invalid', 'Approval policy response is malformed or unbounded.');
77125
77494
  return { source, value };
77126
77495
  };
77127
77496
  try {
@@ -77145,6 +77514,14 @@ async function readEffectiveApprovalPolicy(octokit, target) {
77145
77514
  throw error;
77146
77515
  }
77147
77516
  }
77517
+ function collectDiagnostic(diagnostics, cause) {
77518
+ if (cause instanceof application_error_1.ApplicationError && cause.recovery?.id === 'approval-evidence-read-failed') {
77519
+ diagnostics.push(cause.recovery.variables);
77520
+ }
77521
+ else {
77522
+ diagnostics.push((0, approval_evidence_error_1.approvalEvidenceError)('evidence', 'invalid-response').recovery.variables);
77523
+ }
77524
+ }
77148
77525
 
77149
77526
 
77150
77527
  /***/ }),
@@ -79385,6 +79762,44 @@ class AgentResponseLengthFailure extends Error {
79385
79762
  exports.AgentResponseLengthFailure = AgentResponseLengthFailure;
79386
79763
 
79387
79764
 
79765
+ /***/ }),
79766
+
79767
+ /***/ 98205:
79768
+ /***/ ((__unused_webpack_module, exports) => {
79769
+
79770
+ "use strict";
79771
+
79772
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
79773
+ exports.APPROVAL_EVIDENCE_REASONS = exports.APPROVAL_EVIDENCE_PERMISSIONS = void 0;
79774
+ /** Closed vocabulary: no provider text, URLs, headers, policy values or credentials. */
79775
+ exports.APPROVAL_EVIDENCE_PERMISSIONS = Object.freeze({
79776
+ 'evidence': 'repository-read',
79777
+ 'pull-request': 'pull-requests-read',
79778
+ 'base-ref': 'contents-read',
79779
+ 'merge-commit': 'contents-read',
79780
+ 'policy': 'actions-variables-read',
79781
+ 'files': 'pull-requests-read',
79782
+ 'reviews': 'pull-requests-read',
79783
+ 'comments': 'issues-read',
79784
+ 'head-checks': 'checks-read',
79785
+ 'merge-checks': 'checks-read',
79786
+ 'head-runs': 'actions-read',
79787
+ 'merge-runs': 'actions-read',
79788
+ 'jobs': 'actions-read',
79789
+ 'rules': 'metadata-read',
79790
+ 'classic-protection': 'administration-read',
79791
+ 'authenticated-user': 'authenticated-user-read',
79792
+ 'coverage-artifacts': 'actions-read',
79793
+ 'coverage-download': 'actions-read',
79794
+ 'coverage-parse': 'actions-read',
79795
+ });
79796
+ exports.APPROVAL_EVIDENCE_REASONS = Object.freeze([
79797
+ 'http-400', 'http-401', 'http-403', 'http-404', 'http-409', 'http-422', 'http-429',
79798
+ 'http-500', 'http-502', 'http-503', 'http-504', 'transport-unavailable',
79799
+ 'invalid-response', 'invalid-page', 'page-limit', 'artifact-unavailable', 'artifact-invalid', 'artifact-mismatch',
79800
+ ]);
79801
+
79802
+
79388
79803
  /***/ }),
79389
79804
 
79390
79805
  /***/ 51114:
@@ -79945,6 +80360,25 @@ function countActionableBugbotFindings(counts) {
79945
80360
  }
79946
80361
 
79947
80362
 
80363
+ /***/ }),
80364
+
80365
+ /***/ 21565:
80366
+ /***/ ((__unused_webpack_module, exports) => {
80367
+
80368
+ "use strict";
80369
+
80370
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
80371
+ exports.CLOSED_ISSUE_REASONS = exports.CLOSED_ISSUE_PHASES = exports.CLOSED_ISSUE_RESULTS = void 0;
80372
+ exports.CLOSED_ISSUE_RESULTS = Object.freeze([
80373
+ 'ReconcileClosedIssueMetadataUseCase', 'CloseIssueAfterMergingUseCase', 'RetireBranchSyncUseCase', 'RetireProgressUseCase',
80374
+ ]);
80375
+ exports.CLOSED_ISSUE_PHASES = Object.freeze([
80376
+ 'identity', 'read', 'native-fence', 'label-remove', 'label-add', 'metadata-verification',
80377
+ 'card-inventory', 'card-freshness', 'card-update', 'card-verification',
80378
+ ]);
80379
+ exports.CLOSED_ISSUE_REASONS = Object.freeze(['completed', 'not_planned', 'duplicate', 'reopened', 'unknown', 'unavailable']);
80380
+
80381
+
79948
80382
  /***/ }),
79949
80383
 
79950
80384
  /***/ 77454: