# Capability Interaction Map — Scenario Explorer and Screenshot Acceptance Spec Status: **approved UX contract for 7F implementation** (TASK-16899, 2026-08-09). Owner: UXDesigner. Implementer: 7F (browser scenario explorer), integrating 7C mock adapter output and 7E parity output. Companion records: [Capability contract](../capability-contract.md), `spec/capability/eval-traceability.yaml`, the Capability plan (TASK-16897), [Live console interaction map](live-console-interaction-map.md), [SDK component decisions](sdk-component-decisions.md). (annotated desktop + mobile renders in `.paperclip-local/evidence/capability/`). This map is the interaction contract for the Capability browser scenario explorer. The explorer is a **read surface over records the runtime already produced**. It renders scenario metadata from the generated 7A traceability manifest, run artifacts from the 7C mock control-plane adapter, and parity results from the 7E conformance suite. The UI never owns state, policy, credentials, or execution decisions: it does not compute tool exposure, does not evaluate claims, does not apply redaction, and holds no Paperclip or provider credential. Every allow/deny, redaction, and state transition shown on screen must arrive as a record emitted by the mock core. ## 0. Naming, vocabulary, and canonical labels - Product name in the UI: **Scenario Explorer** (header: “Capability scenario explorer — mock control plane”). - UI copy uses the canonical term **task** (`DESIGN.md` principle 7). Eval case IDs, fixture IDs, route paths, JSON payloads, and source anchors are machine values and stay verbatim (`issues`, `i-0102`, `MCK-102`) in monospace — never “translated.” - The three dispositions render with exactly these labels, everywhere: **Control plane** (`control_plane_owned`), **Always tool** (`always_agent_tool`), **Optional tool** (`optional_agent_tool`). The traceability panel and all badges must use the 7A contract values as their source; no UI-local re-classification. - The five assertion classes render as: **Invariant** (`control_plane_invariant`), **Tool contract** (`agent_tool_contract`), **Authorization** (`authorization_policy`), **Multi-hop** (`combined_multi_hop`), **Restraint** (`restraint_no_call`). - Parity statuses: **Pass**, **Fail**, **Intentional gap**, **Not run**. Each has an icon + text label; color alone never encodes status (WCAG color-independence). ## 1. Shell and information architecture Package-local Vite entry `examples/scenario-explorer/` with script `demo:scenarios` (default port 4183; 4182 belongs to the Standalone demo). It runs from static assets plus checked-in fixtures with **no Paperclip services and no network dependency** in fake-agent mode. Reuse the frozen `0.1.2` SDK surface (`@paperclipai/paperclip-runner/react` + `./styles.css`) through its five approved extension points; do not fork the token layer. Desktop layout (≥ 64rem), one React tree (SDK finding — never render two trees): - **Left rail (`--pcr-rail-width`, 17rem):** scenario picker — search, facet filters, case list grouped by eval group. - **Center column (fluid, max `--pcr-center-width`, 48rem):** run view — the chronological scenario transcript and the run header (verdict banner, mode toggle, run/replay controls). Primary surface, strongest hierarchy. - **Right rail (`--pcr-inspector-width`, 24rem):** inspector with four tabs — **Context · Authorization · State diff · Parity**. Open by default on desktop (unlike the 4b protocol inspector: here the inspector carries first-class acceptance evidence, not diagnostics). Mobile (< 64rem): single column behind a segmented control (`Scenarios · Run · Inspect`), same pattern as the Live console console. The segmented control is sticky at the top; the Run segment is the default when a case is selected via route. Touch targets ≥ `--pcr-touch-target` (2.75rem). Density: diagnostic-console dense, like 4b. All spacing, color, type, radius, motion from `--pcr-*` tokens only (`DESIGN.md` principles 2–4; `pnpm check:token-gates` applies). ## 2. Scenario picker and filters (left rail) **Source of truth:** the generated scenario index derived from `spec/capability/eval-traceability.yaml` (all 106 cases, 16 groups) plus the latest parity results artifact from 7E. The picker consumes these; it never re-derives group membership or dispositions. Contents, top to bottom: 1. **Search input** — substring match on case ID and title. Mono rendering of matched IDs. Debounce ≤ 150ms (Doherty). 2. **Facet filters** (collapsible group, `--pcr-facet-height` baseline): - **Group** — the 16 eval groups (`hb 5 · co 6 · st 8 · cm 6 · se 4 · su 4 · bl 5 · dp 3 · ix 9 · ap 6 · ar 4 · er 9 · rf 22 · mh 4 · rs 3 · wk 8`), each with its case count. - **Case** — direct case-ID picker (combobox) for jump-to-case. - **Disposition** — Control plane / Always tool / Optional tool, from `primaryDisposition`. - **Role** — actor role of the fixture (`ic`, `manager`, `reviewer`, `board_user`, …) from the scenario index (§9 data contract). - **Claim** — required grants from `requiredGrants` (e.g. `approval:read`), rendered mono. - **Parity status** — Pass / Fail / Intentional gap / Not run, from the parity artifact. Facets are AND-combined across facets, OR-combined within a facet. Every facet value shows its live result count; zero-count values stay visible but disabled (recognition over recall — the vocabulary stays learnable). An active-filter chip row with per-chip remove and one **Clear filters** affordance sits above the list (forgiveness). 3. **Case list** — grouped by eval group with sticky group headers. Each row: case ID (mono, `--pcr-font-size-xs`), title (one line, truncated with full text in a tooltip), disposition badge, parity status dot+icon. Selected row uses `--pcr-accent-surface` with a `--pcr-accent` left rule. Keyboard: the list is a single-select listbox (`role="listbox"`, `aria-activedescendant`); Up/Down move, type-ahead jumps, Enter selects and moves focus to the run header. Facets are disclosure buttons + checkbox groups. Filter state mirrors into the route (§7) so any filtered view is linkable; filter state is never stored as protocol state. Empty results: “No scenarios match these filters.” + Clear filters button — never a bare void. ## 3. Run view (center column) ### 3.1 Run header - Case ID (mono) + title, group badge, disposition badge, assertion-class badges. - **Verdict banner** (SDK `Banner`): overall parity verdict for the last completed run — Pass (success surface), Fail (danger surface), Not run (muted). The banner names the assertion counts (“12 assertions · 12 pass”) and deep-links the Parity tab. - **Mode control:** `Fake agent` (default) / `Codex (bounded)` segmented control. Fake mode is always available offline. Codex mode appears only when the local relay is reachable; otherwise the option renders disabled with the reason (“provider relay not running — see tutorial §4”). The browser never holds a provider credential; Codex traffic goes through the same server-side relay boundary proven in SDK. - **Run / Re-run** button and replay controls (SDK `replay-controls`). Deterministic fixtures mean re-run in fake mode reproduces the identical timeline; state diffs and parity always describe the displayed run. ### 3.2 Scenario transcript **Source of truth:** the ordered run artifact timeline emitted by the mock core (§9). Items render in artifact order; the UI never reorders, merges, or re-times entries. The transcript interleaves two visually distinct channels (Similarity + Common Region — a stranger must tell them apart in two seconds): 1. **Agent channel** — model turns and semantic tool calls/results. Aligned with the standard conversation layout (SDK `conversation`/`message`/ `tool-item`). A semantic tool call renders collapsed by default with a one-line summary: operation ID (mono) + disposition badge + one-phrase outcome (“`finish_task` · Always tool · committed”). Expansion is the item-body / request-detail extension point (§8) and shows: operation ID and version, argument JSON, result JSON, idempotency behavior, claims the call required, and redaction chips (§5) — exactly the descriptor fields from the 7D catalog, never re-labeled. 2. **Control-plane channel** — actions the runtime performed with **no tool** (checkout, wake routing, budget stops, reconciliation, blocker wake scheduling…). These render full-width on a `--pcr-muted` surface with a “Control plane” badge and a gutter glyph distinct from tool items, and are indented from the agent lane. Copy states the actor plainly: “Control plane checked out task MCK-102 — no agent tool exists for this.” Each entry expands to the decision record (inputs considered, resulting state change, audit reference). Failed/denied entries render in place on `--pcr-danger-surface` with the exact typed denial (§5) — failures are part of the record, never toast-only. Restraint/no-call scenarios (`rs-*`, `er-*` restraint cases) must remain legible when the correct behavior is *absence*: after the final agent turn the transcript appends a system note “No further operations — N forbidden operations, none invoked” which deep-links the Parity tab’s forbidden list. Restraint evidence must never depend on an empty screen. PRP events stay behind a per-item “Protocol events” disclosure (same nested pattern as SDK `debugEvents`) — the semantic view is primary; raw protocol stays inspectable but secondary (progressive disclosure). Auto-follow, jump-to-latest, streaming affordances, and reduced-motion rules are inherited verbatim from the Live console map §1 — do not redesign them. ## 4. Inspector tabs (right rail) All four tabs are SDK `Tabs`; the active tab mirrors into the route (§7). ### 4.1 Context Answers “who ran, under what authority, with which tools” before any transcript reading (mental model first): - **Actor block:** actor role, task mode, fixture ID (mono), wake payload summary (reason + trigger), budget posture. - **Capability grants:** the scenario’s `requiredGrants` plus every grant the fixture actually issued, rendered as mono chips. - **Tool exposure list** — the acceptance-critical view. Three labeled sections, populated **only** from the mock core’s exposure record: 1. **Always tools (N)** — always-catalog operations visible this run; 2. **Optional tools (N)** — visible optional operations, each with the grant that unlocked it (“`decide_approval` — via `approval:decide`”); 3. **Control plane — no tool (N)** — control-plane-owned capabilities relevant to this scenario, explicitly listed so reviewers can verify absence (negative evidence must be visible, not implied). - **Traceability panel:** source anchor (file:line, mono), skill elements (file · section · lines), evidence IDs, legacy MCP mapping row if any — verbatim from the 7A contract. ### 4.2 Authorization Chronological table of every authorization record the mock core emitted: operation (mono), claims considered, decision (**Allow** / **Deny** with icon + text), reason string, redactions applied, resulting state change reference. Row expansion shows the full record JSON. Denied rows use `--pcr-danger-surface`; a count chip on the tab surfaces denials without opening it (“Authorization · 1 deny”). Selecting a row scrolls/flashes the corresponding transcript entry (uniform connectedness across panels). ### 4.3 State diff Before/after immutable snapshot diff for the displayed run, grouped by the ten entity domains in the plan: **tasks, comments, documents, interactions, approvals, artifacts, blockers, workspace, budget, run**. Each domain is a collapsible section headed by a change summary (“tasks · 1 changed”, “comments · 2 added”); unchanged domains collapse to a single muted row (“unchanged”) so change carries the visual weight (Von Restorff). Rows show entity ID (mono) + field-level changes as `field: before → after`, with added/removed/changed markers as icon + label, not color alone. The diff is computed by the runtime/test layer and delivered as data; the UI performs no diffing of its own. A “final state” toggle shows the complete after-snapshot JSON per domain behind a disclosure. ### 4.4 Parity The eval acceptance surface: - **Verdict header** repeating the banner verdict. - **Assertion list:** one row per conformance assertion — assertion class badge, human-readable expectation, Pass/Fail, and on fail an expected vs observed block (mono, side-by-side at desktop, stacked at mobile). - **Forbidden operations:** each `forbiddenSemantics` entry with “never invoked ✓” or the violating transcript reference. - **Intentional gaps:** entries marked as intentional exclusions render under their own heading with the recorded rationale — visually distinct from Fail (a gap is a decision, not a defect). - **Legacy baseline note:** the case’s skill-present/skill-absent reference note, verbatim, muted — labeled “reference baseline, not the pass condition.” ## 5. Credentials, redaction, and denial rendering - Fake mode performs zero network I/O; Codex mode talks only to the local relay; no route, storage key, or bundle string may contain a Paperclip or provider credential. `localStorage` may hold UI preferences only (inspector tab, filter collapse state) — never run artifacts, grants, or anything from a fixture. - Redacted values arrive pre-redacted from the mock core. The UI renders a redaction chip: `•••` + “redacted” label + the redaction-rule name in a tooltip. The UI must not receive-and-hide secrets; if a raw secret ever reaches the browser payload, that is a 7C/7D defect, and the explorer additionally fails closed by rendering nothing for fields flagged redacted. - A **denied** tool call renders the typed policy denial: operation, the missing claim, and the denial reason — and never any protected state. An **absent** tool simply does not appear in exposure lists; the Context tab’s control-plane section is the affordance proving absence is intentional. - The generic escape hatch (`paperclipApiRequest` successor), when a test scenario grants it, renders with a persistent `--pcr-warning-surface` “test-only escape hatch” banner on every call card (plan §4: visually flagged). ## 6. Accessibility, responsive, empty/error states Accessibility (WCAG POUR; inherits all Live console/5 baseline rules): - Landmarks: `nav` (picker), `main` (run view), `complementary` (inspector); one `h1`; group headers are `h2`/`h3` in order. - Full keyboard operation: picker listbox (§2); `F6`/documented shortcut cycles the three regions; tabs follow the WAI-ARIA tabs pattern (arrow-key activation); every disclosure is a real `button`. Visible focus ring via `--pcr-ring` everywhere. - Live regions: run progress announces via `aria-live="polite"` (“Scenario run settled — verdict pass”); denial entries announce assertively once. - Reduced motion: with `prefers-reduced-motion`, streaming reveal, banner transitions, and scroll-flash connect cues render instantly/statically (SDK rule). - Contrast from the existing token pairs only; parity/authorization statuses always icon + text. Responsive: content-driven single breakpoint at 64rem (§1). At mobile the inspector tabs become full-width stacked sections inside the Inspect segment; expected-vs-observed blocks stack; the transcript keeps every descendant intrinsically shrinkable (`min-w-0` discipline) so no horizontal scrollbar appears at 390px (SDK finding). States that must be designed, not defaulted: | State | Surface | Rendering | | --- | --- | --- | | No scenario selected | Center + inspector | Explorer intro card: corpus stats (106 cases · 16 groups), “Pick a scenario” pointer at the rail, three example deep links. No dead panels. | | Scenario selected, not run | Run view | Header + context render from the index; transcript area shows a primed empty state (“Run to produce the deterministic timeline”) with the Run button duplicated inline (Fitts). Diff/Parity tabs show “No run yet.” | | Run in progress | All | Progress in the header (indeterminate, reduced-motion safe); transcript streams; tabs show live counts; controls disable with reasons. | | Run failed (harness error) | Run view | Danger banner with the exact diagnostic + “Re-run”; partial timeline stays visible and labeled partial. Never silently reset. | | Parity fail | Banner/Parity | Fail is a first-class rendered state (danger banner + failing assertion rows) — the explorer’s job is showing it, not avoiding it. | | Denied/absent tool | Transcript/Context | Per §5. | | Missing/invalid fixture or artifact | Shell | Error card naming the artifact path and the regenerate command (`pnpm generate:capability-inventory` / 7E command). No blank screen. | | Codex relay unavailable | Mode control | Disabled option + reason; fake mode remains fully usable. | ## 7. Deterministic screenshot routes Hash routing (static hosting, no server rewrites): ``` #/ explorer home (picker + intro) #/case/ scenario selected, not run #/case/?run=fake deterministic fake run, auto-executed #/case/?run=fake&view= #/?group=&disposition=&role=&claim=&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 `.paperclip-local/evidence/capability/` 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 — SDK 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. ## 11. Revisions recorded during 7F implementation Per §9, deviations are written here rather than left silent. All of the following were found while building the explorer (TASK-16904) and are now part of the contract. 1. **Timeline entry kinds extended.** §8 listed `semantic_call`, `semantic_result`, `control_plane_action`, and `protocol_event_ref`. The agent channel also needs plain model turns and the restraint note §3.2 requires, so the artifact adds `agent_message` and `system_note`. Protocol event references ride on `semantic_result.protocolEventRefs` rather than standing as their own entry, keeping the semantic view primary. 2. **Parity is computed by the runtime, not the browser.** §4.4 and §10 forbid the UI from judging assertions, and they do: the package-local scenario runner (`src/scenarios/scenario-parity.ts`) produces the verdict from the traceability expectations and the recorded artifact. The conformance suite's own per-case result is carried through verbatim in a separately labelled "Capability conformance suite" block and is never merged into, or used to overrule, the assertion list. When no conformance report is bundled the block reads **Not run**. 3. **Actor role, task mode, and scenario claims are declared, not inferred.** §8 assigned `actorRole`/`taskMode` to the scenario index generator; it shipped without them. They are now declared in `src/scenarios/scenario-index.ts` as an explicit fixture/case profile table, alongside the catalog claims each scenario grants. A claim that cannot unlock any operation in the scenario's task mode or for its actor role is dropped from the profile, so no scenario renders a grant chip that unlocks nothing. 4. **The mobile section switcher is a `radiogroup`, not a `tablist`.** §1 asked for the Live console segmented control. The Live console console renders it only in compact mode; the explorer keeps one CSS-driven tree, so a `tablist` would put two tablists in the document and model the three landmarks as tabpanels. The three regions stay `nav`/`main`/`complementary`, and the only `tablist` on the page is the inspector's. 5. **Two token-layer overrides, scoped to the explorer.** The SDK gives every direct `span` child of a disclosure summary `min-width: 0`; a `Badge` is also a `span` and sets `white-space: nowrap`, so at 390px badges were squeezed below their own content and pushed the page into horizontal scroll. The SDK badge also applies `text-transform: capitalize`, which renders "Optional Tool" and "Not Run" against the §0 vocabulary. Both are corrected in `examples/scenario-explorer/src/explorer.css` under `.pcr7-shell`; the frozen `0.1.2` sheet is unchanged. **These are SDK defects worth fixing at the next SDK revision, not explorer quirks.** 6. **Only the case list scrolls in its own box.** The rails are otherwise in page flow. §9 evidence is captured full-page, and an inner scroll container on the rail clipped exactly what the matrix asks to see (all 16 group facets and their counts). The 106-row case list keeps a bounded height so the page stays navigable. 7. **Shot 2's route uses `parity=not_run`.** §9 specified `#/?group=ix&disposition=always_agent_tool&parity=pass`. Parity status is a property of a *run*, and the picker route runs nothing, so `parity=pass` selects zero scenarios on a cold load. The recorded route filters on `not_run`, which is the honest cold-load state and still demonstrates chips, live counts, disabled zero-count values, and Clear filters. The following revisions were recorded at the 7F UX gate (TASK-16905, 2026-08-09) after reviewing all 24 captures and a live keyboard/responsive session at both viewports. 8. **Shot 8's continuation evidence is split across surfaces.** In `ix-checkbox-01` the continuation appears as the Context tab's control-plane exposure entry (`route_wake` — "Interaction continuations are routed by the control plane, not requested again") plus the parked-note system entry — not as transcript entries. No fixture in the corpus scripts the continuation-consuming side: only the ten §9 cases have bespoke fake-agent plans, `ix-checkbox-result-01` falls back to a generic script that re-raises `request_human_input` (contradicting its own premise), and `inspect_operation_result` is catalogued but never invoked corpus-wide. Accepted for 7F because the request side and routing authority are fully evidenced; the continuation-side fixture upgrade is a filed follow-up, not a silent gap. 9. **Forbidden-operations lists are empty corpus-wide.** All 106 cases record empty `forbiddenSemantics`, so §9 rows 10–11's "never invoked ✓" rows cannot render from real fixture data. Restraint is proven by state-diff absence plus the intentional-gap rationale, and the Parity tab says so explicitly ("This case records no forbidden operations; restraint is proven by the absence of state writes in the diff"). The populated-list path (`never invoked ✓` / violating transcript reference) exists in `inspector-panels.tsx` and is unit-tested; the explanatory empty state is accepted as the §9 rendering. 10. **Keyboard deltas, accepted as interim with a filed follow-up.** The picker listbox uses selection-follows-focus (arrows select immediately — a valid listbox pattern the map did not anticipate); type-ahead and Enter-moves-focus-to-run-header are not implemented; `F6` region cycling is replaced by a "Skip to run view" skip link plus landmark navigation; denial entries do not announce assertively (only a fail verdict does). §7's `inspector-tab-` testids are absent (the frozen SDK `Tabs` exposes roles, not testids) — tests target tabs by accessible name. An unknown case ID in the route falls back silently to the home view rather than a named error card. 11. **Redaction chips render the rule name inline**, not in a tooltip: `••• redacted `. Inline beats hover-only disclosure (works on touch, visible in captures); recorded as the new §5 contract. 12. **Evidence captures expand disclosures.** The recorder opens every `main`/`aside` disclosure before capturing so argument payloads, decision records, and final-state JSON are visible in the §9 images. The live defaults are collapsed exactly as §4.3 specifies (verified in the gate's live session); the expanded captures are a deliberate evidence choice, not the default density. 13. **UX-gate keyboard follow-up.** The explorer handles `F6` for region cycling when the browser delivers that key to the page. Chromium reserves bare `F6` for browser chrome, so the documented in-page alternative is `Control+Period`; `aria-keyshortcuts` exposes both bindings. Either binding cycles picker (`nav`) → run (`main`) → inspector (`complementary`), with `Shift+F6` cycling in reverse where bare `F6` is available. The skip link remains unchanged.