# Plan 1C-α — Manual Test Matrix

Walkthrough for validating the extension on both Chrome and Firefox after the six-slice implementation.

Branch: `feature/typed-items-1c-alpha` @ `3238ef4` (tag candidate: `plan-1c-alpha-complete`)
Worktree: `/home/alee/Sources/relicario/.worktrees/typed-items-1c-alpha`

---

## Pre-flight

- [ ] **P1.** Bundles built:
  ```bash
  cd /home/alee/Sources/relicario/.worktrees/typed-items-1c-alpha/extension
  bun run build:all
  ```
  Expected: "compiled with 2 warnings" (WASM size only) for each bundle. `dist/` and `dist-firefox/` populated.

- [ ] **P2.** Fresh-profile browsers ready (or existing profile's `chrome.storage.local` for this extension cleared). Stale `vaultConfig`/`imageBase64` from the pre-rename `idfoto` era must not persist.

- [ ] **P3.** Test git repo for the vault is reachable (SSH key / HTTPS PAT working). Use a throwaway repo to avoid polluting your real vault history.

- [ ] **P4.** Reference image ready (any JPEG; DCT-steg secret is embedded at init time).

---

## Loading

### Chrome
- [ ] **L1.** `chrome://extensions` → Developer mode ON → "Load unpacked" → select `extension/dist/`.
- [ ] **L2.** Toolbar icon visible (pin if needed).
- [ ] **L3.** Click icon → first open triggers setup tab (not a popup-embedded wizard).

### Firefox
- [ ] **L4.** `about:debugging#/runtime/this-firefox` → "Load Temporary Add-on…" → select `extension/dist-firefox/manifest.json`.
- [ ] **L5.** Toolbar icon visible.
- [ ] **L6.** Click icon → setup tab opens.

---

## 11-step core matrix — Chrome

**Notes column: write what you saw. Check box only when matching expected.**

### 1. Setup tab opens from popup (audit C1)

- [ ] **Do:** Fresh install, click toolbar icon.
- [ ] **Expected:** `setup.html` opens in a new tab; popup closes immediately; WAR is empty so this MUST work via extension-origin tab, not WAR.
- [ ] **Notes:** ___

### 2. zxcvbn gate in setup (audit H3)

- [ ] **Do:** Type weak passphrase like `password`.
- [ ] **Expected:** Submit disabled, bar shows red/orange segments, feedback "Too weak…".
- [ ] **Do:** Type stronger phrase until bar fills.
- [ ] **Expected:** At score ≥ 3, submit enables, feedback "Strong enough."
- [ ] **Notes:** ___

### 3. Setup completes → unlock → list renders

- [ ] **Do:** Upload reference JPEG, fill vault config (git host/URL/repo/token), submit. Then open popup, enter passphrase, unlock.
- [ ] **Expected:** Manifest decrypts client-side. Empty list view appears with toolbar (search, + New, sync, lock, ⚙).
- [ ] **Notes:** ___

### 4. Add Login with TOTP (typed-item wire format)

- [ ] **Do:** "+ New" → Login form. Fill:
  - title: `GitHub`
  - url: `https://github.com`
  - username: your handle
  - password: click "gen" (uses `DEFAULT_PASSWORD_REQUEST` — 20 chars, safe symbols)
  - totp: `JBSWY3DPEHPK3PXP` (well-known base32 test vector)
  - Save.
- [ ] **Expected:** Row appears with 🔑 icon + title + favorite star position.
- [ ] **Expected (CLI cross-check, optional):** From main worktree:
  ```bash
  relicario list
  relicario get "GitHub" --show
  ```
  Should show the same item. TOTP secret should decode identically.
- [ ] **Notes:** ___

### 5. TOFU origin-ack prompt (audit C4 first half)

- [ ] **Do:** Navigate to `https://github.com/login`. Click the blue `id` icon next to the password field.
- [ ] **Expected:** Closed Shadow DOM hint appears ("First autofill on github.com / Open relicario to confirm"). In DevTools, verify `document.querySelector('[data-rel]')` finds the host but `.shadowRoot` is `null` (closed mode).
- [ ] **Expected:** No credentials fill on this click.
- [ ] **Notes:** ___

### 6. Confirm origin + autofill fills correctly

- [ ] **Do:** Open popup (on the github.com tab). Look for a pending-ack prompt OR (α behavior) just confirm manually: any `get_credentials` call after the hostname is acked in `VaultSettings.autofill_origin_acks` will return credentials.
  - Simplest α path: click the item in the popup list, click "autofill" button. This uses the popup-captured tab state path (audit M5).
- [ ] **Expected:** Username + password fields fill. On React/Vue sites, the native-setter trick fires input+change events.
- [ ] **Notes:** ___

### 7. Multiple candidates → picker

- [ ] **Do:** Add a second Login for github.com with a different username. Back on `github.com/login`, click the `id` icon.
- [ ] **Expected:** Picker shows both titles. Click one → fills that set.
- [ ] **Notes:** ___

### 8. Capture prompt → `capture_save_login` flow (Slice 5 critical-fix)

- [ ] **Do:** Go to a site not in your vault. Fill signup form (or real trial). Submit.
- [ ] **Expected:** Capture prompt appears inside closed Shadow DOM. No stable element IDs — running `document.querySelector('#relicario-save-btn')` in the page console returns `null`.
- [ ] **Do:** Click "Save" in the prompt.
- [ ] **Expected:** ✓ Saved confirmation; prompt dismisses. Open popup → item present in list with the new hostname as title.
- [ ] **CRITICAL:** If "Save" silently fails, the `capture_save_login` content-callable handler is broken — file a bug before proceeding.
- [ ] **Notes:** ___

### 9. Edit Login → password rotates; field history captured

- [ ] **Do:** Select the GitHub item → edit → change password → save.
- [ ] **Expected:** Detail view shows new password on reveal. List's "modified" time updates.
- [ ] **Expected (CLI cross-check):**
  ```bash
  relicario get "GitHub" --show
  # confirm field_history now has entry for the old password
  ```
- [ ] **Notes:** ___

### 10. Delete Login → soft-delete

- [ ] **Do:** Select an item → "trash" → confirm.
- [ ] **Expected:** Row disappears from list immediately. Popup list filters `trashed_at !== undefined`.
- [ ] **Expected (CLI cross-check):** `relicario list --trashed` shows the item.
- [ ] **Notes:** ___

### 11. Lock → re-unlock

- [ ] **Do:** Click "lock" in the toolbar. Try to open the popup again.
- [ ] **Expected:** Unlock screen. Session handle was cleared in WASM (not just JS).
- [ ] **Do:** Re-unlock.
- [ ] **Expected:** Same list (including the item from step 10 still in trash, invisible).
- [ ] **Notes:** ___

---

## 11-step core matrix — Firefox

Re-run 1–11 on Firefox. Critical Firefox-only check: the background script runs as a **persistent script** (not MV3 service worker); WASM loads via `initDefault(wasmUrl)` not `initSync`. Anything broken here that works in Chrome indicates WASM-loading drift.

- [ ] **FF1–FF11.** Re-run the 11 steps above. Summarize anomalies:
- **Notes:** ___

---

## Security probes (bonus)

Open DevTools on any page (not extension origin) and try to defeat the router:

### SP1. Content-script-originated popup-only message

- [ ] **Do:** In a page console (not popup DevTools):
  ```js
  chrome.runtime.sendMessage({ type: 'unlock', passphrase: 'guess' }, console.log)
  ```
- [ ] **Expected:** `{ ok: false, error: 'unauthorized_sender' }` (audit C2).
- [ ] **Notes:** ___

### SP2. Cross-origin `get_credentials` attempt

- [ ] **Do:** Pick an item id from the popup (e.g., via popup DevTools: `copy(currentState.selectedId)`). Go to a **different-origin** page's console:
  ```js
  chrome.runtime.sendMessage({ type: 'get_credentials', id: '<the-id>' }, console.log)
  ```
- [ ] **Expected:** `{ ok: false, error: 'origin_mismatch' }` (audit C4). No item data leaks.
- [ ] **Notes:** ___

### SP3. Closed Shadow DOM verification

- [ ] **Do:** Trigger the capture prompt (step 8). In the page console:
  ```js
  const hosts = document.querySelectorAll('[data-rel]');
  for (const h of hosts) console.log(h, h.shadowRoot);  // shadowRoot should be null
  console.log(document.querySelector('#relicario-save-btn'));  // should be null
  console.log(document.querySelector('.relicario-capture'));   // should be null
  ```
- [ ] **Expected:** All `shadowRoot` values are `null`; no stable selectors match (audit C3).
- [ ] **Notes:** ___

### SP4. Captured-tab navigation during fill (audit M5)

- [ ] **Do:** Open popup on `https://github.com/login`. Select a github item, click "autofill", but BEFORE the fill lands, rapidly navigate the github tab to `https://example.com`.
- [ ] **Expected:** No credentials typed on example.com. SW rejects with `tab_navigated`; if somehow the message reaches the content script, `fill.ts` re-checks `expectedHost` and rejects with `origin_changed`.
- [ ] **Notes:** ___ (this one's hard to time; skip if not easily reproducible)

### SP5. WAR probe

- [ ] **Do:** In a page console on any site:
  ```js
  fetch('chrome-extension://<your-extension-id>/setup.html').catch(e => console.log('blocked:', e))
  ```
- [ ] **Expected:** Blocked (either CORS error or net::ERR). WAR is empty, so no resource is web-accessible. `<all_urls>` pages cannot reach setup.html.
- [ ] **Notes:** ___

---

## Final acceptance

- [ ] **A1.** `cargo test --workspace` green (should still be 151+ Rust tests).
- [ ] **A2.** `cd extension && bun run test` green (should be 52 passing — 11 base32 + 41 router).
- [ ] **A3.** `cd extension && bun run build` green (Chrome bundle).
- [ ] **A4.** `cd extension && bun run build:firefox` green (Firefox bundle).
- [ ] **A5.** Lint greps clean:
  ```bash
  git grep -n 'innerHTML\|insertAdjacentHTML' extension/src/content/  # zero hits
  git grep -n 'idfoto' extension/                                      # zero hits
  git grep -n '@ts-nocheck' extension/src/                             # zero hits
  ```
- [ ] **A6.** WAR empty:
  ```bash
  grep -A2 web_accessible_resources extension/manifest.json          # []
  grep -A2 web_accessible_resources extension/manifest.firefox.json  # []
  ```

---

## Sign-off

- [ ] **All 11 core-matrix steps pass on Chrome**
- [ ] **All 11 core-matrix steps pass on Firefox**
- [ ] **All 5 security probes pass (or SP4 skipped, others pass)**
- [ ] **All 6 final acceptance checks pass**
- [ ] **Ready to tag `plan-1c-alpha-complete` and decide on merge path**

### Findings / issues

Use this space to log anything weird:

```
(fill in as you go)
```

### Decision

- [ ] Merge straight to `main`
- [ ] Open a PR first for review
- [ ] Need rework on: ___

---

*Generated 2026-04-20 — source: spec `2026-04-20-relicario-extension-1c-alpha-design.md` §5.4, plan `2026-04-20-relicario-extension-1c-alpha.md` Task 27.*
