# Plan 1C-β (β₁ + β₂) — Manual Test Matrix

Walkthrough for validating the typed-item forms (β₁) and the custom-fields editor + vault-settings + generator-popover surfaces (β₂) on Chrome and Firefox.

Branch: `main` @ `783cb7c` (tags `plan-1c-beta1-complete`, `plan-1c-beta2-complete`).
Pre-req: α matrix already validated — this round assumes the foundation works and focuses on the new β surfaces.

---

## Pre-flight

- [ ] **P1.** Bundles built fresh:
  ```bash
  cd /home/alee/Sources/relicario/extension
  bun run build:all
  ```
  Expected: "compiled with 2 warnings" (WASM size only) for each of Chrome (`dist/`) and Firefox (`dist-firefox/`).

- [ ] **P2.** Test suites green:
  ```bash
  cd /home/alee/Sources/relicario && cargo test --workspace
  cd /home/alee/Sources/relicario/extension && bun run test
  ```
  Expected: 155 Rust + 124 Vitest, all pass.

- [ ] **P3.** Throwaway vault ready (don't pollute your real history). Either reuse the α-validated test vault, or do a fresh `chrome.storage.local` clear and re-init via setup tab.

- [ ] **P4.** Reference JPEG on hand for unlock.

---

## Loading

### Chrome
- [ ] **L1.** `chrome://extensions` → "Load unpacked" → `extension/dist/`. (Or "Update" if already loaded — webpack regenerated everything.)
- [ ] **L2.** Toolbar icon visible. Click → unlock or setup.

### Firefox
- [ ] **L3.** `about:debugging#/runtime/this-firefox` → "Load Temporary Add-on" → `extension/dist-firefox/manifest.json`.
- [ ] **L4.** Toolbar icon visible. Click → unlock or setup.

> Run the entire matrix on Chrome first, then re-run **Section A (β₁ types)** and **Section B (β₂ surfaces)** on Firefox. Section C (cross-cutting) needs to pass on both browsers.

---

## Section A — β₁ typed-item forms

For each new type the matrix checks: **add → list icon → detail render → field round-trip → edit → trash**. Login was validated in α; spot-check it under **A0**.

### A0. Login regression spot-check

- [ ] Open popup → "+ New" → Login.
- [ ] **Expected:** Form has title / url / username / password (with "gen" button) / TOTP secret (optional).
- [ ] Fill and save; verify it appears in the list and detail-view round-trips.
- [ ] **Notes:** ___

### A1. SecureNote

- [ ] **Do:** "+ New" → SecureNote. Title `wifi`. Body `SSID: foo<newline>Password: bar`. Save.
- [ ] **Expected list row:** 📝 (or note icon) + `wifi`.
- [ ] **Expected detail:** Body renders preserving newlines; reveal/copy works on the body.
- [ ] **Edit:** Change body; save; detail reflects new body; modified time bumps.
- [ ] **Trash:** Disappears from list. CLI cross-check: `relicario list --trashed | grep wifi`.
- [ ] **Notes:** ___

### A2. Identity

- [ ] **Do:** "+ New" → Identity. Title `Personal`. Fill at least: full name, email, phone. Leave some fields empty intentionally (e.g. address).
- [ ] **Expected:** Detail view renders only the fields you populated — empty fields should NOT show as blank rows. `core.address === undefined` not `""` (verify via CLI `relicario get Personal --show` if curious).
- [ ] **Edit:** Add a field that was previously empty; save; detail shows the new row.
- [ ] **Trash:** Soft-deletes.
- [ ] **Notes:** ___

### A3. Card

- [ ] **Do:** "+ New" → Card. Title `Visa Test`. Cardholder `J. DOE`. Number `4111111111111111` (the canonical Visa test number — brand should auto-detect to "Visa"). CVV `123`. Expiry `08 / 2029`. PIN `9999`.
- [ ] **Expected during edit:** Brand chip flips to "Visa" once 4+ digits are typed (BIN match on `4`).
- [ ] **Expected detail:** number/cvv/pin are concealed by default; reveal on each works; copy on each puts the value on clipboard. Expiry shows `08/2029`.
- [ ] **Wire-format check (CLI):** `relicario get "Visa Test" --show --json | jq '.core.expiry'` should be `{"month":8,"year":2029}` (numbers, not strings).
- [ ] **Trash:** Soft-deletes.
- [ ] **Notes:** ___

### A4. Key

- [ ] **Do:** "+ New" → Key. Title `gh-deploy`. Algorithm `ed25519` (free-text). Paste a multi-line ASCII key into key_material (any junk is fine — `-----BEGIN OPENSSH PRIVATE KEY-----\nblah\n-----END...`).
- [ ] **Expected:** key_material is concealed/textarea-style; reveal shows full content with line breaks intact; copy puts the multi-line value on clipboard verbatim.
- [ ] **Edit:** Append to algorithm string; save; detail reflects.
- [ ] **Trash:** Soft-deletes.
- [ ] **Notes:** ___

### A5. Totp — TOTP kind (6 digits)

- [ ] **Do:** "+ New" → Totp. Title `GitHub-2FA`. Secret `JBSWY3DPEHPK3PXP` (RFC 6238 vector). Kind: TOTP.
- [ ] **Expected detail signature block:** Big 6-digit code (rotates every 30s); countdown ring shrinks each tick; code refreshes at the rollover without a manual reload.
- [ ] **Cross-check:** `oathtool --totp -b JBSWY3DPEHPK3PXP` (or any TOTP authenticator) → matches what the popup shows for the same wall-clock second.
- [ ] **Copy:** "copy code" button puts current code on clipboard.
- [ ] **Notes:** ___

### A6. Totp — Steam Guard kind (5 alphanumeric)

- [ ] **Do:** "+ New" → Totp. Title `Steam`. Secret `JBSWY3DPEHPK3PXP` (any base32 will do for the test). Toggle kind to **Steam**.
- [ ] **Expected:** Form's `digits` field disappears or locks (Steam is fixed at 5).
- [ ] **Expected detail:** 5-character alphanumeric code (e.g. `H7K2C`). All chars from the Steam alphabet `23456789BCDFGHJKMNPQRTVWXY` (no `0`, `1`, `A`, `E`, `I`, `O`, `S`, `U`, `Z`, `L`).
- [ ] **Edit:** Switch kind to TOTP, save; detail flips to 6-digit decimal. Switch back to Steam; flips back to 5-char.
- [ ] **CRITICAL:** If switching kinds doesn't re-render the detail-view computed code correctly after save, that's a stale-state bug — file before continuing.
- [ ] **Notes:** ___

### A7. Document type — gating

- [ ] **Do:** "+ New" → Document.
- [ ] **Expected:** "Coming soon" placeholder (planned for γ). Back button returns to list. **Should not crash or render a partial form.**
- [ ] **Notes:** ___

---

## Section B — β₂ surfaces

### B1. Custom fields editor — add path

- [ ] **Do:** Open any item form (Login is fine). Scroll to the disclosure labeled "custom fields ▸" (or similar). Click to expand.
- [ ] **Expected:** Disclosure expands; "+ section" / "+ field" controls appear.
- [ ] **Do:** Add a section named `recovery codes`. Add two fields under it: kind=`password` with label `code 1` value `aaaa-bbbb`, and kind=`concealed` with label `code 2` value `cccc-dddd`. Save.
- [ ] **Expected:** Detail view shows the typed Login rows first, then the `recovery codes` section header, then the two custom rows. Each concealed/password row has reveal + copy.
- [ ] **CLI cross-check:** `relicario get <item> --show --json | jq '.sections'` shows the section with both fields.
- [ ] **Notes:** ___

### B2. Custom fields editor — edit path

- [ ] **Do:** Edit the same item. In the disclosure, remove `code 1`, edit `code 2`'s label to `recovery hash`, add a new `text` kind field labeled `notes` value `worked 2024-04`. Save.
- [ ] **Expected:** Detail reflects all three changes (one removed, one renamed, one added).
- [ ] **Edge:** A blank `label` field — does β₂ render as `(unnamed)` or reject save? (Spec says render; verify either is acceptable but consistent.)
- [ ] **Notes:** ___

### B3. Custom fields editor — kind sniff

- [ ] **Do:** On a fresh add of an Identity item (or any type), open custom fields. Add fields of each supported kind (text / password / concealed). For each, verify in detail view: `text` is plain visible; `password` and `concealed` are masked with reveal/copy.
- [ ] **Expected:** No reordering controls (β₂ scope), but adding a new field appends to end.
- [ ] **Notes:** ___

### B4. Vault settings — open path via ⚙ picker

- [ ] **Do:** Click the ⚙ icon in the toolbar. β₂ split this into a picker.
- [ ] **Expected:** A small menu appears with two choices — **device settings** (capture toggle, prompt style, blacklist) and **vault settings** (retention/generator/origin-acks). Pick "vault settings".
- [ ] **Expected:** Vault settings screen renders with a back arrow.
- [ ] **Notes:** ___

### B5. Vault settings — trash retention

- [ ] **Do:** In vault settings, change "trash retention" from default to `7 days`.
- [ ] **Expected:** Save button enables (was disabled because no diff).
- [ ] **Do:** Save; lock; re-unlock; reopen vault settings.
- [ ] **Expected:** Still `7 days` (decrypted from the persisted VaultSettings).
- [ ] **Notes:** ___

### B6. Vault settings — history retention

- [ ] **Do:** Change "field history retention" to `last 5` (or `30 days` if your build offers `last_n` selectors). Save.
- [ ] **Expected:** Persists across lock/unlock.
- [ ] **Notes:** ___

### B7. Vault settings — generator preview + "configure"

- [ ] **Expected by default:** Generator preview line shows current saved default (e.g. `Random, 20 chars, lower+upper+digits+symbols, safe symbols`).
- [ ] **Do:** Click "configure ▾". Popover opens inline (anchored to the preview line).
- [ ] **Do:** Change kind to **BIP39**. Set word count to 8. Set separator to `-`. Set capitalization to `lower`.
- [ ] **Expected:** Preview-string in the popover refreshes per-keystroke (debounced); a sample generated phrase shows.
- [ ] **Do:** Click "save as default". Popover closes. Preview line on the vault-settings screen now reads `BIP39, 8 words, "-" separator, lower`.
- [ ] **Do:** Lock; re-unlock; reopen vault settings.
- [ ] **Expected:** Preview still shows BIP39 default.
- [ ] **Notes:** ___

### B8. Generator popover — open from Login form

- [ ] **Do:** "+ New" → Login. Click the "gen" button next to the password field.
- [ ] **Expected:** Generator popover opens **inheriting the BIP39 default from B7**. Sample phrase visible.
- [ ] **Do:** Click "use this value".
- [ ] **Expected:** The Login form's password field gets the BIP39 phrase. Popover closes.
- [ ] **Edge:** Open popover; toggle kind to **Random**; popover refreshes with random preview; click "use this value" — random string lands in the field. (Toggling shouldn't permanently mutate the saved default.)
- [ ] **Notes:** ___

### B9. Generator popover — kind toggle round-trip

- [ ] **Do:** Open popover from a fresh Login form. Toggle Random ↔ BIP39 several times.
- [ ] **Expected each toggle:** Preview redraws; debounced request shape switches between `generate_password` and `generate_passphrase`.
- [ ] **Smoke:** No console errors on toggle.
- [ ] **Notes:** ___

### B10. Vault settings — origin-ack revoke

- [ ] **Pre-req:** Have at least one acked origin (e.g. github.com from α step 6).
- [ ] **Do:** Vault settings → scroll to "autofill acks". Find the github.com row. Click "revoke".
- [ ] **Expected:** Row disappears (or shows "revoked").
- [ ] **Save** (β₂ batches changes). Lock; re-unlock; reopen.
- [ ] **Expected:** Row stays gone.
- [ ] **Do:** Navigate to github.com/login; click the autofill icon.
- [ ] **Expected:** **TOFU prompt re-fires** — the origin is no longer pre-acked.
- [ ] **CRITICAL:** If autofill silently succeeds without re-prompting, the revoke didn't actually clear `VaultSettings.autofill_origin_acks`.
- [ ] **Notes:** ___

### B11. Vault settings — discard / no-op

- [ ] **Do:** Open vault settings. Don't change anything. Click back arrow.
- [ ] **Expected:** Returns to list with no save attempt (popup didn't network-request).
- [ ] **Do:** Open again; change something; click back without saving.
- [ ] **Expected:** Either a confirm prompt OR silent discard. Reopen; the change is gone (not persisted).
- [ ] **Notes:** ___

### B12. ⚙ picker — device-settings path regression

- [ ] **Do:** ⚙ → "device settings".
- [ ] **Expected:** The α-era device settings screen appears (capture toggle, bar/toast style, blacklist). All controls still functional.
- [ ] **Notes:** ___

---

## Section C — Cross-cutting

### C1. Field history captured for new typed kinds

- [ ] **Do:** Edit the Card item from A3; rotate the cvv. Save.
- [ ] **Do:** Edit the Key item from A4; rotate key_material. Save.
- [ ] **Do:** Edit the Totp item from A5; rotate the secret. Save.
- [ ] **Expected (CLI):** `relicario get <each> --show --json | jq '.field_history'` has an entry for the rotated concealed/password field with old value + timestamp.
- [ ] **Notes:** ___

### C2. List icon parity per type

- [ ] **Do:** Scroll the populated list.
- [ ] **Expected:** Each row's icon matches its type. Login 🔑, SecureNote 📝, Identity 👤, Card 💳, Key 🗝, Totp ⏱ (or whatever the implementation chose — the matrix only checks consistency, not specific glyph).
- [ ] **Notes:** ___

### C3. Search across new types

- [ ] **Do:** Use the search box; type a substring of an item title for each type.
- [ ] **Expected:** Each type-specific item is findable; the type chip/icon is correct in the filtered list.
- [ ] **Notes:** ___

### C4. Sync / git push round-trip

- [ ] **Do:** From your throwaway test vault host, after creating items A1–A6 and the custom-field item from B1, run a sync from the popup (sync icon).
- [ ] **Expected:** Push succeeds; `git log` on the test repo shows new commits.
- [ ] **Do:** From CLI in main worktree, `relicario sync` then `relicario list`.
- [ ] **Expected:** Same items visible. (Tests round-trip integrity of the new wire format on a real git host, not just localStorage.)
- [ ] **Notes:** ___

### C5. Firefox parity

- [ ] **Do:** Re-run Section A (A0–A7) and Section B (B1–B12) on the Firefox-loaded `dist-firefox/`.
- [ ] **Expected:** Behavior identical to Chrome.
- [ ] **Watch for:** WASM-loading drift (FF uses `initDefault(wasmUrl)` not `initSync` because background.js is persistent, not SW). Anything broken on FF that works on Chrome is a WASM-init bug.
- [ ] **Notes:** ___

---

## Final acceptance

- [ ] **A1.** All Section A scenarios pass on Chrome.
- [ ] **A2.** All Section B scenarios pass on Chrome.
- [ ] **A3.** All Section A + B scenarios pass on Firefox.
- [ ] **A4.** Section C cross-cutting all pass.
- [ ] **A5.** Lint sweeps green:
  ```bash
  git grep -n 'idfoto' extension/                                       # 0
  git grep -n '@ts-nocheck' extension/src/                              # 0
  git grep -n 'innerHTML\|insertAdjacentHTML' extension/src/content/    # 0
  git grep -n 'coming-soon\|Coming in' extension/src/popup/components/  # only 'document'
  ```

---

## Findings / issues

Use this space to log anything weird. For each issue: file path + symptom + repro steps. Bug-fix commits go to main as you find them.

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

### Decision

- [ ] All clean — proceed to brainstorm 1C-γ.
- [ ] Bugs found and patched on main; re-run affected sections.
- [ ] Bugs found that warrant a worktree (>3 commits to fix).

---

*Generated 2026-04-24 — sources: spec `2026-04-22-relicario-extension-1c-beta1-design.md` §3.9, spec `2026-04-22-relicario-extension-1c-beta2-design.md` "Manual matrix", α matrix `2026-04-20-1c-alpha-manual-matrix.md`.*
