Skip to content

feat(runway): classify invalid merge requests as terminal - #458

Draft
behinddwalls wants to merge 1 commit into
preetam/remove-dead-pusherfrom
preetam/runway-terminal-errors
Draft

feat(runway): classify invalid merge requests as terminal#458
behinddwalls wants to merge 1 commit into
preetam/remove-dead-pusherfrom
preetam/runway-terminal-errors

Conversation

@behinddwalls

@behinddwalls behinddwalls commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Why?

Runway is stateless and the sole responder on the client's correlation id: SubmitQueue records the in-flight work before publishing and waits for exactly one MergeResult echoing that id. A request that resolves to neither a success nor a failure leaves the client waiting forever.

Today the controllers treat exactly one error as a nameable outcome — merger.ErrConflict — and nack everything else for retry. But some requests are unusable as written: an unknown or unsupported merge strategy, a malformed change URI, an invalid strategy composition. Retrying those can never succeed. Nacking them burns the retry budget and then dead-letters, which — with nothing draining the dead-letter topics — is precisely the silent hang described above.

What?

Adds a second terminal sentinel to the merger contract, merger.ErrInvalidRequest, for requests that can never be applied as written, and merger.IsTerminal as the single classification point over both sentinels.

Switches both the merge and mergeconflictcheck controllers from an errors.Is(err, merger.ErrConflict) test to merger.IsTerminal(err), so an invalid request now publishes a FAILED MergeResult and acks instead of nacking. The two cases stay distinguishable in observability: invalid requests increment an invalid_requests counter and log the underlying error, conflicts keep the existing merge_conflicts counter.

No implementation returns the new sentinel yet — the git-backed merger later in this stack is its first producer. This lands the contract and the controller behavior first so that change is only about merge mechanics.

Also corrects the Runway README, which still claimed the controllers merely deserialize and log the request.

Test Plan

bazel test //runway/... — 4/4 pass, including new TestProcess_InvalidRequest cases on both controllers asserting a FAILED result is published and the delivery is acked
make gazelle, make fmt

Stack

  1. chore(submitqueue): remove dead pusher extension #457
  2. @ feat(runway): classify invalid merge requests as terminal #458
  3. feat(runway): dlq reconciler for merge topics #459
  4. feat(runway): git-backed merger with REBASE #460
  5. feat(runway): git merger SQUASH_REBASE and MERGE #461
  6. feat(runway): git merger PROMOTE #462
  7. feat(runway): wire the git merger into the server #463

## Summary

### Why?

Runway is stateless and the sole responder on the client's correlation id: SubmitQueue records the in-flight work before publishing and waits for exactly one `MergeResult` echoing that id. A request that resolves to neither a success nor a failure leaves the client waiting forever.

Today the controllers treat exactly one error as a nameable outcome — `merger.ErrConflict` — and nack everything else for retry. But some requests are unusable as written: an unknown or unsupported merge strategy, a malformed change URI, an invalid strategy composition. Retrying those can never succeed. Nacking them burns the retry budget and then dead-letters, which — with nothing draining the dead-letter topics — is precisely the silent hang described above.

### What?

Adds a second terminal sentinel to the merger contract, `merger.ErrInvalidRequest`, for requests that can never be applied as written, and `merger.IsTerminal` as the single classification point over both sentinels.

Switches both the `merge` and `mergeconflictcheck` controllers from an `errors.Is(err, merger.ErrConflict)` test to `merger.IsTerminal(err)`, so an invalid request now publishes a `FAILED` `MergeResult` and acks instead of nacking. The two cases stay distinguishable in observability: invalid requests increment an `invalid_requests` counter and log the underlying error, conflicts keep the existing `merge_conflicts` counter.

No implementation returns the new sentinel yet — the git-backed merger later in this stack is its first producer. This lands the contract and the controller behavior first so that change is only about merge mechanics.

Also corrects the Runway README, which still claimed the controllers merely deserialize and log the request.

## Test Plan

✅ `bazel test //runway/...` — 4/4 pass, including new `TestProcess_InvalidRequest` cases on both controllers asserting a FAILED result is published and the delivery is acked
✅ `make gazelle`, `make fmt`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant