gabriel / musehub public
Open #124 Enhancement
filed by gabriel human · 57 days ago

Fork + cross-repo merge proposals — org-aware, supercharged beyond GitHub's model

0 Anchors
Blast radius
Churn 30d
0 Proposals

Fork + Cross-Repo Merge Proposals — Org-Aware, Supercharged Beyond GitHub's Model

Background

Forking a repo already works: POST /repos/{repo_id}/fork (musehub/api/routes/musehub/repos.py::fork_repo) creates a new, fully independent repo owned by the caller and records the relationship in musehub_forks; list_repo_forks/get_repo_fork_network already expose the fork graph. What does not exist: any way to open a merge proposal from a fork back to its upstream. create_proposal (musehub/services/musehub_proposals.py::create_proposal) and run_proposal_create (muse/cli/commands/hub/proposals.py) both only know about --from-branch/--to-branch within a single repo — there is no cross-repo source concept anywhere in the proposal-creation path today. Fork and propose are two real, working features on either side of a gap that simply hasn't been bridged yet.

GitHub's fork-and-PR model is the obvious reference point, and it's worth being explicit about why copying it verbatim would waste what Muse already has. GitHub's PR is fundamentally a text/line diff between two branches that happen to live in different repos, with no semantic model of what changed, no cryptographic chain of who's vouching for the change, and no structural sense of what the change means to the version number. Muse already has all three of those things, for reasons that predate this ticket:

  • Content addressing means a commit that exists in a fork and a commit that exists upstream, if they produced byte-identical state, are the same object — Muse can know a fork's change already landed upstream (e.g. via cherry-pick or independent convergent authorship) by object-ID comparison alone, no fetch-and-diff required.
  • The domain-plugin architecture means "diff" is never just text — a cross-repo proposal against a code repo gets a symbol-level diff; against a midi/music repo it gets a note-level diff; whatever domain eventually lands (CAD, datasets, knowledge graphs, per the domain plans already filed on this tracker) gets that domain's own semantic diff, for free, from the same mechanism that already powers same-repo proposals.
  • Harmony already learns conflict resolutions per-repo, three tiers deep (policy → exact replay → semantic replay). A cross-repo proposal is a chance to query the upstream's Harmony memory for a pattern the fork's divergent history reproduces — surfacing "this exact conflict shape has already been resolved once merging into this repo" even though the fork never saw that resolution happen.
  • The SemVer classifier computes a structural bump from the AST/symbol diff at commit time, independent of which repo the commit lives in. A cross-repo proposal can show the real, structurally-computed version impact of merging fork→upstream in the proposal view itself, not a vague "42 files changed."
  • Identity, attestations, and (once #org-quorum-wiring lands) org quorum are all handle-scoped, not repo-scoped, for identity-level claims. A fork-based contributor's code:reviewed/code:approved history and org membership travel with their handle across the fork boundary — something GitHub's plain "contributor" badge doesn't approach with any cryptographic backing.

None of that requires new cryptographic or storage primitives — it requires teaching the proposal-creation and proposal-review paths to look across the fork relationship that musehub_forks already records, and to surface what the object store, domain plugins, Harmony, the classifier, and the identity graph already know. That's the actual scope of "supercharge" here: wiring existing differentiators into a workflow gap, not inventing new ones.

Goal

  • muse hub proposal create can open a proposal whose source branch lives in a fork and whose target is the fork's recorded upstream — a genuine cross-repo merge proposal, not a same-repo one with a misleading label.
  • The proposal view for a cross-repo proposal surfaces, at minimum: which source commits are already object-identical to something reachable from the upstream target (content-addressed convergence detection); the structurally-computed SemVer impact of the merge; and any Harmony pattern match against the upstream's own conflict-resolution memory.
  • Cross-repo proposals are governed by the upstream's governance.json/ quorum rules exactly as a same-repo proposal would be — forking never bypasses review policy.
  • The mechanism is domain-agnostic by construction — it works for a code repo today and for any future domain plugin's repo without bespoke per-domain glue, because it rides the same StructuredDelta/domain-plugin interface every same-repo proposal already uses.

"Done" means a real fork-of-muse (or a realistic staging-fixture equivalent) can open a proposal back to the real muse repo, have it reviewed under the upstream's governance rules, and merged — with the proposal view showing genuinely more than GitHub's PR view would for the identical change.

Non-Goals / Out of Scope

  • Automatic fork syncing (keeping a fork's main up to date with upstream automatically) — a real, separate feature; not attempted here.
  • Multi-way forks (a fork of a fork of a fork, proposing back more than one hop) — v0 targets a direct fork→immediate-upstream relationship, the case musehub_forks already records; deeper chains are a named future direction, not designed now.
  • Rewriting the fork mechanism itself. fork_repo already works; this plan only adds the proposal-creation bridge on top of it.
  • Full Harmony auto-resolution of cross-repo conflicts. Surfacing a Harmony pattern match in the proposal view (read-only signal) is in scope; automatically applying that resolution without human review is not — that would bypass the review-policy goals of the companion org/quorum ticket.
  • A general-purpose "compare any two repos" tool. Scoped specifically to the fork relationship musehub_forks already tracks, not arbitrary repo pairs with no recorded relationship.

Design

Where "org" fits into forking

Forking today creates a new repo owned by the caller's own handle — org ownership of a fork is a distinct, orthogonal question from the cross-repo- proposal mechanism this ticket adds. Two things worth deciding explicitly (see Open Questions) rather than assuming: whether a fork can be owned by an org (useful when an org wants to maintain its own working copy of an upstream repo collectively) and whether a cross-repo proposal's originating identity, for governance/quorum purposes, is the human/agent who authored the commits or the org that owns the fork repo (if forks-owned-by-orgs is supported). This plan does not assume org-owned forks work today — Phase 1 verifies.

Cross-repo proposal identity

A same-repo proposal is keyed by (repo_id, from_branch, to_branch). A cross-repo proposal needs a (source_repo_id, source_branch, target_repo_id, target_branch) shape — the target repo is where the proposal actually lives (reviewed, quorum-checked, merged against its governance), and the source repo is only ever read from, never written to, by the proposal-merge path. This asymmetry matters: merging a cross-repo proposal commits into the target repo's history; it never pushes anything back into the fork.

Convergence detection

Given the source branch's commit list and the target branch's reachable commit set, a straightforward object-ID set intersection identifies which source commits (if any) are already present upstream — either because they were already merged through another path, or because independent authors produced byte-identical state. This needs no new storage or hashing scheme; it's a query over data that already exists.

Structural SemVer impact preview

The same StructuredDelta/classify_delta() machinery that computes sem_ver_bump at commit time already exists per-commit. Aggregating across the cross-repo proposal's full commit range (source-branch-tip vs. merge- base with target) and running it through the existing aggregation logic (the same logic the version-resolution work already extracts into a reusable function) produces a real, structurally-grounded version-impact preview for the proposal view — not a new computation, a new place to show an existing one.

Harmony pattern lookup

Harmony's pattern store is per-repo. A cross-repo proposal's conflict check (computed the same way a same-repo proposal's dry-run merge would compute it) can be checked against the target repo's Harmony patterns even though the conflicting commits originated in the source/fork repo — the pattern matching is content/semantic-fingerprint-based, not repo-scoped, so this is a query against existing infrastructure, not new inference logic.


Phases

Each phase is fully green before the next begins. Test IDs use the XREPO_NN prefix.

Phase 1 — Audit fork + org interaction (no new code, tests only)

  • XREPO_01 — Confirm whether fork_repo supports an org as the forking owner today, or only individual handles — test directly against the real endpoint/service function, don't infer from the request schema alone.
  • XREPO_02 — Confirm exactly what musehub_forks records (source repo ID, fork repo ID, forked-by handle, timestamp — read the actual table/ model) so Phase 2's cross-repo proposal lookup uses real, existing fields rather than assumed ones.
  • XREPO_03 — Confirm whether get_repo_fork_network already exposes enough structure to resolve "this fork's recorded upstream" in one query, or whether the cross-repo proposal creation path needs its own lookup.

Phase 2 — Cross-repo proposal creation

  • XREPO_10 — Extend create_proposal (server) to accept a source_repo_id distinct from the target repo, validated against musehub_forks — a proposal cannot claim a cross-repo source that isn't a real, recorded fork relationship.
  • XREPO_11 — Extend run_proposal_create (CLI) with a way to specify the source repo (exact flag shape TBD — see Open Questions), defaulting to today's same-repo behavior when omitted, so no existing caller's behavior changes.
  • XREPO_12 — Reject a cross-repo proposal whose claimed source branch doesn't actually exist in the source repo, and whose claimed target branch doesn't exist in the target repo — client-side validation before any network I/O, consistent with the existing same-repo validation order.

Phase 3 — Convergence detection and diff rendering

  • XREPO_20 — Object-ID intersection between the source branch's commit set and the target branch's reachable set; surfaced in the proposal's structured output as "already present upstream" per commit.
  • XREPO_21 — Confirm the existing domain-plugin diff machinery produces a correct structured diff across the repo boundary (same objects, same domain, different repo) with no bespoke per-domain code required — test against at least the code domain to start.

Phase 4 — SemVer impact preview and Harmony pattern surfacing

  • XREPO_30 — Aggregate sem_ver_bump across the cross-repo proposal's commit range using the extracted, reusable aggregation function (shared with the version-resolution work), surfaced in the proposal view.
  • XREPO_31 — Query the target repo's Harmony pattern store for a match against the cross-repo proposal's dry-run-computed conflicts, surfaced as a read-only "known resolution available" signal — never auto-applied.

Phase 5 — Governance and merge

  • XREPO_40 — Confirm a cross-repo proposal's merge is gated by the target repo's governance.json/quorum rules exactly as a same-repo proposal would be — a fork must never be a way to route around review policy.
  • XREPO_41 — End-to-end regression: fork a real (or staging-fixture) repo, make a change on the fork, open a cross-repo proposal back to the upstream, have it reviewed under upstream governance, and merge — commits land in the upstream's history with correct attribution to the original author's identity, not the fork-owner's.

Phase 6 — Docs and UI

  • XREPO_50 — Document the cross-repo proposal flow end-to-end (CLI flags, what the proposal view shows, how governance applies) — this is new user-facing surface area and needs its own documentation, not a addendum buried in the existing proposal docs.
  • XREPO_51 — MuseHub UI: the proposal view for a cross-repo proposal should visibly label it as cross-repo (source repo/owner shown prominently, not just inferred from branch names) and render the convergence/SemVer/Harmony signals from Phases 3-4.

Acceptance Criteria

  • A fork-of-muse (real or staging-fixture) can open a proposal back to muse naming a source repo distinct from the target, rejected if the fork relationship isn't real.
  • The proposal correctly identifies any source commits already present upstream via object-ID intersection, with a real test fixture containing at least one such commit.
  • The proposal view surfaces a structurally-computed SemVer impact, not a file-count-only summary.
  • A cross-repo proposal against a governed repo (per the companion org/ quorum ticket) is blocked/allowed by the exact same governance rules a same-repo proposal would face — verified directly, not assumed.
  • Merging a cross-repo proposal correctly attributes commits to their original author's identity in the target repo's history.
  • No existing same-repo proposal test regresses — the cross-repo path is additive, gated behind an explicit source-repo argument that defaults to today's behavior when absent.

Risks

  • Governance-bypass risk: the single most important thing to get right is Phase 5's confirmation that forking never provides a side door around review policy. This must be tested adversarially (try to merge a cross- repo proposal against a governed repo without satisfying quorum, and confirm it's rejected) before this ticket is considered done, not assumed from the design alone.
  • Attribution risk: merging a cross-repo proposal must preserve the original commit author's identity, not silently reattribute to whoever owns the fork or whoever clicked merge — get this wrong and the entire identity/attestation value proposition of "supercharging" this workflow is undermined.
  • Scope-creep risk: the "supercharge" framing invites piling on features (auto-sync, multi-hop forks, full Harmony auto-resolution) that are explicitly out of scope above. Any temptation to add them mid- implementation should be treated as a new, separate ticket, not a scope change to this one.
  • Domain-plugin generality risk: Phase 3's claim that cross-repo diff "just works" for any domain plugin needs to actually be verified against at least one domain beyond code before being treated as proven — if midi or another active domain's plugin makes an assumption that breaks across a repo boundary, that's a real finding, not a theoretical risk to wave away.

Open Questions

  • Exact CLI flag shape for specifying a cross-repo source — --from-repo <owner>/<repo>, or inferring it automatically when --from-branch names a branch that only exists in a known fork of the target? The former is more explicit; the latter is more ergonomic but riskier to get wrong silently. Needs a decision before Phase 2.
  • Does an org need to be able to own a fork directly (Phase 1's XREPO_01 finding), and if not today, is that worth adding now or deferring to a separate ticket once the core cross-repo proposal mechanism is proven?
  • Should the convergence-detection signal (Phase 3) be advisory-only in the proposal view, or should it actively prevent re-proposing commits that are already upstream (to avoid duplicate-effort proposals)? Leaning advisory-only for v0 — a hard block risks false positives on legitimately divergent work that merely shares some history.
  • How does this interact with the org/quorum wiring ticket's self-review policy — does a cross-repo proposal authored by gabriel (from a personal fork) get the same self-review bypass as a same-repo proposal, and should that be verified as part of this ticket's Phase 5, or treated as already covered by the other ticket's own end-to-end tests? Leaning toward: this ticket's Phase 5 should include one direct test of this specific interaction, since it's the one place the two tickets' behaviors compose.

Implementation Order

Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5 → Phase 6. Convergence detection and SemVer/Harmony surfacing (Phases 3-4) are pulled after basic cross-repo proposal creation (Phase 2) works, since there's nothing to diff/preview/match against until a cross-repo proposal actually exists as a first-class object. Governance (Phase 5) comes last deliberately — it's the integration point with the companion org/quorum ticket and should be verified once both mechanisms are independently proven, not built in parallel with unproven assumptions on either side.


Plan-only issue. This depends on (but does not block on completion of) the companion org/quorum wiring ticket for Phase 5's governance verification — both can proceed in parallel through their earlier phases.

Activity
gabriel opened this issue 57 days ago
No activity yet. Use the CLI to comment.