---
name: fluent-ui-record
description: "Record annotated UI walkthroughs of Fluent Commerce apps. Captures before/after screenshots with DOM annotations, Chrome DevTools screencast recording, manifest diffs, and generates self-contained HTML evidence reports. Triggers on \"record UI\", \"record walkthrough\", \"capture UI changes\", \"document UI\", \"UI recording\", \"before after\", \"evidence report\", \"annotated recording\"."
user-invocable: true
read-only: false
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
argument-hint: "[--app oms|store|servicepoint] [--feature <slug>] [--steps <description>] [--baseline] [--no-video] [--no-report]"
cursor-tier: manual
---

# Annotated UI Recording for Fluent Commerce

Record annotated walkthroughs of Fluent Commerce UX apps. Captures before/after evidence of UI changes with DOM-injected step labels, Chrome DevTools screencast, manifest JSON diffs, and generates a self-contained HTML evidence report.

**Token optimization:** This skill uses a shared helper library (`BROWSER_OPTIMIZATIONS.md` in the `fluent-ui-test` skill directory) that reduces browser interaction token costs by ~90%. Annotations, highlights, and timeline tracking use `__fcTest` one-liners instead of inline JS blocks. `pageState()` + `diffState()` handle quick before/after structural comparisons (~300 tokens). `pageSummary()` is used for detailed page checks; `take_snapshot` is reserved for precise DOM delta computation and failure debugging.

## Pre-flight: Feedback Check

Before starting, check for past learnings from this skill:

1. Check if `accounts/<PROFILE>/feedback/fluent-ui-record.jsonl` exists
2. If yes, read last 20 records
3. Filter to: FAILURE or USER_CORRECTED outcomes, confidence = CONFIRMED or PROMOTED
4. Extract top 3 relevant learnings — use as internal guidance during execution
5. Do NOT show raw feedback to the user — silently adjust approach based on past issues

## Ownership Boundary

This skill owns orchestrated multi-step UI recording with evidence generation.

Other skills own:
- Single-page UI verification: `/fluent-ui-test`
- Offline manifest validation: `/fluent-mystique-validate`
- Manifest construction: `/fluent-mystique-builder`
- API-level E2E testing: `/fluent-e2e-test`

**Key distinction from `/fluent-ui-test`:** This skill produces a durable HTML evidence report with before/after comparisons. `/fluent-ui-test` does single-page pass/fail verification.

## Prerequisites

### Required
1. **Chrome DevTools MCP configured** with screencast recording enabled in `.mcp.json`:
   ```json
   {
     "chrome-devtools": {
       "command": "npx",
       "args": [
         "-y", "chrome-devtools-mcp@latest",
         "--save-video"
       ],
       "env": {
         "FLUENT_UI_BASE_URL": "https://<account>.<env>.apps.engineering.fluentcommerce.com"
       }
     }
   }
   ```
2. **Chrome DevTools MCP connected** — `navigate_page`, `take_snapshot`, `take_screenshot`, `evaluate_script` tools must be available.
3. **Valid Fluent credentials** — resolved automatically via profile-first strategy (see Credential Resolution below). No manual env vars needed if `FLUENT_PROFILE` is set.

### Credential Resolution

Resolve credentials using this priority chain. The **default path** (Priority 1) reads directly from the profile that's already configured in `.mcp.json` — zero extra setup needed. Override sources exist for when you need a different user or want to skip the login form.

| Priority | Source | How to read |
|----------|--------|-------------|
| 1 (default) | **CLI profile from `.mcp.json`** | Read `FLUENT_PROFILE` from any MCP server's env in `.mcp.json` → resolve username + password from profile files. This is the normal path — no extra config. |
| 2 (override) | Explicit creds in `.mcp.json` `chrome-devtools.env` | `FLUENT_UI_USERNAME` + `FLUENT_UI_PASSWORD` — overrides profile user for browser testing |
| 3 (role file) | `.fluent-ui-credentials.json` in workspace root | Role-based entries (`--role admin`, `--role store`). For testing same page as different users. |
| 4 (token) | `FLUENT_UI_TOKEN` in `.mcp.json` or shell env | Skip form login entirely — inject token via `evaluate_script` |
| 5 (env vars) | Shell env `FLUENT_USERNAME` + `FLUENT_PASSWORD` | Legacy fallback |
| 6 (fallback) | Ask user | Prompt for credentials |

**Resolution steps:**
1. Read `.mcp.json` → find `FLUENT_PROFILE` from any MCP server's `env` (typically `fluent-mcp-extn` or `fluent-mcp`). If found:
   a. Read `~/.fluentcommerce/<FLUENT_PROFILE>/profile.json` → extract `"user"` field (default username)
   b. If `FLUENT_PROFILE_RETAILER` is also set, check `~/.fluentcommerce/<FLUENT_PROFILE>/retailer.<ref>.json` → if it has a `"user"` field, use that instead
   c. Read `~/.fluentcommerce/<FLUENT_PROFILE>/user.<resolved-username>.json` → extract `"username"` and `"password"`
   d. If found → use these credentials. Done.
