Files
Felis/docs/changes/2026-07-05-felis-migrate-command.md
T
flyemoji fad48ff21d docs(changes): establish the change ledger for functional changes
Add docs/changes/ — a durable, in-repo map of every functional change and
the commit that records it, independent of git log. INDEX.md carries the
convention (each functional change gets a dated detail doc plus a ledger
row) and the full oldest-first ledger, regenerable losslessly from git.
Seed detail docs for the two changes just landed: the break-glass halt op
(c2ee21a) and the /felis migrate command (c1aa38b).
2026-07-06 23:52:12 +09:00

81 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `/felis migrate` in-game command (§B3 inherit, Velocity side)
- **Type:** feature (addition)
- **Date:** 2026-07-05
- **Area:** `plugins/velocity` + `plugins/shared` — Velocity proxy plugin (Java, compile-verified)
- **Commit:** `c1aa38b` — feat(velocity): add /felis migrate to open an account migration (§B3 inherit)
- **Task:** completes the code-only gap named in `internal/api/handlers_account_migrate.go`
## What it does
Adds the in-game `/felis migrate` command that a player runs to **open an account
migration** — the first step of handing their owned servers to another account (spec
§B3 "inherit", scenario A). The command posts the player's Mojang-verified UUID to the
backend, which puts that account into migrate mode (`state=initiated`). The player then
finishes the migration on the web console (prove it's them, name the receiving account,
redeem a one-time code).
The Go backend (`handleMigrateStart` and the web-driven steps 2–4) already existed and
was tested; its header comment explicitly named **"the `/felis migrate` command that
calls handleMigrateStart"** as the code-only gap. This change closes that gap.
## Why
Without the in-game command, the migration flow had no entry point — the backend
handler was reachable only in theory. `/felis migrate` is the trustworthy initiator:
Velocity has already established the caller's online-mode UUID, so the sensitive proof
can be deferred to the web step-up while the in-game command just opens the migration.
## Design decisions
- **Mirrors the existing command suite verbatim.** `doMigrate` follows `doClaim`;
`migrateError` follows `claimError`; `migrateStart` follows `claim`/`opLoginApprove`.
No new imports, types, or idioms — every construct already appears in the same files.
- **Identity-bound + out-of-limbo, but server-independent.** Like `claim`, it requires
a real player past the login limbo (`requirePlayer` + `ensureOutOfLimbo`). Unlike
`claim`, it acts on the caller's *account*, not the server they stand on, so there is
**no** `registry`/current-server check.
- **Expects HTTP 201.** `migrateStart` posts to
`/api/v1/internal/account/migrate/start` and expects **201 Created** (`handleMigrateStart`
returns `StatusCreated`) — not 200 like the other calls. A 201 that does not affirm
`started:true` is treated as a contract breach, not a refusal.
- **Error mapping matches the handler's refusals:** 404 `not_linked` → "Link your
account on the web console before migrating"; 409 `account_retired` → "This account
can't start a migration (already migrated or retired)"; transport (0) and default →
generic retry text.
- **Points the player to the console on success.** The command only *opens* the
migration, so on success it prints the player web console URL
(`https://console.<root_domain>`, derived from config — never a hardcoded domain) and
a one-line description of the remaining steps. A proxy-side `logger.info` records the
initiating username against the UUID (the backend audit only has the UUID).
## Files
| File | Change |
|---|---|
| `plugins/shared/.../link/FelisApiClient.java` | **+`migrateStart(UUID)`** — POST mc_uuid, expect 201, affirm `started:true` |
| `plugins/velocity/.../FelisVelocityPlugin.java` | `migrate` literal in the Brigadier tree; **`doMigrate`** handler; **`migrateError`** mapper; `/felis migrate` help line |
## Verification
Java is not oracle-verifiable via the Go suite, but it **is** compile-verifiable via
the podman gradle toolchain established in #63/#65:
```
podman run --rm -v plugins:/work -w /work/velocity \
docker.io/library/gradle:jdk17 gradle --no-daemon compileJava
→ BUILD SUCCESSFUL in 19s (compiled against real velocity-api:3.3.0-SNAPSHOT)
```
The change compiles clean against the real Velocity API jar (including the shared
`FelisApiClient` compiled straight into the velocity module). The backend contract it
speaks to (`handleMigrateStart`) is covered by `handlers_account_migrate_test.go` on
the Go side.
## Self-review outcome
- **ponytail (over-engineering):** lean — pure mirror of three existing, compiling
methods; no speculative abstraction. Nothing cut.
- **correctness:** the one contract divergence (201 vs 200) was verified against the Go
handler source before writing.