Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5255,6 +5255,12 @@ exports.APPLICATION_ERROR_METADATA = {
action: 'Correct the input and retry.',
retainedState: UNCHANGED_STATE,
},
'validation.evidence-unavailable': {
kind: 'validation', retryable: false,
impact: 'Progress was not assessed because GitHub omitted a required file patch.',
action: 'Inspect the exact base/head comparison manually or split the work into smaller reviewed changes with complete patches before a new assessment. Retrying the same commits cannot restore omitted evidence.',
retainedState: 'Existing progress labels and published assessments were preserved; no model assessment was made.',
},
'validation.evidence-too-large': {
kind: 'validation', retryable: false,
impact: 'Progress could not be assessed within the fixed evidence limits.',
Expand Down
2 changes: 1 addition & 1 deletion build/api/src/data/model/application_error.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type BranchSyncValidationCode } from '../../domain/branch_sync_validation';
export type ApplicationErrorKind = 'configuration' | 'authorization' | 'provider' | 'agent' | 'validation' | 'workflow' | 'unknown';
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' | 'workflow.invalid-event' | 'workflow.stale' | 'workflow.cancelled' | 'workflow.failed' | 'workflow.presentation-pending' | 'timeout' | 'unexpected';
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';
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"];
export type ApplicationErrorRecoveryId = typeof APPLICATION_ERROR_RECOVERY_IDS[number];
interface ApplicationErrorRecoveryVariables {
Expand Down
95 changes: 75 additions & 20 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39201,6 +39201,11 @@ const SPANISH_CONTENT = Object.freeze({
action: 'Corrige la entrada y reintΓ©ntalo.',
retainedState: UNCHANGED_STATE_ES,
}),
'validation.evidence-unavailable': Object.freeze({
impact: 'No se evaluΓ³ el progreso porque GitHub omitiΓ³ un patch necesario.',
action: 'Inspecciona manualmente la comparaciΓ³n entre los commits base y head exactos o divide el trabajo en cambios revisados mΓ‘s pequeΓ±os con patches completos antes de otra evaluaciΓ³n. Reintentar los mismos commits no recuperarΓ‘ la evidencia omitida.',
retainedState: 'Se conservaron las etiquetas de progreso y las evaluaciones publicadas; no se realizΓ³ ninguna evaluaciΓ³n del modelo.',
}),
'validation.evidence-too-large': Object.freeze({
impact: 'No se pudo evaluar el progreso dentro de los lΓ­mites fijos de evidencia.',
action: 'Reduce el tamaΓ±o de la comparaciΓ³n o divide el trabajo en cambios revisados mΓ‘s pequeΓ±os antes de iniciar otra evaluaciΓ³n.',
Expand Down Expand Up @@ -71916,13 +71921,13 @@ async function runLinkPullRequestIssue(param, taskId, port, delay) {
let retainedIssueReference = false;
let alreadyLinked = false;
if (!hasOwnedPendingOperation) {
alreadyLinked = await port.isLinked(param.pullRequestNumber);
alreadyLinked = await port.isLinked(param.pullRequestNumber, param.issueNumber);
if (alreadyLinked)
return [];
}
else {
try {
alreadyLinked = await port.isLinked(param.pullRequestNumber);
alreadyLinked = await port.isLinked(param.pullRequestNumber, param.issueNumber);
}
catch (error) {
primaryError = error;
Expand Down Expand Up @@ -78106,6 +78111,12 @@ exports.APPLICATION_ERROR_METADATA = {
action: 'Correct the input and retry.',
retainedState: UNCHANGED_STATE,
},
'validation.evidence-unavailable': {
kind: 'validation', retryable: false,
impact: 'Progress was not assessed because GitHub omitted a required file patch.',
action: 'Inspect the exact base/head comparison manually or split the work into smaller reviewed changes with complete patches before a new assessment. Retrying the same commits cannot restore omitted evidence.',
retainedState: 'Existing progress labels and published assessments were preserved; no model assessment was made.',
},
'validation.evidence-too-large': {
kind: 'validation', retryable: false,
impact: 'Progress could not be assessed within the fixed evidence limits.',
Expand Down Expand Up @@ -85414,8 +85425,10 @@ class ProgressDiffRepository {
throw unavailable();
paths.add(path);
const generated = path.startsWith('build/') || path === 'specs/CATALOG.md';
if (!generated && (file.additions > 0 || file.deletions > 0) && (typeof file.patch !== 'string' || !file.patch))
throw unavailable();
const unchanged = file.status === 'unchanged' && file.additions === 0 && file.deletions === 0;
if (!generated && !unchanged && (typeof file.patch !== 'string' || !file.patch)) {
throw new application_error_1.ApplicationError('validation.evidence-unavailable', 'GitHub omitted a required file patch from the pinned comparison. Progress was not assessed and existing state was retained.');
}
return { path, status: file.status, additions: file.additions, deletions: file.deletions,
...(generated ? { evidence: 'generated-metadata-only' } : { patch: file.patch ?? '' }) };
});
Expand Down Expand Up @@ -86701,6 +86714,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.PullRequestLifecycleRepository = void 0;
const logger_1 = __nccwpck_require__(91151);
const application_error_1 = __nccwpck_require__(75999);
const github_error_policy_1 = __nccwpck_require__(58791);
class PullRequestLifecycleRepository {
constructor(githubClient) {
this.githubClient = githubClient;
Expand Down Expand Up @@ -86762,26 +86776,69 @@ class PullRequestLifecycleRepository {
throw semanticError;
}
};
this.isLinked = async (owner, repository, pullRequestNumber, _token) => {
const pullRequestUrl = `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/pull/${pullRequestNumber}`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), PullRequestLifecycleRepository.IS_LINKED_FETCH_TIMEOUT_MS);
/** Reads at most 200 closing references through the credential-bound API. */
this.isLinked = async (owner, repository, pullRequestNumber, issueNumber, token) => {
try {
const res = await fetch(pullRequestUrl, { signal: controller.signal });
if (!res.ok) {
throw new Error(`Pull-request linkage query returned HTTP ${res.status}.`);
if (![pullRequestNumber, issueNumber].every(number => Number.isSafeInteger(number) && number > 0)) {
throw new application_error_1.ApplicationError('validation.invalid-input', 'Linkage requires positive PR and issue numbers.');
}
const client = this.githubClient.getClient(token);
if (!client.graphql)
throw new application_error_1.ApplicationError('configuration.unsupported', 'Authenticated PR linkage queries are not supported by this installation.');
const expectedRepository = `${owner}/${repository}`.toLowerCase();
let cursor = null;
for (let page = 0; page < 2; page += 1) {
const result = await client.graphql(`
query PullRequestIssueLinkage($owner: String!, $repo: String!, $number: Int!, $cursor: String) {
repository(owner: $owner, name: $repo) {
nameWithOwner
pullRequest(number: $number) {
number
closingIssuesReferences(first: 100, after: $cursor) {
nodes { number repository { nameWithOwner } }
pageInfo { hasNextPage endCursor }
}
}
}
}`, { owner, repo: repository, number: pullRequestNumber, cursor });
const resource = result?.repository;
if (resource === null || resource?.pullRequest === null) {
throw new application_error_1.ApplicationError('provider.not-found', 'The requested pull request is unavailable to the configured credential.');
}
const pr = resource?.pullRequest;
const references = pr?.closingIssuesReferences;
if (typeof resource?.nameWithOwner !== 'string' || resource.nameWithOwner.toLowerCase() !== expectedRepository || pr?.number !== pullRequestNumber
|| !Array.isArray(references?.nodes) || references.nodes.length > 100
|| typeof references.pageInfo?.hasNextPage !== 'boolean'
|| references.nodes.some(node => !node || !Number.isSafeInteger(node.number) || node.number <= 0
|| typeof node.repository?.nameWithOwner !== 'string' || !node.repository.nameWithOwner)) {
throw new application_error_1.ApplicationError('provider.contract-invalid', 'GitHub did not return verifiable PR linkage evidence.');
}
if (references.nodes.some(node => node.number === issueNumber
&& node.repository.nameWithOwner.toLowerCase() === expectedRepository))
return true;
if (!references.pageInfo.hasNextPage)
return false;
const nextCursor = references.pageInfo.endCursor;
if (typeof nextCursor !== 'string' || !nextCursor || nextCursor === cursor) {
throw new application_error_1.ApplicationError('provider.contract-invalid', 'GitHub returned incomplete linkage pagination.');
}
cursor = nextCursor;
}
const htmlContent = await res.text();
return !htmlContent.includes('has_github_issues=false');
throw new application_error_1.ApplicationError('provider.contract-invalid', 'PR linkage exceeds the fixed 200-reference inspection bound.');
}
catch (error) {
const semanticError = (0, application_error_1.toApplicationError)(error, 'provider.unavailable', `Unable to inspect issue linkage for pull request #${pullRequestNumber}.`);
const status = (0, github_error_policy_1.getGithubErrorStatus)(error);
const types = typeof error === 'object' && error !== null && 'errors' in error && Array.isArray(error.errors)
? error.errors.map((entry) => entry?.type) : [];
const code = (0, github_error_policy_1.isGithubRateLimited)(error) || types.includes('RATE_LIMITED') ? 'provider.rate-limited'
: status === 401 ? 'authorization.credential-invalid'
: status === 403 || types.includes('FORBIDDEN') || types.includes('UNAUTHORIZED') ? 'authorization.denied'
: status === 404 || types.includes('NOT_FOUND') ? 'provider.not-found' : 'provider.unavailable';
const semanticError = (0, application_error_1.toApplicationError)(error, code, `Unable to inspect issue linkage for pull request #${pullRequestNumber}.`);
(0, logger_1.logError)(semanticError);
throw semanticError;
}
finally {
clearTimeout(timeoutId);
}
};
this.updateBaseBranch = async (owner, repository, pullRequestNumber, branch, token) => {
const octokit = this.githubClient.getClient(token);
Expand Down Expand Up @@ -86850,8 +86907,6 @@ class PullRequestLifecycleRepository {
}
}
exports.PullRequestLifecycleRepository = PullRequestLifecycleRepository;
/** Default timeout (ms) for isLinked fetch. */
PullRequestLifecycleRepository.IS_LINKED_FETCH_TIMEOUT_MS = 10000;
function toBugbotPullRequestIdentity(value) {
const number = value.number;
const state = value.state;
Expand Down Expand Up @@ -94970,7 +95025,7 @@ function bindBranchWorkflow(port, binding) {
}
function bindPullRequestIssueLink(port, binding) {
return Object.freeze({
isLinked: (pullRequestNumber) => port.isLinked(binding.owner, binding.repository, pullRequestNumber, binding.token),
isLinked: (pullRequestNumber, issueNumber) => port.isLinked(binding.owner, binding.repository, pullRequestNumber, issueNumber, binding.token),
getDetails: (pullRequestNumber) => port.getDetails(binding.owner, binding.repository, pullRequestNumber, binding.token),
updateBaseBranch: (pullRequestNumber, baseBranch) => port.updateBaseBranch(binding.owner, binding.repository, pullRequestNumber, baseBranch, binding.token),
updateDescription: (pullRequestNumber, description) => port.updateDescription(binding.owner, binding.repository, pullRequestNumber, description, binding.token),
Expand Down
Loading
Loading