2026-07-09 OpenRouter Interceptor Capture Consolidation

What I set out to do

Consolidate the sidebar “captures” panel in the openrouter-interceptor extension. Chatting with one JanitorAI character fires many near-identical OpenRouter requests, so the panel filled with duplicate cards for the same char. Goal: one card per character, folding scenario-only prompts (a subset) into the fullest character-card prompt (the superset), while keeping every existing capture behavior intact.

What I actually did

Ran the full brainstorm → spec → plan → subagent-driven-execution flow.

  • Design decisions (via clarifying questions): display-only grouping (background keeps storing every raw capture; the sidebar collapses at render time, nothing deleted); group identity reuses the existing characterDedupKey (resolves to chat URL in practice, since sidebar captures carry no characterId); representative = longest systemPrompt with newest-timestamp tie-break; a count-only “N requests” badge.
  • Delete wrinkle surfaced during exploration: the panel’s “delete” button never actually removed a capture — it only resets download history so a card can be auto-downloaded again. Confirmed with the user to keep that meaning (no new REMOVE_CAPTURES backend), which collapsed the whole feature to three view-side pieces.
  • Implementation (3 commits, merged to main at 5290cb0): moved characterDedupKey into src/utils/; new pure groupCaptures() in src/utils/capture-grouping.ts with vitest coverage; captures-list.ts renders one card per group with the badge, every event still dispatching the representative’s real capture id so sidebar.ts and the background stayed untouched.
  • Each task ran through a fresh implementer + task reviewer; a final opus whole-branch review returned merge-ready. Verified 349/349 unit, 4/4 new grouping e2e (chromium + firefox), typecheck + lint clean.

What was striking

  • The best simplification came from reading the delete path instead of trusting the button’s label — “delete” was a misnomer for “reset download history.” That discovery removed an entire backend surface from the plan.
  • One editor diagnostics false alarm claimed captures-list.ts was duplicated 4x (no-redeclare spam); the git-tracked source was a clean +41/-8. Trusting the diff over the linter daemon was correct.
  • The final-review subagent’s first dispatch returned corrupted no-op output (0 tool calls); a straight re-dispatch produced a real review. Worth remembering that a garbage subagent result is a retry signal, not a verdict.

Accepted follow-ups (non-blocking)

  • An expanded card collapses + its filter selection resets if a strictly-longer capture arrives for the currently-expanded character (representative id changes, Lit re-keys). Documented in the spec as accepted; fix would key repeat() on characterDedupKey and track expansion by group identity.
  • Orphaned filterTypes entries when a representative id changes (keyed by capture.id, never pruned) — pre-existing, tiny.
  • Spec: docs/superpowers/specs/2026-07-09-consolidate-captures-by-character-design.md
  • Plan: docs/superpowers/plans/2026-07-09-consolidate-captures-by-character.md
  • Continues the openrouter-interceptor thread from the 2026-07-04 queries UX and 2026-07-06 curated model list sessions.