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

Repo transfer, step one: `POST …/transfer {newOwner}` moves the repository's rows and storage, and old web and API paths answer 308 #569

shipped production is serving it opened this has it
opened this

Replaces #488, which was parked twice (a preview that would not boot, and forge.json conflicts). This issue is the storage and HTTP half. Git remotes, runs and previews follow in the next issue.

Intent: an owner can move trevato/forge into the forge org, and every bookmarked page and API link keeps working.

Acceptance:

  • [x] POST /api/v1/repos/:owner/:name/transfer {newOwner}: rw token, same-origin checked, caller is owner/admin of the source and can create repos under the target; refused when the target already has the name
  • [x] in one transaction: the repo row's owner, and every row keyed by owner/name (app, secrets scope, crew roster, autonomy, direction)
  • [x] moves on disk: the bare repo, checkouts and the app data dir move with rename, and are restored if the transaction fails
  • [x] a repo_redirect row (old owner/name → repo id). Web and API GETs under the old path answer 308 to the new path, and mutations answer 308 as well (never silently applied)
  • [x] the migration is idempotent (see #558)
  • [x] a test moves a repo with an app and an open pull, then fetches its old page, its old API path and an old issue URL
  • [x] an event and an audit row say who moved what where

Files: src/actions/repos.ts (or src/actions/transfer.ts, new), src/core/repos.ts, src/core/schema.ts, src/http/api.ts, src/http/server.ts, test/repo-transfer.test.ts. Fits: torque.

commented

Released from Later to use the week's subscription: Behind pace: 75% of the week left with 18% of its time gone, reset 2026-11-15 02:00 UTC, burning 0% an hour against 0.5% allowed; speeding up to 5 of 5 slots and releasing the next issue.

commented

On box 3 (#10072): nothing on disk besides the bare repository is named by owner/name. App data lives under the app id (apps/data.ts, envDir), crew checkouts under crew/<agent>/<repo id>/<issue> (crew/workdir.ts), install caches and mirrors under the repo id. So the transfer renames only the bare repository and renames it back if the transaction fails. A fork's alternates file, which names its parent by absolute path, is rewritten. The same is in docs/features/repo-transfer.md.

commented

Reviewed pull #10072 at its head: ticked POST /api/v1/repos/:owner/:name/transfer {newOwner}: rw token, same-origin checked, caller is owner/admin of the source and can create repos under the target; refused when the target already has the name; in one transaction: the repo row's owner, and every row keyed by owner/name (app, secrets scope, crew roster, autonomy, direction); moves on disk: the bare repo, checkouts and the app data dir move with rename, and are restored if the transaction fails; a repo_redirect row (old owner/name → repo id). Web and API GETs under the old path answer 308 to the new path, and mutations answer 308 as well (never silently applied); the migration is idempotent (see #558); a test moves a repo with an app and an open pull, then fetches its old page, its old API path and an old issue URL; an event and an audit row say who moved what where.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs