&parity=&q= filtered picker
+```
+
+Determinism contract for screenshot/CI use:
+
+- `run=fake` auto-runs the scenario to completion on load; when the run and
+ all panels are settled the shell sets `data-run-state="settled"`
+ (Playwright wait target). Until then it is `pending`.
+- Fake runs render **fixture time only** — no wall-clock timestamps,
+ durations, or locale-dependent formatting anywhere in fake mode; ordering
+ is by artifact sequence. Two loads of the same route are pixel-identical
+ at the same viewport (modulo font rasterization).
+- `view=` selects the inspector tab (desktop) or segment+tab (mobile);
+ `view=transcript` closes nothing — it just guarantees focus/scroll starts
+ at the transcript top.
+- Stable `data-testid`s (minimum set): `scenario-picker`, `facet-`,
+ `case-row-`, `run-header`, `verdict-banner`, `mode-toggle`,
+ `transcript`, `transcript-item-`, `cp-action-`,
+ `inspector-tab-`, `exposure-always`, `exposure-optional`,
+ `exposure-control-plane`, `authorization-row-`, `diff-domain-`,
+ `parity-assertion-`, `redaction-chip`.
+
+## 8. Package-local implementation guidance (7F)
+
+- **Reuse first.** Shell, transcript, and inspector compose the frozen SDK:
+ `RunnerConsoleApp` is *not* the shell (this is not a chat console);
+ compose `conversation`, `message`, `tool-item`, `tabs`, `card`, `badge`,
+ `banner`, `menu`, `dialog`, `tooltip`, `replay-controls` from
+ `@paperclipai/paperclip-runner/react`. Semantic call detail uses the
+ item-body renderer (ext point 1); control-plane detail uses the
+ request-detail renderer contract (ext point 2); theming via scoped
+ `--pcr-*` overrides (ext point 4). Do not add SDK exports or break the
+ `0.1.2` freeze — explorer-only components live in the explorer entry.
+- **New explorer-local components** (system proposals, one job each):
+ `ScenarioPicker` (search + facets + listbox), `ExposureList`,
+ `AuthorizationTable`, `StateDiff` (domain sections + field rows),
+ `ParityPanel`, `RedactionChip`, `ChannelEntry` (control-plane transcript
+ entry). Each uses tokens only; any genuinely missing token (e.g. a
+ `--pcr-diff-*` pair if muted/success/danger surfaces prove insufficient)
+ is proposed in the 7F PR as a token addition, never inlined.
+- **Data contract consumed (owned by 7C/7E, not the UI):**
+ - *Scenario index* (generated from `eval-traceability.yaml`): `id`,
+ `title`, `group`, `fixture`, `actorRole`, `taskMode`,
+ `primaryDisposition`, `requiredGrants[]`, `assertionClasses[]`,
+ `sourceAnchor`, `skillElements[]`, `evidenceIds[]`,
+ `forbiddenSemantics[]`. If `actorRole`/`taskMode` are not yet in the
+ manifest, 7C adds them to the index generator — the UI must not infer
+ them from prose.
+ - *Run artifact* (per scenario × mode): ordered `timeline[]` entries
+ discriminated as `semantic_call` / `semantic_result` /
+ `control_plane_action` / `protocol_event_ref`; `exposure` (always /
+ optional-with-grant / control-plane lists); `authorizationRecords[]`
+ (operation, claims considered, decision, reason, redactions, state-change
+ ref); `snapshots.before` / `snapshots.after` per entity domain;
+ `parity` (assertions with class/expected/observed/status, intentional
+ gaps with rationale, legacy baseline note, verdict).
+- **Authority rule (binding):** every rendered fact traces to one of those
+ records. Grep-able heuristics, UI-side claim math, or re-deriving
+ dispositions from case IDs are defects. If a view needs a fact the records
+ lack, extend the record upstream.
+- **`DESIGN.md` alignment:** tokens-only values; one component per job;
+ hierarchy by structure not decoration (domain sections and channels use
+ surface + position, minimal borders); status vocabulary systematic (§0
+ labels everywhere); machine values (IDs, anchors, grants, JSON) in
+ `--pcr-font-mono`; canonical copy “task.”
+- **Testing hooks 7F must ship:** component tests for the four inspector
+ tabs and both transcript channels; keyboard-path tests (listbox, tabs,
+ region cycle); reduced-motion test; the §7 route determinism test
+ (two loads → identical settled DOM); accessibility assertions (landmarks,
+ names, contrast tokens); screenshot capture script for §9.
+
+## 9. Screenshot acceptance matrix
+
+Evidence lands in `knowledge/evidence/phase-07/` as
+`-.png`, at both **1440×900** and **390×844**, captured from
+the §7 routes after `data-run-state="settled"`. These twelve shots are the
+UX acceptance set for 7F (they also cover the plan’s human-checkpoint
+scenario families: heartbeat, planning, blockers, approvals, artifacts,
+interactions, manager, multi-hop, restraint).
+
+| # | Slug | Route | Expected visible evidence |
+| --- | --- | --- | --- |
+| 1 | `explorer-home` | `#/` | Picker with all 16 group facets and counts summing to 106; intro card with corpus stats; no dead panels. |
+| 2 | `picker-filtered` | `#/?group=ix&disposition=always_agent_tool&parity=pass` | Active filter chips; filtered count visible; zero-count facet values disabled but legible; Clear filters affordance. |
+| 3 | `hb-heartbeat` | `#/case/hb-scoped-wake-01?run=fake&view=transcript` | Control-plane channel entries for scoped-wake routing and checkout (“no agent tool exists for this”), then `get_task_context` agent call; pass banner. |
+| 4 | `wk-planning` | `#/case/wk-plan-directive-01?run=fake&view=transcript` | `write_document` call with revision fields, revision-bound `request_human_input` (confirmation), review-parked disposition; no re-plan loop. |
+| 5 | `bl-blockers-diff` | `#/case/bl-create-blocked-01?run=fake&view=diff` | State diff: tasks domain shows the new task created blocked; blockers domain shows first-class `blockedByIssueIds`; unchanged domains collapsed. |
+| 6 | `ap-approval-denied` | `#/case/ap-mcp-gate-01?run=fake&view=authorization` | Authorization table with a Deny row (missing claim + reason, danger surface, no protected state); “Authorization · 1 deny” tab chip; transcript shows the typed denial card. |
+| 7 | `ar-artifacts` | `#/case/ar-upload-before-done-01?run=fake&view=transcript` | `register_deliverable` completing **before** `finish_task` in artifact order; deliverable metadata in the expanded call card. |
+| 8 | `ix-interaction` | `#/case/ix-checkbox-01?run=fake&view=transcript` | `request_human_input(kind=checkbox)` call card with options payload; control-plane wake/continuation entry; result inspection entry. |
+| 9 | `rf-manager` | `#/case/rf-api-mgr-heartbeat-01?run=fake&view=context` | Context tab: manager role; optional discovery tools listed **with their unlocking grants**; control-plane “no tool” list populated; traceability panel showing source anchor + skill elements. |
+| 10 | `mh-multihop` | `#/case/mh-subtask-tree-01?run=fake&view=parity` | Multi-hop assertion rows (child creation, chained blockers) all Pass; forbidden operations section showing “never invoked ✓”. |
+| 11 | `rs-restraint` | `#/case/rs-question-only-01?run=fake&view=parity` | Restraint verdict: answer-only transcript with the “No further operations” note; parity forbidden list showing zero state writes; Pass banner. |
+| 12 | `rs-secret-redaction` | `#/case/rs-secret-hygiene-01?run=fake&view=transcript` | Redaction chips (`•••` + rule name) in call/result cards; comment content free of the key; parity Pass. |
+
+Mobile captures use the same routes; the segmented control must show the
+correct active segment (`Run` for transcript shots, `Inspect` for tab shots)
+and no horizontal scrollbar may be present.
+
+Acceptance procedure: UXDesigner reviews all 24 images against this matrix
+before 7F is accepted (plan §6: “UXDesigner supplies the interaction map and
+reviews screenshots before implementation acceptance”). Any deviation is
+either fixed or written into this document as a revision — silent divergence
+fails the gate.
+
+## 10. Out of scope / rejected
+
+- No second app shell, no Tailwind/Radix, no dark mode, no markdown
+ renderer, no command palette, no transcript virtualization — Phase 5
+ rejections stand.
+- No editing affordances of any kind: fixtures, grants, and policies are
+ changed in code/YAML, not in the browser. The mode toggle and run/replay
+ controls are the only mutating-looking controls, and they mutate only the
+ in-browser demo run.
+- No parity “re-judging” in the UI: the Parity tab renders 7E output; it
+ never recomputes assertions.
+- No live-Paperclip or ACPX affordances; the future binding boundary stays a
+ docs note, not a UI surface.
diff --git a/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-1440x900.png b/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-1440x900.png
new file mode 100644
index 00000000000..bc6aac0e3fd
Binary files /dev/null and b/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-1440x900.png differ
diff --git a/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-390x844.png b/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-390x844.png
new file mode 100644
index 00000000000..3bb7e1e493c
Binary files /dev/null and b/packages/paperclip-runner/knowledge/evidence/phase-07/mockup-explorer-ap-mcp-gate-390x844.png differ
diff --git a/packages/paperclip-runner/scripts/shot-phase7-mockup.mjs b/packages/paperclip-runner/scripts/shot-phase7-mockup.mjs
new file mode 100644
index 00000000000..e2b3995505b
--- /dev/null
+++ b/packages/paperclip-runner/scripts/shot-phase7-mockup.mjs
@@ -0,0 +1,16 @@
+import { chromium } from "@playwright/test";
+
+const exe = "/srv/paperclip/home/paperclipai/paperclip/.paperclip/browser-runtime/chromium-arm64/bin/chromium-agent-browser";
+const url = "file:///srv/paperclip/home/paperclipai/paperclip/.paperclip/worktrees/PAP-16679-paperclip-runner/packages/paperclip-runner/docs/design/mockups/phase-7-scenario-explorer.html";
+const out = "knowledge/evidence/phase-07";
+
+const browser = await chromium.launch({ executablePath: exe });
+for (const [w, h, tag] of [[1440, 900, "1440x900"], [390, 844, "390x844"]]) {
+ const page = await browser.newPage({ viewport: { width: w, height: h } });
+ await page.goto(url);
+ await page.waitForTimeout(400);
+ await page.screenshot({ path: `${out}/mockup-explorer-ap-mcp-gate-${tag}.png` });
+ await page.close();
+}
+await browser.close();
+console.log("done");