Fix #328: PR composer From/To + cross-repo #336

Merged
crueber merged 2 commits from fix/issue-328 into main 2026-09-11 17:15:41 +00:00
Owner

Fixes #328.

What: the /pulls/new composer speaks From (head — where changes come from) / To (base — where they go) instead of base/head, UI-only: wire head_ref/base_ref and ?base=/?head= prefill are unchanged (law 12). Each side pairs a repo dropdown with a single-select branch listbox fed by that side's ref stream — no free-form ref entry (the filter narrows the streamed list but never sets the value). Selecting a different From repo re-keys the branch stream and arms fork: {repo} on submit (the OpenInput.ForkInfo path — backend was ready); the open targets the To repo's client.

Details:

  • Repo sources: current owner's listing split by resolved role — To = write+, From = any resolved role, current repo always present. Cross-owner repos out of scope for v1 (no listing endpoint serves them; same-owner forks are the default fork target) — documented in code + 12_web_ui.md decision.
  • Cross-repo preview reads the From history from the fork's client (Promise.allSettled so a missing branch names its side); short display (repo@branch only across repos, no refs/heads/ leak).
  • Unreachable-head 422 surfaces inline, direction-mapped ('push the From branch first') plus the existing tray report; dup-pair 409 and unknown revisions mapped too. Note: cross-fork unreachable heads open fine server-side (HeadPublished=false, PR page shows 'head ref pending') — the inline error covers the same-repo 422.
  • PR page mergeability block renders from {fork.repo} when pr.fork is set.
  • Same-repo defaults (refs/heads/main To, empty From) and deep links behave as today.

Tests: new web/test/unit/pr-composer.test.js (label/direction mapping, same/cross-repo open calls, repo→ref state machine incl. repo-change-resets-ref, permission split, error copy, From/To-vs-base/head source pins + wire-key stability pins). node --test (non-smoke): 641/641 green; vite build green. Existing pull-dropdown-opaque pins kept (dropdown classes, 150ms debounce, props.repo.refStream(, props.onPick(r.name)). No backend touched (no go tests needed); no new deps.

Browser: open — shared obscura daemon blocks loopback per workspace rules, no private daemon started; needs a real-browser pass (composer, repo page, /setup, both themes, zero console errors).

Fixes #328. **What:** the `/pulls/new` composer speaks **From** (head — where changes come from) / **To** (base — where they go) instead of base/head, UI-only: wire `head_ref`/`base_ref` and `?base=`/`?head=` prefill are unchanged (law 12). Each side pairs a repo dropdown with a single-select branch listbox fed by that side's ref stream — no free-form ref entry (the filter narrows the streamed list but never sets the value). Selecting a different From repo re-keys the branch stream and arms `fork: {repo}` on submit (the OpenInput.ForkInfo path — backend was ready); the open targets the To repo's client. **Details:** - Repo sources: current owner's listing split by resolved role — To = write+, From = any resolved role, current repo always present. Cross-owner repos out of scope for v1 (no listing endpoint serves them; same-owner forks are the default fork target) — documented in code + 12_web_ui.md decision. - Cross-repo preview reads the From history from the fork's client (`Promise.allSettled` so a missing branch names its side); short display (`repo@branch` only across repos, no `refs/heads/` leak). - Unreachable-head 422 surfaces inline, direction-mapped ('push the From branch first') plus the existing tray report; dup-pair 409 and unknown revisions mapped too. Note: cross-fork unreachable heads open fine server-side (HeadPublished=false, PR page shows 'head ref pending') — the inline error covers the same-repo 422. - PR page mergeability block renders `from {fork.repo}` when `pr.fork` is set. - Same-repo defaults (`refs/heads/main` To, empty From) and deep links behave as today. **Tests:** new `web/test/unit/pr-composer.test.js` (label/direction mapping, same/cross-repo open calls, repo→ref state machine incl. repo-change-resets-ref, permission split, error copy, From/To-vs-base/head source pins + wire-key stability pins). `node --test` (non-smoke): 641/641 green; `vite build` green. Existing `pull-dropdown-opaque` pins kept (dropdown classes, 150ms debounce, `props.repo.refStream(`, `props.onPick(r.name)`). No backend touched (no go tests needed); no new deps. **Browser:** open — shared obscura daemon blocks loopback per workspace rules, no private daemon started; needs a real-browser pass (composer, repo page, /setup, both themes, zero console errors).
Labels become From (head) / To (base), UI-only: wire head_ref/base_ref
and ?base=/?head= prefill unchanged (law 12). Each side pairs a repo
dropdown (To = writable, From = readable, current always present) with a
single-select branch listbox fed by that side's ref stream (no free-form
entry; re-keyed on repo change). Cross-repo opens target the To repo's
client with fork:{repo}; preview reads the From history from the fork's
client with per-side errors, and the unreachable-head 422 surfaces inline
direction-mapped. PR page renders pr.fork. Headless cover:
web/test/unit/pr-composer.test.js; node --test green; vite build green.
Doc decision appended in 12_web_ui.md.
Follow-up from review of fix/issue-328: the mergeability block's
HeadPublished=false line still said 'head ref pending' (the one
UI-visible leftover after the From/To swap), and the adjacent policy
comment said 'base ref'. Both now use the From/To mental model; the
pr-composer pin test covers Pull.jsx rendered copy too. node --test
645/645 green; vite build green.
Sign in to join this conversation.
No description provided.