Files
mokosh/smoke.sh
Mark bf076199b4 docs(fix-d12): resolve debug session and update STATE
- Mark .planning/debug/d12-blob-port-transfer-fails.md as
  status: resolved; fill in the Resolution section with the
  applied fix (5 commit hashes, files changed), verification
  output (15/15 tests, tsc clean, vite build green, zero
  as-any/ts-ignore in fix-touched files), and inline answers
  to the specialist-review questions raised by the planner.
  Move the file to .planning/debug/resolved/.
- Update STATE.md frontmatter (stopped_at) + Decisions log
  + Session Continuity to record the D-12 fix landing and
  the open Plan 07 ffprobe gate (still requires operator
  smoke.sh + ffprobe re-run before Phase 1 can close).
- Land smoke.sh — the operator's D-12 acceptance-gate harness
  that surfaced the original failure. Self-contained: dedicated
  /tmp/mokosh-smoke-profile, auto-accept desktop-capture picker,
  Downloads polling, ffprobe gate, fixture staging.

REQ-video-ring-buffer remains NOT-complete — Plan 07 owns it,
operator must re-run ./smoke.sh to verify the fix end-to-end
in Chrome.

Refs: debug session d12-blob-port-transfer-fails (resolved).
2026-05-15 20:23:29 +02:00

