## Step 1  -  Dynamic Credential Discovery

Instead of checking only hardcoded key names, discover ALL user keys and intelligently map them to services. Uses `credential-store.sh list`:

**1a. Discover all keys:**
```bash
DISCOVERED=$(~/.claude/lib/credential-store.sh list 2>/dev/null | grep -i "^${USER}" | sort -u)
echo "$DISCOVERED"
```

**1b. Search keywords (per service):**

Standard key names  -  the ones to use when creating a NEW key  -  live in `$HOME/.claude/multi-agent-refs/keychain.md` "Standard key names"; read the name for a service from there. Existing keys under other names are perfectly valid, which is what these keywords are for.

| Service ID | Search Keywords |
|------------|-----------------|
| `jira` | `jira` |
| `bitbucket_token` | `bitbucket` + (`token` or `access` or `pass`) |
| `bitbucket_user` | `bitbucket` + (`user` or `name`) |
| `github` | `github` or `gh` (exclude `ssh`) |
| `confluence` | `confluence` |
| `figma` | `figma` + (`token` or `pat` or `access`) + NOT `mcp` |
| `figma_mcp` | `figma` + `mcp` |
| `fortify` | `fortify` |
| `graylog` | `graylog` |
| `graylog_test` | `graylog` + `test` |
| `firebase` | `firebase` (any variant: `sa`, `service`, `account`, `access`, `json`) |
| `jenkins` | `jenkins` |
| `appstore_connect_key_id` | (`appstore` or `asc` or `app_store`) + (`key` or `keyid`) |
| `appstore_connect_issuer_id` | (`appstore` or `asc` or `app_store`) + `issuer` |
| `appstore_connect_private_key` | (`appstore` or `asc` or `app_store`) + (`private` or `p8`) |
| `appstore_connect_apple_id` | (`appstore` or `asc`) + (`apple` or `account` or `user`) |
| `appstore_connect_password_item` | (`appstore` or `asc` or `altool`) + (`password` or `app_specific`) |
| `google_play` | (`googleplay` or `google_play` or `play_console` or `play`) + (`service` or `account` or `access` or `json` or `console`), NOT `firebase` |

> Multi-agent setup onboards **both** Figma tokens because the pipeline's 3-tier Figma access chain (see `$HOME/.claude/rules/figma-pipeline.md`) uses them in priority order: Tier 1 = MCP (`figma_mcp`), Tier 2 = REST PAT (`figma`), Tier 3 = user screenshot fallback. Missing either token forces a downshift; missing both leaves Tier 3 as the only path. The Tier 2 logical key is `figma`, not `figma_pat`: `figma_pat` is the pre-v13.6 name that `migrate-prefs.mjs` consolidates away, and writing a new mapping under it means the next migration deletes it.

> The five App Store Connect entries are **iOS-only and optional**  -  skipping them all reports Gate 2 of `/multi-agent:store-ready` as `SKIPPED`, never as a pass. Two hold identifiers, one an email, one a keychain ITEM NAME, and `appstore_connect_private_key` the `.p8` itself; all go through the mapping layer. `google_play` is the Android-only counterpart (Step 3d). Tiers and mechanics: Step 3b and `$HOME/.claude/multi-agent-refs/setup/app-store-connect.md`.

> The `firebase` entry is **optional and any-platform**. Discovery maps a stored service-account JSON like any other credential but cannot see what decides whether Crashlytics is readable: a fresh `firebase-adminsdk-*` account authenticates perfectly and holds no Crashlytics role. Step 3c measures the tier rather than assuming it.

**1c. Resolution logic (per service):**

For each service in the table above, follow this order:

1. **Check standard key** -> `~/.claude/lib/credential-store.sh get "<standard_key>" 2>/dev/null`
   - Found (non-empty stdout, exit 0) -> auto-map, done
3. **Search discovered keys** -> filter `$DISCOVERED` by the service's search keywords (case-insensitive)
   - Exactly 1 match -> show: `"Found '${matched_key}' for ${service}  -  use this? (enter=yes / n=skip)"` -> if yes, map it
   - Multiple matches -> show numbered list, user picks:
     ```
     Multiple keys found for GitHub:
       1. ${USER}_Github
       2. ${USER}_Github_Access_Token_Old
       3. ${USER}_Github_Access_Token
     Select [number] or skip:
     ```
   - 0 keyword matches -> go to Step 4
4. **Suggest candidates from full keychain** -> When keyword search yields 0 results, do NOT immediately mark as MISSING. Show broad candidates from `$DISCOVERED_ALL` and ask the user:
   ```
   No exact match found for Jira in your keychain.
   
   These entries might be your Jira token  -  do any look right?
     1. MyPersonalJiraToken
     2. work_jira_pat
     3. JiraPAT_2024
     n. None of these (skip)
   
   Select [number] or n to skip:
   ```
   
   Candidate selection:
   - Show entries that partially match the service name, common token suffixes (`token`, `pat`, `key`, `secret`, `access`, `password`), or relaxed keyword matching (substring instead of prefix)
   - Exclude entries already mapped to other services and system entries (Apple, com.apple.*, iCloud, etc.)
   - Cap at 10 candidates, sorted by relevance (shortest edit distance to service keywords)
   - If 0 candidates even after broad search -> MISSING (truly not in keychain)

**Important:** Steps 3-4 search the FULL `$DISCOVERED` list (both passes from 1a), not just user-prefixed entries. This catches tokens saved under any naming convention.

**1d. Check preferences for existing mapping:**

Before running discovery, check `prefs.global.keychainMapping`. If a mapping already exists for a service AND the mapped key still exists in Keychain -> skip discovery for that service (show as already mapped from preferences).

If a previously mapped key NO LONGER exists in Keychain -> clear the stale mapping, re-run discovery for that service.
