Handoff: Session 104.2 — Chrome Extension Two-Embeddable Architecture
Complete test plan for the Chrome extension dual-panel portal, Agent Observatory, bridge adapter, and production fixes
Handoff: Session 104.2 — Chrome Extension Two-Embeddable Architecture
Author: Coding Agent (Claude Code / editor2) Date: 2026-02-10 Session: 104.2 For: Testing Director (Cowork / gspace1) Scope: 6-phase implementation — production fix, centralized config, bridge adapter, Agent Observatory, embed wrappers, dual-panel extension portal
What Changed (Executive Summary)
Session 104.2 delivered a 6-phase overhaul of the Chrome extension and two new embeddable components:
| Phase | What | Key Files |
|---|---|---|
| A | Fixed isDev detection (was always true in prod), switched to allowlist, created prod manifest |
content/inject.js, manifest.prod.json, scripts/build-prod.sh |
| B | Centralized all extension URLs into shared/env-config.js (single source of truth) |
shared/env-config.js, background/service-worker.js |
| C | Created vanilla JS bridge adapter matching embeddable-bridge protocol (rate limiting, heartbeat, origin validation) | shared/bridge-adapter.js, content/inject.js, sidepanel/sidepanel.js |
| D | Agent Observatory — real-time window into AI agent activity (SSE + WebSocket unified view) | agent-observatory/ (3 files), observatory-page/ (3 files), app.routes.ts |
| E | Universal embed wrappers: <gograb-ai-panel> and <gograb-observatory> for any Angular app |
gograb-ai-panel.component.ts, gograb-observatory.component.ts, index.ts |
| F | Dual-panel Chrome extension portal — tab switching (sidepanel) + mode toggle (floating panel) + cross-panel orchestration | sidepanel.html, sidepanel.js, inject.js, panel.css, service-worker.js |
Test Environment Requirements
Services That Must Be Running
| Service | Port | Health Check | Required For |
|---|---|---|---|
| gograb-space | 6200 | http://localhost:6200 |
Extension context (host app detection) |
| gograb-ai | 6400 | http://localhost:6400 |
Observatory + Studio iframe content |
| backend-api | 3000 | curl http://localhost:3000/api/health |
Auth sync, general API |
| backend-ai-api | 3001 | curl http://localhost:3001/ai-api/health |
SSE events, orchestration |
| backend-vitto-api | 3003 | curl http://localhost:3003/health |
Vitto SSE for Observatory |
Extension Setup
- Go to
chrome://extensions - Enable Developer Mode
- Load unpacked → select
apps/gograb-ai-extension/ - Open the extension side panel (click extension icon → "Open Side Panel")
Pre-Test: Verify Auth
- Log in at
http://localhost:6200/auth/loginwith test credentials - The extension should auto-sync auth from the main app tab
Test Plan
Section 1: Phase A — Production Fix Verification
Goal: Confirm
isDevis correctly detected and the extension works on both localhost and production domains.
Test 1.1: isDev on localhost
- URL: Any
localhost:*page - Expected: Console shows
[GoGrab AI Extension] Environment: DEV - Expected: FAB (floating action button) appears
- Expected: Clicking FAB loads panel from
http://localhost:5400/panel(editor2 port — note: for gspace1 testing, check that the URL includes the correct AI_APP_URL) - Verify: Open DevTools Console, filter for
[GoGrab AI Extension]
Test 1.2: isDev on production domains (if accessible)
- URL:
https://app.gograb.space - Expected: Console shows
[GoGrab AI Extension] Environment: PROD - Expected: FAB loads panel from
https://ai.gograb.space/panel - Expected: NO Chrome Private Network Access (PNA) prompt
Test 1.3: Allowlist enforcement
- URL: Navigate to
https://api.gograb.spaceorhttps://admin.gograb.space - Expected: NO FAB injected (subdomain not in allowlist)
- Verify: Console should show
[GoGrab AI Extension] Skipping injection: subdomain not in allowlist
Test 1.4: Production manifest
- File:
apps/gograb-ai-extension/manifest.prod.json - Verify: File exists, does NOT contain
localhostinhost_permissionsorcontent_scripts.matches
Section 2: Phase B — Centralized Configuration
Test 2.1: ExtensionEnv availability
- How: In extension side panel, open DevTools Console
- Run:
ExtensionEnv.isDev→ should returntrueon localhost - Run:
ExtensionEnv.AI_APP_URL→ should returnhttp://localhost:5400(or the configured AI port) - Run:
ExtensionEnv.MAIN_APP_URL→ should returnhttp://localhost:5200
Test 2.2: Service worker env caching
- How: In
chrome://extensions→ service worker → Inspect - Verify: Console shows
[GoGrab AI] Environment: { isDev: true, aiAppUrl: 'http://localhost:5400', ... } - Verify: No hardcoded
localhost:5200orai.gograb.spacestrings in service-worker.js (replaced withExtensionEnv.*)
Section 3: Phase C — Bridge Adapter
Goal: Confirm the new
ExtensionBridgeAdapterreplaces raw postMessage and speaks the full BridgeMessage protocol.
Test 3.1: Bridge initialization (floating panel)
- URL: Any localhost page
- Click: FAB to open floating panel
- Console filter:
[GoGrab AI Extension] - Expected logs (in order):
Agent panel injected (http://localhost:5400/studio)Agent panel ready (bridge connected)- No
postMessageerrors or "invalid message" warnings
Test 3.2: Bridge initialization (side panel)
- Open: Extension side panel
- Console filter:
[SidePanel] - Expected logs:
Initializing...Loading studio : http://localhost:5400/panel?embedded=true&...(token redacted)studio loadedBridge initializedAuth sent via bridge
Test 3.3: Message format verification
- How: In the gograb-ai app's DevTools (the iframe), check console for incoming messages
- Expected: Messages contain
id,timestamp,instanceId,type,payloadfields - Expected:
typeis a valid BridgeMessage type (e.g.,AI_AUTH_INIT,AI_CONTEXT_UPDATE)
Test 3.4: Heartbeat
- Wait: 30+ seconds after panel loads
- Console filter:
heartbeatorhealth - Expected: Periodic heartbeat messages, connection stays
connected - If iframe is blocked/frozen: Health should degrade to
degraded→disconnectedafter timeout
Test 3.5: Rate limiting
- How: This is hard to trigger manually. Verify by reading
bridge-adapter.jsthatcheckRateLimit()exists with:- Global: 500 messages per 10 seconds
- Auth: 10 messages per 60 seconds
- Per-type: 100 messages per 10 seconds
Section 4: Phase D — Agent Observatory
Goal: Verify the new
/observatoryroute loads and displays real-time agent activity.
Test 4.1: Observatory route (standalone)
- URL:
http://localhost:6400/observatory - Expected: Full-mode observatory page loads
- Expected: Header with "Agent Observatory" title, activity count badge, health indicator
- Expected: Sections visible: Active Agents strip, Engine Status, Active Task, Event Feed
Test 4.2: Observatory route (embedded compact)
- URL:
http://localhost:6400/observatory?embedded=true&mode=compact - Expected: Compact single-column layout
- Expected: No engine status bar (full mode only)
- Expected: No autonomy controls (full mode only)
- Expected: Scrolling event feed visible
Test 4.3: Observatory route (with auth)
- URL:
http://localhost:6400/observatory?embedded=true&mode=compact&extension=true&token=VALID_TOKEN - Expected: Same as 4.2 but with auth applied (if Observatory needs auth context)
Test 4.4: VittoMonitorService SSE connection
- Precondition:
backend-vitto-apirunning on port 3003 - How: Open Observatory, check Network tab for EventSource to
/vitto-api/engine/events - Expected: SSE connection established (if Vitto API is running)
- If Vitto API is NOT running: Observatory should show
disconnectedhealth indicator, no crash
Test 4.5: OrchestrationClientService WebSocket
- Precondition:
backend-ai-apirunning on port 3001 - How: Open Observatory, check Network tab for WebSocket to
/command-center - Expected: Socket.IO connection established
- Note: This service is a singleton — it auto-connects in constructor. The Observatory does NOT control its lifecycle.
Test 4.6: Observatory with active task
- How: Submit a task through the AI panel (or via API) while Observatory is open
- Expected: Active task card appears with 4-phase progress bar (ANALYZE → PLAN → EXECUTE → VERIFY)
- Expected: Tool calls and delegations populate in real-time
- Expected: Event feed scrolls with new events tagged as
vittoororchestration
Section 5: Phase E — Universal Embed Wrappers
Goal: Verify the
<gograb-ai-panel>and<gograb-observatory>components work when dropped into any Angular app.
Test 5.1: Component exports
- How: Check that the following imports work (build test):
import { GoGrabAiPanelComponent, GoGrabObservatoryComponent } from '@libs-ux/embeddable-bridge'; - Verify: Run
npx nx build libs-ux-embeddable-bridge— should succeed with no errors
Test 5.2: Panel component URL construction
- File:
libs/libs-ux/embeddable-bridge/src/lib/components/gograb-ai-panel.component.ts - Verify on localhost:
panelUrl()returnshttp://localhost:5400/panel?embedded=true&theme=dark&source=embed-component&userRole=user - Verify tier mapping: T1 →
developer, T2 →builder, T3 →user
Test 5.3: Observatory component URL construction
- File:
libs/libs-ux/embeddable-bridge/src/lib/components/gograb-observatory.component.ts - Verify on localhost:
observatoryUrl()returnshttp://localhost:5400/observatory?embedded=true&mode=compact&controls=true&source=embed-component
Section 6: Phase F — Dual-Panel Chrome Extension Portal
Goal: Verify the extension side panel has Studio/Observatory tabs and the floating panel has a mode toggle.
Test 6.1: Side panel tab bar
- Open: Extension side panel
- Expected: Tab bar visible at top with two buttons: "✨ Studio" (active) and "🔭 Observatory"
- Expected: Studio tab is active by default (highlighted with purple underline)
Test 6.2: Side panel tab switching — Studio → Observatory
- Click: "🔭 Observatory" tab
- Expected: Tab highlights switch (Observatory gets purple underline, Studio loses it)
- Expected: Iframe reloads with
/observatory?embedded=true&mode=compact&extension=true&... - Console:
[SidePanel] Loading observatory : http://localhost:5400/observatory?... - Expected: Observatory compact view renders inside the side panel
Test 6.3: Side panel tab switching — Observatory → Studio
- Click: "✨ Studio" tab
- Expected: Tab highlights switch back
- Expected: Iframe reloads with
/panel?embedded=true&extension=true&... - Console:
[SidePanel] Loading studio : http://localhost:5400/panel?... - Expected: AI Creation Studio loads
Test 6.4: Side panel tab persistence
- Steps:
- Switch to Observatory tab
- Close the side panel
- Reopen the side panel
- Expected: Observatory tab is still active (persisted via
chrome.storage.local)
Test 6.5: Floating panel mode toggle button
- URL: Any localhost page with the FAB
- Click: FAB to open floating panel
- Expected: Panel header bar visible at top with:
- Left: Mode toggle button showing "🔭 Observatory" (pill-shaped, purple tint)
- Right: Close button (×)
- Expected: Panel loads Studio (default mode)
Test 6.6: Floating panel mode toggle — Studio → Observatory
- Click: "🔭 Observatory" toggle button
- Expected: Button text changes to "✨ Studio"
- Expected: Iframe reloads with
/observatory?embedded=true&mode=compact&... - Console:
[GoGrab AI Extension] Switching floating panel to: observatory - Expected: Compact Observatory view renders in the floating panel
Test 6.7: Floating panel mode toggle — Observatory → Studio
- Click: "✨ Studio" toggle button
- Expected: Button text changes back to "🔭 Observatory"
- Expected: Iframe reloads with
/panel?embedded=true&...
Test 6.8: Floating panel mode persistence
- Steps:
- Toggle to Observatory mode
- Close the panel (click ×)
- Click FAB to reopen
- Expected: Panel opens in Observatory mode (persisted via
chrome.storage.local)
Test 6.9: Cross-panel orchestration routing
- How: Submit an orchestration task from Studio panel
- Service worker console: Should show
AI_ORCHESTRATE_REQUESTbeing handled - Expected: If Vitto API is running, task is submitted and status broadcast to all tabs
- Note: If Vitto is not running, the response should be
{ success: false, error: 'Orchestration not available — MCP connector offline' }
Test 6.10: Panel header doesn't overlap iframe content
- Open: Floating panel
- Expected: The 40px header bar sits above the iframe content — no overlap
- Verify: Scroll the iframe content — it should not go behind the header
Section 7: Cross-Cutting Concerns
Test 7.1: Auth flow end-to-end
- Steps:
- Log in at
http://localhost:6200/auth/login - Open extension side panel
- Open floating panel on the same page
- Log in at
- Expected: Both panels receive auth (token visible in URL params or bridge message)
- Console:
Auth sent via bridgeorAuth included in panel URL
Test 7.2: Context forwarding
- Steps:
- Open floating panel on
http://localhost:6200/space/chrome-space-1 - Navigate to a different page
- Open floating panel on
- Expected: Panel receives context update with new URL, title, hostApp detection
- Console:
AI_CONTEXT_UPDATEmessage logged
Test 7.3: Bridge adapter in both contexts
- Verify: Both sidepanel.js and inject.js use
ExtensionBridgeAdapter(not raw postMessage) - Verify: No
window.addEventListener('message', ...)in either file (all handled by bridge)
Test 7.4: Error resilience
- Test: Open extension panel when gograb-ai (port 6400/5400) is NOT running
- Expected in side panel: Error state shown ("Unable to load AI Studio")
- Expected in floating panel: Iframe shows connection error, bridge health degrades to
disconnected - Expected: No unhandled exceptions or crashes
Files Changed (Complete Reference)
Chrome Extension (apps/gograb-ai-extension/)
| File | Phase | Change Type |
|---|---|---|
content/inject.js |
A, C, D, F | Edit: isDev fix, allowlist, bridge adapter, mode toggle, panel header |
manifest.prod.json |
A | NEW: Production manifest without localhost |
scripts/build-prod.sh |
A | NEW: Store build script |
shared/env-config.js |
B | NEW: Centralized URL config (ExtensionEnv) |
shared/bridge-adapter.js |
C | NEW: Vanilla JS bridge adapter (BridgeMessage protocol) |
background/service-worker.js |
B, F | Edit: env-config, AI_ORCHESTRATE_REQUEST handler |
sidepanel/sidepanel.html |
C, F | Edit: script tags, tab bar HTML + CSS |
sidepanel/sidepanel.js |
C, F | Edit: bridge adapter, tab switching, buildFrameUrl, switchTab, loadTabContent |
styles/panel.css |
F | Edit: panel header, mode toggle button, iframe offset |
manifest.json |
C | Edit: Added bridge-adapter.js to content_scripts |
Angular Libraries
| File | Phase | Change Type |
|---|---|---|
libs/libs-ai/gograb-ai-lib/.../agent-observatory/agent-observatory.component.ts |
D | NEW: Unified observatory component (SSE + WS) |
libs/libs-ai/gograb-ai-lib/.../agent-observatory/agent-observatory.component.html |
D | NEW: Compact + full mode templates |
libs/libs-ai/gograb-ai-lib/.../agent-observatory/agent-observatory.component.scss |
D | NEW: Dark theme, dual-mode styles |
libs/libs-ai/gograb-ai-lib/.../components/index.ts |
D | Edit: Export AgentObservatoryComponent |
libs/libs-ux/embeddable-bridge/.../gograb-ai-panel.component.ts |
E | NEW: Universal AI panel embed wrapper |
libs/libs-ux/embeddable-bridge/.../gograb-observatory.component.ts |
E | NEW: Universal observatory embed wrapper |
libs/libs-ux/embeddable-bridge/src/index.ts |
E | Edit: Export new components |
Angular Apps
| File | Phase | Change Type |
|---|---|---|
apps/gograb-ai/src/app/pages/observatory/observatory-page.component.ts |
D | NEW: Embeddable page wrapper |
apps/gograb-ai/src/app/pages/observatory/observatory-page.component.html |
D | NEW: Template |
apps/gograb-ai/src/app/pages/observatory/observatory-page.component.scss |
D | NEW: Styles |
apps/gograb-ai/src/app/app.routes.ts |
D | Edit: Added /observatory route |
Build Verification
Before testing, confirm the builds succeed:
# Extension: vanilla JS — just reload in chrome://extensions after changes
# Click the refresh icon on the extension card
# Angular builds (only needed if testing Observatory route):
npx nx build libs-ux-embeddable-bridge
npx nx build gograb-ai --configuration=development
# Or just ensure the dev server is running:
# gograb-ai on port 6400 (gspace1 testing port)
Known Limitations
Observatory requires Vitto API (port 3003) — If not running, the SSE connection won't establish. The component should show "disconnected" gracefully, not crash.
OrchestrationClientService is a singleton — It auto-connects to
/command-centerWebSocket. The Observatory doesn't control its lifecycle. If the WebSocket is already connected from another component, the Observatory reuses it.Floating panel mode toggle is NOT a split view — The floating panel shows either Studio OR Observatory, never both simultaneously. Only the side panel can show them via tab switching (also one at a time).
Cross-panel orchestration depends on MCP connector being initialized in the service worker. If
globalThis.mcpConnectoris null, the orchestration request returns an error.Embed wrappers (
<gograb-ai-panel>,<gograb-observatory>) useFRONTEND_PORTS.gograbAiwhich defaults to port 5400 (editor2). For gspace1 testing, the actual port might differ — the components detect localhost and use the appropriate port from@libs-foundational/api-config.
Verification Checklist (Quick Pass)
Use this for a rapid smoke test. Detailed tests are in the sections above.
- Extension loads without errors on
chrome://extensions - FAB appears on localhost pages
- Floating panel opens and loads Studio content
- Floating panel mode toggle switches to Observatory
- Floating panel mode toggle switches back to Studio
- Side panel opens and loads Studio content
- Side panel tab switches to Observatory
- Side panel tab switches back to Studio
- Side panel tab choice persists after close/reopen
- Observatory route loads at
http://localhost:6400/observatory - No console errors related to bridge, postMessage, or origin validation
- Auth flows through to iframe in both panel types
-
npx nx build libs-ux-embeddable-bridgesucceeds
Report Template
When done testing, please create E2E-TEST-REPORT-SESSION-104.md in this folder using this structure:
---
title: "E2E Test Report - Session 104.2 - Chrome Extension Two-Embeddable Architecture"
session: 104
status: ✅ Complete / ⚠️ Issues Found
---
# E2E Test Report — Session 104.2
| # | Test | Result | Notes |
|---|------|--------|-------|
| 1 | isDev on localhost | ✅/❌ | ... |
| 2 | Side panel tab switching | ✅/❌ | ... |
| ... | ... | ... | ... |
## Issues Found
[Describe any issues with screenshots]
## Handoffs for Coding Agent
[If fixes needed, create specific handoffs]