* fix(cli,web): link re-points a directory to another server and keeps .pad.toml's other keys; the move-to-Cloud guide is browser-only (TASK-3612)
Verifying the guide's step 5 found it wrong for the people it is written
for. A project linked to a self-hosted server pins that server's url in
.pad.toml, and the pin wins over the global cloud mode, so
`pad workspace link <slug>` talked to the OLD server, found the slug and
printed "Already linked". With --url it still did, whenever Cloud had the
same slug, which is the usual case after an import.
- link is "already linked" only when the existing link's url equals the
target. An explicit --url naming another server re-points it, and the
output names the old and the new server.
- WriteWorkspaceLink UPDATES .pad.toml instead of rewriting it: only
workspace and url change, so agent_name, the [push] consent table and
unknown keys survive every relink (link, switch and the three init
paths). A file it cannot parse is refused and left as it was.
- Step 5 now reads `pad workspace link <slug> --url https://app.getpad.dev`.
- Lead ruling: the guide as a whole steers to Pad Cloud, so inside a mobile
app's web view the route shows one line ("Open Pad in a browser to move a
workspace") and both entry points are hidden. The entry points are one
component carrying that gate, with a wiring test for each mount.
Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ
* fix(cli): link compares server URLs normalised, so case, a default port or a trailing slash is not a different server (TASK-3612, codex r1)
Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ
* fix(cli): server URL comparison keeps escaped path distinctions and strips one trailing slash only (TASK-3612, codex r2)
Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