2. Check `.mcp.json` for `FLUENT_UI_USERNAME` + `FLUENT_UI_PASSWORD` in `chrome-devtools.env`. If both set → use them (overrides profile user).
3. Check for `.fluent-ui-credentials.json` in workspace root. If exists → read it (see Role File in `/fluent-ui-test`).
4. Check `.mcp.json` for `FLUENT_UI_TOKEN` in `chrome-devtools.env` (or shell env). If set → use Token Injection (skip form login entirely).
5. Check shell env vars `FLUENT_USERNAME` and `FLUENT_PASSWORD`. If both set → use them.
6. If no source provides credentials → ask the user.

**IMPORTANT:** Never store or log the resolved password. Never echo it. Use it only as the value for `type_text` into the password field during login. The Chrome DevTools MCP browser session persists cookies, so login only needs to happen once per session.

### Optional
- **FFmpeg on PATH** — for post-processing (trim, convert .webm to .mp4). Install: `winget install ffmpeg` (Windows), `brew install ffmpeg` (macOS).
- **Feature context** — if recording evidence for a feature, the feature slug determines output path.
- **Chrome MCP Extension** — can be used as a fallback for screenshot-based recording when Chrome DevTools MCP is unavailable. Enable with `--with-chrome` during `mcp-setup`. Note: Chrome MCP Extension does NOT support `--save-video` — screencast recording requires Chrome DevTools MCP.

### Prerequisite Check (Phase 0)

```
1. Check for Chrome DevTools MCP tools first (navigate_page, evaluate_script, take_screenshot)
2. If Chrome DevTools not available, check for Chrome MCP Extension tools (navigate, screenshot, execute_script from chrome-manual server)
   - If Chrome MCP Extension: warn that screencast recording is unavailable, screenshots only
3. Resolve credentials using the Credential Resolution chain above
4. Verify --save-video is in .mcp.json args (warn if missing — recording still works via screenshots)
5. Create output directory:
   - With feature: accounts/<PROFILE>/features/<feature-slug>/assets/<YYYY-MM-DD_HHmmss>/
   - Without feature: .tmp/ui-record/<YYYY-MM-DD_HHmmss>/
6. Ask user for recording plan if --steps not provided
```

If neither browser MCP is connected:
```
-> BLOCKED: No browser MCP connected. Install Chrome DevTools MCP (recommended) or Chrome MCP Extension. Restart IDE to pick up .mcp.json changes.
```

### Phase 0a: Manifest Selector Cache (once per session, before browser)

Pre-analyze manifests to get exact element labels. Eliminates "guess the label" misses and replaces the first `take_snapshot` for element discovery.

```bash
# Check if cache exists
ls accounts/<PROFILE>/manifests/selector-cache-slim.json 2>/dev/null

# If missing, generate it (~2 seconds, reads local manifest JSONs)
node fluent-ai-skills/tools/manifest-selector-map.mjs --profile <PROFILE>
```

Then read `selector-cache-slim.json` once (~15K tokens) for exact navigation labels, button names, card headings, and column headers. See `fluent-ui-test/BROWSER_OPTIMIZATIONS.md` "Layer 4: Manifest Selector Cache" for details.

**Auto-refresh:** If the cache is missing or older than 24 hours, regenerate: `node fluent-ai-skills/tools/manifest-selector-map.mjs --profile <PROFILE>`. Skills check freshness automatically in pre-flight.

**Skip if:** Manifest files don't exist under `accounts/<PROFILE>/manifests/`.

### Phase 0b: Inject Helper Library (MANDATORY — use initScript)

**Use `initScript` on `navigate_page`** to auto-inject `__fcTest` during navigation — eliminates a separate round trip.

```
navigate_page:
  url: <target URL>
  initScript: <full IIFE from BROWSER_OPTIMIZATIONS.md "Layer 1: Helper Library">
```

