@vypdev/copilot 3.9.3 → 3.10.0

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.
package/action.yml CHANGED
@@ -53,6 +53,9 @@ inputs:
53
53
  pre-branch-sdd:
54
54
  description: "Require a validated SDD first commit for started feature and contract-change issues. Requires issue-managed-branches."
55
55
  default: "false"
56
+ verification-setup:
57
+ description: "Run the trusted repository composite action before repository verification in generated workflows. Disabled by default."
58
+ default: "false"
56
59
  issue-workflow-profile:
57
60
  description: "JSON profile of enabled issue workflows."
58
61
  default: ""
@@ -558,6 +561,8 @@ outputs:
558
561
  runs:
559
562
  using: "node24"
560
563
  main: "build/github_action/index.js"
564
+ post: "build/github_action/index.js"
565
+ post-if: "always() && env.COPILOT_NATIVE_PHASE != ''"
561
566
  branding:
562
567
  icon: "cpu"
563
568
  color: "blue"
@@ -68,6 +68,45 @@ function runAtApplicationErrorBoundary(operation) {
68
68
  }
69
69
 
70
70
 
71
+ /***/ }),
72
+
73
+ /***/ 2937:
74
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
75
+
76
+
77
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
78
+ exports.BugbotMutationError = void 0;
79
+ exports.bugbotMutationError = bugbotMutationError;
80
+ const application_error_1 = __nccwpck_require__(5999);
81
+ const pull_request_review_errors_1 = __nccwpck_require__(6445);
82
+ class BugbotMutationError extends application_error_1.ApplicationError {
83
+ constructor(diagnostic) {
84
+ super(diagnostic.errorCode, `Bugbot ${diagnostic.stage} failed: operation=${diagnostic.operation}; finding=${diagnostic.findingId}; affected=${diagnostic.affectedCount}; ${diagnostic.providerStatus}; provider-reference=${diagnostic.providerReference}. Completed effects are retained; correct the named cause and recheck the same target.`);
85
+ this.diagnostic = diagnostic;
86
+ }
87
+ }
88
+ exports.BugbotMutationError = BugbotMutationError;
89
+ function bugbotMutationError(cause, stage, findingIds, fallback, affectedCount = findingIds.length) {
90
+ if (cause instanceof BugbotMutationError)
91
+ return cause;
92
+ const provider = cause instanceof pull_request_review_errors_1.PullRequestReviewOperationError ? cause : undefined;
93
+ const status = provider?.providerStatus;
94
+ const errorCode = status === 401 ? 'authorization.credential-invalid' : status === 403 ? 'authorization.denied'
95
+ : status === 404 ? 'provider.not-found' : status === 409 ? 'provider.conflict'
96
+ : status === 429 ? 'provider.rate-limited'
97
+ : status === 408 || status === 425 ? 'provider.unavailable'
98
+ : status && status < 500 ? 'provider.contract-invalid' : 'provider.unavailable';
99
+ const first = findingIds[0];
100
+ return new BugbotMutationError(Object.freeze({
101
+ stage, operation: provider?.operation ?? fallback,
102
+ findingId: typeof first === 'string' && /^[A-Za-z0-9_./:-]{1,200}$/.test(first) ? first : 'unavailable',
103
+ affectedCount: Math.min(affectedCount, 10000),
104
+ providerStatus: status ? `http-${status}` : 'http-unavailable',
105
+ providerReference: provider?.providerReference ?? 'unavailable', errorCode,
106
+ }));
107
+ }
108
+
109
+
71
110
  /***/ }),
72
111
 
73
112
  /***/ 601:
