Files
kandev/docs/plans/github-auto-merge-reliability/plan.md
T

172 lines
7.8 KiB
Markdown

---
created: 2026-08-28
status: done
requirements:
- REQ-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002
- REQ-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-RECOVERY-003
system_design:
- ../../specs/integrations/system-design/github-pr-merge-queue.md
- ../../specs/integrations/system-design/github-pr-merge-queue-recovery.md
legacy_specs: []
---
# Implementation Plan: GitHub Auto-Merge Reliability
## Overview
Make each automatic merge attempt safe across force-pushes, provider delays,
concurrent evaluations, and backend restarts. Bind GitHub to the reviewed head.
Reserve each attempt before the provider call. Reconcile accepted queue and
merged states, and expose one explicit retry for a failed attempt.
The provider contract lands first. The durable attempt journal follows it. The
API and shared responsive controls then expose explicit retry. Desktop and
mobile E2E tests prove the complete outcome. Public documentation closes the
delivery package.
## Scope
### In scope
- Send the reviewed head SHA with automatic GitHub merge requests.
- Pace asynchronous merge-status polling within the operation context.
- Store one durable attempt reservation and result per pull request.
- Prevent unchanged automatic retries across events and restarts.
- Reconcile active queue and merged observations with the attempt journal.
- Classify recognized legacy automatic-merge errors during migration.
- Add a scoped explicit retry command for a failed automatic attempt.
- Provide the same retry outcome in the desktop popover and phone drawer.
- Document retry and merge-queue behavior for users.
### Out of scope
- Changing the auto-merge option value or default.
- Retrying an unchanged failed attempt without a user command.
- Bypassing GitHub checks, reviews, mergeability, rules, or permissions.
- Changing the manual merge action's current head-binding contract.
- Adding a new dialog, drawer, page, or automation preference.
- GitLab merge-request automation.
## Technical approach
### Provider request and polling
Replace positional merge parameters with a request value that can carry the
merge method and expected head SHA. Require the SHA on the automatic path and
send it through both GitHub clients. Preserve the manual merge behavior.
Decode the provider's expected-head diagnostic. Wait at least one second
between pending reads. Keep the existing two-minute budget and stop waits when
the context ends. Use an injected wait boundary in focused tests.
### Durable attempt journal
Extend `github_task_ci_pr_state` with the attempt result and typed error kind.
Reserve the readiness signature and head in a transaction before GitHub is
called. Record `failed` or `accepted` after the provider result. Treat the
existing per-PR singleflight as an optimization, not the correctness boundary.
Adopt an observed active queue entry and reconcile a merged pull request as
accepted. Clear only a typed `auto_merge` error. On restart, reconcile an
`in_flight` attempt before it expires to a failed state. Do not submit the same
signature automatically.
### Explicit retry and responsive controls
Add a task-scoped endpoint that accepts the exact linked repository and pull
request. Persist retry authorization, publish the normal evaluation event, and
return `202 Accepted`. The evaluator refreshes state and runs every readiness
gate before it can call GitHub.
The shared automation controls call this command only for an `auto_merge`
error. Other stored errors show Refresh. State-loading failures keep their
existing refresh retry. Mobile continues to use the inset status drawer with
one scroll owner and a shared target of at least 44 by 44 CSS pixels.
### Integrated test support and documentation
Extend the mock GitHub controller and the E2E API client with pending, failure,
queue, merge, and head-mismatch outcomes. Prove one provider request per
authorization on desktop and mobile. Update the public GitHub integration and
session-review guides with the user-visible retry rules.
## Tests
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.1`: provider request and service
tests prove expected-head transmission and mismatch failure.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.2`: store and evaluator tests prove
reservation before the side effect, concurrent deduplication, and restart
safety.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.3`: signature tests prove changed
head or gates can rearm one attempt only after every gate passes.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.4`: controller, service, evaluator,
and E2E tests prove exact-PR retry without gate or permission bypass.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.5`: sync and evaluator tests prove
queue and merged reconciliation with typed error clearing.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.6`: store-failure tests prove that
GitHub is not called and a retryable per-PR error appears.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.7`: deterministic wait tests prove
one-second pacing, cancellation, and the bounded deadline.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.8`: migration tests prove narrow
legacy classification and preservation of unknown errors.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-002.9`: component and desktop/mobile
E2E tests prove explicit merge retry and separate state refresh.
- `AC-INTEGRATIONS-GITHUB-PR-MERGE-QUEUE-RECOVERY-003.1-.8`: evaluator
regression tests preserve queue adoption, same-head blocking, and new-head
requeue behavior.
## E2E tests
- Desktop: extend `apps/web/e2e/tests/pr/ci-automation-options.spec.ts`. Produce
an automatic merge failure, select Retry, and assert one request for the
named pull request. Then publish an active queue entry and assert that the
obsolete automatic-merge error clears.
- Mobile: extend
`apps/web/e2e/tests/pr/mobile-ci-automation-options.spec.ts` with the same
outcome under touch input. Assert the 44-pixel target, internal drawer
scrolling, and no document-level horizontal overflow.
- Both flows cover a provider head mismatch, an unchanged failed signature, an
accepted queue entry, a merged pull request, and a non-merge loading error.
## Work orders
- [x] [Task 01: Bind and pace asynchronous merge](task-01-bind-and-pace-asynchronous-merge.md)
- [x] [Task 02: Journal automatic merge attempts](task-02-journal-automatic-merge-attempts.md)
- [x] [Task 03: Expose explicit merge retry](task-03-expose-explicit-merge-retry.md)
- [x] [Task 04: Prove responsive merge retry](task-04-prove-responsive-merge-retry.md)
- [x] [Task 05: Document merge retry behavior](task-05-document-merge-retry-behavior.md)
## Verification commands
```bash
make -C apps/backend test
make -C apps/backend lint
cd apps && pnpm --filter @kandev/web lint
cd apps/web && pnpm run typecheck
cd apps/web && pnpm run i18n:check
cd apps/web && pnpm e2e:raw --project=chromium e2e/tests/pr/ci-automation-options.spec.ts
cd apps/web && pnpm e2e:raw --project=mobile-chrome e2e/tests/pr/mobile-ci-automation-options.spec.ts
python3 scripts/lint-spec-files.py --all
node --test scripts/validate-public-docs.test.mjs
node scripts/validate-public-docs.mjs
git diff --check
```
## Risks
- The GitHub client interface has many mocks. A request-value migration can
expose callers outside automation.
- Timer tests can become slow or flaky without an injected wait boundary.
- Legacy error classification can erase the wrong error if it accepts broad
text. The migration must recognize only stable server prefixes.
- A crashed `in_flight` attempt can require user retry when GitHub exposes no
queue or merge observation. This failure is safer than duplicate submission.
- Concurrent HTTP and event evaluations can race. The durable reservation and
transaction must remain the correctness boundary.
- New error-kind and action copy requires all five locale catalogs.
- Manual merge behavior must remain unchanged while the automatic path gains
expected-head enforcement.
## Open questions
None.