Pass `initScript` on **every `navigate_page` call** (it's idempotent). Combine queries with `batch()` — target **max 2-3 MCP calls per page** (navigate + combined evaluate + optional screenshot).

**Fallback:** If `initScript` doesn't work, inject via `evaluate_script`. Verify: `window.__fcTest ? 'ready' : 'not_injected'`.

**MUI-aware finders** (use with labels from selector-cache-slim.json):
- `findCard(title)`, `findButton(label)`, `findTab(label)`, `findNav(label)`, `findInput(label)`
- `findTableColumn(header)`, `findTableRow(col, val)`
- `clickNav()`, `clickButton()`, `clickTab()`, `clickTableRow()`, `fillInput()`, `navigateTo()`
- `batch([ops])` — multiple operations in one call
- `pageState()` — ultra-compact page summary (~150 tokens)
- `diffState(before, after)` — compute DOM/state diff between two snapshots (~300 tokens)
- `installGuard(component)` — verify a component is registered before rendering (~100 tokens)

**Optimized login:** If `__fcTest` is injected, use `__fcTest.login(user, pass)` to handle login without take_snapshot (~200 tokens vs ~30K).

## Recording Protocol

### Phase 1: Plan the Recording

Before starting, establish the recording plan:

1. **What app?** OMS App, Store App
2. **What pages?** List of routes to visit (e.g., `/oms/orders`, `/oms/orders/123`)
3. **What changed?** Manifest setting name, what was modified, why
4. **What to verify?** Expected visual differences
5. **Baseline needed?** If this is the first capture, skip "before" phase

Present the plan to the user:
```
Recording Plan:
  App: OMS
  Pages: /oms/orders, /oms/orders/456
  Change: Added appeasement column to order list
  Setting: fc.mystique.manifest.oms.fragment.ordermanagement
  Steps: 3 (baseline → deploy → verify)
```

### Phase 2: Capture Baseline (Before)

If a change is being recorded (not just a walkthrough), capture the current state first.

For each target page:

**Step 2a: Navigate and annotate**
```
1. navigate_page to about:blank (clears cached SPA state)
2. navigate_page to the page URL
3. wait_for page to settle (2-3 seconds) — ensures fresh manifest loads
3b. evaluate_script → JSON.stringify(window.__fcTest.pageSummary())
    - If loading: true, wait 2-3s and retry
    - If alerts present, investigate before proceeding
    - This is a quick ~500 token check, NOT a replacement for the DOM snapshot in step 5
4. evaluate_script → window.__fcTest.annotate("BEFORE — <page description>", "BEFORE")
5. take_screenshot → save as baseline-<N>.png
6. evaluate_script → window.__fcTest.clearAnnotation()
```

**Step 2b: Capture evidence**
```
5. take_snapshot → save DOM snapshot to baseline-<N>-snapshot.txt
6. list_console_messages → save to baseline-<N>-console.json
7. list_network_requests → save to baseline-<N>-network.json
```

**Step 2c: Capture manifest state**
If a manifest setting is being changed, query the current value:
```
8. Use graphql.query or setting.get to fetch the current setting value
9. Save to manifest-before.json
```

### Phase 3: Apply the Change

This phase is a PAUSE — the change happens outside this skill.

```
-> REVIEW: Baseline captured. Apply your change now.
   Options:
   a) Deploy manifest via /fluent-mystique-builder → fluent module install
   b) Deploy workflow via /fluent-workflow-deploy
   c) Deploy settings via /fluent-settings
   d) Manual change (user does it themselves)

   Say "continue recording" when ready.
```

Wait for user confirmation before proceeding to Phase 4.

**If --baseline flag is set:** Skip this phase entirely — only capture the current state.

### Phase 4: Capture After State

After the change is deployed, capture the new state.

For each target page:

**Step 4a: Force-refresh the browser**
```
1. navigate_page to about:blank (clear cache)
2. navigate_page back to the page URL
3. Wait 2 seconds for manifest reload
```

**Step 4b: Navigate and annotate**
```
4. evaluate_script → window.__fcTest.annotate("AFTER — <page description>", "AFTER")
5. take_screenshot → save as after-<N>.png
6. evaluate_script → window.__fcTest.clearAnnotation()
```

**Step 4c: Capture evidence**
```
7. take_snapshot → save DOM snapshot to after-<N>-snapshot.txt
8. list_console_messages → save to after-<N>-console.json
9. list_network_requests → save to after-<N>-network.json
```

**Step 4d: Capture manifest state**
```
10. Query the updated setting value
11. Save to manifest-after.json
```

### Phase 5: Generate Evidence Report

Compose a self-contained HTML evidence report.

**Step 5a: Compute diffs**
```
1. Read manifest-before.json and manifest-after.json
2. Compute JSON diff (added/removed/changed keys)
3. Compare DOM snapshots (added/removed elements)
```

**Step 5b: Encode screenshots**
Use bash to base64-encode screenshots for embedding in HTML:
```bash
# PowerShell on Windows
$bytes = [System.IO.File]::ReadAllBytes("baseline-1.png")
$b64 = [Convert]::ToBase64String($bytes)
[System.IO.File]::WriteAllText("baseline-1.b64", $b64)
```

**Step 5c: Generate HTML report**
Write the HTML report using the template structure below. The report MUST be self-contained — all images embedded as base64 data URIs, no external dependencies.

Output path:
- With feature: `accounts/<PROFILE>/features/<feature-slug>/assets/<timestamp>/report.html`
- Without feature: `.tmp/ui-record/<timestamp>/report.html`

### Phase 6: Post-Processing (Optional)

If `--save-video` was enabled and FFmpeg is available:

```bash
# Find the Chrome DevTools recording
ls .tmp/chrome-devtools/*.webm

# Convert to MP4 (wider compatibility)
ffmpeg -i .tmp/chrome-devtools/recording.webm -c:v libx264 -crf 23 -preset fast recording.mp4

# Trim to just the relevant portion (skip login/idle)
ffmpeg -i recording.mp4 -ss 00:00:10 -t 00:01:30 trimmed.mp4

# Generate thumbnail from key frame
ffmpeg -i recording.mp4 -ss 00:00:15 -frames:v 1 thumbnail.png
```

If FFmpeg is NOT available, skip post-processing — the raw `.webm` is still usable.

## DOM Annotation Protocol

Inject a temporary overlay into the page to label each step. The overlay appears in both screenshots AND Chrome DevTools screencast recording.

### Inject Annotation (Preferred — via helper library)

```javascript
// Via evaluate_script — one-line call (~100 tokens vs ~800 tokens inline)
window.__fcTest.annotate("STEP_TEXT", "STEP_BADGE")
```

Replace `STEP_BADGE` with the step type (`BEFORE`, `AFTER`, `VERIFY`, `STEP N`) and `STEP_TEXT` with the description.

### Remove Annotation

```javascript
window.__fcTest.clearAnnotation()
```

### Fallback (if helper library not injected)

<details>
<summary>Inline JS annotation (use only if __fcTest is unavailable)</summary>

```javascript
(function() {
  var existing = document.getElementById('__fc-record-annotation');
  if (existing) existing.remove();
  var overlay = document.createElement('div');
  overlay.id = '__fc-record-annotation';
  overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:2147483647;background:linear-gradient(135deg,#1a1a2e,#16213e);color:#fff;padding:14px 24px;font:600 15px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;box-shadow:0 4px 12px rgba(0,0,0,0.3);display:flex;align-items:center;gap:12px;border-bottom:3px solid #e94560';
  var badge = document.createElement('span');
  badge.style.cssText = 'background:#e94560;color:#fff;padding:3px 10px;border-radius:4px;font-size:12px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;white-space:nowrap';
  badge.textContent = 'STEP_BADGE';
  var text = document.createElement('span');
  text.textContent = 'STEP_TEXT';
  var time = document.createElement('span');
  time.style.cssText = 'margin-left:auto;opacity:0.6;font-size:12px;font-weight:400';
  time.textContent = new Date().toLocaleTimeString();
  overlay.appendChild(badge);
  overlay.appendChild(text);
  overlay.appendChild(time);
  document.body.appendChild(overlay);
})();
```

Remove: `(function() { var el = document.getElementById('__fc-record-annotation'); if (el) el.remove(); })();`

</details>

### Annotation Timing

1. Inject annotation
2. Wait 500ms (let the overlay render and appear in recording)
3. Take screenshot
4. Wait 500ms (let the recording capture the annotated frame)
5. Remove annotation
6. Proceed with next action

This ensures the annotation appears in both the screenshot AND at least 1 second of Chrome DevTools screencast.

### Thought Bubble Annotations (for NEW components)

When recording before/after evidence of NEW UI components (not step labels), use the thought-bubble pattern instead of the overlay banner. This keeps the annotation clearly separated from the actual component.

**Key rule:** Annotations must be OUTSIDE the injected component — never inside it. This prevents confusion between what's the actual UI and what's an explanation note.

```javascript
// Inject after the new component, NOT inside it
(function() {
  var wrapper = document.createElement('div');
  wrapper.style.cssText = 'position:relative;margin-top:6px;margin-left:30px;';
  // Up-pointing arrow
  wrapper.innerHTML = '<div style="position:absolute;top:-8px;left:40px;">' +
    '<div style="width:0;height:0;border-left:10px solid transparent;' +
    'border-right:10px solid transparent;border-bottom:10px solid #ffb300;"></div></div>' +
    '<div style="background:#fff8e1;border:1.5px solid #ffb300;border-radius:10px;' +
    'padding:10px 14px;font-family:Lato,sans-serif;font-size:11px;color:#5d4037;' +
    'box-shadow:0 2px 6px rgba(0,0,0,0.08);max-width:420px;">' +
    '<strong style="color:#e65100;">ANNOTATION_TITLE</strong> — ANNOTATION_TEXT</div>';
  // Insert after the target element
  TARGET_ELEMENT.parentElement.insertBefore(wrapper, TARGET_ELEMENT.nextSibling);
})();
```

Use with the pulsing glow border on the new component itself:
```css
@keyframes pulseGlow {
  0%, 100% { box-shadow: 0 0 4px rgba(26,115,232,0.3); }
  50% { box-shadow: 0 0 14px rgba(26,115,232,0.5), 0 0 28px rgba(26,115,232,0.15); }
}
.mock-glow { animation: pulseGlow 2s ease-in-out infinite; border: 2px solid #1a73e8 !important; border-radius: 8px !important; }
.mock-badge { display:inline-block; background:#1a73e8; color:white; font-size:9px; font-weight:700; padding:2px 6px; border-radius:8px; margin-left:8px; vertical-align:middle; }
```

**OMS native styling:** When injecting into the OMS app, match the native card styling (Lato font, MuiCard structure, 16px padding, uppercase h3 headings). See `knowledge/frontend/mystique-patterns.md` § "OMS DOM Structure and Styling Reference".

## Click Telemetry Protocol

Chrome DevTools MCP dispatches clicks programmatically — no visible cursor or click indicator appears on screen. Click Telemetry injects visual highlights and maintains an action timeline so recordings and screenshots are self-documenting.

### Phase 0: Initialize Action Timeline

The `__fcTest` helper library (injected in Phase 0b) includes the action timeline, highlight keyframes, and all telemetry infrastructure. No separate initialization needed.

**Verify ready:**
```javascript
// Via evaluate_script
window.__fcTest ? 'ready' : 'not_injected'
```

If not injected, inject the helper library from `BROWSER_OPTIMIZATIONS.md` (see Phase 0b above).

### Highlight Before Click

Before each `click` tool call, highlight the target element using the helper library:

```javascript
// Via evaluate_script — one-line call (~100 tokens vs ~800 tokens inline)
JSON.stringify(window.__fcTest.highlight("Cancel Order", "CLICK: CANCEL ORDER"))
```

Returns `{found: true, rect: {...}}` or `{found: false}`. The helper automatically logs to the action timeline.

Replace `"Cancel Order"` with the button text or CSS selector, and the second arg with the label badge text.

**If element not found:** Use `__fcTest.exists("Cancel Order", "button")` to check, then escalate to `take_snapshot` for full DOM investigation.

### Click Sequence

For each interactive action during recording:

```
1. evaluate_script → JSON.stringify(window.__fcTest.highlight("Button Text", "CLICK: LABEL"))
2. wait_for 300ms — let highlight render + screencast captures the pulse
3. take_screenshot — capture the highlighted state as evidence
4. click the element — execute the actual interaction
5. wait_for 500ms — let the page settle after the action
6. evaluate_script → window.__fcTest.clearHighlight()
7. take_screenshot — capture the post-click state
```

**Timing budget:** ~1.3s per click action. The 0.8s pulse animation ensures at least one full cycle appears in screencast recording.

### Post-Click Feedback Toast (Optional)

After a click completes, show a brief confirmation toast:

```javascript
// Via evaluate_script — replace ACTION_TEXT
(function() {
  var toast = document.createElement('div');
  toast.id = '__fc-click-toast';
  toast.style.cssText = [
    'position: fixed',
    'bottom: 24px',
    'right: 24px',
    'background: #27ae60',
    'color: #fff',
    'padding: 10px 20px',
    'border-radius: 6px',
    'z-index: 2147483647',
    'font: 600 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
    'box-shadow: 0 4px 12px rgba(0,0,0,0.2)',
    'animation: fcClickFade 1.5s ease-in-out forwards',
    'animation-delay: 0.8s'
  ].join(';');
  toast.textContent = '✓ ACTION_TEXT';
  document.body.appendChild(toast);
  setTimeout(function() { toast.remove(); }, 2300);
})();
```

### Extract Action Timeline

At the end of the recording session, extract the timeline for the HTML evidence report:

```javascript
// Via evaluate_script — returns JSON string (~200 tokens)
JSON.stringify(window.__fcTest.getTimeline())
```

### Action Timeline in HTML Report

Embed the timeline as a table in the evidence report (add after the summary section in the HTML template):

```html
<!-- Action Timeline (generated from __fcTimeline) -->
<div class="step">
  <div class="step-header">
    <span class="step-badge info">TIMELINE</span>
    <span class="step-title">Action Sequence (TOTAL_ACTIONS actions, DURATION)</span>
  </div>
  <div class="step-body">
    <table style="width:100%;border-collapse:collapse;font-size:13px">
      <thead>
        <tr style="border-bottom:2px solid #eee;text-align:left">
          <th style="padding:8px 12px;width:40px">#</th>
          <th style="padding:8px 12px;width:80px">Elapsed</th>
          <th style="padding:8px 12px;width:100px">Action</th>
          <th style="padding:8px 12px">Target</th>
          <th style="padding:8px 12px">Label</th>
        </tr>
      </thead>
      <tbody>
        <!-- ACTION_ROWS: one <tr> per timeline entry -->
        <tr style="border-bottom:1px solid #f0f0f0">
          <td style="padding:6px 12px;color:#999">1</td>
          <td style="padding:6px 12px;font-family:monospace">+0.3s</td>
          <td style="padding:6px 12px"><span class="step-badge verify" style="font-size:10px">click</span></td>
          <td style="padding:6px 12px;font-family:monospace">cancel-order-btn</td>
          <td style="padding:6px 12px">Cancel Order</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>
```

### Integration with Recording Phases

| Phase | Click Telemetry Action |
|-------|----------------------|
| Phase 0b (Inject) | Inject `__fcTest` helper library (includes timeline, highlights, annotations) |
| Phase 2 (Before) | `pageSummary()` for quick page check + `take_snapshot` for DOM delta baseline |
| Phase 4 (After) | **Full highlight sequence** — `__fcTest.highlight()` → screenshot → click → screenshot → `clearHighlight()` |
| Phase 5 (Report) | Extract timeline via `__fcTest.getTimeline()`, embed as table in HTML report |
| Phase 6 (Post) | Timeline data included in recording metadata JSON |

### Cleanup

Remove all telemetry artifacts at the end of the session:

```javascript
// Via evaluate_script — one call clears all highlights + annotations
window.__fcTest.clearAll()
```

Also remove the click toast if present:
```javascript
var toast = document.getElementById('__fc-click-toast');
if (toast) toast.remove();
```

## HTML Evidence Report Template

The generated HTML report follows this structure:

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UI Change Evidence — FEATURE_TITLE</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
         background: #f5f5f5; color: #1a1a1a; line-height: 1.6; }
  .header { background: linear-gradient(135deg, #1a1a2e, #16213e); color: #fff;
             padding: 32px 40px; }
  .header h1 { font-size: 24px; margin-bottom: 8px; }
  .header .meta { font-size: 14px; opacity: 0.8; }
  .header .meta span { margin-right: 20px; }
  .summary { background: #fff; margin: 24px 40px; padding: 24px;
             border-radius: 8px; border-left: 4px solid #e94560; }
  .summary h2 { font-size: 18px; margin-bottom: 12px; }
  .summary .change-desc { font-size: 15px; color: #444; }
  .step { background: #fff; margin: 16px 40px; border-radius: 8px;
          overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
  .step-header { padding: 16px 24px; border-bottom: 1px solid #eee;
                 display: flex; align-items: center; gap: 12px; }
  .step-badge { padding: 4px 12px; border-radius: 4px; font-size: 12px;
                font-weight: 700; text-transform: uppercase; color: #fff; }
  .step-badge.before { background: #3498db; }
  .step-badge.after { background: #27ae60; }
  .step-badge.verify { background: #e94560; }
  .step-badge.info { background: #8e44ad; }
  .step-title { font-size: 16px; font-weight: 600; }
  .step-time { margin-left: auto; font-size: 13px; color: #999; }
  .step-body { padding: 24px; }
  .screenshot-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .screenshot-single { max-width: 100%; }
  .screenshot-pair img, .screenshot-single img {
    width: 100%; border-radius: 4px; border: 1px solid #ddd; cursor: pointer; }
  .screenshot-pair img:hover, .screenshot-single img:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
  .screenshot-label { font-size: 13px; font-weight: 600; color: #666;
                      text-transform: uppercase; margin-bottom: 8px; }
  .diff-section { margin-top: 20px; }
  .diff-section h3 { font-size: 15px; margin-bottom: 8px; }
  .diff-block { background: #f8f9fa; border-radius: 6px; padding: 16px;
                font-family: 'SF Mono', Consolas, monospace; font-size: 13px;
                overflow-x: auto; white-space: pre-wrap; line-height: 1.5; }
  .diff-add { color: #27ae60; }
  .diff-remove { color: #e74c3c; }
  .evidence-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px;
                   margin-top: 16px; }
  .evidence-card { background: #f8f9fa; padding: 12px; border-radius: 6px; }
  .evidence-card h4 { font-size: 13px; color: #666; margin-bottom: 6px; }
  .evidence-card .value { font-size: 14px; font-weight: 600; }
  .evidence-card .value.pass { color: #27ae60; }
  .evidence-card .value.fail { color: #e74c3c; }
  .evidence-card .value.warn { color: #f39c12; }
  details { margin-top: 12px; }
  details summary { cursor: pointer; font-size: 14px; font-weight: 600;
                    color: #3498db; padding: 4px 0; }
  details pre { margin-top: 8px; background: #f8f9fa; padding: 12px;
                border-radius: 6px; font-size: 12px; overflow-x: auto; }
  .footer { text-align: center; padding: 24px 40px; font-size: 13px; color: #999; }
  /* Lightbox */
  .lightbox { display: none; position: fixed; inset: 0; z-index: 999999;
              background: rgba(0,0,0,0.9); align-items: center; justify-content: center;
              cursor: zoom-out; }
  .lightbox.active { display: flex; }
  .lightbox img { max-width: 95vw; max-height: 95vh; border-radius: 4px; }
  @media (max-width: 768px) {
    .screenshot-pair { grid-template-columns: 1fr; }
    .evidence-grid { grid-template-columns: 1fr; }
    .header, .summary, .step { margin-left: 16px; margin-right: 16px; }
  }
</style>
</head>
<body>

<div class="header">
  <h1>UI Change Evidence — FEATURE_TITLE</h1>
  <div class="meta">
    <span>App: APP_NAME</span>
    <span>Profile: PROFILE_NAME</span>
    <span>Date: RECORD_DATE</span>
    <span>Steps: STEP_COUNT</span>
  </div>
</div>

<div class="summary">
  <h2>Change Summary</h2>
  <p class="change-desc">CHANGE_DESCRIPTION</p>
  <div class="evidence-grid">
    <div class="evidence-card">
      <h4>Setting Modified</h4>
      <div class="value">SETTING_NAME</div>
    </div>
    <div class="evidence-card">
      <h4>Console Errors</h4>
      <div class="value CONSOLE_STATUS">CONSOLE_COUNT</div>
    </div>
    <div class="evidence-card">
      <h4>Rendering</h4>
      <div class="value RENDER_STATUS">RENDER_VERDICT</div>
    </div>
  </div>
</div>

<!-- STEPS_PLACEHOLDER -->
<!-- Repeat this block for each step:

<div class="step">
  <div class="step-header">
    <span class="step-badge BADGE_CLASS">BADGE_TEXT</span>
    <span class="step-title">STEP_TITLE</span>
    <span class="step-time">STEP_TIME</span>
  </div>
  <div class="step-body">
    <div class="screenshot-pair">
      <div>
        <div class="screenshot-label">Before</div>
        <img src="data:image/png;base64,BEFORE_B64" alt="Before" onclick="openLightbox(this)">
      </div>
      <div>
        <div class="screenshot-label">After</div>
        <img src="data:image/png;base64,AFTER_B64" alt="After" onclick="openLightbox(this)">
      </div>
    </div>
    <div class="diff-section">
      <h3>Manifest Diff</h3>
      <div class="diff-block">DIFF_CONTENT</div>
    </div>
    <details>
      <summary>Console Messages (COUNT)</summary>
      <pre>CONSOLE_MESSAGES</pre>
    </details>
    <details>
      <summary>Network Requests (COUNT)</summary>
      <pre>NETWORK_REQUESTS</pre>
    </details>
    <details>
      <summary>DOM Snapshot Delta</summary>
      <pre>SNAPSHOT_DELTA</pre>
    </details>
  </div>
</div>

-->

<div class="footer">
  Generated by <code>/fluent-ui-record</code> — Fluent Commerce AI Skills
</div>

<!-- Lightbox -->
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
  <img id="lightbox-img" src="" alt="Full size">
</div>

<script>
function openLightbox(img) {
  var lb = document.getElementById('lightbox');
  document.getElementById('lightbox-img').src = img.src;
  lb.classList.add('active');
}
function closeLightbox() {
  document.getElementById('lightbox').classList.remove('active');
}
document.addEventListener('keydown', function(e) {
  if (e.key === 'Escape') closeLightbox();
});
</script>
</body>
</html>
```

### Report Generation Steps

1. Read the HTML template above (from this skill doc or generate inline)
2. Replace all `FEATURE_TITLE`, `APP_NAME`, etc. placeholders
3. For each step, generate a `<div class="step">` block:
   - Embed screenshots as `data:image/png;base64,...`
   - Include manifest diff with `diff-add`/`diff-remove` spans
   - Include console, network, DOM snapshot evidence in collapsible `<details>`
4. Write the complete HTML to the output directory
5. Report the file path to the user

### Screenshot Base64 Encoding (Windows)

```powershell
$bytes = [System.IO.File]::ReadAllBytes("C:\path\to\screenshot.png")
[Convert]::ToBase64String($bytes)
```

Or via bash/node:
```bash
node -e "const fs=require('fs'); process.stdout.write(fs.readFileSync(process.argv[1]).toString('base64'))" screenshot.png
```

## Walkthrough Mode (No Before/After)

If recording a walkthrough (not a change), use a simplified protocol:

1. Plan the route sequence
2. For each page:
   - Navigate
   - Inject annotation: `STEP N — <description>`
   - Screenshot
   - Capture evidence
3. Generate HTML report with single-column screenshots (not side-by-side)
4. Use `<div class="screenshot-single">` instead of `screenshot-pair`

## Integration with Other Skills

### After `/fluent-mystique-builder` (most common)

```
/fluent-ui-record --app oms --feature appeasement-list
  Phase 2: Captures BEFORE state of OMS pages
  → User deploys manifest change
  Phase 4: Captures AFTER state
  Phase 5: Generates HTML evidence report
```

### After `/fluent-workflow-deploy`

Record how workflow changes affect the UI — new user actions appearing, status transitions, component visibility changes.

### After `/fluent-settings`

Record how settings changes affect app behavior — new features enabled, configuration reflected in UI.

### Standalone Walkthrough

```
/fluent-ui-record --app oms --baseline
  Captures current state of all key OMS pages
  Generates walkthrough report — useful for onboarding/documentation
```

## Output Files

```
accounts/<PROFILE>/features/<feature-slug>/assets/<timestamp>/
├── report.html              # Self-contained HTML evidence report
├── recording-plan.json      # What was recorded and why
├── baseline-1.png           # Before screenshots
├── baseline-2.png
├── after-1.png              # After screenshots
├── after-2.png
├── manifest-before.json     # Setting value before change
├── manifest-after.json      # Setting value after change
├── manifest-diff.json       # Computed diff
├── baseline-1-snapshot.txt  # DOM snapshots
├── after-1-snapshot.txt
├── baseline-1-console.json  # Console messages
├── after-1-console.json
├── baseline-1-network.json  # Network requests
└── after-1-network.json
```

Additionally, if `--save-video` is enabled in Chrome DevTools MCP:
```
.tmp/chrome-devtools/
└── recording-*.webm          # Raw browser screencast recording
```

## Video Post-Processing

If FFmpeg is available on PATH, offer these post-processing options:

### Convert to MP4
```bash
ffmpeg -i .tmp/chrome-devtools/recording.webm -c:v libx264 -crf 23 -preset fast \
  accounts/<PROFILE>/features/<slug>/assets/<timestamp>/recording.mp4
```

### Trim idle sections
```bash
ffmpeg -i recording.mp4 -ss 00:00:05 -t 00:01:00 -c copy trimmed.mp4
```

### Generate GIF preview (for PRs/Confluence)
```bash
ffmpeg -i recording.mp4 -vf "fps=5,scale=800:-1:flags=lanczos" -t 10 preview.gif
```

### Extract keyframe thumbnails
```bash
ffmpeg -i recording.mp4 -vf "select=eq(ptype\\,I)" -vsync vfr thumb-%03d.png
```

If FFmpeg is NOT available, skip this phase and note in the report that raw `.webm` is available.

## Auto-Embed in Spec (MANDATORY)

After producing before/after screenshots, **always embed them in the feature's spec.md** (section 9a, Visual Mocks subsection). This ensures:
1. The spec is self-documenting with visual evidence
2. The feature dashboard renders the mocks inline (the dashboard generator base64-encodes relative image paths at build time)
3. Reviewers see what the feature looks like without navigating to separate report files

**Embedding protocol:**
1. Check if `accounts/<PROFILE>/features/<slug>/spec.md` exists
2. If section "### Visual Mocks" exists in the spec, update it with new screenshots
3. If it doesn't exist, insert it after "### Component Approach" (or before "### UX Notes" if Component Approach is missing) in section 9a
4. Use relative paths from the spec to the asset images: `assets/<timestamp>/before.png`
5. Include a brief caption describing how the mock was produced

**Example spec insertion:**
```markdown
### Visual Mocks (Live OMS Injection)

**Before** — Order detail page without the new component:

![Before](assets/<timestamp>/before.png)

**After** — New component injected at target location:

![After](assets/<timestamp>/after.png)

> Mocks produced via live DOM injection on the real OMS app (entity REF).
```

If no spec exists (standalone recording, not tied to a feature), skip this step.

## Handoff Protocol

| Signal | Meaning |
|--------|---------|
| `-> READY: <report-path>` | Evidence report generated, ready for review |
| `-> REVIEW: Baseline captured` | Waiting for user to deploy change |
| `-> BLOCKED: Chrome DevTools MCP not connected` | Cannot record |
| `-> SKIP: --no-video` | Screencast recording skipped |
| `-> SKIP: FFmpeg not available` | Post-processing skipped |
| `-> NEXT: /fluent-ui-test` | Run pass/fail verification after recording |

## Error Reporting

| Severity | Code | Example |
|----------|------|---------|
| CRITICAL | `PREREQ_MISSING` | Chrome DevTools MCP not connected |
| HIGH | `LOGIN_FAILED` | Cannot authenticate to UX app |
| HIGH | `SCREENSHOT_FAILED` | Screenshot capture returned error |
| MEDIUM | `VIDEO_MISSING` | --save-video not configured, screenshots only |
| MEDIUM | `FFMPEG_MISSING` | FFmpeg not on PATH, post-processing skipped |
| LOW | `CACHE_STALE` | Page may show cached manifest |

## Session Tracking

```json
{
  "skill": "fluent-ui-record",
  "type": "ui_recording",
  "app": "oms",
  "feature": "appeasement-list",
  "steps": 3,
  "hasVideo": true,
  "hasReport": true,
  "reportPath": "accounts/<PROFILE>/features/appeasement-list/assets/2026-02-26_143022/report.html",
  "result": "COMPLETE",
  "timestamp": "<ISO>"
}
```

## Known Limitations

1. **Chrome DevTools screencast records the entire session** — includes login, navigation, idle time. Use FFmpeg to trim to relevant sections.
2. **DOM annotation overlays are transient** — they appear for ~1 second during recording. The annotation is captured in the screenshot and in the recording frame.
3. **Base64 encoding increases report size** — each embedded screenshot adds ~0.5-2MB. For reports with 10+ screenshots, consider linking to files instead of embedding.
4. **Console/network capture is point-in-time** — `list_console_messages` and `list_network_requests` return accumulated messages since page load, not since last capture.
5. **Manifest diff is JSON-level** — does not show which UI components were visually affected. Use DOM snapshot delta for component-level changes.

## Post-Execution: Feedback Capture

After completing this skill (whether success or failure), write a feedback record:

1. **Classify outcome:** `SUCCESS` (completed cleanly), `PARTIAL_SUCCESS` (completed after fixing issues), `FAILURE` (could not complete), `BLOCKED` (prereq missing), `USER_CORRECTED` (user overrode approach)
2. **Build record:** Create a `feedback-record-v1` JSON object with:
   - `skill`: `"fluent-ui-record"`
   - `feature`: feature slug if applicable, null otherwise
   - `phases`: trace of each phase with PASS/FAIL/SKIP
   - `learnings`: any gotchas discovered during execution
   - `userCorrections`: any corrections the user provided
3. **Redact secrets:** Strip tokens, passwords, API keys. Keep business identifiers.
4. **Append** one JSON line to `accounts/<PROFILE>/feedback/fluent-ui-record.jsonl`
5. **Update** `accounts/<PROFILE>/feedback/index.json` counters (create with `mkdir -p` if missing)

**Skip capture if:** The execution was trivially simple (e.g., user asked a question, no actual write operation happened).

## Known Pitfalls
<!-- feedback-promoted: managed section, updated by feedback loop -->

_(No promoted learnings yet.)_

<!-- end feedback-promoted -->
