# Dev C Kickoff Prompt — arch-followup Plan C Paste everything below the `---` line into a fresh Claude Code terminal as the first user message. --- You are a **senior developer** owning Plan C for the arch-followup "architecture-review followups" release train. Plan C is the **extension restructure** — the largest of the three (multi-day to multi-week). It eliminates the two steepest learning cliffs in the extension. After this plan ships, `setup.ts` no longer imports `relicario-wasm` directly (it isn't the pattern; it was the exception); `vault.ts` shrinks from 1027 LOC to ~200 of routing + state; `shared/state.ts` becomes type-checked end-to-end; the duplicated SW router helpers consolidate into one home each; and the extension closes its last CLI-parity gap (`relicario status` → vault-sidebar status indicator). Six phases. A PM in another terminal coordinates you with Dev-A (security & docs polish) and Dev-B (CLI restructure). With the relay server running, you communicate via `post_message` / `read_messages` directly — no user copy-paste needed. If the relay MCP tools are not registered in your session, use the Python shim fallback (see **Relay server** section below). ## Setup (do this first) ```bash cd /home/alee/Sources/relicario git fetch git checkout main git pull git worktree add ../relicario-plan-c -b feature/2026-05-04-c-extension-restructure cd ../relicario-plan-c pwd # should print /home/alee/Sources/relicario-plan-c (or similar absolute path) ``` **ALL subsequent work happens in `/home/alee/Sources/relicario-plan-c`**. Force-cd subagents into this directory — the project's `CLAUDE.md` memory rule explicitly requires that subagent prompts MUST start with `cd /home/alee/Sources/relicario-plan-c` so subagents don't accidentally commit to main. This is non-negotiable. Today: 2026-05-04. Project rules in `CLAUDE.md` apply. ## Relay server A message-bus MCP server is running on `localhost:7331`. You have three native tools: - `post_message(from, to, kind, body)` — push a message; your `from` is always `"dev-c"` - `read_messages(for)` — drain your inbox; call with `for="dev-c"` before each task - `list_pending(for)` — check inbox count without consuming Recipients: `pm, dev-a, dev-b, dev-c`. Use these instead of asking the user to copy-paste. Before starting each task: `read_messages(for="dev-c")`. After emitting any status/question block: `post_message(from="dev-c", to="pm", kind="status"|"question", body="...")`. **Fallback:** If the relay MCP tools are not registered in your session, use the Python shim: ```bash cd /home/alee/Sources/relicario/tools/relay python3 call.py post_message '{"from":"dev-c","to":"pm","kind":"status","body":"..."}' python3 call.py read_messages '{"for":"dev-c"}' ``` ## Required reading (in order) 1. `CLAUDE.md` — project rules (Spanish flourish in chat replies only, capitalization, autonomy defaults, CLI/extension parity philosophy, security defense-in-depth) 2. `docs/superpowers/reviews/2026-05-04-architecture-review.md` — synthesis (your scope is **P1.4, P1.5, P1.6, P1.9 + the in-scope extension P2s + the `relicario status` parity gap only**) 3. `docs/superpowers/specs/2026-05-04-extension-restructure-design.md` — your plan, execute phase by phase 4. `docs/superpowers/reviews/2026-05-04-dev-c-notes.md` — DEV-C's full notes (your primary source — the synthesis abbreviates) 5. `docs/superpowers/reviews/2026-05-04-dev-b-notes.md` — read **only** the "Boundary notes for DEV-C" section near the end (14 numbered contracts the JS side must respect when interacting with WASM; several inform your scope) You do NOT need to read Plan A or Plan B in detail. Skim Plan A's Phase 2 (the `service-worker/session.ts:26` swallow removal) and Plan B's Phase 8 (WASM parser exports) only if a coordination question arises. ## Execution mode Use **subagent-driven-development** (per project memory's default for any multi-task plan). Invoke `superpowers:subagent-driven-development` and follow it: fresh subagent per phase, two-stage review between phases. **Every subagent prompt MUST start with**: ``` cd /home/alee/Sources/relicario-plan-c ``` …before any other instruction. This is non-negotiable per project memory. **Sequencing matters.** Phase 1 (typed `StateHost`) is the precondition for phases 3 and 4. Phase 2 (SW storage extraction) is independent and can ship in parallel. Phases 3 and 4 both depend on phase 1. Phase 5 (P2 cluster) and phase 6 (`get_vault_status`) are independent of 3 and 4 — they can run in parallel. ## Your scope and boundaries **In scope:** - Phase 1 — Typed `StateHost` interface in `extension/src/shared/state.ts` (no `any` in public surface) + generic `getState`/`setState` over `keyof PopupState` + double-registration guard + `__resetHostForTests` helper. Includes migration of `View` and `PopupState` from `extension/src/popup/popup.ts` to `extension/src/shared/types.ts` (or a new `shared/popup-state.ts`) to avoid a `popup → shared → popup` circular import. - Phase 2 — Extract `extension/src/service-worker/storage.ts` (`loadDeviceSettings`, `loadBlacklist`, `saveBlacklist` from both router files) + move `itemToManifestEntry` to `extension/src/service-worker/vault.ts`. - Phase 3 — Setup wizard SW migration: add `create_vault` and `attach_vault` SW messages; rewrite `setup.ts` as UI-only that posts those messages; convert the 6-step procedural wizard to a step-registry pattern; add `clearWizardState()` on `beforeunload` + step-0 reset. - Phase 4 — Split `vault.ts` into `vault-shell.ts` / `vault-sidebar.ts` / `vault-list.ts` / `vault-drawer.ts` / `vault-form-wrapper.ts`. Lift `vault_locked` RPC intercept into `shared/state.ts`. Reset `state.drawerOpen` on non-list `renderPane`. Debounce sidebar search (50-100ms). - Phase 5 — P2 cluster: inactivity-timer reset on content-callable messages (with documented exclusion set); `state.gitHost` clear on session expiry; teardown helper extraction (`teardownSettingsCommon`); `Promise.allSettled` in devices/trash; MutationObserver debounce in `content/detector.ts`. - Phase 6 — `get_vault_status` SW message + vault-sidebar status indicator (closes the `relicario status` parity gap). **Out of scope:** anything in Plan A (security/docs polish — `impl Drop`, `service-worker/session.ts:26` swallow removal, `.free()` audit, `recovery_qr.rs` docs, server hardening, env-var audit) or Plan B (CLI restructure — `cli/main.rs` split, `git_run`, parser migration to core; you only **consume** the WASM exports as a deferred follow-up). Extension P3s (form-header `isInTab()` redundancy, popup.ts `isInTab()` heuristic, item-form.ts `renderComingSoon` dead code, `types/login.ts` size, `vault.ts:18-26` backup-panel comment, capture/detector/fill username-finder dedup, capture submit-button hook scope, setup.ts passphrase-score `-1` sentinel, `setup.ts:1056-1062` chrome.storage bypass, `setup.ts:1-7` "5-step" header, glyphs.ts partial adoption, `types.ts` TotpKind flat-union, `totp-tools.ts:39-46` swallowed rejections, generator-panel cleanup guard, `item-list.ts` popover listeners, popup `popup.ts:178-181` unconditional teardowns). Other parity items (per-attachment `delete_attachment` SW message, `list --tag` filter doc note). Cross-cutting items not explicitly listed (chrome.storage.local direct reads outside the setup migration, bun test runner doc note, manifest version sync). The 8 "Open architectural decisions". WASM JS-naming snake_case → camelCase (deferred to a separate plan). Anything touching the in-flight uncommitted v0.5.x work. If you trip over an out-of-scope issue or a new bug, file it via a `## QUESTION TO PM` block and keep moving. **Hard rules:** - **Phase 1 first.** Do NOT start phases 3 or 4 until phase 1 is green and committed. The typed `StateHost` is the contract phases 3 and 4 build against. - **`View` / `PopupState` migration is part of phase 1**, not phase 4. Doing it later creates circular imports that surface mid-refactor and waste a day. - **Do NOT redo Plan A's `.free()` swallow removal** at `extension/src/service-worker/session.ts:26`. That's Dev-A's. But wherever your refactor *moves* a `.free()` callsite — most notably during phase 3 when `setup.ts`'s `verifiedHandle` retires and the new `create_vault`/`attach_vault` SW handlers acquire their own handles — the new location MUST call `wasm.lock(handle)` first regardless of whether Plan A's Rust-side `impl Drop` has landed yet. Cite Plan A as the source of the policy in your phase 3 commit message. - **`extension/src/wasm.d.ts` coordination with Plan B.** Plan B Phase 8 will touch this file for new parser exports. Verify by reading `extension/src/service-worker/vault.ts` whether your `create_vault`/`attach_vault` SW handlers need new WASM entry points — they likely don't (the SW already orchestrates `unlock`/`embed_image_secret`/`register_device`/`manifest_encrypt`). If you DO need new entries, escalate via `## QUESTION TO PM` so the touch order with Plan B can be sequenced. - **`create_vault` and `attach_vault` SW handlers must be transactional** — they hold their own internal session reference for the duration of the operation and do NOT consult or reset the user-facing inactivity timer until they return successfully. Document this contract in the handler header comments. - **Phase 4 `vault_locked` channel unification** keeps both signals (the SW's `session_expired` event AND the new `shared/state.ts` wrapper's intercept) firing during the migration window. Collapse only after both surfaces are verified consuming from `shared/state.ts`. Add a regression test asserting popup lock screen renders on `session_expired` and vault tab lock screen renders on the SW response intercept. - **Round out the WASM stub at `extension/src/__stubs__/relicario_wasm.stub.ts`** as part of phase 3. DEV-C noted only 7 of ~25 exports are stubbed; phase 3's vitest tests for `create_vault`/`attach_vault` need stubs for `embed_image_secret`, `register_device`, `manifest_encrypt`. Add them rather than file a separate ticket. - The `recovery_qr_generated_at` direct chrome.storage.local write at `setup.ts:1056-1062` is **out of scope** — leave it as-is; defer to a P3 cleanup. - Do not merge your branch to main. The PM owns merges. - Do not push `--force` or run `git reset --hard`. Per `CLAUDE.md`: ask first. ## Coordination protocol You are one of multiple terminals. The relay routes messages between them. **At every phase boundary** (complete, blocked, or question): call `read_messages(for="dev-c")` first, then post your update via `post_message(from="dev-c", to="pm", kind="status"|"question", body="...")` and also print it here. Use this format: ``` ## STATUS UPDATE — DEV-C Time: Branch: feature/2026-05-04-c-extension-restructure Task: Status: STARTED | IN-PROGRESS | DONE | BLOCKED | REVIEW-READY Last commit: Tests: Notes: ``` **When you need PM input mid-task**: post via `post_message(kind="question")` with format: ``` ## QUESTION TO PM — DEV-C Time: Context: Options: Recommended: Blocker: yes | no (does work stop without an answer?) ``` **You'll receive**: `## DIRECTIVE TO DEV-C` blocks from the PM via relay (or relayed by user if relay is down). Acknowledge and act. ## Cross-plan coordination - **Plan A owns the `.free()` swallow removal** (`service-worker/session.ts:26`) and the Rust `impl Drop for SessionHandle`. Do not redo that work. Do honor the policy where you move callsites (see Hard Rules). - **Plan B Phase 8 ships WASM parser exports** (`parse_month_year` / `base32_decode_lenient` / `guess_mime`) that the extension can eventually consume. Consumption (SW message handlers wrapping the new exports) is **explicitly deferred to a future plan** — do NOT design those handlers in this train. The seam exists; nobody is wiring it yet. - **`extension/src/wasm.d.ts` shared touchpoint with Plan B.** See Hard Rules — you likely don't need to touch it. If you do, escalate to PM for sequencing. ## Authority within the plan You don't need PM permission to: - Execute phase-to-phase per the plan - Make implementation decisions consistent with the plan and synthesis - Choose how the typed `StateHost` exposes `setState` (the plan suggests `setState`; pick the variant that gives the cleanest call-site ergonomics) - Pick which file `View` and `PopupState` migrate to (`shared/types.ts` vs new `shared/popup-state.ts`) — the plan accepts either - Add new tests, refactor your own code, fix bugs you introduce - Push commits to your feature branch You **do** escalate to PM when: - You discover phase 3's setup-to-SW migration needs new WASM entry points (would touch `wasm.d.ts` and conflict with Plan B Phase 8) - You discover the `view`/`PopupState` migration in phase 1 surfaces more TS errors than the plan estimated (~15-30); if you hit 100+ errors, the surface area is bigger than the plan accounts for - You discover a real bug in the existing `vault_locked` channels (e.g. popup currently doesn't actually receive `session_expired` despite the plan's premise) - A vitest test you can't make green after honest debugging (don't fudge — debug) - A discovered bug not in your plan - Anything destructive (per project rules) - Before opening the PR for review ## Final steps before REVIEW-READY Run the project's full validation: ```bash # From the worktree root (/home/alee/Sources/relicario-plan-c): cd extension npm test # vitest npm run build # Chrome build npm run build:firefox # Firefox build cd .. # Done-criteria sanity greps: grep -n ': any' extension/src/shared/state.ts # should return zero grep -rn 'relicario-wasm' extension/src/setup/ # should return zero (post-Phase-3) wc -l extension/src/setup/setup.ts # should be ≤ ~500 wc -l extension/src/vault/vault.ts # should be ~200 grep -n 'loadDeviceSettings\|loadBlacklist\|saveBlacklist' \ extension/src/service-worker/router/popup-only.ts \ extension/src/service-worker/router/content-callable.ts # should be imports only, no defs grep -n 'itemToManifestEntry' extension/src/service-worker/router/ # should be imports only ``` Then push and open the PR: ```bash git push -u origin feature/2026-05-04-c-extension-restructure gh pr create --base main --head feature/2026-05-04-c-extension-restructure --title "refactor(ext): typed StateHost + setup→SW + vault.ts split (Plan C)" --body "$(cat <<'EOF' ## Summary Architecture-review followup Plan C (extension restructure — eliminates the two steepest learning cliffs). Source: `docs/superpowers/specs/2026-05-04-extension-restructure-design.md`. - **P1.6** — `extension/src/shared/state.ts` now has a concrete `StateHost` interface (no `any` in public surface), `getState`/`setState` generic over `keyof PopupState`, double-registration guard, `__resetHostForTests` helper. `View` and `PopupState` migrated from `popup/popup.ts` to `shared/types.ts` to break circular import. - **P1.9** — `service-worker/storage.ts` extracted; `itemToManifestEntry` moved to `service-worker/vault.ts`. Both router files import; no more duplicated definitions. - **P1.4** — `setup.ts` no longer imports `relicario-wasm`. New `create_vault` / `attach_vault` SW messages handle vault creation transactionally. Procedural wizard converted to a step-registry pattern (`{ id, render, attach }[]`). `clearWizardState()` on `beforeunload` + step-0 reset wipes sensitive `Uint8Array` material best-effort. `setup.ts` LOC dropped from 1220 to ~500. - **P1.5** — `vault.ts` split into `vault-shell.ts` / `vault-sidebar.ts` / `vault-list.ts` / `vault-drawer.ts` / `vault-form-wrapper.ts`. `vault.ts` retained at ~200 LOC (routing + state only). `vault_locked` RPC intercept lifted into `shared/state.ts` so popup and vault tab consume one channel. Drawer auto-closes on non-list views. Sidebar search debounced. - **P2 cluster** — inactivity timer resets on all messages except a documented exclusion set; `state.gitHost` clears on session expiry; `teardownSettingsCommon` extracted; `devices.ts`/`trash.ts` use `Promise.allSettled`; `content/detector.ts` MutationObserver debounced. - **`get_vault_status`** — closes the `relicario status` parity gap. New SW message returns cached `{ ahead, behind, lastSyncAt, pendingItems }`; vault sidebar renders an indicator on mount + manual refresh. ## Cross-plan coordination respected - **Plan A** owns the `service-worker/session.ts:26` swallow removal and the Rust `impl Drop`. This PR does NOT redo that work. Wherever this refactor moved a `.free()` callsite (Phase 3 setup-to-SW migration), the new location calls `wasm.lock(handle)` first regardless of Plan A's status. - **Plan B Phase 8** WASM parser exports are a seam this PR does NOT consume in this train. Future plan wires the SW handlers. - **`extension/src/wasm.d.ts`** not touched by this PR (verified at Phase 3). ## Test plan - [ ] `cd extension && npm test` passes (vitest including new tests for typed state, SW storage helpers, `clearWizardState`, drawer auto-close, `vault_locked` channel, `get_vault_status`) - [ ] `cd extension && npm run build && npm run build:firefox` clean - [ ] `grep -n ': any' extension/src/shared/state.ts` returns zero - [ ] `grep -rn 'relicario-wasm' extension/src/setup/` returns zero - [ ] `wc -l extension/src/setup/setup.ts` ≤ ~500 - [ ] `wc -l extension/src/vault/vault.ts` ~200 - [ ] Manual smoke: load extension → setup → unlock → vault tab → drawer behavior → settings → trash - [ ] Manual smoke: trigger session expiry, confirm both popup and vault tab show lock screen - [ ] Manual smoke: vault sidebar status indicator updates on sync ## Done criteria Per `docs/superpowers/specs/2026-05-04-extension-restructure-design.md` Done criteria — every checkbox. 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )" ``` Emit a `## STATUS UPDATE` with `Status: REVIEW-READY` and the PR URL (post via `post_message`). ## First action After reading: emit a `## STATUS UPDATE` confirming setup complete (worktree created at `/home/alee/Sources/relicario-plan-c`, plan absorbed, on `feature/2026-05-04-c-extension-restructure`). Post it via `post_message(from="dev-c", to="pm", kind="status", body="...")`. Then start Phase 1 of your plan (typed `StateHost` + `View`/`PopupState` migration). Remember: phase 1 is the precondition for phases 3 and 4 — do not start them until phase 1 is green.