diff --git a/docs/superpowers/test-runs/2026-04-24-1c-beta-manual-matrix.md b/docs/superpowers/test-runs/2026-04-24-1c-beta-manual-matrix.md new file mode 100644 index 0000000..ac3bce4 --- /dev/null +++ b/docs/superpowers/test-runs/2026-04-24-1c-beta-manual-matrix.md @@ -0,0 +1,287 @@ +# 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: fooPassword: 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 --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 --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`.*