Skip to content

Merge coordinator: reference implementation + design questions (policy-gated protected-ref merges) #4822

Description

@willgriffin

Context

VISION_PROJECTS.md lists the merge coordinator among the designed-but-unbuilt forge roles: the agent that "runs the merge train, requests human sign-off." We've been self-hosting a relay as the system of record for our org's repos and needed that role now, so we built a reference implementation and would like to converge it with wherever the project wants this to go: buzz-merge-coordinator on our fork.

Shape

One small binary (no daemon state, no unsafe, ~600 lines + 15 predicate tests) that gates a protected-ref fast-forward on three fail-closed predicates, all bound to the exact head OID:

  1. Approvals — kind:46030 grants / 46031 denies with tags ["t","git-merge"] ["r",<repo_id>] ["x",<head_oid>]; N distinct signers qualifying by bound-channel role (default: admin) or allowlist; any qualifying veto refuses.
  2. CI evidence — kind:1630 events (["t",<context>] ["l","success"|"failure"] + the same r/x binding); latest attestation per required context from listed attestor pubkeys must be success.
  3. External disposition (optional) — POST {repo_id, head_oid, dst_ref} to a configured URL must return {"allow":true}; the seam for an external tracker to hold merge-authority state without the coordinator knowing anything about it.

Then it pushes oid:ref over smart HTTP with its own identity (a bound-channel admin), posts kind:1631 + a human-readable decision to the bound channel, and refusals name the failed predicate. The relay's transport protections (push: role floor, no-force-push) stay as the independent layer underneath — we've been running that combination live.

Design questions for maintainers

  • Kind/tag conventions: we reused KIND_APPROVAL_GRANT/DENY (46030/46031) with a t=git-merge scope, and 1630 for CI results per the VISION merge-flow sketch, with single-letter tags (r/x/t/l) so evidence is server-side filterable with standard filters. Is that the vocabulary you'd want, or is a dedicated kind range planned for review approvals / CI attestations?
  • Placement: workspace crate (as in the branch) vs folding the logic into the relay behind require-approval transport enforcement? We see value in both existing — transport counting as defense-in-depth, the coordinator for the richer predicates (CI, disposition, vetoes).
  • Client reuse: the crate carries a self-contained ~150-line NIP-98 client because buzz-cli's BuzzClient is private; if a shared client were extracted into buzz-sdk, that module disappears.

Happy to adapt the branch to your conventions and open a PR — or treat this as field input if you'd rather build it differently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions