32 KiB
Scenario chat Interaction Map — Mobile Chat with Visible Mock Activity
Status: approved UX contract, implemented in TASK-16916 (2026-08-09).
Deviations taken during implementation are recorded in §11a; the §11 matrix is
kept as an acceptance contract; the recorded screenshot campaign is deferred.
Owner: UXDesigner. Implementer: 7I implementation (TASK-16916), consuming the
existing 7C mock adapter, 7D semantic catalog, and 7F explorer components.
Companion records: Capability interaction map
(the 7F contract — everything not overridden here is inherited verbatim),
Live console interaction map (chat console
baseline), SDK component decisions.
(annotated desktop + mobile renders in .paperclip-local/evidence/capability/,
scenario-chat-* slugs).
Scenario chat adds a Codex-style interactive chat over the same package-local
Capability runner and mock ControlPlanePort. The board opens a scenario, chats
with the agent, and — this is the point of the phase — sees the mock
Paperclip activity for every turn instead of having it hidden behind chat.
The 7F explorer remains unchanged as the corpus-review surface; the chat is a
sibling surface in the same app shell, one React tree, sharing the picker,
transcript grammar, inspector components, and token layer.
The 7F authority rule is binding here too: the chat UI renders records the runtime produced. It computes no exposure, evaluates no claim, applies no redaction, holds no credential, and never re-times or reorders the artifact stream.
0. Naming, vocabulary, and the turn model
- Surface name in the UI: Scenario chat (header: “Capability scenario chat — mock control plane”).
- All 7F §0 vocabulary carries over exactly: task in copy; machine values
(case IDs,
MCK-102, grants, JSON) verbatim in--pcr-font-mono; disposition labels Control plane / Always tool / Optional tool; parity statuses Pass / Fail / Intentional gap / Not run always icon + text. - A turn is one user message and everything the runtime records until the agent settles (model output, semantic calls/results, authorization decisions, control-plane actions, state changes, reconciliation events, and the turn parity verdict). Turns are numbered from 1 in artifact order.
- Session start is turn 0: the control-plane-owned seeding record (wake payload, checkout, initial tool exposure) that exists before any user input. It renders with full control-plane styling — the demo must show that work happened with no agent tool before the first message.
1. Shell and information architecture
Same Vite entry, same app shell as 7F (examples/scenario-explorer/), new
routes under #/chat/… (§9). No second app, no second token sheet.
Desktop (≥ 64rem), three regions, one React tree:
- Left rail (
--pcr-rail-width, 17rem): the existing 7F scenario picker, reused as-is (search, facets, grouped case list). Selecting a case in chat context seeds a chat session for that scenario instead of a fake run. - Center column (fluid, max
--pcr-center-width, 48rem): the chat — session header, conversation, and composer. Primary surface, strongest hierarchy. The composer is pinned to the bottom of the column. - Right rail (26rem, proposed token
--pcr-activity-width): the Activity stream — the ordered per-turn evidence (§4). Open by default on desktop; it carries the acceptance evidence, so it is never a collapsed-by-default drawer on desktop.
Mobile (< 64rem): single column behind the established segmented control
pattern (a radiogroup, per 7F revision 4), with exactly two segments:
Chat · Activity. The segmented control is sticky at the top; the
composer is sticky at the bottom of the Chat segment (thumb zone). The
Activity segment contains the complete §4 stream as stacked cards — mobile
gets all the evidence, never a summary-only view. A count chip on the
Activity segment surfaces attention states without opening it
(“Activity · 1 deny”).
Density: diagnostic-console dense. All values from --pcr-* tokens only
(DESIGN.md principles 2–4); the two 7F token-layer overrides (badge
min-width / text-transform, 7F revision 5) apply to the chat surface too.
2. Session header, scenario selection, reset and replay
The session header replaces the 7F run header on chat routes:
- Row 1: case ID (mono) + title, group badge, disposition badge.
- Row 2 — session status chip, exactly one of:
- Live · Codex (bounded) (accent surface) — a real model is connected through the server-side relay;
- Replay · deterministic (muted surface) — the scripted session is playing or played;
- Idle (muted) — seeded, no turns yet.
- Row 3 — controls: mode segmented control (
Replay (scripted)default /Codex (bounded)when the relay is reachable — same availability and disabled-reason rules as 7F §3.1), Reset session, and Replay (re-runs the recorded session deterministically). Reset is a destructive affordance: it clears the session back to the seeded turn-0 state and asks for confirmation via the SDKDialog(“Reset this session? The transcript and mock state return to the seeded scenario.” — Forgiveness). Replay needs no confirmation; it reproduces the identical timeline (7F determinism contract). - Parity-so-far chip: “Parity · 2 turns pass” (success) or “Parity · turn 2 fail” (danger), deep-linking the newest parity entry in the Activity stream.
Scenario switching mid-session prompts the same reset confirmation (the mock core is seeded per scenario; two scenarios never share mutable state).
3. Chat transcript
The conversation composes the frozen SDK chat kit: conversation,
message, tool-item, reasoning-item, Composer. Three actors render
with three distinct visual grammars (Similarity + Common Region — a stranger
must attribute any row to its actor in two seconds):
- User turns — right-aligned message bubbles on
--pcr-accent-surface. - Model output — left-aligned message cards on
--pcr-cardwith the standard streaming affordances (token reveal, jump-to-latest, auto-follow inherited verbatim from Live console §1; reduced-motion renders instantly). Reasoning summaries, when the driver emits them, usereasoning-itembehind its existing disclosure. - Agent tool work — semantic calls render inline in the conversation as
collapsed
tool-itemcards on--pcr-surface-raised: operation ID (mono) + disposition badge + one-phrase outcome. Expansion shows the 7D descriptor fields exactly as in 7F §3.2 (arguments, result, claims, idempotency, redaction chips). Denied calls render in place on--pcr-danger-surfacewith the typed denial (operation, missing claim, reason) — never toast-only, never any protected state. - Control-plane work — full-width strips on
--pcr-mutedwith the “Control plane” badge and the 7F gutter glyph, copy naming the actor plainly (“Control plane registered the interaction and scheduled the continuation wake — no agent tool exists for this.”). Turn 0 seeding uses this grammar.
Turn activity strip (new, binding): after each completed turn the
conversation appends a full-width summary strip for that turn:
Turn 2 activity · 3 tools · 1 denied · 2 control plane · diff 2 domains · parity ✓, rendered as a single button. Counts are icon + text, never color
alone. Activation opens that turn’s group in the Activity stream (desktop:
scroll + flash the group, same connect cue as 7F authorization rows;
mobile: switch to the Activity segment anchored at the group, with a
“Back to chat” affordance that restores scroll position). The strip is the
guaranteed per-turn bridge — evidence is reachable from the turn itself,
not only by scanning the rail (Recognition over Recall).
The composer is the SDK Composer with its existing state machine —
idle / submitting / active-turn (Steer) / interrupting / disconnected / terminal — and keyboard contract (Enter sends, Shift+Enter newline, Escape
moves focus to Stop during a turn). In Replay mode the composer renders
disconnected-style with the reason “Scripted replay — switch to Codex
(bounded) to chat live” and the Replay control adjacent; free-text input is
a live-driver capability, not a replay capability.
4. Activity stream (per-turn evidence)
Source of truth: the ordered run artifact timeline plus per-turn
records (§10 data contract). The stream is grouped by turn; groups render
newest-last and auto-follow the conversation. Each turn group is a
collapsible section headed by Turn N + the same counts as the turn strip;
the newest turn group is expanded by default, earlier groups collapse to
their header (progressive disclosure; Miller — never all turns fully open).
Inside a group, ordered sections, each rendered only when non-empty, always
in this order:
- Exposed tools — the turn’s exposure record: Always (N) / Optional (N,
each with its unlocking grant) / Control plane — no tool (N), reusing the
7F
ExposureList. Collapsed to counts by default; the acceptance-critical full list is one disclosure away. - Calls and results — one row per semantic call: operation (mono), disposition badge, outcome; expansion identical to the inline tool card. Rows and their inline conversation twins highlight each other on selection (uniform connectedness).
- Authorization — the turn’s slice of the 7F
AuthorizationTable: decision (Allow/Deny, icon + text), claims considered, reason, redactions; denied rows on--pcr-danger-surface. - Control plane — the turn’s control-plane actions with their decision records (inputs considered, resulting state change, audit reference).
- State diff — the 7F
StateDiffscoped to the turn: per-domain change summaries, unchanged domains as one muted row,field: before → afterin mono. Wake/reconciliation events (continuation scheduling, blocker resolution, terminal reconciliation) render here as rows in their domains, badged “Control plane”. - Parity — the turn verdict row (icon + text + assertion counts),
expanding to the 7F
ParityPanelassertion rows for that turn. The session-level verdict lives in the session footer group.
The stream ends with a Session group: cumulative diff against the seed, session parity verdict, and the restraint note when the correct behavior was absence (“No further operations — N forbidden operations, none invoked”, 7F §3.2 rule carried over).
A filter chip row at the top of the stream (All · Tools · Authorization · Control plane · Diffs · Parity) narrows sections across all turns;
filtering never deletes groups, it collapses non-matching sections (the
turn skeleton stays scannable). Filter state is view state, mirrored to the
route (§9), never protocol state.
5. Actor distinction (binding summary)
| Actor | Surface | Marker |
|---|---|---|
| User | --pcr-accent-surface bubble, right-aligned |
— |
| Model output | --pcr-card message card, left-aligned |
streaming caret |
| Agent tool work | --pcr-surface-raised collapsed card, conversation-indented |
mono operation + disposition badge |
| Control plane | --pcr-muted full-width strip |
“Control plane” badge + gutter glyph |
| Denied (any) | --pcr-danger-surface in place |
Deny icon + text |
No view — chat, activity, expanded detail — may render a fact without its actor attribution. If a record ever lacks attribution, that is a 7C artifact defect to fix upstream, not a UI guess.
6. Credentials, redaction, denial
7F §5 applies verbatim (including revision 11: redaction chips render
••• redacted <rule> inline, no tooltip-only disclosure). Additionally for
chat: the composer submits user text to the package-local runner only; in
Codex mode the provider credential stays server-side behind the same relay
boundary proven in SDK, and no user-typed text is ever written to
localStorage (a page reload may drop an unsent draft; that is the accepted
trade against persisting conversation content).
7. States that must be designed, not defaulted
| State | Surface | Rendering |
|---|---|---|
| No scenario selected | Chat + activity | Intro card (corpus stats + “Pick a scenario to start chatting” + three example deep links); activity rail shows the 7F corpus summary. No dead panels. |
| Seeded, no turns | Chat | Turn-0 control-plane strips render immediately (checkout, exposure); composer focused and ready in Codex mode; primed hint in Replay mode (“Replay the scripted session, or switch to Codex (bounded)”). |
| Turn streaming | All | Model card streams; composer in active-turn (Steer + Stop); the pending turn’s activity group shows a live-updating header (“Turn 3 · running”); controls that would corrupt the run (Reset, Replay, mode switch, scenario switch) disable with reasons. |
| Turn failed (harness/driver error) | Chat | Danger strip in place with the exact diagnostic and a “Retry turn” affordance; the partial activity group stays visible and is labeled partial. Never silently reset. |
| Denied operation | Chat + activity | §3/§4 danger rendering; the Activity segment chip counts denials. |
| Relay unavailable | Session header + composer | Codex option disabled with reason; Replay mode fully usable; composer disabled-reason names the relay, not a generic error. |
| Session terminal | Composer | terminal state with its existing hint (“This session is closed. Replay it or reset the session state.”). |
| Reset confirm | Dialog | §2; focus returns to the composer after either outcome. |
| Long JSON / long code / long unbroken strings | Everywhere | overflow-wrap: anywhere on prose and key-value values; pre blocks may scroll internally (x) with a visible affordance, but the page never scrolls horizontally at any viewport ≥ 320px — the 7F min-w-0 discipline is a hard gate at 390×844. |
| Unknown case ID in route | Chat | The 7F named error card (“No scenario named …”) — not a silent home fallback (closes 7F revision 10’s accepted gap for chat routes). |
8. Touch, keyboard, focus, accessibility
- Touch targets ≥
--pcr-touch-target(2.75rem) for segments, turn strips, group headers, filter chips, composer buttons. - Landmarks:
nav(picker),main(chat),complementary(activity); oneh1;F6/Control+Periodregion cycling and the skip link carry over from 7F (revision 13) with the activity rail as the third region. - Composer keyboard contract per §3; the turn strip and group headers are
real
buttons; the mobile segment switch preserves and restores scroll position per segment. - Focus order on a completed turn: model card → inline tool cards → turn strip → composer. After “Back to chat”, focus returns to the originating turn strip.
- Live regions: turn settle announces politely (“Turn 2 settled — 1 denied operation, parity pass”); a denial announces assertively once, when it lands (upgrading 7F revision 10’s interim).
- Streaming, flash cues, and auto-follow honor
prefers-reduced-motion(instant/static variants, SDK rule). Contrast from existing token pairs; every status icon + text.
9. Routes and determinism
Hash routing, extending the 7F scheme:
#/chat chat home (picker + intro)
#/chat/<case-id> live session, seeded (turn 0)
#/chat/<case-id>?replay=fake deterministic scripted session, auto-played
#/chat/<case-id>?replay=fake&view=<chat|activity>&turn=<n>
#/chat/<case-id>?replay=fake&filter=<tools|authorization|control|diff|parity>
replay=fakeauto-plays the scripted session to completion; when the conversation and all activity groups are settled the shell setsdata-chat-state="settled"(Playwright wait target;pendinguntil then). Fixture time only, no wall-clock values, pixel-identical across loads at the same viewport — the 7F determinism contract applies unchanged.view=activityselects the Activity segment on mobile and scrolls the rail on desktop;turn=<n>anchors that turn’s group and expands it.- Live chat routes (
#/chat/<case-id>withoutreplay) are the primary board experience and are exempt from pixel determinism; QA acceptance of live behavior uses the scripted routes plus one real Codex turn. - Stable
data-testids (minimum new set):chat-shell,session-header,session-status,chat-conversation,chat-turn-<n>,turn-activity-strip-<n>,activity-stream,activity-turn-<n>,activity-section-<turn>-<name>,activity-filter-<name>,composer,reset-session,replay-session,segment-chat,segment-activity. 7F testids keep their meanings.
10. Package-local implementation guidance (7I)
- Reuse first. The chat column composes the SDK chat kit
(
conversation,message,tool-item,reasoning-item,Composer,Dialog,Badge,Banner) —RunnerConsoleAppitself is not the shell (same 7F ruling: compose parts, don’t adopt the console’s session-timeline IA). The activity stream composes the existing explorer components:ExposureList,AuthorizationTable,StateDiff,ParityPanel,ChannelEntry,RedactionChip. - New explorer-local components (one job each):
SessionHeader,TurnStrip(the per-turn summary button),ActivityStream(turn grouping + filter chips),ActivityTurnGroup. Tokens only; the two candidate new tokens (--pcr-activity-width: 26rem,--pcr-touch-targetif not yet minted) are proposed as token additions in the 7I PR, never inlined. - Data contract extension (owned by the runtime, not the UI): timeline
entries gain a
turnnumber (0 = session seed); the artifact gainsturns[]with per-turnexposure,authorizationRecords,diff,reconciliationEvents, andparity, each shaped exactly like their session-level 7F counterparts. Auser_messagetimeline kind joins the existing five kinds. The session-level records remain and feed the Session group. The UI must not derive turn boundaries, per-turn diffs, or per-turn verdicts itself — if the runtime can’t supply a per-turn record, extend the runtime. - Driver seam: Replay mode consumes recorded fixtures; Codex mode uses the bounded driver through the SDK relay. The chat UI is driver-agnostic — it renders whatever the artifact stream says happened.
- Testing hooks 7I must ship: component tests for
TurnStrip,ActivityStreamgrouping/filtering, and per-turn diff/parity rendering; keyboard tests (composer contract, strip→group→back-to-chat focus round-trip, region cycling with the new rail); the §9 route determinism test; a 390×844 no-horizontal-scroll assertion on a long-JSON fixture; reduced-motion test; screenshot capture script for §11.
11. Screenshot acceptance matrix
Evidence lands in .paperclip-local/evidence/capability/ as
scenario-chat-<slug>-<viewport>.png at 1440×900 and 390×844, captured
from §9 routes after data-chat-state="settled". Mobile captures must show
the correct active segment and no horizontal scrollbar.
| # | Slug | Route | Expected visible evidence |
|---|---|---|---|
| 1 | chat-home |
#/chat |
Picker + chat intro; no dead panels. |
| 2 | chat-session |
#/chat/ix-checkbox-01?replay=fake |
Turn-0 control-plane strips; user/model/tool/control-plane grammars all distinguishable; turn activity strips with counts; composer in its mode-correct state; desktop shows the activity rail with the newest turn expanded. |
| 3 | chat-denied |
#/chat/ap-mcp-gate-01?replay=fake&view=activity&turn=2 |
Denied call on danger surface in both conversation and authorization section; missing claim + reason; Activity segment chip counting the deny (mobile). |
| 4 | chat-activity-diff |
#/chat/ix-checkbox-01?replay=fake&view=activity&turn=3 |
Turn group with exposure counts, calls, authorization, control-plane rows, per-turn state diff (changed vs unchanged domains), reconciliation/wake row, turn parity verdict. |
| 5 | chat-streaming |
#/chat/ap-mcp-gate-01?replay=fake&stage=streaming |
Streaming model card, composer active-turn (Steer/Stop), pending activity group header, disabled session controls with reasons. Exempt from pixel determinism; capture once per viewport. |
The scripted fixtures for ix-checkbox-01 and ap-mcp-gate-01 chat
sessions are new 7I fixtures (the 7F fake-agent plans are single-shot, not
conversational); they must script at least two user turns each so turn
grouping is actually exercised.
Acceptance procedure: UXDesigner reviews all ten images against this matrix before 7I implementation is accepted; deviations are fixed or recorded as revisions in this document — silent divergence fails the gate (7F §9 rule).
11a. Revisions recorded during 7I implementation (TASK-16916)
Silent divergence fails the §11 gate, so every deviation the implementation made from §1–§11 is recorded here rather than left for a reviewer to discover.
- Mode names:
Scripted (deterministic)/Codex (bounded), and the composer stays usable in scripted mode. §3 said replay mode renders the composerdisconnectedbecause "free-text input is a live-driver capability". With the provider relay unavailable in the package-local demo (the same 7F §3.1 condition), that rule would have made the phase's own acceptance — "a user can complete successful and denied mock Paperclip interactions from chat" — unreachable. Scripted mode therefore accepts a prompt and drives the next recorded turn against the live mock core, and the composer note says exactly that, with the number of scripted turns left. The board is never told a model wrote the reply. replay=fakeplays once per session, not per route. An explicit Reset session on areplay=fakeroute returns to the seeded turn 0 and stays there; the route's auto-play applies to the first session only. Otherwise reset would be undone in the same frame by the route replaying, which is the opposite of what §2 promises.stage=streamingadded to §9. §11 shot 5 said "live route, staged". Staging by hand is a race, so the route parameter holds the last scripted turn mid-reveal and the shell reportsdata-chat-state="streaming"— a stable, linkable wait target.pendingcovers the mid-reveal transition.data-chat-statelives on the shell, not only onchat-shell. On the mobile Activity segment the chat column is hidden, so a wait target that only existed inside it was unreachable exactly where the evidence is. Both elements now carry it.- Tool cards reuse the 7F transcript disclosure rather than the SDK
tool-item.ToolItemrequires a reducerSessionItemSnapshot, which a semantic call is not. The 7F rendering already follows the samepcr-disclosuregrammarToolItemitself uses, and reusing it keeps one description of a semantic call across both surfaces.Message,Composer,Dialog,Badge, andBannerare used as specified. - Turn 0 reports
Pass, not a blank verdict. The seed makes exactly one checkable claim — control-plane work with no agent tool — and reports a verdict over that claim alone. Turns that did no agent work and made no control-plane claim reportNot run; no turn ever reports a verdict for work nobody did. - A third authored turn for
ix-checkbox-01. §11 shot 4 asks for a wake/reconciliation row, but the mock core schedules the continuation wake when an interaction is resolved, not when it is requested. Turn 3 is the board answering its own checkbox — a control-plane-owned action with no agent tool — which is a better demonstration of the phase's point than a fabricated wake would have been. Shot 4's route is thereforeturn=3. - Filtering the picker no longer clears the selected case on the chat surface. Narrowing the rail must not end a live session; the explorer keeps its existing behaviour.
- Evidence captures neutralise the sticky composer. A full-page capture stretches the viewport to the content height, which leaves a sticky element floating mid-document. Pinning is proven by a browser test that scrolls the transcript and asserts the composer is still on screen.
11b. Acceptance review rulings (TASK-16920, UXDesigner, 2026-08-09)
Review method: all ten §11 captures inspected at full resolution, plus a live
pass against demo:scenarios at 1440×900 and 390×844 (scripted composer driven
interactively, streaming route inspected in-DOM). Shots 1–4 are accepted.
Shot 5 is rejected pending revisions 12–14 below; only shot 5 needs
re-capture.
Rulings on §11a: revisions 1, 2, 4, 5, 6, 7, 8, and 9 are accepted as recorded. Notes where the ruling needed evidence beyond the text:
- 1 (composer usable in scripted mode) — accepted. Verified live: a
free-text prompt drives the next recorded turn; the status chip, mode
segment, and the persistent composer note carry the attribution. The
exhausted state is genuinely designed: with zero scripted turns left, a
send runs a real
get_task_contextagainst the mock core and the reply says the script is finished and how to replay — no silent drop, no faked answer (Forgiveness; visibility of system status). - 3 (
stage=streamingroute) — the route and wait-target approach are accepted; hand-staged captures were a race and a deterministic stage is the right fix. The current staging depth fails §7 and the shot-5 matrix row — see revisions 12–13. - 7 (
turn=3) — accepted. The mock core wakes on interaction resolution, not request; a board-owned resolution producing the realwake.scheduledrow demonstrates the phase's point without fabricating a wake. Shot 4's evidence confirms the reconciliation row renders in the turn diff, badged Control plane.
Reviewer-recorded revisions (10–11 accepted as built, 12–14 are the fixes shot 5 is gated on):
- Mobile keeps three segments: Scenarios · Chat · Activity. §1 said "exactly two", which was written for the session surface and forgot that mobile has no other path to the picker; with two segments a phone user could never switch scenarios. The 7F segmented-control precedent (7F revision 4) already includes the picker segment. §1 is amended; the Activity count chip behaviour is unchanged. Accepted as built.
- Chat-home activity rail renders a purpose hint, not the 7F corpus summary. §7 asked for the corpus summary in the rail; the intro card already carries the corpus stats, so repeating them one column away is redundancy, not information (Occam). The hint card instead sets the rail's mental model — what will appear there once a session starts — so the panel is neither dead nor duplicate. §7 is amended. Accepted as built.
- Fix required — staged streaming must hold the activity stream too.
At
stage=streamingthe transcript is held mid-turn and the chat shows "Turn 2 · running", but the activity rail renders the held turn fully settled: calls the transcript has not shown yet, the state diff, and a parity verdict — under a header promising evidence appears "as the conversation happens". A surface whose whole premise is honest evidence must not show a verdict for a turn it says is still running (§4 source of truth; §7 streaming row: "the pending turn's activity group shows a live-updating header"). Fix: scope theActivityStreamto the same reveal ceiling the transcript uses (chat-surface.tsxholds the transcript atholdAtbut passes the full artifact to the rail); the held turn's group renders the running header and only the evidence revealed so far — no diff, no parity, no counts that include unrevealed calls. - Fix required — the staged hold must leave a model card visibly
mid-reveal. The §11 shot-5 row's first expected item is "streaming
model card"; the current hold point (
firstSequence + 2) lands on tool entries forap-mcp-gate-01, so the only caret on screen is the pending-turn strip's. Choose the hold so a model-output entry is partially revealed (caret + partial text on a--pcr-cardmessage card), or stage against a turn whose early entries include model output. - Fix required — disabled session controls need visible reasons.
During a running turn, Reset/Replay disable with the reason only in
title— invisible in evidence, unreachable on touch, unannounced by screen readers (§7 says "disable with reasons"; same rule that banned tooltip-only redaction disclosure in 7F revision 11). Fix: one muted line adjacent to the controls (e.g. "Controls return when the turn settles."), wired viaaria-describedby; keep thetitleif you like, it just can't be the only channel.
Re-acceptance: re-run deferred recorded-evidence campaign and hand back only the two
chat-streaming captures for review; shots 1–4 stand.
11c. Re-acceptance ruling (TASK-16920 closing, UXDesigner, 2026-08-09)
Shot 5 (chat-streaming, both viewports, re-captured in c8422d1aaa) is
accepted. Revisions 12–14 verified against the new captures and the
code:
- 12 fixed. The activity rail now consumes the same reveal ceiling as
the transcript (
ChatActivityStreamtakes the conversation's inclusive timeline ceiling); the held Turn 2 group renders the running header with only the revealedrequest_approvalcall, its denial, and the one authorization record — no diff, no parity verdict, no settled counts. - 13 fixed. The hold lands on a model card mid-reveal: partial message
text with the caret on a
--pcr-cardmessage card, in addition to the pending-turn strip. - 14 fixed. "Controls return when the turn settles." renders as a
visible muted line adjacent to the controls, wired via
aria-describedbyon all four mid-turn-disabled controls;titleretained as a secondary channel.
Reproducibility spot-check in the review run: deferred recorded-evidence campaign re-run
produced byte-identical chat-streaming PNGs at both viewports.
- Post-capture copy change (demo-hardening commits
0566a9f3ca,362754a633) — accepted, evidence refreshed. After shots 1–4 were recorded, the composer helper gained a synthetic-demo disclaimer ("Synthetic demo only. Session state is memory-only and is cleared on reset, expiry, or restart; do not enter confidential or regulated data.") and the mock-core wording changed to "isolated" / "package-local". Both are improvements — the disclaimer is an honest trust signal consistent with this surface's premise, and the wording is more accurate — but they made the committed shot 1–4 captures stale (divergence would otherwise be silent, failing §11's own rule). The five affected PNGs (chat-homedesktop,chat-sessionboth,chat-denieddesktop,chat-activity-diffdesktop) were re-recorded at review time; the disclaimer verified to wrap cleanly at 390×844 with no horizontal scroll.
The §11 matrix is fully accepted. The gate is closed.
12. Out of scope / rejected
- All 7F §10 rejections stand (no Tailwind/Radix, no dark mode, no markdown renderer, no virtualization, no parity re-judging, no live-Paperclip affordances).
- No bottom-sheet drawer on mobile. The segmented Chat · Activity pattern is proven, keeps one React tree, and route-mirrors; a drawer adds a new component and an un-linkable state for no evidence gain. Rejected after comparison, not by omission.
- No hiding of activity behind a “developer mode”. Visible mock activity is the phase’s purpose; there is no toggle that suppresses the activity stream or turn strips.
- No per-message edit/regenerate affordances. The mock core’s determinism story is session-scoped (reset/replay), not message-scoped.
- Multi-session tabs, session persistence across reloads, and export are out of scope for the demo.