Files
relicario/docs/superpowers/coordination/2026-05-04-arch-followup-dev-c-prompt.md
adlee-was-taken bd3d53fddb docs(coordination): arch-followup kickoff prompts (PM + Dev A/B/C)
Generated by /multi-agent-kickoff for the three architecture-review
followup plans. PM coordinates; Dev-A owns Plan A (security & docs polish,
S, ships first); Dev-B owns Plan B (CLI restructure, M-L); Dev-C owns
Plan C (extension restructure, L).

Each dev prompt forces cd into its worktree (per project memory rule),
includes the relay tool calls + Python shim fallback, scopes hard-rules
to the planning subagents' flagged judgment calls, and ships an opinionated
PR title + body template that mirrors the plan's Done criteria.

PM prompt enforces the cross-plan boundaries: A is independent; B Phase 8
WASM exports are a seam C does not consume in this train; A owns the
.free() swallow removal and Drop impl; if both B and C touch wasm.d.ts,
B sequences first.

Launcher discovers these via `ls -t coordination/*-<role>-prompt.md | head -1`
so they take precedence over previous kickoff sets.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 20:12:19 -04:00

18 KiB

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)

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:

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: <iso8601 like 2026-05-04T14:30:00-07:00>
Branch: feature/2026-05-04-c-extension-restructure
Task: <phase number / short name>
Status: STARTED | IN-PROGRESS | DONE | BLOCKED | REVIEW-READY
Last commit: <short sha + first line of message>
Tests: <green | red (which failed) | N/A>
Notes: <anything PM needs to know — keep to 3 sentences max>

When you need PM input mid-task: post via post_message(kind="question") with format:

## QUESTION TO PM — DEV-C
Time: <iso8601>
Context: <what phase, what decision point>
Options: <A: ... / B: ... / C: ...>
Recommended: <your pick + one-sentence rationale>
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<K extends keyof PopupState>; 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:

# 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:

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.