`migrate-channel-auth` now tries to derive v2-required keys that v1 never
stored by calling the channel's API with the credential v1 did have. When
the gap can be closed automatically, the keys land in v2 `.env` before
the missing-required check, and the step reports `success` instead of
`partial`. When it can't, the existing followup fires unchanged.
## Discord
v1 used raw `discord.js` (bot token only). v2's Chat SDK needs
`DISCORD_APPLICATION_ID` + `DISCORD_PUBLIC_KEY`. Both can be fetched with
the bot token via:
GET /oauth2/applications/@me
Authorization: Bot <DISCORD_BOT_TOKEN>
→ { id, verify_key, … }
For a stock v1 Discord user, this means `bash nanoclaw.sh` now produces
a fully working v2 Discord adapter with zero manual key-setting — just
stop v1, and v2 takes over.
## Surface
- `autoResolveV2Keys(channelType, lookup)` in `setup/migrate-v1/shared.ts`
— pluggable per-channel resolver, returns a `{key: value}` map. Never
throws; returns `{}` on any failure (network, auth, unexpected shape).
Logs keys resolved, never values.
- `migrate-channel-auth` wiring: build a lookup over v1 + v2 .env, call
the resolver, append resolved keys to v2 .env (never overwriting), sync
to `data/env/env`, then re-check `requiredV2Keys` to compute the real
gap. Sidecar annotation `(auto-resolved)` on `env_keys_copied` in the
handoff so the skill can tell which came from v1 vs derived.
## Extending to other channels
Slack has `/auth.test` (bot token → team/app info), Telegram has `/getMe`,
Matrix has `/whoami`. Most don't cover the full required-key set v2 needs
(e.g. Slack's `SLACK_SIGNING_SECRET` lives only in app config and has no
API equivalent). Add resolvers case-by-case when the API supports it; the
registry's `requiredV2Keys` + followup fallback covers the rest.
## Testing
- Stripped `DISCORD_APPLICATION_ID` + `DISCORD_PUBLIC_KEY` from v2 `.env`
- Re-ran migration (wired-only, 301 groups): resolver populated both keys
via the API; `migrate-channel-auth: success` (was `partial`);
`overall_status: success`
- Restarted v2: Discord adapter booted clean, Gateway connected,
`GUILD_CREATE` received
- v1 stopped, v2 handling Discord traffic