Skip to content

[OPIK-6425] [FE] feat: add Explain bridge contract + AssistantStore#7130

Closed
awkoy wants to merge 1 commit into
mainfrom
awkoy/opik-6425-explain-bridge
Closed

[OPIK-6425] [FE] feat: add Explain bridge contract + AssistantStore#7130
awkoy wants to merge 1 commit into
mainfrom
awkoy/opik-6425-explain-bridge

Conversation

@awkoy

@awkoy awkoy commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Details

Implements the opik-frontend "Explain" bridge slice of OPIK-6425 (G1, G1b, FE-1a, FE-1b, FE-2). This lands the host-side foundation the Explain popover/cells build on; there is no user-facing UI yet (FE-3/FE-4/FE-5 follow). Frontend-only — no backend, migrations, or v1 changes.

  • Bridge contract (G1): adds the 7 explain/chat/console events (explain:run/cancel, chat:continue, console:ready, explain:chunk/done/error) + ExplainTarget to assistant-sidebar.ts, bumps BRIDGE_PROTOCOL_VERSION 1→2, and wires createHostListeners() + the console:ready/explain:* handlers in the comet AssistantSidebar. Must stay byte-identical with ollie-console/src/bridge.ts (separate repo; the mirror lands in lockstep).
  • AssistantStore (FE-1b): console lifecycle status (incl. a named waking), capabilities from console:ready, a queued emitToConsole that flushes on ready, per-explainId stream state, runExplain/cancelExplain/clearExplain with a hard concurrency cap of 3, and ownership-guarded emit registration (mirrors the window.opikBridge guard for the sidebar↔OlliePage instance switch).
  • Sidebar state migration (FE-1a): moves sidebar open/width out of PageLayout useState into the store, seeded from the same assistant-sidebar-open/-width localStorage keys the console writes (survives refresh/remount). Nav-sidebar key (sidebar-expanded) untouched.
  • VER-4 (G1b): a compile-time contract assertion (tsc --noEmit fails if any event key is missing from either map) + a runtime emitHostEvent dispatch smoke test.

Out of scope (follow-ups): FE-3 ExplainButton/Popover, FE-4 Error-cell wiring, FE-5 BI events, the G3 EXPLAIN_ENABLED flag, and the ollie-console/ollie-assist repo changes.

Change checklist

  • User facing
  • Documentation update

Issues

  • OPIK-6425

AI-WATERMARK

AI-WATERMARK: yes

  • Tools: Claude-Code
  • Model: Opus-4.8
  • Scope: All files in this PR
  • Human verification: Yes — implementation went through a multi-agent adversarial review pass; one real bug (orphaned explains leaking concurrency-cap slots on unregister) was fixed and re-verified.

Testing

Commands (from apps/opik-frontend):

  • npm run typecheck — clean
  • npx vitest run src/store/AssistantStore.test.ts src/plugins/comet/AssistantSidebar.bridge.test.ts — 23 passing
  • npx eslint on all changed files — 0 problems

Scenarios validated (unit, happy-dom):

  • Console handshake: console:ready promotes status to ready and stores capabilities.
  • emitToConsole buffers before ready and flushes in order once console:ready arrives.
  • Ownership guard: a non-owner unregister is a no-op; the owner's unregister tears down the channel/status and drops in-flight explains (cap-slot leak fix).
  • Concurrency cap: runExplain returns null at cap; a finished or cancelled explain frees a slot.
  • Stream transitions: chunk→streaming (text accumulates), done, error; an unknown/cleared explainId is ignored on chunk/done/error (cancel race).
  • VER-4: verified tsc fails when an event key is removed (then restored); emitHostEvent dispatches each new host event to subscribers with no cross-delivery.
  • FE-1a seed: the store seeds default / stored / collapsed width from localStorage.

Regression check: a full npx vitest run shows only 2 pre-existing failing files (src/lib/ls-migrations/*, a localStorage.clear env quirk) that fail identically on main. This change adds 23 passing tests and introduces no regressions.

Not run: Playwright E2E (VER-9) and cross-repo INT-1 — they depend on the ollie-console mirror + UI (FE-3+), which are out of this PR's scope.

Documentation

N/A — no user-facing docs change (internal host bridge + zustand store only).

🤖 Generated with Claude Code

Implements the opik-frontend "Explain" bridge slice (G1, G1b, FE-1a,
FE-1b, FE-2). Host-side foundation only; no user-facing UI yet.

- Bridge contract: 7 explain/chat/console events + ExplainTarget in
  assistant-sidebar.ts; bump BRIDGE_PROTOCOL_VERSION 1->2; wire
  createHostListeners() + console:ready/explain:* handlers in the comet
  AssistantSidebar. Must stay byte-identical with ollie-console.
- AssistantStore (zustand): console lifecycle status incl. 'waking',
  capabilities from console:ready, queued emitToConsole (flush on ready),
  explain state keyed by explainId, runExplain/cancelExplain/clearExplain
  with concurrency cap 3, ownership-guarded emit registration.
- FE-1a: migrate sidebar open/width from PageLayout useState into the
  store, seeded from the same localStorage keys the console writes.
- VER-4: compile-time contract assertion (tsc fails on a missing event)
  + runtime emitHostEvent dispatch smoke test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@awkoy
awkoy requested a review from a team as a code owner June 16, 2026 13:07
@awkoy awkoy closed this Jun 16, 2026
Comment on lines +201 to +205
clearExplain: (explainId) => {
set((s) => {
if (!s.explains[explainId]) return s;
const next = { ...s.explains };
delete next[explainId];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clearExplain duplicates the state-removal block in cancelExplain, should we extract it into a shared helper or call it from cancelExplain after emitToConsole?

Severity

Want Baz to fix this for you? Activate Fixer

Comment on lines +211 to +213
applyConsoleReady: ({ capabilities }) => {
const { consoleEmit, emitQueue } = get();
set({ status: "ready", capabilities, emitQueue: [] });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyConsoleReady should gate console:ready on bridgeVersion matching BRIDGE_PROTOCOL_VERSION, otherwise a mismatched console can still become ready and receive queued explain:*/chat:continue events.

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/store/AssistantStore.ts around lines 211-220, in the
applyConsoleReady logic, gate the transition to status "ready" and the flushing of
emitQueue on the incoming console:ready payload’s bridgeVersion matching the expected
bridge protocol (BRIDGE_PROTOCOL_VERSION). Refactor applyConsoleReady to first read
bridgeVersion from the payload, compare it to the frontend’s expected version (or an
allowed range), and only then set status/capabilities and flush the queue; otherwise set
status to "error" or "unavailable" and clear or retain emitQueue so mismatched consoles
don’t receive explain:run/chat:continue events. Update imports/types as needed so
BRIDGE_PROTOCOL_VERSION is available in this store and bridgeVersion is correctly typed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant