New repository page: reserve/push-only (drop mirror option) with push-first copy; Import is the sole mirror path #487

Closed
opened 2026-09-13 19:29:46 +00:00 by crueber · 3 comments
Owner

What's requested

Split the two things /new currently does. The New repository page becomes reserve/push-only: it creates an empty placeholder and nothing else. The mirror-from-URL mode moves out of it entirely, and Import (/import) becomes verified as the sole mirror path in the UI.

Push-first copy stays at the top of the page and is the only intro: the placeholder model (reserve a name, first push adopts it — nothing to approve, never a conflict) is the thing /new exists to teach, and that explanation should lead the page, not share it with a second mode's field set.

Evidence (current tree)

  • web/src/pages/New.jsx:175-183 — radiogroup "Repository kind" offers empty vs mirror from URL (pull-only, scheduled syncs); the mirror radio carries its own source-URL + schedule field block (New.jsx:218-247) and the create button swaps labels (New.jsx:294).
  • New.jsx:33-35, 92, 107-116 — mirror mode already has a complete parallel submit path (repos.mirrors.create → POST /api/v1/repos/mirrors) duplicating what /import's mirror mode already does (web/src/pages/Import.jsx:36-38, 119-120, header comment noting the shared validateMirrorCreate/MIRROR_PRESETS and that the pull-only paragraph deliberately lives on New.jsx only).
  • New.jsx:164-167 — the push-first intro ("Reserve a name and get push instructions. The first push adopts the placeholder — nothing to approve, never a conflict.") already renders above the form; keep and elevate this.

Architecture notes

  • The backend needs no change: POST /api/v1/repos/mirrors (create-from-URL) stays where it is (internal/mirror/http.go:47, twin registration at :101); Import already targets it. This is pure client-side surface removal on one page.
  • Two entry points to one mirror endpoint is redundant UI, not a feature: New's mirror mode reuses the exact same shared validation lib and endpoint Import uses, so the second form adds maintenance (two copies of schedule-preset UI, two error paths) with no capability.
  • After removal, New.jsx's mirror-only signals/validation (getSource, getSchedule, mirrorError, the Show when={getMode() === "mirror"} blocks) can be deleted, shrinking the page.

Acceptance criteria

  • /new has no mirror radio, no source-URL field, no schedule select; the kind radiogroup is gone (there is only one kind)
  • Page top copy states the push-first model (reserve a name → first push adopts the placeholder, no approval, no conflict) as the intro
  • /import mirror mode is exercised once to confirm it still creates mirrors via POST /api/v1/repos/mirrors (shared lib untouched) — it is the only UI path to a mirror
  • Mirror help copy (pull-only, pushes rejected, first sync immediate) still renders on the Import page so the model isn't lost with the New page radio
  • MIRROR_PRESETS/validateMirrorCreate imports removed from New.jsx; no dead signals/branches left
  • No API changes; internal/mirror untouched
## What's requested Split the two things `/new` currently does. The New repository page becomes **reserve/push-only**: it creates an empty placeholder and nothing else. The mirror-from-URL mode moves out of it entirely, and Import (`/import`) becomes verified as the **sole** mirror path in the UI. Push-first copy stays at the top of the page and is the only intro: the placeholder model (reserve a name, first push adopts it — nothing to approve, never a conflict) is the thing /new exists to teach, and that explanation should lead the page, not share it with a second mode's field set. ## Evidence (current tree) - `web/src/pages/New.jsx:175-183` — radiogroup "Repository kind" offers `empty` vs `mirror from URL (pull-only, scheduled syncs)`; the mirror radio carries its own source-URL + schedule field block (`New.jsx:218-247`) and the create button swaps labels (`New.jsx:294`). - `New.jsx:33-35`, `92`, `107-116` — mirror mode already has a complete parallel submit path (`repos.mirrors.create` → `POST /api/v1/repos/mirrors`) duplicating what `/import`'s mirror mode already does (`web/src/pages/Import.jsx:36-38`, `119-120`, header comment noting the shared `validateMirrorCreate`/`MIRROR_PRESETS` and that the pull-only paragraph deliberately lives on New.jsx only). - `New.jsx:164-167` — the push-first intro ("Reserve a name and get push instructions. The first push adopts the placeholder — nothing to approve, never a conflict.") already renders above the form; keep and elevate this. ## Architecture notes - The backend needs no change: `POST /api/v1/repos/mirrors` (create-from-URL) stays where it is (`internal/mirror/http.go:47`, twin registration at :101); Import already targets it. This is pure client-side surface removal on one page. - Two entry points to one mirror endpoint is redundant UI, not a feature: New's mirror mode reuses the exact same shared validation lib and endpoint Import uses, so the second form adds maintenance (two copies of schedule-preset UI, two error paths) with no capability. - After removal, New.jsx's mirror-only signals/validation (`getSource`, `getSchedule`, `mirrorError`, the `Show when={getMode() === "mirror"}` blocks) can be deleted, shrinking the page. ## Acceptance criteria - [ ] `/new` has no mirror radio, no source-URL field, no schedule select; the kind radiogroup is gone (there is only one kind) - [ ] Page top copy states the push-first model (reserve a name → first push adopts the placeholder, no approval, no conflict) as the intro - [ ] `/import` mirror mode is exercised once to confirm it still creates mirrors via `POST /api/v1/repos/mirrors` (shared lib untouched) — it is the only UI path to a mirror - [ ] Mirror help copy (pull-only, pushes rejected, first sync immediate) still renders on the Import page so the model isn't lost with the New page radio - [ ] `MIRROR_PRESETS`/`validateMirrorCreate` imports removed from New.jsx; no dead signals/branches left - [ ] No API changes; `internal/mirror` untouched
crueber added this to the v1 milestone 2026-09-13 19:29:58 +00:00
Author
Owner

Fix PR: #494 (branch fix/issue-487). /new is reserve/push-only, Import verified as the sole mirror path; client-only change, internal/mirror untouched. node --test 1087 total / 1085 pass / 2 fail (pre-existing live-server smoke, identical on main); vite+esbuild green.

Fix PR: https://git.packden.us/crueber/walhub/pulls/494 (branch fix/issue-487). /new is reserve/push-only, Import verified as the sole mirror path; client-only change, internal/mirror untouched. node --test 1087 total / 1085 pass / 2 fail (pre-existing live-server smoke, identical on main); vite+esbuild green.
Author
Owner

REVIEW PR #494 (fix/issue-487) — all acceptance criteria verified in scratch worktree (removed afterward; main untouched, still clean).

(1) Mirror gone from New — PASS. web/src/pages/New.jsx: radiogroup (was aria-label=Repository kind) deleted, source-URL block (new-source) and schedule select (new-schedule, Sync schedule) deleted, button label swap gone — single 'create repository' label (New.jsx:231), submit has no repos.mirrors.create branch. Format/visibility unwrapped from empty-mode Show, intact.
(2) Dead code removed — PASS. Grep of New.jsx: no getMode/setMode/getSource/setSource/getSchedule/setSchedule/mirrorError; no MIRROR_PRESETS/DEFAULT_MIRROR_PRESET/validateMirrorCreate, no ../lib/mirror.js import. For import kept and still used (owners dropdown New.jsx:165) — not dead. Only mirror mentions left are the #487 header comment + Import pointer sentence — intentional.
(3) Push-first intro leads — PASS. New.jsx:141-145 keeps 'Reserve a name and get push instructions...nothing to approve, never a conflict' as the only intro, plus one sentence pointing mirror-seekers at /import (href=/import) in the same muted paragraph. #479 one-h2-one-intro shell intact.
(4) Import mirror intact — PASS. Import.jsx keeps mirror radio (getMode()==='mirror', 'mirror continuously (recurring pull, pushes rejected)'), schedule select + MIRROR_PRESETS, validateMirrorCreate wiring, repos.mirrors.create(payload) submit + navigate to repo. Shared lib web/src/lib/mirror.js byte-identical (no diff); pull-only help paragraph added as import-mirror-help (Import.jsx:367) with aria-describedby wiring on the schedule select (Import.jsx:363) — model survives the New radio removal.
(5) Reserve submit path intact — PASS. repos.repos.create (New.jsx:99) + navigate to created repo (New.jsx:111) + busy/fieldError/owner/name gate (New.jsx:229) unchanged; shared-rule exercise of surviving path covered in new test.
(6) #479 pins faithful — PASS. create-forms-479.test.js updates only retarget the moved paragraph (mirror-copy-once now on Import, New mirror-create absence, help-wiring pair new-mirror-help -> import-mirror-help); no weakening, full suite green.
(7) No backend change — PASS. Zero .go files in diff; internal/mirror untouched; mirror endpoint intact.
(8) No new deps; docs accurate — PASS. No package.json/pnpm diff; docs/go/12_web_ui.md gains one Law-12 decision line accurately describing the change.

TESTS (scratch worktree, node_modules symlinked): full node --test web/test/unit/*.test.js = 1085 pass / 2 fail — the 2 are the pre-existing smoke.test.js live-server subtests, confirmed identical on pristine main worktree (smoke alone: 1 pass / 2 fail there too). Targeted new-reserve-pushonly-487 + create-forms-479 + mirror + sdk-mirror = 24/24 pass. vite build green (2.47s). Grep confirms zero mirror leftovers in New.jsx. No fixes pushed — none needed.

No browser drive per review instructions (node tests + reasoning; stated explicitly). No docker/compose/system changes; no live instance touched.

MERGE RECOMMENDATION: ready to merge.

REVIEW PR #494 (fix/issue-487) — all acceptance criteria verified in scratch worktree (removed afterward; main untouched, still clean). (1) Mirror gone from New — PASS. web/src/pages/New.jsx: radiogroup (was aria-label=Repository kind) deleted, source-URL block (new-source) and schedule select (new-schedule, Sync schedule) deleted, button label swap gone — single 'create repository' label (New.jsx:231), submit has no repos.mirrors.create branch. Format/visibility unwrapped from empty-mode Show, intact. (2) Dead code removed — PASS. Grep of New.jsx: no getMode/setMode/getSource/setSource/getSchedule/setSchedule/mirrorError; no MIRROR_PRESETS/DEFAULT_MIRROR_PRESET/validateMirrorCreate, no ../lib/mirror.js import. For import kept and still used (owners dropdown New.jsx:165) — not dead. Only mirror mentions left are the #487 header comment + Import pointer sentence — intentional. (3) Push-first intro leads — PASS. New.jsx:141-145 keeps 'Reserve a name and get push instructions...nothing to approve, never a conflict' as the only intro, plus one sentence pointing mirror-seekers at /import (href=/import) in the same muted paragraph. #479 one-h2-one-intro shell intact. (4) Import mirror intact — PASS. Import.jsx keeps mirror radio (getMode()==='mirror', 'mirror continuously (recurring pull, pushes rejected)'), schedule select + MIRROR_PRESETS, validateMirrorCreate wiring, repos.mirrors.create(payload) submit + navigate to repo. Shared lib web/src/lib/mirror.js byte-identical (no diff); pull-only help paragraph added as import-mirror-help (Import.jsx:367) with aria-describedby wiring on the schedule select (Import.jsx:363) — model survives the New radio removal. (5) Reserve submit path intact — PASS. repos.repos.create (New.jsx:99) + navigate to created repo (New.jsx:111) + busy/fieldError/owner/name gate (New.jsx:229) unchanged; shared-rule exercise of surviving path covered in new test. (6) #479 pins faithful — PASS. create-forms-479.test.js updates only retarget the moved paragraph (mirror-copy-once now on Import, New mirror-create absence, help-wiring pair new-mirror-help -> import-mirror-help); no weakening, full suite green. (7) No backend change — PASS. Zero .go files in diff; internal/mirror untouched; mirror endpoint intact. (8) No new deps; docs accurate — PASS. No package.json/pnpm diff; docs/go/12_web_ui.md gains one Law-12 decision line accurately describing the change. TESTS (scratch worktree, node_modules symlinked): full node --test web/test/unit/*.test.js = 1085 pass / 2 fail — the 2 are the pre-existing smoke.test.js live-server subtests, confirmed identical on pristine main worktree (smoke alone: 1 pass / 2 fail there too). Targeted new-reserve-pushonly-487 + create-forms-479 + mirror + sdk-mirror = 24/24 pass. vite build green (2.47s). Grep confirms zero mirror leftovers in New.jsx. No fixes pushed — none needed. No browser drive per review instructions (node tests + reasoning; stated explicitly). No docker/compose/system changes; no live instance touched. MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #494 (review clean — all 8 checks pass, mirror fully excised, Import sole path verified), merged. Closing.

Fixed by PR #494 (review clean — all 8 checks pass, mirror fully excised, Import sole path verified), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#487
No description provided.