Skip to content

feat(api/runway): add external merge queue contract#260

Open
behinddwalls wants to merge 1 commit into
messagequeue-rfcfrom
preetam/messagequeue-runway
Open

feat(api/runway): add external merge queue contract#260
behinddwalls wants to merge 1 commit into
messagequeue-rfcfrom
preetam/messagequeue-runway

Conversation

@behinddwalls

@behinddwalls behinddwalls commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Why?

Runway's merge queues are the first cross-domain message queue contract: a client (potentially non-Go) publishes a merge request and consumes the result without access to Runway's Go types or storage. Per the RFC (#259) this needs a language-neutral, proto-defined contract.

What?

Establishes the message queue contract pattern using Runway's merge queues as the reference:

  • Adds api/runway/messagequeue/proto/merge.proto defining MergeRequest/MergeResult, reusing the shared Change and Strategy types and the topics option from api/base; generated into protopb.
  • Payloads are serialized as protobuf JSON via thin protojson helpers (MergeRequestToBytes/MergeRequestFromBytes and the MergeResult counterparts); a Topics() reflection helper exposes the topic binding. Topic keys are co-located with the contract.
  • Moves the merge bindings out of runway/entity into api/runway/messagequeue.
  • A drift test round-trips the payloads and asserts every Runway topic key is bound to exactly one message via the topics option.

Test Plan

  • ./tool/bazel test //api/runway/messagequeue:messagequeue_test
  • ./tool/bazel build //...

Issues

Stack

  1. refactor(api/base): name the message queue option topic_keys #264
  2. docs(rfc): add message queue contract RFC #259
  3. @ feat(api/runway): add external merge queue contract #260
  4. feat(orchestrator): make merge-conflict check asynchronous via runway #245
  5. feat(merge): make merge asynchronous via runway #247

behinddwalls added a commit that referenced this pull request Jun 17, 2026
## Summary
Move the cross-domain shared trees out of the repo root into a single
`platform/` umbrella. Domain-scoped trees (`submitqueue/`, `stovepipe/`,
`runway/`) keep their own `core/`, `entity/`, `extension/`.

| From | To |
|------|----|
| `core/{errs,metrics,consumer}` | `platform/{errs,metrics,consumer}` |
| `core/httpclient` | `platform/http` |
| `entity/*` (`change`, `mergestrategy`, `messagequeue`) |
`platform/base/*` |
| `extension/*` (`counter`, `messagequeue`) | `platform/extension/*` |

Details:
- `platform/http`: package renamed `httpclient` -> `http`; `net/http`
aliased as `nethttp` inside the package; callers that also import
`net/http` use a `phttp` alias.
- `platform/base`: root doc package renamed `entity` -> `base`; `change`
/ `mergestrategy` / `messagequeue` preserved as subpackages.
- Rewrites all Go import paths and Bazel labels; updates `Makefile`
schema/admin paths and `go:generate` roots.
- Docs refreshed to the `platform/` layout (`CLAUDE.md`, package
READMEs, RFCs), documenting current state only.

> Stacked on #256 (hermetic Go SDK). Review/merge that first.

## Test plan
- [x] `make gazelle` clean (BUILD files in sync)
- [x] `make build` — 201 targets build (hermetic)
- [x] `make test` — 54/54 unit tests pass

Made with [Cursor](https://cursor.com)

## Test Plan


## Issues


## Stack
1. @ #257
1. #258
1. #259
1. #260

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from 35e8531 to cee7fb5 Compare June 17, 2026 18:00
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from cee7fb5 to 5c1798d Compare June 17, 2026 18:34
behinddwalls added a commit that referenced this pull request Jun 17, 2026
## Summary

### Why?

The "Rebase Stacked PRs" job silently no-ops when the repo's "Automatically delete head branches" setting is on. On merge, GitHub deletes the merged head branch, which auto-retargets every child PR's base from the merged head branch to the merged base (e.g. main) *before* the job runs. The job discovers children via `gh pr list --base <merged-head>`, which then returns nothing — so it rebases nothing, force-pushes nothing, and still reports success. Run #257 (and #256 before it) did exactly this, leaving the downstream stack (#258/#259/#260) with doubled diffs that had to be rebased by hand.

The fix is to keep "Automatically delete head branches" off (now done at the repo level) and let this workflow own head-branch cleanup. With the setting off, GitHub leaves child bases untouched, the lookup finds them, and the job rebases the stack and then deletes the merged branch itself — for both stacked and non-stacked PRs.

### What?

- Document the hard requirement that "Automatically delete head branches" stays off, with the retarget-race rationale, in the workflow header.
- Log a clear line when a merge has no child PRs instead of returning silently.
- Clarify the branch-deletion step: it runs on every merged PR (stacked or not) and is skipped only when a child rebase failed; fix the misleading "All stacked PRs rebased successfully" message that printed even on no-op runs.

No behavior change on the happy path — the job already deleted the merged branch on success; this makes the intent explicit and the logs legible.

## Test Plan

- ✅ `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/rebase-stack.yml'))"` — YAML parses.
- Confirmed `delete_branch_on_merge` is `false` via `gh api repos/uber/submitqueue`.
- Post-merge: the next Rebase Stack run should log child rebases or "No open child PRs … nothing to rebase", then "Deleting merged branch …" and actually remove it.

Co-authored-by: Cursor <cursoragent@cursor.com>
behinddwalls added a commit that referenced this pull request Jun 17, 2026
## Summary

### Why?

The "Rebase Stacked PRs" job was silently no-opping: with the repo's
"Automatically delete head branches" setting on, GitHub deleted the
merged head branch on merge, which auto-retargeted every child PR's base
from the merged head branch to the merged base (e.g. `main`) *before*
the job ran. Its child lookup (`gh pr list --base <merged-head>`) then
found nothing, so it rebased nothing and still went green. Run #257 (and
#256 before it) did exactly this, leaving #258/#259/#260 with doubled
diffs that had to be rebased by hand.

The repo setting is now off, making this workflow the sole owner of
head-branch cleanup. But that exposed a second gap: the job only deleted
the merged branch at the end of its own run. When a child rebase
conflicts, the run intentionally keeps the merged branch (the child
still bases on it) and never fires again — so after the author manually
rebases and retargets the child, nothing ever deletes the orphaned
merged branch.

### What?

- Document the hard requirement that "Automatically delete head
branches" stays **off**, with the retarget-race rationale, in the
workflow header.
- Replace the outcome-gated single-branch delete with an invariant-based
sweep, `cleanup_orphaned_merged_branches`, that runs on every
invocation: for each recently merged PR whose head branch still exists,
delete it **iff** no open PR references it as a base or head. This:
- deletes the just-merged branch on a clean run (children retargeted
away),
- **keeps** it when an immediate child rebase failed (that child still
bases on it — deleting it would retarget the child to `main` and
recreate the broken diff),
- reaps branches stranded by an earlier conflicted run on the next
merge, once their children were manually fixed.
- Branch existence is snapshotted in one `git ls-remote`; the merged
base (e.g. `main`) is never touched. Conflicted runs also emit a
`::warning::` for visibility while still exiting non-fatally, and
no-stack merges log a clear line.

## Test Plan

- ✅ `yaml.safe_load` parses the workflow.
- ✅ `bash -n` on the extracted `run:` script.
- ✅ Simulated the sweep loop with mock data (dedup, skip-gone,
skip-base, consider-delete all behave correctly).
- Post-merge: the next Rebase Stack run should log child rebases (or "No
open child PRs … nothing to rebase"), then a sweep that deletes the
merged branch when nothing depends on it.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from 5c1798d to 557ebb7 Compare June 17, 2026 20:22
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from 557ebb7 to 58313d2 Compare June 17, 2026 21:30
behinddwalls added a commit that referenced this pull request Jun 17, 2026
…y) (#261)

## Summary
### Why?

Each service's gateway proto redefines its own `Change` message (and
SubmitQueue its own `Strategy` enum), duplicating wire contracts that
are meant to stay identical across domains. This is the proto-level gap
mirroring what `platform/base/{change,mergestrategy}` already solved for
the Go entities, and Stovepipe's upcoming gateway API would otherwise
add a third copy of `Change`.

### What?

Introduces an `api/base/` proto tree as the shared-wire-contract analog
of `platform/base/`:

- Adds `api/base/change` (`uber.base.change.Change`) and
`api/base/mergestrategy` (`uber.base.mergestrategy.Strategy`), each a
message-only contract that domains import instead of redefining.
- Teaches the hermetic codegen rule (`tool/proto/proto_codegen.bzl`) to
resolve cross-package proto `import`s (exec-root proto_path + imported
sources as action inputs) and to skip the gRPC/YARPC generators for
service-less contracts via a new `gen_services` attribute.
- Migrates the SubmitQueue gateway proto to import the shared
`Change`/`Strategy` and drops its local definitions; updates Go
consumers (land controller + unit/integration/e2e tests) to the shared
`protopb` packages.
- Makefile now creates the `protopb/` output dir and marks copied stubs
writable, so net-new proto packages generate cleanly.

Known cosmetic gazelle warning: resolving the new cross-proto import
prints "multiple rules may be imported" for the `# keep` rules_go
go_proto_library alias. It does not change generated BUILD files, the
build, or the committed-files path that consumers resolve via the root
`# gazelle:resolve go` directives.

## Test Plan
- ✅ `make proto` (regenerates stubs, including the message-only base
protos)
- ✅ `./tool/bazel build //...` (213 targets)
- ✅ `make test` (54 unit tests pass)
- ✅ `make fmt`

## Issues


## Stack
1. @ #261
1. #259
1. #260

Co-authored-by: Cursor <cursoragent@cursor.com>
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch 2 times, most recently from 823ec31 to 738e35e Compare June 17, 2026 21:55
@behinddwalls behinddwalls changed the title feat(api/runway): add external message queue contract as JSON Schema feat(api/runway): add external merge queue contract Jun 17, 2026
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch 2 times, most recently from 8696ae5 to e4512bb Compare June 17, 2026 22:20
@behinddwalls behinddwalls force-pushed the messagequeue-rfc branch 2 times, most recently from b4860ba to b514f27 Compare June 17, 2026 23:42
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from e4512bb to 7f52edf Compare June 17, 2026 23:42
@behinddwalls behinddwalls marked this pull request as ready for review June 18, 2026 00:05
@behinddwalls behinddwalls requested review from a team and sbalabanov as code owners June 18, 2026 00:05
@behinddwalls behinddwalls force-pushed the preetam/messagequeue-runway branch from 7f52edf to 265fcfe Compare June 18, 2026 04:22
Add Runway's published, language-neutral merge queue contract. merge.proto
defines MergeRequest/MergeResult, reusing the shared Change and Strategy
types and the topic_keys option, generated into protopb and serialized as
protobuf JSON so the queue keeps storing self-describing JSON. The Go
helpers wrap protojson and expose the topic binding via reflection; topic
keys are co-located with the contract. A drift test round-trips the
payloads and verifies every topic key is bound to exactly one message.
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