@@ -796,6 +835,7 @@ exports.BUGBOT_MESSAGE_IDS = Object.freeze([
796
835
  'bugbot.overflow.body',
797
836
  'bugbot.common.more',
798
837
  'bugbot.diagnostic.operationFailed',
838
+ 'bugbot.diagnostic.findingMutationFailed',
799
839
  'bugbot.diagnostic.identityUnavailable',
800
840
  'bugbot.diagnostic.pullRequestCommentsFailed',
801
841
  'bugbot.diagnostic.reviewThreadsFailed',
@@ -881,6 +921,7 @@ const ENGLISH_MESSAGES = Object.freeze({
881
921
  'bugbot.overflow.heading': 'More findings (comment limit)',
882
922
  'bugbot.overflow.body': Object.freeze({ one: 'There is {count} more finding that was not published as an individual comment. Review locally or in the full diff to see the list.', other: 'There are {count} more findings that were not published as individual comments. Review locally or in the full diff to see the list.' }),
883
923
  'bugbot.common.more': Object.freeze({ one: 'and {count} more.', other: 'and {count} more.' }),
924
+ 'bugbot.diagnostic.findingMutationFailed': 'Finding mutation failed: stage={stage}; operation={operation}; finding={findingId}; affected={affectedCount}; {providerStatus}; provider reference={providerReference}. Completed effects are retained. Correct access/contract errors before retrying; for transient failures restore provider availability, then recheck the same PR. Recheck reconciles existing reviews and threads.',
884
925
  'bugbot.diagnostic.operationFailed': 'Bugbot could not complete one or more finding mutations.',
885
926
  'bugbot.diagnostic.identityUnavailable': 'The authenticated Bugbot identity is unavailable.',
886
927
  'bugbot.diagnostic.pullRequestCommentsFailed': 'Unable to re-read pull request review comments.',
@@ -970,6 +1011,7 @@ const SPANISH_MESSAGES = Object.freeze({
970
1011
  'bugbot.overflow.heading': 'Más hallazgos (límite de comentarios)',
971
1012
  'bugbot.overflow.body': Object.freeze({ one: 'Hay {count} hallazgo más que no se publicó como comentario individual. Revísalo localmente o en el diff completo para consultar la lista.', many: 'Hay {count} hallazgos más que no se publicaron como comentarios individuales. Revísalos localmente o en el diff completo para consultar la lista.', other: 'Hay {count} hallazgos más que no se publicaron como comentarios individuales. Revísalos localmente o en el diff completo para consultar la lista.' }),
972
1013
  'bugbot.common.more': Object.freeze({ one: 'y {count} más.', many: 'y {count} más.', other: 'y {count} más.' }),
1014
+ 'bugbot.diagnostic.findingMutationFailed': 'Falló una mutación: fase={stage}; operación={operation}; hallazgo={findingId}; afectados={affectedCount}; {providerStatus}; referencia del proveedor={providerReference}. Se conservan los efectos completados. Corrige acceso/contrato antes de reintentar; para fallos transitorios restaura el proveedor y revisa la misma PR. La revisión reconcilia comentarios e hilos existentes.',
973
1015
  'bugbot.diagnostic.operationFailed': 'Bugbot no pudo completar una o más mutaciones de hallazgos.',
974
1016
  'bugbot.diagnostic.identityUnavailable': 'La identidad autenticada de Bugbot no está disponible.',
975
1017
  'bugbot.diagnostic.pullRequestCommentsFailed': 'No se pudieron volver a leer los comentarios del review del pull request.',
@@ -1009,6 +1051,7 @@ async function resolveBugbotCatalog(locale, configuration, resolver) {
1009
1051
  }
1010
1052
  function renderBugbotDiagnostic(diagnostic, catalog) {
1011
1053
  switch (diagnostic.code) {
1054
+ case 'finding-mutation-failed': return catalog.message('bugbot.diagnostic.findingMutationFailed', { ...diagnostic.mutation });
1012
1055
  case 'operation-failed': return catalog.message('bugbot.diagnostic.operationFailed');
1013
1056
  case 'identity-unavailable': return catalog.message('bugbot.diagnostic.identityUnavailable');
1014
1057
  case 'snapshot-pull-request-comments-failed': return catalog.message('bugbot.diagnostic.pullRequestCommentsFailed');
@@ -1558,6 +1601,10 @@ function renderBugbotStatusCard(projection, catalogOrLocale, links) {
1558
1601
  facts.push(catalog.message('bugbot.coverage.omitted', { count: source.omittedItems }));
1559
1602
  if (source.truncatedItems > 0)
1560
1603
  facts.push(catalog.message('bugbot.coverage.truncated', { count: source.truncatedItems }));
1604
+ const truncatedIds = source.truncatedItemIds?.slice(0, 50)
1605
+ .filter(id => /^(?:issue:[0-9]+|review:[A-Za-z0-9_=-]+)$/.test(id) && id.length <= 160);
1606
+ if (truncatedIds?.length)
1607
+ facts.push(`truncated-source-ids=${truncatedIds.join(', ')}`);
1561
1608
  if (source.providerLimitReached)
1562
1609
  facts.push(catalog.message('bugbot.coverage.providerLimit'));
1563
1610
  return `- ${source.source}: ${source.status}; ${facts.join(', ')}`;
@@ -1647,9 +1694,21 @@ function normalizeHistoricalSnapshot(originalBody, analyzedHeadSha, catalog) {
1647
1694
  function renderFindingRow(finding) {
1648
1695
  const label = (0, github_comment_publication_policy_1.sanitizeAgentMarkdown)(finding.title || finding.id, 500).replace(/[\r\n]+/gu, ' ');
1649
1696
  const state = stateLabel(finding.state);
1697
+ const identity = finding.state === 'unknown'
1698
+ ? ` — ${renderFindingId(finding.id)}`
1699
+ : '';
1650
1700
  return finding.url
1651
- ? `- ${state} — [${label}](${finding.url})`
1652
- : `- ${state} — ${label}`;
1701
+ ? `- ${state} — [${label}](${finding.url})${identity}`
1702
+ : `- ${state} — ${label}${identity}`;
1703
+ }
1704
+ function renderFindingId(value) {
1705
+ const id = (0, github_comment_publication_policy_1.sanitizeAgentMarkdown)(value, 200).replace(/[\r\n]+/gu, ' ');
1706
+ // A delimiter longer than every embedded backtick run keeps links, images
1707
+ // and HTML inert even when the marker permits arbitrary Markdown punctuation.
1708
+ let delimiter = '`';
1709
+ while (id.includes(delimiter))
1710
+ delimiter += '`';
1711
+ return `${delimiter} ${id} ${delimiter}`;
1653
1712
  }
1654
1713
  function stateLabel(state) {
1655
1714
  return `[ ] ${state}`;
@@ -2136,6 +2195,13 @@ class PullRequestReviewOperationError extends Error {
2136
2195
  super(buildMessage(operation, context));
2137
2196
  this.name = "PullRequestReviewOperationError";
2138
2197
  this.operation = operation;
2198
+ const status = context?.providerStatus;
2199
+ if (typeof status === 'number' && Number.isInteger(status) && status >= 400 && status <= 599) {
2200
+ this.providerStatus = status;
2201
+ }
2202
+ if (typeof context?.providerReference === 'string' && /^[A-Za-z0-9:-]{1,64}$/.test(context.providerReference)) {
2203
+ this.providerReference = context.providerReference;
2204
+ }
2139
2205
  }
2140
2206
  }
2141
2207
  exports.PullRequestReviewOperationError = PullRequestReviewOperationError;
@@ -2235,7 +2301,7 @@ function partitionFailureCategory(error) {
2235
2301
  return error instanceof Error ? error.name : 'unknown';
2236
2302
  }
2237
2303
  function suppressDismissedFindings(execution, context, prepared) {
2238
- const activeFindings = (prepared.activeFindings ?? prepared.toPublish).filter((finding) => {
2304
+ const activeFindings = (0, finding_1.retainExistingFindingIds)(prepared.activeFindings ?? prepared.toPublish, context.existingByFindingId).filter((finding) => {
2239
2305
  const existing = (0, finding_1.findExistingFindingInfo)(context.existingByFindingId, finding);
2240
2306
  return existing?.issue?.resolution !== 'dismissed' && existing?.pullRequest?.resolution !== 'dismissed';
2241
2307
  });
@@ -2254,6 +2320,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
2254
2320
  exports.applyDetectedFindings = applyDetectedFindings;
2255
2321
  const mark_findings_resolved_use_case_1 = __nccwpck_require__(6963);
2256
2322
  const publish_findings_use_case_1 = __nccwpck_require__(8442);
2323
+ const bugbot_mutation_error_1 = __nccwpck_require__(2937);
2257
2324
  const pull_request_review_errors_1 = __nccwpck_require__(6445);
2258
2325
  async function applyDetectedFindings(operation, context, prepared, publicationPorts, resolutionPorts, catalog) {
2259
2326
  try {
@@ -2269,7 +2336,7 @@ async function applyDetectedFindings(operation, context, prepared, publicationPo
2269
2336
  });
2270
2337
  }
2271
2338
  catch (error) {
2272
- const publicationError = error instanceof pull_request_review_errors_1.PullRequestReviewOperationError
2339
+ const publicationError = error instanceof bugbot_mutation_error_1.BugbotMutationError || error instanceof pull_request_review_errors_1.PullRequestReviewOperationError
2273
2340
  ? error
2274
2341
  : new Error("Unable to publish findings.");
2275
2342
  return [publicationError];
@@ -2540,6 +2607,7 @@ function buildPreviousFindingsContext(previousFindings) {
2540
2607
  `;
2541
2608
  const suffix = `
2542
2609
  **Your task 2:** For each finding above, analyze the current code and decide:
2610
+ - Trace the original failure precondition through current callers, validation helpers and effective configuration, including modules outside the original anchor. A retained finding is not proof that its old explanation is still true. Reuse its exact id if reporting the same defect again; do not create a second report for it.
2543
2611
  - If the problem **still exists** (same code or same issue present): do **not** include it in \`resolved_findings\`.
2544
2612
  - If the problem **no longer applies** (e.g. that code was removed or refactored away): include \`{ "id": "<exact id>", "resolution": "obsolete" }\` in \`resolved_findings\`.
2545
2613
  - If the problem **has been fixed** (code was changed and the issue is resolved): include \`{ "id": "<exact id>", "resolution": "fixed" }\` in \`resolved_findings\`.
@@ -2658,6 +2726,10 @@ function buildReviewConversationContext(issueComments, commentsByPullRequest, bo
2658
2726
  omitted,
2659
2727
  truncated: chronological.filter((entry) => entry.truncated).length,
2660
2728
  retained: chronological.length,
2729
+ ...(chronological.some(entry => entry.truncated) ? {
2730
+ truncatedItemIds: chronological.filter(entry => entry.truncated)
2731
+ .map(entry => entry.providerId).filter(id => /^(?:issue:[0-9]+|review:[A-Za-z0-9_=-]+)$/.test(id) && id.length <= 160),
2732
+ } : {}),
2661
2733
  };
2662
2734
  }
2663
2735
  function appendConversationEntry(entries, author, kind, body, createdAt, providerId) {
@@ -3339,6 +3411,7 @@ async function loadBugbotContext(request, ports, resolvedPreflight) {
3339
3411
  itemsFetched: conversationContext.retained + conversationContext.omitted,
3340
3412
  omittedItems: conversationContext.omitted,
3341
3413
  truncatedItems: conversationContext.truncated,
3414
+ ...(conversationContext.truncatedItemIds ? { truncatedItemIds: conversationContext.truncatedItemIds } : {}),
3342
3415
  limitReached: conversationContext.omitted > 0 || conversationContext.truncated > 0,
3343
3416
  },
3344
3417
  {
@@ -3537,11 +3610,11 @@ Object.defineProperty(exports, "markFindingsResolved", ({ enumerable: true, get:
3537
3610
 
3538
3611
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3539
3612
  exports.markFindingsResolved = markFindingsResolved;
3540
- const pull_request_review_errors_1 = __nccwpck_require__(6445);
3541
3613
  const logging_ports_1 = __nccwpck_require__(6152);
3542
3614
  const resolve_issue_finding_1 = __nccwpck_require__(5300);
3543
3615
  const resolve_pull_request_finding_1 = __nccwpck_require__(4567);
3544
3616
  const review_state_1 = __nccwpck_require__(9200);
3617
+ const bugbot_mutation_error_1 = __nccwpck_require__(2937);
3545
3618
  const application_error_1 = __nccwpck_require__(5999);
3546
3619
  async function markFindingsResolved(param) {
3547
3620
  const errors = [];
@@ -3569,8 +3642,8 @@ async function repairExistingPullRequestFinding(ports, operation, findingId, des
3569
3642
  try {
3570
3643
  await ports.pullRequestComments.unresolvePullRequestReviewThread(destination.pullRequestNumber, destination.commentIdentity);
3571
3644
  }
3572
- catch {
3573
- addResolutionError(errors, 'pull request');
3645
+ catch (cause) {
3646
+ addResolutionError(errors, 'pull request', findingId, cause);
3574
3647
  }
3575
3648
  }
3576
3649
  }
@@ -3584,7 +3657,7 @@ async function resolveIssueIfNeeded(param, findingId, destination, errors) {
3584
3657
  return;
3585
3658
  const comment = param.context.issueComments.find(item => item.id === destination.commentId);
3586
3659
  if (comment?.body == null) {
3587
- addResolutionError(errors, 'issue');
3660
+ addResolutionError(errors, 'issue', findingId);
3588
3661
  return;
3589
3662
  }
3590
3663
  try {
@@ -3595,8 +3668,8 @@ async function resolveIssueIfNeeded(param, findingId, destination, errors) {
3595
3668
  resolution: param.resolvedFindingResolutions?.get(findingId),
3596
3669
  }, param.catalog);
3597
3670
  }
3598
- catch {
3599
- addResolutionError(errors, 'issue');
3671
+ catch (cause) {
3672
+ addResolutionError(errors, 'issue', findingId, cause);
3600
3673
  }
3601
3674
  }
3602
3675
  async function tryResolvePullRequestFinding(ports, findingId, destination, errors, resolution, catalog) {
@@ -3608,17 +3681,14 @@ async function tryResolvePullRequestFinding(ports, findingId, destination, error
3608
3681
  resolution,
3609
3682
  }, catalog);
3610
3683
  }
3611
- catch {
3612
- addResolutionError(errors, 'pull request');
3684
+ catch (cause) {
3685
+ addResolutionError(errors, 'pull request', findingId, cause);
3613
3686
  }
3614
3687
  }
3615
- function addResolutionError(errors, destination) {
3616
- const cause = destination === 'pull request'
3617
- ? new pull_request_review_errors_1.PullRequestReviewOperationError('mark-resolved')
3618
- : new Error('Unable to mark an issue finding as resolved.');
3619
- const semanticError = new application_error_1.ApplicationError('provider.unavailable', destination === 'pull request'
3620
- ? 'Unable to mark a pull request finding as resolved.'
3621
- : 'Unable to mark an issue finding as resolved.', { cause });
3688
+ function addResolutionError(errors, destination, findingId, cause) {
3689
+ const semanticError = destination === 'pull request'
3690
+ ? (0, bugbot_mutation_error_1.bugbotMutationError)(cause, 'resolution', [findingId], 'mark-resolved')
3691
+ : new application_error_1.ApplicationError('provider.unavailable', 'Unable to mark an issue finding as resolved.');
3622
3692
  (0, logging_ports_1.logError)(semanticError);
3623
3693
  errors.push(semanticError);
3624
3694
  }
@@ -3867,6 +3937,7 @@ function isRecord(value) {
3867
3937
  */
3868
3938
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3869
3939
  exports.publishFindings = publishFindings;
3940
+ const bugbot_mutation_error_1 = __nccwpck_require__(2937);
3870
3941
  const finding_1 = __nccwpck_require__(1011);
3871
3942
  const publish_issue_finding_comment_1 = __nccwpck_require__(4950);
3872
3943
  const publish_pr_review_comments_1 = __nccwpck_require__(352);
@@ -3885,12 +3956,17 @@ async function publishFindings(param) {
3885
3956
  catalog,
3886
3957
  })
3887
3958
  : undefined;
3888
- for (const finding of findings) {
3959
+ for (const finding of (0, finding_1.retainExistingFindingIds)(findings, existingByFindingId)) {
3889
3960
  if (operation.target.issueNumber > 0 && !reviewPublisher) {
3890
3961
  await (0, publish_issue_finding_comment_1.publishIssueFindingComment)(ports.issueComments, operation.target.issueNumber, finding, (0, finding_1.findExistingFindingInfo)(existingByFindingId, finding), commitSha, catalog);
3891
3962
  }
3892
3963
  if (reviewPublisher) {
3893
- await reviewPublisher.publish(finding, (0, finding_1.findExistingFindingInfo)(existingByFindingId, finding));
3964
+ try {
3965
+ await reviewPublisher.publish(finding, (0, finding_1.findExistingFindingInfo)(existingByFindingId, finding));
3966
+ }
3967
+ catch (cause) {
3968
+ throw (0, bugbot_mutation_error_1.bugbotMutationError)(cause, 'publication', [finding.id], 'publish-comments');
3969
+ }
3894
3970
  }
3895
3971
  }
3896
3972
  await reviewPublisher?.flush(overflowCount, overflowTitles);
@@ -3959,6 +4035,7 @@ ${catalog.message('bugbot.overflow.body', { count: `**${overflowCount}**` }, ove
3959
4035
 
3960
4036
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3961
4037
  exports.PullRequestReviewCommentPublisher = void 0;
4038
+ const bugbot_mutation_error_1 = __nccwpck_require__(2937);
3962
4039
  const bugbot_finding_marker_policy_1 = __nccwpck_require__(8024);
3963
4040
  const path_validation_1 = __nccwpck_require__(124);
3964
4041
  const logging_ports_1 = __nccwpck_require__(6152);
@@ -4030,11 +4107,16 @@ class PullRequestReviewCommentPublisher {
4030
4107
  if (this.findingsToCreate.length === 0 && overflowCount === 0)
4031
4108
  return;
4032
4109
  const { repository, operation, openPrNumber, prContext } = this.options;
4033
- await repository.createReviewWithComments(openPrNumber, prContext.prHeadSha, buildReviewSummary(this.findingsToCreate, this.commentsToCreate.length, this.unanchoredBodies, overflowCount, overflowTitles, operation.analysis.reviewConfiguration.traceRules
4034
- ? this.options.ruleSources ?? []
4035
- : [], operation.analysis.reviewConfiguration.traceRules
4036
- ? this.options.omittedRuleCount ?? 0
4037
- : 0, prContext.prHeadSha, this.catalog), this.commentsToCreate);
4110
+ try {
4111
+ await repository.createReviewWithComments(openPrNumber, prContext.prHeadSha, buildReviewSummary(this.findingsToCreate, this.commentsToCreate.length, this.unanchoredBodies, overflowCount, overflowTitles, operation.analysis.reviewConfiguration.traceRules
4112
+ ? this.options.ruleSources ?? []
4113
+ : [], operation.analysis.reviewConfiguration.traceRules
4114
+ ? this.options.omittedRuleCount ?? 0
4115
+ : 0, prContext.prHeadSha, this.catalog), this.commentsToCreate);
4116
+ }
4117
+ catch (cause) {
4118
+ throw (0, bugbot_mutation_error_1.bugbotMutationError)(cause, 'publication', this.findingsToCreate.map(finding => finding.id), 'publish-comments', this.findingsToCreate.length + overflowCount);
4119
+ }
4038
4120
  }
4039
4121
  get catalog() {
4040
4122
  return this.options.catalog ?? (0, bugbot_message_catalog_1.resolveStaticBugbotCatalog)(this.options.operation.locale.pullRequest);
@@ -4188,6 +4270,8 @@ async function queryBugbotPartitionFindings(repository, configuration, prompt, t
4188
4270
 
4189
4271
  Object.defineProperty(exports, "__esModule", ({ value: true }));
4190
4272
  exports.reconcileBugbotReviewState = reconcileBugbotReviewState;
4273
+ const bugbot_mutation_error_1 = __nccwpck_require__(2937);
4274
+ const finding_1 = __nccwpck_require__(1011);
4191
4275
  const review_projection_1 = __nccwpck_require__(859);
4192
4276
  const bugbot_reconciliation_policy_1 = __nccwpck_require__(8128);
4193
4277
  const bugbot_provider_projection_policy_1 = __nccwpck_require__(5821);
@@ -4218,11 +4302,12 @@ async function reconcileBugbotReviewState(input) {
4218
4302
  };
4219
4303
  }
4220
4304
  const snapshot = snapshotResult.snapshot;
4305
+ const activeFindings = (0, finding_1.retainExistingFindingIds)(input.activeFindings, input.loadedContext.existingByFindingId);
4306
+ const expectedPublishedFindings = (0, finding_1.retainExistingFindingIds)(input.expectedPublishedFindings ?? input.activeFindings, input.loadedContext.existingByFindingId);
4221
4307
  const diagnostics = [
4222
- ...(input.mutationErrors ?? []).map((error) => ({
4223
- code: 'operation-failed',
4224
- operatorMessage: toSafeOperationMessage(error),
4225
- })),
4308
+ ...(input.mutationErrors ?? []).map((error) => error instanceof bugbot_mutation_error_1.BugbotMutationError
4309
+ ? { code: 'finding-mutation-failed', mutation: error.diagnostic }
4310
+ : { code: 'operation-failed', operatorMessage: toSafeOperationMessage(error) }),
4226
4311
  ...(!input.target.trustedAuthorLogin?.trim()
4227
4312
  ? [{ code: 'identity-unavailable' }]
4228
4313
  : []),
@@ -4231,7 +4316,7 @@ async function reconcileBugbotReviewState(input) {
4231
4316
  const providerProjection = (0, bugbot_provider_projection_policy_1.projectBugbotProviderEvidence)({
4232
4317
  snapshot,
4233
4318
  trustedAuthorLogin: input.target.trustedAuthorLogin,
4234
- activeFindings: input.activeFindings,
4319
+ activeFindings,
4235
4320
  existingByFindingId: input.loadedContext.existingByFindingId,
4236
4321
  });
4237
4322
  const plan = (0, bugbot_reconciliation_policy_1.buildBugbotReconciliationPlan)({
@@ -4241,8 +4326,8 @@ async function reconcileBugbotReviewState(input) {
4241
4326
  id,
4242
4327
  (0, bugbot_finding_marker_policy_1.extractTitleFromBody)(fullBody) || id,
4243
4328
  ])),
4244
- activeFindings: input.activeFindings,
4245
- expectedPublishedFindings: input.expectedPublishedFindings ?? input.activeFindings,
4329
+ activeFindings,
4330
+ expectedPublishedFindings,
4246
4331
  diagnostics,
4247
4332
  coverage: input.loadedContext.coverage,
4248
4333
  });
@@ -4697,14 +4782,16 @@ function toPresentationFailure(diagnostic) {
4697
4782
  const message = (0, bugbot_message_catalog_1.bugbotDiagnosticOperatorMessage)(diagnostic);
4698
4783
  return {
4699
4784
  diagnostic,
4700
- error: diagnostic.code === 'review-updates-pending'
4701
- ? new application_error_1.ApplicationError('workflow.presentation-pending', message, {
4702
- recovery: {
4703
- id: 'bugbot-review-blocks-pending',
4704
- variables: { pendingCount: diagnostic.count },
4705
- },
4706
- })
4707
- : new Error(message),
4785
+ error: diagnostic.code === 'finding-mutation-failed'
4786
+ ? new application_error_1.ApplicationError(diagnostic.mutation.errorCode, message)
4787
+ : diagnostic.code === 'review-updates-pending'
4788
+ ? new application_error_1.ApplicationError('workflow.presentation-pending', message, {
4789
+ recovery: {
4790
+ id: 'bugbot-review-blocks-pending',
4791
+ variables: { pendingCount: diagnostic.count },
4792
+ },
4793
+ })
4794
+ : new Error(message),
4708
4795
  };
4709
4796
  }
4710
4797
  function report(projection, reviewUpdates, pendingReviewUpdates, statusCardOperation, errors) {
@@ -5168,9 +5255,16 @@ exports.APPLICATION_ERROR_RECOVERY_IDS = Object.freeze([
5168
5255
  'branch-sync-resolution-invalid',
5169
5256
  'sdd-draft-size-exceeded',
5170
5257
  'sdd-draft-format-invalid',
5258
+ 'sdd-draft-case-identity-invalid',
5259
+ 'sdd-draft-case-budget-invalid',
5260
+ 'sdd-draft-requirement-mapping-invalid',
5261
+ 'sdd-draft-repository-path-invalid',
5171
5262
  'workspace-changes-rejected',
5172
5263
  'closed-issue-reconciliation-failed',
5173
5264
  'approval-evidence-read-failed',
5265
+ 'stored-configuration-schema-missing',
5266
+ 'stored-configuration-schema-unsupported',
5267
+ 'stored-configuration-invalid',
5174
5268
  ]);
5175
5269
  const PRESERVED_STATE = 'Existing persisted state and completed external effects were preserved.';
5176
5270
  const UNCHANGED_STATE = 'No new state or external effect was created.';
@@ -5362,6 +5456,9 @@ class ApplicationError extends Error {
5362
5456
  exports.ApplicationError = ApplicationError;
5363
5457
  _ApplicationError_cause = new WeakMap();
5364
5458
  const RECOVERY_VARIABLE_KEYS = Object.freeze({
5459
+ 'stored-configuration-schema-missing': Object.freeze(['issueNumber', 'expectedVersion']),
5460
+ 'stored-configuration-schema-unsupported': Object.freeze(['issueNumber', 'expectedVersion', 'actualVersion']),
5461
+ 'stored-configuration-invalid': Object.freeze(['issueNumber', 'expectedVersion']),
5365
5462
  'closed-issue-reconciliation-failed': Object.freeze(['resultId', 'phase', 'state', 'reason', 'changed', 'remaining']),
5366
5463
  'approval-evidence-read-failed': Object.freeze(['operation', 'reason', 'permission']),
5367
5464
  'pull-request-link-restored': Object.freeze([]),
@@ -5376,6 +5473,10 @@ const RECOVERY_VARIABLE_KEYS = Object.freeze({
5376
5473
  'branch-sync-resolution-invalid': Object.freeze(['validationCode', 'command']),
5377
5474
  'sdd-draft-size-exceeded': Object.freeze([]),
5378
5475
  'sdd-draft-format-invalid': Object.freeze([]),
5476
+ 'sdd-draft-case-identity-invalid': Object.freeze([]),
5477
+ 'sdd-draft-case-budget-invalid': Object.freeze([]),
5478
+ 'sdd-draft-requirement-mapping-invalid': Object.freeze([]),
5479
+ 'sdd-draft-repository-path-invalid': Object.freeze([]),
5379
5480
  'workspace-changes-rejected': Object.freeze([]),
5380
5481
  });
5381
5482
  function normalizeApplicationErrorRecovery(recovery) {
@@ -5391,6 +5492,13 @@ function normalizeApplicationErrorRecovery(recovery) {
5391
5492
  || actualKeys.some((key, index) => key !== expectedKeys[index])) {
5392
5493
  throw new TypeError(`Application error recovery variables are invalid for ${recovery.id}.`);
5393
5494
  }
5495
+ if (recovery.id.startsWith('stored-configuration-')
5496
+ && (![variables.issueNumber, variables.expectedVersion].every(value => typeof value === 'number' && Number.isSafeInteger(value) && value > 0)
5497
+ || (recovery.id === 'stored-configuration-schema-unsupported'
5498
+ && !(typeof variables.actualVersion === 'number'
5499
+ && Number.isSafeInteger(variables.actualVersion) && variables.actualVersion >= 0)))) {
5500
+ throw new TypeError('Stored configuration diagnostic is invalid.');
5501
+ }
5394
5502
  if (recovery.id === 'managed-branch-enrichment-failed'
5395
5503
  && (typeof variables.branchName !== 'string'
5396
5504
  || !/^[A-Za-z0-9][A-Za-z0-9._/-]{0,254}$/u.test(variables.branchName))) {
@@ -5642,9 +5750,14 @@ function matchesConstrainedHead(target, candidate) {
5642
5750
  /***/ }),
5643
5751
 
5644
5752
  /***/ 1011:
5645
- /***/ ((__unused_webpack_module, exports) => {
5753
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
5646
5754
 
5647
5755
 
5756
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
5757
+ exports.isExistingFindingFullyResolved = isExistingFindingFullyResolved;
5758
+ exports.findExistingFindingInfo = findExistingFindingInfo;
5759
+ exports.findExistingFindingMatch = findExistingFindingMatch;
5760
+ exports.retainExistingFindingIds = retainExistingFindingIds;
5648
5761
  /**
5649
5762
  * Provider-neutral Bugbot finding and durable identity contracts.
5650
5763
  *
@@ -5652,9 +5765,7 @@ function matchesConstrainedHead(target, candidate) {
5652
5765
  * them in the domain prevents policies from depending on a particular use-case
5653
5766
  * folder and gives every adapter one stable semantic vocabulary.
5654
5767
  */
5655
- Object.defineProperty(exports, "__esModule", ({ value: true }));
5656
- exports.isExistingFindingFullyResolved = isExistingFindingFullyResolved;
5657
- exports.findExistingFindingInfo = findExistingFindingInfo;
5768
+ const finding_identity_1 = __nccwpck_require__(1853);
5658
5769
  function isExistingFindingFullyResolved(finding) {
5659
5770
  const destinations = [finding.issue, finding.pullRequest].filter((destination) => destination != null);
5660
5771
  return (destinations.length > 0 &&
@@ -5662,21 +5773,62 @@ function isExistingFindingFullyResolved(finding) {
5662
5773
  finding.pullRequest?.verificationRequired !== true);
5663
5774
  }
5664
5775
  function findExistingFindingInfo(existingByFindingId, finding) {
5776
+ return findExistingFindingMatch(existingByFindingId, finding)?.info;
5777
+ }
5778
+ /** A match carries its durable key so updating a comment cannot re-key it. */
5779
+ function findExistingFindingMatch(existingByFindingId, finding) {
5665
5780
  const direct = existingByFindingId[finding.id];
5666
5781
  if (direct && identitiesAreCompatible(direct, finding))
5667
- return direct;
5668
- const candidates = Object.values(existingByFindingId);
5782
+ return { id: finding.id, info: direct };
5783
+ const candidates = Object.entries(existingByFindingId);
5669
5784
  if (finding.fingerprint) {
5670
- const locationMatch = candidates.find((candidate) => candidate.issue?.fingerprint === finding.fingerprint
5785
+ const locationMatches = candidates.filter(([, candidate]) => candidate.issue?.fingerprint === finding.fingerprint
5671
5786
  || candidate.pullRequest?.fingerprint === finding.fingerprint);
5672
- if (locationMatch)
5673
- return locationMatch;
5787
+ if (locationMatches.length > 0)
5788
+ return uniqueFindingMatch(locationMatches);
5674
5789
  }
5675
5790
  if (!finding.semanticFingerprint)
5676
5791
  return undefined;
5677
- const semanticMatches = candidates.filter((candidate) => candidate.issue?.semanticFingerprint === finding.semanticFingerprint
5792
+ const semanticMatches = candidates.filter(([, candidate]) => candidate.issue?.semanticFingerprint === finding.semanticFingerprint
5678
5793
  || candidate.pullRequest?.semanticFingerprint === finding.semanticFingerprint);
5679
- return semanticMatches.length === 1 ? semanticMatches[0] : undefined;
5794
+ return uniqueFindingMatch(semanticMatches);
5795
+ }
5796
+ /**
5797
+ * Preserve trusted IDs across title/location changes, before limits, publication
5798
+ * and final projection. Multiple reports of one uniquely matched durable finding
5799
+ * share one record; unmatched or ambiguous reports remain independent evidence.
5800
+ */
5801
+ function retainExistingFindingIds(findings, existingByFindingId) {
5802
+ const retained = new Set();
5803
+ const durableIds = new Set(Object.keys(existingByFindingId));
5804
+ // Reserve the whole input up front so a generated ID cannot take another
5805
+ // report's ID, including one that occurs later in the response.
5806
+ const reservedIds = new Set([...durableIds, ...findings.map((finding) => finding.id)]);
5807
+ const emittedIds = new Set();
5808
+ return findings.flatMap((finding) => {
5809
+ const match = findExistingFindingMatch(existingByFindingId, finding);
5810
+ if (!match) {
5811
+ let id = finding.id;
5812
+ if (durableIds.has(id) || emittedIds.has(id)) {
5813
+ const base = `candidate-${(0, finding_identity_1.buildFindingFingerprint)(finding)}`;
5814
+ id = base;
5815
+ let suffix = 0;
5816
+ while (reservedIds.has(id))
5817
+ id = `${base}-${++suffix}`;
5818
+ reservedIds.add(id);
5819
+ }
5820
+ emittedIds.add(id);
5821
+ return [id === finding.id ? finding : { ...finding, id }];
5822
+ }
5823
+ if (retained.has(match.id))
5824
+ return [];
5825
+ retained.add(match.id);
5826
+ emittedIds.add(match.id);
5827
+ return [finding.id === match.id ? finding : { ...finding, id: match.id }];
5828
+ });
5829
+ }
5830
+ function uniqueFindingMatch(matches) {
5831
+ return matches.length === 1 ? { id: matches[0][0], info: matches[0][1] } : undefined;
5680
5832
  }
5681
5833
  function identitiesAreCompatible(existing, finding) {
5682
5834
  const existingFingerprints = [
@@ -6539,11 +6691,13 @@ function getAnswerIssueHelpPrompt(params) {
6539
6691
 
6540
6692
 
6541
6693
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6694
+ exports.BUGBOT_FINDING_GROUNDING_INSTRUCTION = void 0;
6542
6695
  exports.getBugbotPrompt = getBugbotPrompt;
6543
6696
  /**
6544
6697
  * Prompt for Bugbot detection (detect potential problems on push).
6545
6698
  */
6546
6699
  const fill_1 = __nccwpck_require__(2559);
6700
+ exports.BUGBOT_FINDING_GROUNDING_INSTRUCTION = 'Before returning a finding, try to disprove it against the exact reviewed source. Read effective repository configuration and inherited library/protocol defaults before inferring behavior from a missing command flag. Trace short-circuit guards, validation helpers and the actual caller/render path before claiming that an unsafe branch is reachable or that separate call sites coexist. Recompute arithmetic and check cited line numbers against the current file/diff. If your evidence refutes the alleged defect, withdraw the finding entirely; do not publish a contradictory or self-refuting report. These checks apply equally to low/info severity. Report a confirmed failure when those protections are absent; do not assume they exist.';
6547
6701
  const TEMPLATE = `You are analyzing the latest code changes for potential bugs and issues.
6548
6702
 
6549
6703
  Write every human-readable finding title, description, evidence, and suggestion in {{targetLocale}}. Preserve identifiers, code, symbols, paths, refs, commands, and URLs verbatim. Echo \`outputLocale\` exactly as \`{{targetLocale}}\`.
@@ -6579,6 +6733,8 @@ For every finding:
6579
6733
  - include the nearest stable \`symbol\` and a minimal exact \`codeSnippet\` when available so the finding can survive rebases, line movement, and file renames.
6580
6734
  - when a fix is a safe replacement of exactly the reported line range, include only the replacement text in \`suggestedCode\`; otherwise omit it.
6581
6735
 
6736
+ ${exports.BUGBOT_FINDING_GROUNDING_INSTRUCTION}
6737
+
6582
6738
  Return every finding field required by the response schema. Use null for file, line, endLine, severity, confidence, category, evidence, suggestion, symbol, codeSnippet, or suggestedCode when that value does not safely apply. Only include files outside the ignore list.
6583
6739
  {{previousBlock}}
6584
6740
 
@@ -3,9 +3,22 @@ import { type ApprovalEvidenceDiagnostic } from '../../domain/approval_evidence_
3
3
  import { type BranchSyncValidationCode } from '../../domain/branch_sync_validation';
4
4
  export type ApplicationErrorKind = 'configuration' | 'authorization' | 'provider' | 'agent' | 'validation' | 'workflow' | 'unknown';
5
5
  export type ApplicationErrorCode = 'configuration.invalid' | 'configuration.unsupported' | 'authorization.denied' | 'authorization.credential-invalid' | 'provider.not-found' | 'provider.conflict' | 'provider.rate-limited' | 'provider.unavailable' | 'provider.contract-invalid' | 'agent.policy-rejected' | 'agent.failed' | 'locale.output-invalid' | 'locale.translation-failed' | 'validation.invalid-input' | 'validation.evidence-too-large' | 'validation.evidence-unavailable' | 'workflow.invalid-event' | 'workflow.stale' | 'workflow.cancelled' | 'workflow.failed' | 'workflow.presentation-pending' | 'timeout' | 'unexpected';
6
- export declare const APPLICATION_ERROR_RECOVERY_IDS: readonly ["pull-request-link-restored", "pull-request-link-base-retained", "pull-request-link-reference-retained", "pull-request-link-base-and-reference-retained", "managed-branch-enrichment-failed", "inactivity-explanation-failed", "bugbot-review-blocks-pending", "branch-sync-agent-disabled", "branch-sync-fixer-unavailable", "branch-sync-resolution-invalid", "sdd-draft-size-exceeded", "sdd-draft-format-invalid", "workspace-changes-rejected", "closed-issue-reconciliation-failed", "approval-evidence-read-failed"];
6
+ export declare const APPLICATION_ERROR_RECOVERY_IDS: readonly ["pull-request-link-restored", "pull-request-link-base-retained", "pull-request-link-reference-retained", "pull-request-link-base-and-reference-retained", "managed-branch-enrichment-failed", "inactivity-explanation-failed", "bugbot-review-blocks-pending", "branch-sync-agent-disabled", "branch-sync-fixer-unavailable", "branch-sync-resolution-invalid", "sdd-draft-size-exceeded", "sdd-draft-format-invalid", "sdd-draft-case-identity-invalid", "sdd-draft-case-budget-invalid", "sdd-draft-requirement-mapping-invalid", "sdd-draft-repository-path-invalid", "workspace-changes-rejected", "closed-issue-reconciliation-failed", "approval-evidence-read-failed", "stored-configuration-schema-missing", "stored-configuration-schema-unsupported", "stored-configuration-invalid"];
7
7
  export type ApplicationErrorRecoveryId = typeof APPLICATION_ERROR_RECOVERY_IDS[number];
8
8
  interface ApplicationErrorRecoveryVariables {
9
+ readonly 'stored-configuration-schema-missing': Readonly<{
10
+ issueNumber: number;
11
+ expectedVersion: number;
12
+ }>;
13
+ readonly 'stored-configuration-schema-unsupported': Readonly<{
14
+ issueNumber: number;
15
+ expectedVersion: number;
16
+ actualVersion: number;
17
+ }>;
18
+ readonly 'stored-configuration-invalid': Readonly<{
19
+ issueNumber: number;
20
+ expectedVersion: number;
21
+ }>;
9
22
  readonly 'closed-issue-reconciliation-failed': ClosedIssueReconciliationFacts;
10
23
  readonly 'approval-evidence-read-failed': ApprovalEvidenceDiagnostic;
11
24
  readonly 'pull-request-link-restored': Readonly<Record<string, never>>;
@@ -33,6 +46,10 @@ interface ApplicationErrorRecoveryVariables {
33
46
  }>;
34
47
  readonly 'sdd-draft-size-exceeded': Readonly<Record<string, never>>;
35
48
  readonly 'sdd-draft-format-invalid': Readonly<Record<string, never>>;
49
+ readonly 'sdd-draft-case-identity-invalid': Readonly<Record<string, never>>;
50
+ readonly 'sdd-draft-case-budget-invalid': Readonly<Record<string, never>>;
51
+ readonly 'sdd-draft-requirement-mapping-invalid': Readonly<Record<string, never>>;
52
+ readonly 'sdd-draft-repository-path-invalid': Readonly<Record<string, never>>;
36
53
  readonly 'workspace-changes-rejected': Readonly<Record<string, never>>;
37
54
  }
38
55
  export type ApplicationErrorRecovery = {
@@ -8,6 +8,8 @@ export interface BugbotSourceCoverage {
8
8
  readonly itemsRetained: number;
9
9
  readonly omittedItems: number;
10
10
  readonly truncatedItems: number;
11
+ /** Exact bounded provider identities of retained conversation items truncated by the prompt. */
12
+ readonly truncatedItemIds?: readonly string[];
11
13
  readonly limitReached: boolean;
12
14
  /** True when a provider page cap hides an unknown number of older records. */
13
15
  readonly providerLimitReached?: boolean;
@@ -1,10 +1,3 @@
1
- /**
2
- * Provider-neutral Bugbot finding and durable identity contracts.
3
- *
4
- * These types are shared by analysis, reconciliation, and publication. Keeping
5
- * them in the domain prevents policies from depending on a particular use-case
6
- * folder and gives every adapter one stable semantic vocabulary.
7
- */
8
1
  export interface BugbotFinding {
9
2
  id: string;
10
3
  title: string;
@@ -59,3 +52,14 @@ export interface ExistingFindingInfo {
59
52
  export type ExistingByFindingId = Record<string, ExistingFindingInfo>;
60
53
  export declare function isExistingFindingFullyResolved(finding: ExistingFindingInfo): boolean;
61
54
  export declare function findExistingFindingInfo(existingByFindingId: ExistingByFindingId, finding: Pick<BugbotFinding, 'id' | 'fingerprint' | 'semanticFingerprint'>): ExistingFindingInfo | undefined;
55
+ /** A match carries its durable key so updating a comment cannot re-key it. */
56
+ export declare function findExistingFindingMatch(existingByFindingId: ExistingByFindingId, finding: Pick<BugbotFinding, 'id' | 'fingerprint' | 'semanticFingerprint'>): {
57
+ id: string;
58
+ info: ExistingFindingInfo;
59
+ } | undefined;
60
+ /**
61
+ * Preserve trusted IDs across title/location changes, before limits, publication
62
+ * and final projection. Multiple reports of one uniquely matched durable finding
63
+ * share one record; unmatched or ambiguous reports remain independent evidence.
64
+ */
65
+ export declare function retainExistingFindingIds(findings: readonly BugbotFinding[], existingByFindingId: ExistingByFindingId): BugbotFinding[];