240 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Mokosh Phase 1 — D-12 ffprobe acceptance gate smoke test.
#
# Architecture:
# - Launches Chrome with a DEDICATED `/tmp/mokosh-smoke-profile` user-data-dir
# so we don't interfere with your daily Chrome session.
# - Opens a data: URL with title "Mokosh Smoke Test" as the share target.
# - Passes `--auto-select-desktop-capture-source="Mokosh Smoke Test"` so the
# getDisplayMedia picker auto-accepts that tab — no manual pick.
# - Logs Chrome stderr/stdout to /tmp/mokosh-chrome.log.
# - Polls ~/Downloads for the new session_report_*.zip.
# - Runs ffprobe gate, stages fixture, opens WebM in Chrome for visual check.
#
# Chrome 148+ removed `--load-extension`, so the extension load is the ONE
# manual step that has to happen each time the smoke profile is fresh. (Once
# loaded, it persists in the profile until the dir is wiped — see KEEP_PROFILE.)
#
# What YOU do (~3 clicks total):
# 1. Run this script.
# 2. In the Chrome window that opens, address-bar → chrome://extensions
# → toggle "Developer mode" ON → "Load unpacked" → select
# /home/parf/projects/work/repremium/dist (only needed if profile is fresh).
# 3. Click the extension icon (puzzle-piece menu if not pinned).
# The screen-share picker auto-accepts the "Mokosh Smoke Test" tab.
# 4. Wait ~35 seconds (or move mouse / scroll the smoke tab for frame deltas).
# 5. Click the icon again → click "Сохранить отчёт об ошибке".
# This script handles everything after that.
#
# Env knobs:
# CHROME_BIN — Chrome binary (default: /usr/bin/google-chrome-stable)
# KEEP_PROFILE=1 — don't wipe the smoke profile, so extension stays loaded
# across runs (default: wipe, fresh-load each run)
# POLL_TIMEOUT — max seconds to wait for the download (default: 900 = 15m)
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DIST_DIR="${REPO_DIR}/dist"
PROFILE_DIR="/tmp/mokosh-smoke-profile"
DOWNLOADS_DIR="${HOME}/Downloads"
FIXTURE_DEST="${REPO_DIR}/tests/fixtures/last_30sec.webm"
WEBM_TMP="/tmp/mokosh-last_30sec.webm"
CHROME_LOG="/tmp/mokosh-chrome.log"
SHARE_TARGET="Mokosh Smoke Test"
CHROME_BIN="${CHROME_BIN:-/usr/bin/google-chrome-stable}"
KEEP_PROFILE="${KEEP_PROFILE:-0}"
POLL_TIMEOUT="${POLL_TIMEOUT:-900}"
red() { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
blue() { printf '\033[34m%s\033[0m\n' "$*"; }
echo
blue "==> Mokosh Phase 1 smoke test (D-12 ffprobe gate)"
echo
# --- pre-flight ---
[[ -d "${DIST_DIR}" ]] || { red "FAIL: ${DIST_DIR} missing — run \`npm run build\` first"; exit 1; }
[[ -x "${CHROME_BIN}" ]] || { red "FAIL: ${CHROME_BIN} not found (set CHROME_BIN=...)"; exit 1; }
command -v ffprobe >/dev/null || { red "FAIL: ffprobe not installed"; exit 1; }
command -v unzip >/dev/null || { red "FAIL: unzip not installed"; exit 1; }
grep -q '"desktopCapture"' "${DIST_DIR}/manifest.json" || { red "FAIL: dist/manifest.json missing desktopCapture"; exit 1; }
! grep -q '"tabCapture"' "${DIST_DIR}/manifest.json" || { red "FAIL: dist/manifest.json still has tabCapture"; exit 1; }
green "✓ pre-flight checks passed"
echo " chrome: ${CHROME_BIN} ($("${CHROME_BIN}" --version 2>/dev/null || echo 'unknown'))"
echo " dist: ${DIST_DIR}"
echo " profile: ${PROFILE_DIR} (KEEP_PROFILE=${KEEP_PROFILE})"
echo " log: ${CHROME_LOG}"
echo " downloads: ${DOWNLOADS_DIR}"
echo
# --- snapshot Downloads ---
BEFORE_LIST=$(find "${DOWNLOADS_DIR}" -maxdepth 1 -name 'session_report_*.zip' 2>/dev/null | wc -l)
echo " existing session_report_*.zip in Downloads: ${BEFORE_LIST}"
echo
# --- profile prep ---
PROFILE_HAS_EXTENSION=0
if [[ "${KEEP_PROFILE}" != "1" ]]; then
rm -rf "${PROFILE_DIR}"
fi
mkdir -p "${PROFILE_DIR}"
# Detect if a previous run already loaded the extension into this profile
if [[ -d "${PROFILE_DIR}/Default/Extensions" ]] && \
find "${PROFILE_DIR}/Default/Extensions" -maxdepth 3 -name 'manifest.json' 2>/dev/null | head -1 | xargs -r grep -q 'AI Call Recorder' 2>/dev/null; then
PROFILE_HAS_EXTENSION=1
green "✓ extension already loaded in profile from previous KEEP_PROFILE=1 run"
fi
# --- compose the smoke tab data URL ---
read -r -d '' SMOKE_HTML <<'EOF' || true
<title>Mokosh Smoke Test</title>
<style>body{font-family:sans-serif;background:#222;color:#eee;padding:40px;line-height:1.5}code{background:#444;padding:2px 6px;border-radius:3px}ol li{margin:6px 0}.flash{animation:flash 1s infinite;background:#0a4;padding:4px 8px;display:inline-block}@keyframes flash{0%,100%{opacity:1}50%{opacity:.4}}</style>
<body>
<h1>🧵 Mokosh Smoke Test</h1>
<p>This tab is the share-screen target. The picker auto-accepts because the title matches <code>--auto-select-desktop-capture-source</code>.</p>
<h2>Steps:</h2>
<ol>
<li><strong class="flash">First time only:</strong> Go to <code>chrome://extensions</code> → toggle <strong>Developer mode</strong> ON → <strong>Load unpacked</strong> → select <code>/home/parf/projects/work/repremium/dist</code>.<br>(Set <code>KEEP_PROFILE=1</code> when re-running this script to skip the reload.)</li>
<li>Click the <strong>AI Call Recorder</strong> toolbar icon (or puzzle-piece menu).</li>
<li>The picker auto-accepts <em>this tab</em>. Confirm Chrome's "Sharing your screen" indicator appears.</li>
<li>Wait <strong>≥ 35 seconds</strong>. Move the mouse around or scroll this page so vp9 has frame deltas.</li>
<li>Click the toolbar icon again → click <strong>Сохранить отчёт об ошибке</strong>.</li>
</ol>
<p>The script in your terminal will detect the download and finish the ffprobe gate automatically.</p>
</body>
EOF
SMOKE_DATA_URL="data:text/html,$(printf '%s' "${SMOKE_HTML}" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(), safe=""))' 2>/dev/null || printf '%s' "${SMOKE_HTML}")"
# --- launch Chrome ---
blue "==> launching Chrome with smoke profile + auto-accept picker..."
"${CHROME_BIN}" \
--user-data-dir="${PROFILE_DIR}" \
--auto-select-desktop-capture-source="${SHARE_TARGET}" \
--no-first-run \
--no-default-browser-check \
--new-window \
"${SMOKE_DATA_URL}" \
> "${CHROME_LOG}" 2>&1 &
CHROME_PID=$!
echo " Chrome PID: ${CHROME_PID}"
echo " Tail with: tail -f ${CHROME_LOG}"
sleep 4
if ! ps -p "${CHROME_PID}" >/dev/null 2>&1; then
# Chrome may have exec'd into a singleton; check if any chrome procs are running with our profile dir
if ! pgrep -f "${PROFILE_DIR}" >/dev/null 2>&1; then
red "FAIL: Chrome exited within 4 seconds"
echo " Last 30 lines of ${CHROME_LOG}:"
tail -30 "${CHROME_LOG}" 2>/dev/null || true
exit 3
fi
fi
green "✓ Chrome running"
echo
# --- prompt ---
if [[ ${PROFILE_HAS_EXTENSION} -eq 1 ]]; then
yellow "==> Extension is already loaded. Just click the icon → wait 35s → click save."
else
yellow "==> In the Chrome window:"
echo " 1. chrome://extensions → Developer mode ON → Load unpacked → ${DIST_DIR}"
echo " 2. Confirm 'AI Call Recorder' appears with no red error."
echo " 3. Click the extension icon."
echo " 4. WAIT >= 35 seconds."
echo " 5. Click the icon again → 'Сохранить отчёт об ошибке'."
fi
echo
blue "==> waiting for a new session_report_*.zip to appear..."
echo " (Ctrl+C aborts. Auto-detects, ffprobes, stages fixture, opens WebM.)"
echo
# --- poll Downloads ---
NEW_ARCHIVE=""
WAITED=0
while [[ ${WAITED} -lt ${POLL_TIMEOUT} ]]; do
NEW_COUNT=$(find "${DOWNLOADS_DIR}" -maxdepth 1 -name 'session_report_*.zip' 2>/dev/null | wc -l)
if [[ ${NEW_COUNT} -gt ${BEFORE_LIST} ]]; then
sleep 2 # let the download settle
LATEST=$(find "${DOWNLOADS_DIR}" -maxdepth 1 -name 'session_report_*.zip' -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2- || true)
if [[ -n "${LATEST}" ]]; then
NEW_ARCHIVE="${LATEST}"
break
fi
fi
sleep 1
WAITED=$((WAITED + 1))
if [[ $((WAITED % 30)) -eq 0 ]]; then
yellow " ...still waiting (${WAITED}s elapsed, count=${NEW_COUNT}/baseline=${BEFORE_LIST})"
fi
done
if [[ -z "${NEW_ARCHIVE}" ]]; then
red "FAIL: no new archive appeared in ${POLL_TIMEOUT}s"
echo " Chrome log tail:"
tail -30 "${CHROME_LOG}" 2>/dev/null || true
echo " Chrome still running — close manually or: kill ${CHROME_PID}"
exit 4
fi
green "✓ archive detected: ${NEW_ARCHIVE}"
echo
# --- extract + ffprobe gate ---
unzip -p "${NEW_ARCHIVE}" video/last_30sec.webm > "${WEBM_TMP}"
SIZE_BYTES=$(stat -c %s "${WEBM_TMP}")
SIZE_HUMAN=$(ls -lh "${WEBM_TMP}" | awk '{print $5}')
echo " WebM size: ${SIZE_HUMAN} (${SIZE_BYTES} bytes)"
if [[ ${SIZE_BYTES} -lt 100000 ]]; then
yellow "⚠ WebM is smaller than 100 KB — buffer may not have rotated; capture longer"
fi
echo
blue "==> D-12 ACCEPTANCE GATE — ffprobe -v error"
echo "---"
ffprobe -v error -f matroska -i "${WEBM_TMP}" && GATE=0 || GATE=$?
echo "---"
echo "ffprobe exit: ${GATE}"
if [[ ${GATE} -eq 0 ]]; then
green "✓ D-12 ACCEPTANCE GATE PASSED"
else
red "✗ D-12 ACCEPTANCE GATE FAILED — D-13 fallback (restart-segments) required"
yellow "==> diagnostic for D-13 escalation:"
ffprobe -v error -show_packets -i "${WEBM_TMP}" 2>&1 | head -50 || true
fi
echo
blue "==> stream / format dump (paste this back to the orchestrator):"
echo "---"
ffprobe -v error -show_format -show_streams "${WEBM_TMP}" 2>&1 | head -30 || true
echo "---"
echo
# --- stage fixture ---
if [[ ${GATE} -eq 0 ]]; then
mkdir -p "$(dirname "${FIXTURE_DEST}")"
cp "${WEBM_TMP}" "${FIXTURE_DEST}"
green "✓ fixture staged: ${FIXTURE_DEST} ($(ls -lh "${FIXTURE_DEST}" | awk '{print $5}'))"
fi
# --- open the WebM for visual check ---
echo
blue "==> opening the WebM for visual playback (SPEC §10 #7)..."
"${CHROME_BIN}" --user-data-dir="${PROFILE_DIR}" --new-window "file://${WEBM_TMP}" >/dev/null 2>&1 &
echo
green "==> smoke complete."
echo " Chrome (smoke profile) PID ${CHROME_PID} still running."
echo " Kill when done: kill ${CHROME_PID}"
echo " To keep the extension loaded across runs: KEEP_PROFILE=1 ./smoke.sh"
echo
if [[ ${GATE} -eq 0 ]]; then
green "==> NEXT: reply 'approved' to the orchestrator with the stream/format dump."
else
red "==> NEXT: reply 'ffprobe-failed' with the show_packets diagnostic above."
fi
exit "${GATE}"