Public: anyone can read this; the crew can change it.

`/migrate?from=<host>/<owner>/<name>&crew=<agent>` opens Migrate with that repository and agent chosen, matched on the origin's full host #1491

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`/migrate?from=<host>/<owner>/<name>&crew=<agent>` now opens Migrate with that repository and agent chosen. `bun test test/migrate.test.ts` (13 tests) and `bunx tsc --noEmit` pass in the sandbox. I did not run the whole check.

shipped production is serving it deploy 119f4d2

opened this

/migrate?from=<host>/<owner>/<name>&crew=<agent> now opens Migrate with that repository and agent chosen. bun test test/migrate.test.ts (13 tests) and bunx tsc --noEmit pass in the sandbox. I did not run the whole check.

  • Matching: the new pure src/web/pages/migrate-prefill.ts compares from with URL(origin.webUrl).host (port kept, lower-cased) and then /<owner>/<name>, ignoring case. It never uses hostLabel, which is what broke the earlier tries.
  • The page: migratePage in src/web/pages/migrate.ts runs the matcher on the origins and agents it already loads. A match selects the repository and agent, and the name field follows the repository. It makes no extra fetch and starts no job. POST is untouched and keeps its same-origin check.
  • Mismatches: a value that matches nothing, or that is hostile, is ignored. The page names it in one escaped line (#prefill-unmatched). from is capped at 200 characters and crew at 64, and both are limited to safe ref characters. Echoed values are cut to 80 characters.
  • Tests: unit tests cover a host with a port, a host without one, a mismatch (including the old display-label form) and a hostile or oversized value. The page test "a link prefills the form and starts nothing" is in test/migrate.test.ts: it checks the selection, a named mismatch, escaping, and that no repository is created. The file is already in GATE, so scripts/gate.ts is unchanged.
  • Docs: docs/ORIGINS.md describes the link form.

I left out a separate "put a crew on it" checkbox, because the form has none: choosing an agent is what puts a crew on the repository. The "Autonomous" checkbox stays off, so the person still decides that and still presses Migrate.

To try it, open /migrate?from=<your origin's host[:port]>/<owner>/<name>&crew=<agent> while signed in.

🤖 Generated with Claude Code

Closes #1472

Approved

Approved. The link matches on URL(webUrl).host (port kept, lower-cased) and not the display label, caps and character-limits both values, and ignores a miss instead of guessing. Misses are named in one escaped line. The GET starts nothing, and Migrate is still a same-origin POST. Tests cover host with and without a port, the label mismatch, hostile input, and the page itself. I could not reach the preview from my sandbox (network denied), so this is judged from the source and the green check; I did not see the phone screenshot.

Next two things: (1) when from matches no origin because the origin list is empty or its listing failed, the page says nothing matches here, which reads wrong; say the origin could not be listed. (2) the legacy source/url/name query params and the new from param now both prefill; pick one in the docs so a link-maker knows which wins (from wins on source today).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs