Skip to content

fix: persist manual GitHub retry failures - #30

Merged
Codename-11 merged 8 commits into
mainfrom
codex/github-manual-retry
Jul 14, 2026
Merged

Codename-11 merged 8 commits into
mainfrom
codex/github-manual-retry

Conversation

@Codename-11

Copy link
Copy Markdown
Owner

Summary

  • persist provider reset/backoff and diagnostics when manual or MCP GitHub refresh fails before resource upsert
  • open the same mapping-wide circuit as scheduled reconciliation for rate limits, auth failures, and timeouts
  • exclude all named Next build caches from release Docker contexts

Review context

Addresses the valid post-merge Codex finding on #29: #29 (comment)

Verification

  • focused reconciliation/completion suite: 23/23
  • lint (existing warnings only)
  • typecheck
  • git diff --check

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5cdf83455

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Math.max(1, args.maxMinutes),
Math.max(1, args.baseMinutes) * 2 ** Math.max(0, failureCount - 1),
);
const fallbackRetryAt = new Date(args.now.getTime() + exponentialMinutes * 60_000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Base manual retry delays on failure time

When a workspace uses an allowed 1-minute githubSyncBackoffMinutes and a manual/MCP refresh consumes a long githubRequestTimeoutSeconds (up to 60s, or longer across sequential PR/check calls), args.now is the timestamp captured before fetchGitHubSnapshot runs, so fallbackRetryAt can already be at or before the time this failure is persisted. Once the manual cooldown has also elapsed, the next refresh can claim immediately instead of honoring the intended timeout/permission backoff circuit; compute the fallback from the failure/persist time, or clamp it to a future time.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71794d586e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

provider: GITHUB_PROVIDER,
connectionMappingId: args.connectionMappingId,
},
data: { syncRetryAt: retryAt, syncLastError: message },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve later mapping-wide retry gates

When a manual refresh hits a mapping-wide error on one PR while another resource on the same mapping already has a later syncRetryAt (for example a GitHub reset header from a previous 429), this unconditional updateMany replaces the later gate with this failure's retry time. A timeout or auth error with only the fallback backoff can therefore shorten an existing longer rate-limit circuit and let the reconciler/manual refreshes poll that mapping early; only extend rows whose retry is null or earlier than the new value.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f303062a53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +760 to +766
} catch (error) {
// Scheduled reconciliation owns its own retry accounting. Manual/MCP
// refreshes must persist the provider reset/backoff before returning the
// error so a short collision lease cannot accidentally become the retry
// policy and the issue panel retains the real diagnostic.
if (!args.skipCollisionGuard) {
await persistGitHubManualSyncFailure({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle partial GitHub checks failures for manual syncs

This only persists retry/backoff when fetchGitHubSnapshot throws, but PR checks/status provider failures do not throw: getGitHubPullRequestChecks settles those calls into metadata.checks.partial and upsertExternalResource only updates the current row. In a manual/MCP refresh where the PR request succeeds but checks are rate-limited, 403, or time out, sibling resources on the same mapping still avoid the new mapping-wide circuit and can immediately keep refreshing against the failing provider; scheduled reconciliation has a separate partial-checks path for this case.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2e619c5dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +85 to +87
return new GitHubRequestError(
message,
rateLimited ? 429 : timedOut ? 408 : 403,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid fabricating auth failures for generic partial checks

When getGitHubPullRequestChecks returns partial: true for a non-auth reason (for example truncated check-suite pagination or a transient 5xx from one checks/status request, both of which leave rateLimited and timedOut false), this creates a synthetic 403. persistGitHubManualSyncFailure() then treats that 403 as a mapping-wide permission failure, so manually refreshing one unusually large or transiently failing PR blocks every sibling GitHub resource on the mapping until backoff expires; only auth/rate-limit/timeout partials should open that circuit.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3ff55ecc4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

},
data: {
syncAttemptedAt: args.now,
syncRetryAt: retryAt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve later retry gates on the failing resource

When two manual/MCP refreshes for different resources on the same GitHub mapping overlap, one failure can extend this row via the mapping-wide update below while this refresh is still in flight. This unconditional assignment then overwrites that later provider reset with this call's shorter fallback/retry time, so the resource can become retryable before the mapping-wide circuit actually expires. Apply the same max/syncRetryAt < retryAt guard to the primary row before writing syncRetryAt.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52b4c04488

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const partialChecksError = gitHubPartialChecksError(snapshot.metadata);
if (!args.skipCollisionGuard && partialChecksError) {
const failureNow = new Date();
const failure = await persistGitHubManualSyncFailure({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid double-counting manual partial-check failures

When a manual refresh succeeds on the PR read but returns partial Checks metadata, the transaction above has already called upsertExternalResource, whose checksDiagnostic path increments syncFailureCount and stores the diagnostic. This new persistGitHubManualSyncFailure call increments syncFailureCount again for the same provider failure, so one Checks API 403/timeout/rate-limit is recorded as two failures and subsequent retries skip an exponential-backoff step. Consider either not counting partials in upsertExternalResource for this path or applying the provider retry circuit without another increment.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d023136f5b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .dockerignore
**/node_modules
.next
.next-e2e
.next-*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop ignoring next-env.d.ts from Docker contexts

In the release worker build path I checked (RELEASE.md builds forge-worker and the Dockerfile's worker stage does COPY ... next-env.d.ts ...), this pattern also matches the generated next-env.d.ts file at the repo root, not just .next-* build directories. Because ignored files are removed from the Docker context, docker compose build forge-worker will fail at that COPY once this line is present; add an exception such as !next-env.d.ts or use a narrower directory-only pattern.

Useful? React with 👍 / 👎.

id: args.externalResourceId,
workspaceId: args.workspaceId,
provider: GITHUB_PROVIDER,
OR: [{ syncRetryAt: null }, { syncRetryAt: { lt: retryAt } }],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace the manual lease when recording shorter backoffs

When a manual/MCP refresh has already called claimGitHubManualSync, the row's syncRetryAt contains the temporary collision lease. For workspaces with a short configured backoff, such as 1 minute, and a request timeout of 5 seconds or more, the computed failure retryAt can be earlier than that lease, so this extend-only predicate skips the update and leaves the temporary lease as the persisted retry gate returned to the UI. That blocks retries longer than the workspace backoff policy after the provider call has already failed; the primary row needs to distinguish the just-acquired lease from an older provider reset before refusing to shorten it.

Useful? React with 👍 / 👎.

@Codename-11

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 0fa9f312ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Codename-11
Codename-11 merged commit 99c875f into main Jul 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant