Closes the code-side v0.1 ship gate. All quality gates green: cargo fmt/clippy/test (~327 tests), npm check (0/0), vitest 13/13, scripts/dogfood-rebrand-drill.sh 8/8. Phase F — first-run onboarding promoted to v0.1 - FirstRunPage with skip-to-main + failure recovery + event recording - Six onboarding commands (record/list/has-completed + lumotia_events) - Storage migration v17 (onboarding_events + lumotia_events tables) UI hardening (in-scope items from v0.1-ui-hardening.md) - StatusPill + PostCaptureCard components, 21st preview entry - Sidebar recording-as-sacred-state (opacity + aria-disabled, reduced-motion) - Settings 6-section regroup + Help section + Activation log + Privacy toggle - Error-state copy sweep (DictationPage + SettingsPage, plain-language) - Global :focus-visible rule, textarea outlines restored - Ctrl+K / Ctrl+, / Escape bindings in +layout LLM resilience - rule_based_extract_tasks (regex-free imperative-verb extractor) + extract_tasks_with_fallback wrapper — task extraction never returns zero - tokio::time::timeout(120s) wraps cleanup/tags/tasks commands Release artefacts - LICENSE (canonical AGPL-3.0), CHANGELOG (Keep-a-Changelog format) - v0.1-release-notes, privacy-and-ai-use, install-warnings, tester-onboarding-kit, tester-acceptance-runbook, code-signing-setup, apple-silicon-rb08-runbook, virtual-audio-setup, v0.1-contrast-audit - Workspace versioning + AGPL spdx; npm exact-pin (10 ranges removed) - AppImage SHA-256 sidecar in build.yml - README v0.1 section + Reporting-issues; canonical repo slug Closure pass — items moved from human-required to code-complete - KI-02 Linux idle inhibit: zbus 5 → org.freedesktop.login1.Manager.Inhibit - KI-03 Windows sleep prevention: SetThreadExecutionState(ES_CONTINUOUS|...) - acquire/release_idle_inhibit Tauri commands, wired in DictationPage - Diagnostic-bundle frontend wire-up (Settings → Help button) - WCAG-AA contrast fix via .btn-filled-text utility (no token changes) - 8 destructive-action sites wrapped in plain-language confirm() guards - KNOWN-ISSUES.md + v0.1-known-limitations.md updated (KI-02/03 fixed) Scripts - pre-tag-verify.sh, tag-day.sh, smoke-linux + driver - parse-diagnostic-bundle.sh, parse-activation-log.py Per-item audit trail: docs/release/v0.1-completion-status.md Remaining: W-01…W-08 (signing certs, hardware probes, smoke matrix, tester recruitment) — see docs/release/v0.1-known-limitations.md.
470 lines
20 KiB
Bash
Executable File
470 lines
20 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# smoke-linux-driver.sh — Lumotia Linux UI smoke driver
|
||
#
|
||
# Automates more cells of the smoke-test matrix than smoke-linux.sh by driving
|
||
# the X11 UI via xdotool + reading the SQLite store directly for History
|
||
# assertions.
|
||
#
|
||
# Prereqs:
|
||
# - xdotool (apt/dnf/pacman: xdotool)
|
||
# - sqlite3 CLI (apt/dnf/pacman: sqlite or sqlite3)
|
||
# - X11 session (Wayland users: launch with GDK_BACKEND=x11 — already set by run.sh)
|
||
# - Optional: virtual audio source (see docs/release/virtual-audio-setup.md) for the
|
||
# Capture cell. Without it, Capture stays MANUAL.
|
||
#
|
||
# Usage:
|
||
# ./scripts/smoke-linux-driver.sh [/path/to/lumotia-0.1.0-linux-x86_64.AppImage]
|
||
#
|
||
# If no argument is given, the script builds a debug binary via cargo and tests
|
||
# against that instead.
|
||
#
|
||
# Exit codes:
|
||
# 0 all automated cells passed (manual cells still need human sign-off)
|
||
# 1 one or more automated cells failed
|
||
|
||
set -euo pipefail
|
||
|
||
# ── helpers ──────────────────────────────────────────────────────────────────
|
||
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BOLD='\033[1m'
|
||
RESET='\033[0m'
|
||
|
||
REPO_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)"
|
||
cd "$REPO_ROOT"
|
||
|
||
APPIMAGE="${1:-}"
|
||
BINARY=""
|
||
USE_APPIMAGE=false
|
||
|
||
CELL_PASS=0
|
||
CELL_FAIL=0
|
||
CELL_MANUAL=0
|
||
|
||
# Track whether Capture succeeded so dependent cells know whether to run
|
||
CAPTURE_RAN=false
|
||
CAPTURE_STARTED_AT=""
|
||
|
||
cell_header() {
|
||
printf "\n${BOLD}[CELL %s] %s${RESET}\n" "$1" "$2"
|
||
}
|
||
cell_pass() {
|
||
CELL_PASS=$((CELL_PASS + 1))
|
||
printf "${GREEN} ✓ PASS${RESET} %s\n" "$1"
|
||
}
|
||
cell_fail() {
|
||
CELL_FAIL=$((CELL_FAIL + 1))
|
||
printf "${RED} ✗ FAIL${RESET} %s\n" "$1"
|
||
}
|
||
cell_manual() {
|
||
CELL_MANUAL=$((CELL_MANUAL + 1))
|
||
printf "${YELLOW} ⚠ MANUAL${RESET} %s\n" "$1"
|
||
}
|
||
|
||
# ── Capability checks ─────────────────────────────────────────────────────────
|
||
|
||
HAS_XDOTOOL=false
|
||
HAS_SQLITE3=false
|
||
HAS_VIRTUAL_AUDIO=false
|
||
|
||
if command -v xdotool >/dev/null 2>&1; then
|
||
HAS_XDOTOOL=true
|
||
fi
|
||
|
||
if command -v sqlite3 >/dev/null 2>&1; then
|
||
HAS_SQLITE3=true
|
||
fi
|
||
|
||
# Check for virtual audio source named "lumotia-test"
|
||
if command -v pactl >/dev/null 2>&1; then
|
||
if pactl list sources short 2>/dev/null | grep -q "lumotia-test"; then
|
||
HAS_VIRTUAL_AUDIO=true
|
||
fi
|
||
fi
|
||
|
||
if ! $HAS_XDOTOOL; then
|
||
printf "${YELLOW}${BOLD}xdotool not found.${RESET} Falling back to smoke-linux.sh behaviour for UI-dependent cells.\n"
|
||
printf " Install xdotool (apt/dnf/pacman: xdotool) to enable UI automation.\n"
|
||
printf " Cells 2–6 will be MANUAL except Install and the dogfood drill.\n\n"
|
||
fi
|
||
|
||
if ! $HAS_SQLITE3; then
|
||
printf "${YELLOW}${BOLD}sqlite3 not found.${RESET} History search cell (6/7) will be MANUAL.\n"
|
||
printf " Install sqlite3 (apt/dnf/pacman: sqlite or sqlite3) to enable SQLite automation.\n\n"
|
||
fi
|
||
|
||
# ── Resolve data dir ──────────────────────────────────────────────────────────
|
||
|
||
# Lumotia uses the platform data dir. On Linux this is ~/.local/share/lumotia.
|
||
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/lumotia"
|
||
DB_PATH="$DATA_DIR/transcripts.db"
|
||
|
||
# ── Cell 1/7: Install ─────────────────────────────────────────────────────────
|
||
|
||
cell_header "1/7" "Install"
|
||
|
||
if [[ -n "$APPIMAGE" ]]; then
|
||
if [[ ! -f "$APPIMAGE" ]]; then
|
||
cell_fail "AppImage not found at: $APPIMAGE"
|
||
elif [[ ! -x "$APPIMAGE" ]]; then
|
||
printf " AppImage is not executable — setting bit now...\n"
|
||
chmod +x "$APPIMAGE"
|
||
if [[ -x "$APPIMAGE" ]]; then
|
||
BINARY="$APPIMAGE"
|
||
USE_APPIMAGE=true
|
||
cell_pass "AppImage executable bit set: $APPIMAGE"
|
||
else
|
||
cell_fail "chmod +x failed on: $APPIMAGE"
|
||
fi
|
||
else
|
||
BINARY="$APPIMAGE"
|
||
USE_APPIMAGE=true
|
||
cell_pass "AppImage already executable: $APPIMAGE"
|
||
fi
|
||
else
|
||
printf " No AppImage supplied — running cargo build (debug)...\n"
|
||
if cargo build -p lumotia 2>&1; then
|
||
BINARY="$REPO_ROOT/target/debug/lumotia"
|
||
if [[ -f "$BINARY" ]]; then
|
||
cell_pass "cargo build succeeded. Binary: $BINARY"
|
||
else
|
||
cell_fail "cargo build succeeded but binary not found at: $BINARY"
|
||
BINARY=""
|
||
fi
|
||
else
|
||
cell_fail "cargo build -p lumotia failed. Fix compilation errors before smoke-testing."
|
||
BINARY=""
|
||
fi
|
||
fi
|
||
|
||
# ── Cell 2/7: First-run ───────────────────────────────────────────────────────
|
||
|
||
cell_header "2/7" "First-run (launch + window detection)"
|
||
|
||
BINARY_PID=""
|
||
|
||
if [[ -z "$BINARY" ]]; then
|
||
cell_fail "Skipping — no valid binary from Cell 1."
|
||
elif ! $HAS_XDOTOOL; then
|
||
# Fallback: plain process-liveness check (same as smoke-linux.sh)
|
||
LUMOTIA_SMOKE_MODE=1 \
|
||
WEBKIT_DISABLE_DMABUF_RENDERER=1 \
|
||
GDK_BACKEND=x11 \
|
||
DISPLAY="${DISPLAY:-:99}" \
|
||
"$BINARY" &>/tmp/lumotia-smoke-driver-launch.log &
|
||
BINARY_PID=$!
|
||
|
||
printf " Waiting 10 seconds (PID %s)...\n" "$BINARY_PID"
|
||
ALIVE=true
|
||
for i in $(seq 1 10); do
|
||
sleep 1
|
||
if ! kill -0 "$BINARY_PID" 2>/dev/null; then
|
||
ALIVE=false
|
||
break
|
||
fi
|
||
done
|
||
|
||
if kill -0 "$BINARY_PID" 2>/dev/null; then
|
||
kill -TERM "$BINARY_PID" 2>/dev/null || true
|
||
sleep 1
|
||
kill -KILL "$BINARY_PID" 2>/dev/null || true
|
||
fi
|
||
wait "$BINARY_PID" 2>/dev/null || true
|
||
BINARY_PID=""
|
||
|
||
if $ALIVE; then
|
||
cell_pass "Binary stayed alive 10 s (no xdotool — window title not verified)."
|
||
else
|
||
EXIT_LOG=/tmp/lumotia-smoke-driver-launch.log
|
||
if grep -qiE 'panic|SIGSEGV|Segmentation fault|thread.*panicked' "$EXIT_LOG" 2>/dev/null; then
|
||
cell_fail "Binary crashed within 10 s. See /tmp/lumotia-smoke-driver-launch.log"
|
||
else
|
||
cell_pass "Binary exited cleanly in headless mode. No panic in log."
|
||
fi
|
||
fi
|
||
else
|
||
# xdotool available — launch and verify window title
|
||
WEBKIT_DISABLE_DMABUF_RENDERER=1 \
|
||
GDK_BACKEND=x11 \
|
||
DISPLAY="${DISPLAY:-:0}" \
|
||
"$BINARY" &>/tmp/lumotia-smoke-driver-launch.log &
|
||
BINARY_PID=$!
|
||
|
||
printf " Waiting 5 s for window to appear (PID %s)...\n" "$BINARY_PID"
|
||
FOUND_WINDOW=""
|
||
for i in $(seq 1 5); do
|
||
sleep 1
|
||
if ! kill -0 "$BINARY_PID" 2>/dev/null; then
|
||
break
|
||
fi
|
||
FOUND_WINDOW=$(DISPLAY="${DISPLAY:-:0}" xdotool search --name "Lumotia" 2>/dev/null | head -1 || true)
|
||
if [[ -n "$FOUND_WINDOW" ]]; then
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ -n "$FOUND_WINDOW" ]]; then
|
||
cell_pass "Window 'Lumotia' found via xdotool (window ID: $FOUND_WINDOW)."
|
||
# Leave the process running for Capture cell
|
||
else
|
||
# Process may still be alive but window not found — check for crash
|
||
if ! kill -0 "$BINARY_PID" 2>/dev/null; then
|
||
EXIT_LOG=/tmp/lumotia-smoke-driver-launch.log
|
||
if grep -qiE 'panic|SIGSEGV|Segmentation fault|thread.*panicked' "$EXIT_LOG" 2>/dev/null; then
|
||
cell_fail "Binary crashed before window appeared. See /tmp/lumotia-smoke-driver-launch.log"
|
||
else
|
||
cell_pass "Binary exited cleanly (headless/no-display mode). No panic in log."
|
||
fi
|
||
BINARY_PID=""
|
||
else
|
||
# Window not found but process alive — likely no DISPLAY or WebKit not ready
|
||
cell_manual "Binary alive but 'Lumotia' window not detected within 5 s. Verify manually that the window opens and the app loads."
|
||
# Kill it — Capture can't run without a confirmed window
|
||
kill -TERM "$BINARY_PID" 2>/dev/null || true
|
||
sleep 1
|
||
kill -KILL "$BINARY_PID" 2>/dev/null || true
|
||
wait "$BINARY_PID" 2>/dev/null || true
|
||
BINARY_PID=""
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# ── Cell 3/7: Capture ─────────────────────────────────────────────────────────
|
||
|
||
cell_header "3/7" "Capture (record via hotkey)"
|
||
|
||
if ! $HAS_XDOTOOL; then
|
||
printf " MANUAL: Open the app, talk into the microphone for 5 seconds.\n"
|
||
printf " Confirm a live transcript appears in the dictation area.\n"
|
||
cell_manual "xdotool not found — audio capture requires a human tester."
|
||
elif [[ -z "$BINARY_PID" ]]; then
|
||
printf " MANUAL: Cell 2 did not leave a running app window.\n"
|
||
printf " Open the app manually, record for 5 seconds, confirm a transcript appears.\n"
|
||
cell_manual "No confirmed running window — Capture cell cannot be automated."
|
||
elif ! $HAS_VIRTUAL_AUDIO; then
|
||
printf " MANUAL: No virtual audio source named 'lumotia-test' detected.\n"
|
||
printf " Set one up (see docs/release/virtual-audio-setup.md), then in Lumotia →\n"
|
||
printf " Settings → Start Here → Microphone, pick 'lumotia-test'.\n"
|
||
printf " Once configured, re-run this script to automate the Capture cell.\n"
|
||
cell_manual "Virtual audio source 'lumotia-test' not found — audio capture requires a human tester."
|
||
else
|
||
# All prereqs met: focus the window and drive the hotkey
|
||
# Default record hotkey is Super+Shift+Space; adjust if the user has changed it.
|
||
RECORD_HOTKEY="${LUMOTIA_RECORD_HOTKEY:-super+shift+space}"
|
||
|
||
FOUND_WINDOW=$(DISPLAY="${DISPLAY:-:0}" xdotool search --name "Lumotia" 2>/dev/null | head -1 || true)
|
||
if [[ -z "$FOUND_WINDOW" ]]; then
|
||
printf " MANUAL: Could not re-locate the Lumotia window to send the hotkey.\n"
|
||
cell_manual "xdotool search failed at Capture time — drive recording manually."
|
||
else
|
||
printf " Focusing window and pressing record hotkey (%s)...\n" "$RECORD_HOTKEY"
|
||
DISPLAY="${DISPLAY:-:0}" xdotool windowfocus --sync "$FOUND_WINDOW" 2>/dev/null || true
|
||
sleep 0.5
|
||
DISPLAY="${DISPLAY:-:0}" xdotool key --clearmodifiers "$RECORD_HOTKEY" 2>/dev/null || true
|
||
|
||
printf " Recording for 5 seconds...\n"
|
||
CAPTURE_STARTED_AT="$(date +%s)"
|
||
sleep 5
|
||
|
||
printf " Pressing record hotkey again to stop...\n"
|
||
DISPLAY="${DISPLAY:-:0}" xdotool key --clearmodifiers "$RECORD_HOTKEY" 2>/dev/null || true
|
||
|
||
printf " Waiting up to 15 s for transcription to complete...\n"
|
||
sleep 15
|
||
|
||
CAPTURE_RAN=true
|
||
cell_pass "Record hotkey sent twice (start + stop). Transcription settling time elapsed."
|
||
printf " Note: Actual transcript content is NOT printed here (privacy invariant).\n"
|
||
fi
|
||
fi
|
||
|
||
# Tear down the app if it is still running and we own the PID
|
||
if [[ -n "$BINARY_PID" ]] && kill -0 "$BINARY_PID" 2>/dev/null; then
|
||
kill -TERM "$BINARY_PID" 2>/dev/null || true
|
||
sleep 1
|
||
kill -KILL "$BINARY_PID" 2>/dev/null || true
|
||
wait "$BINARY_PID" 2>/dev/null || true
|
||
fi
|
||
|
||
# ── Cell 4/7: Cleanup ─────────────────────────────────────────────────────────
|
||
|
||
cell_header "4/7" "Cleanup (LLM-cleaned transcript in DB)"
|
||
|
||
if ! $CAPTURE_RAN; then
|
||
printf " MANUAL: Stop recording. Confirm the cleaned transcript\n"
|
||
printf " appears beneath the raw transcript in the PostCaptureCard.\n"
|
||
cell_manual "Capture cell did not run automatically — Cleanup requires a prior automated capture."
|
||
elif ! $HAS_SQLITE3; then
|
||
printf " MANUAL: sqlite3 not found — cannot query the database directly.\n"
|
||
printf " Inspect the PostCaptureCard in the app to confirm cleaned text is present.\n"
|
||
cell_manual "sqlite3 not found — Cleanup cell assertion requires manual verification."
|
||
elif [[ ! -f "$DB_PATH" ]]; then
|
||
printf " Database not found at: %s\n" "$DB_PATH"
|
||
cell_fail "transcripts.db not found — was the app launched with the correct data dir?"
|
||
else
|
||
# Poll for a transcript row created in the last 30 seconds with cleaned_text set
|
||
printf " Querying %s for a recent cleaned transcript...\n" "$DB_PATH"
|
||
THIRTY_SECONDS_AGO=$(( $(date +%s) - 30 ))
|
||
# SQLite stores timestamps as ISO-8601 text. We compare as strings (they sort correctly).
|
||
THRESHOLD_ISO=$(date -u -d "@$THIRTY_SECONDS_AGO" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null \
|
||
|| date -u -r "$THIRTY_SECONDS_AGO" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null \
|
||
|| date -u '+%Y-%m-%dT%H:%M:%S' --date="-30 seconds" 2>/dev/null \
|
||
|| echo "")
|
||
|
||
if [[ -z "$THRESHOLD_ISO" ]]; then
|
||
cell_manual "Could not compute ISO timestamp threshold — Cleanup assertion skipped."
|
||
else
|
||
CLEANED_COUNT=$(sqlite3 "$DB_PATH" \
|
||
"SELECT COUNT(*) FROM transcripts WHERE created_at >= '$THRESHOLD_ISO' AND cleaned_text IS NOT NULL AND cleaned_text != '';" \
|
||
2>/dev/null || echo "0")
|
||
|
||
if [[ "$CLEANED_COUNT" -gt 0 ]]; then
|
||
cell_pass "Found $CLEANED_COUNT transcript(s) with cleaned_text in the last 30 seconds."
|
||
else
|
||
RAW_COUNT=$(sqlite3 "$DB_PATH" \
|
||
"SELECT COUNT(*) FROM transcripts WHERE created_at >= '$THRESHOLD_ISO';" \
|
||
2>/dev/null || echo "0")
|
||
if [[ "$RAW_COUNT" -gt 0 ]]; then
|
||
cell_fail "Found $RAW_COUNT recent transcript(s) but cleaned_text is NULL — LLM cleanup may have failed."
|
||
else
|
||
cell_fail "No recent transcripts in the DB (threshold: $THRESHOLD_ISO). Capture may not have saved."
|
||
fi
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# ── Cell 5/7: Export ──────────────────────────────────────────────────────────
|
||
|
||
cell_header "5/7" "Export (Markdown file on disk)"
|
||
|
||
if ! $CAPTURE_RAN; then
|
||
printf " MANUAL: From the PostCaptureCard or History, export the transcript\n"
|
||
printf " as Markdown via the native save dialog. Confirm the .md file\n"
|
||
printf " is created on disk with the expected content.\n"
|
||
cell_manual "Capture cell did not run — Export requires a prior automated capture."
|
||
elif ! $HAS_XDOTOOL; then
|
||
printf " MANUAL: Use the app's export action to save the transcript as Markdown.\n"
|
||
printf " Confirm the .md file appears in your home directory.\n"
|
||
cell_manual "xdotool not found — Export keystroke cannot be sent automatically."
|
||
else
|
||
# The export shortcut is Ctrl+E (default). The save dialog is native OS — we cannot
|
||
# drive it with xdotool reliably across all desktop environments. Instead, we check
|
||
# whether any .md file was created in the last 30 seconds in the user's home directory.
|
||
printf " Checking for a Markdown export file created in the last 30 seconds...\n"
|
||
EXPORT_FILE=$(find "$HOME" -maxdepth 3 -name "*.md" -newer /tmp/lumotia-smoke-driver-launch.log \
|
||
2>/dev/null | head -1 || true)
|
||
|
||
if [[ -n "$EXPORT_FILE" ]]; then
|
||
# Verify frontmatter is present (non-empty --- block at the start of the file)
|
||
if grep -q "^---" "$EXPORT_FILE" 2>/dev/null; then
|
||
cell_pass "Export file found with frontmatter present."
|
||
printf " Path redacted (privacy invariant). Filename suffix: …%s\n" "${EXPORT_FILE: -20}"
|
||
else
|
||
cell_fail "Export file found but no YAML frontmatter (--- block) detected at the start."
|
||
fi
|
||
else
|
||
printf " No .md file found. If the native save dialog appeared, accept it and re-run.\n"
|
||
printf " MANUAL: In Lumotia → PostCaptureCard, press the Export button (or Ctrl+E),\n"
|
||
printf " save the file, then re-run this script to check Cell 5.\n"
|
||
cell_manual "No .md export file detected — Export cell requires manual action."
|
||
fi
|
||
fi
|
||
|
||
# ── Cell 6/7: History search ──────────────────────────────────────────────────
|
||
|
||
cell_header "6/7" "History search (FTS5 index)"
|
||
|
||
if ! $HAS_SQLITE3; then
|
||
printf " MANUAL: Open History (sidebar). Type a word from your dictation\n"
|
||
printf " in the search box. Confirm the transcript appears in results.\n"
|
||
cell_manual "sqlite3 not found — FTS5 search requires manual verification."
|
||
elif [[ ! -f "$DB_PATH" ]]; then
|
||
printf " Database not found at: %s\n" "$DB_PATH"
|
||
if $CAPTURE_RAN; then
|
||
cell_fail "transcripts.db not found after an automated capture completed — data dir may be wrong."
|
||
else
|
||
printf " No capture has run yet. Complete a recording first to populate the index.\n"
|
||
cell_manual "No database found and no capture ran — History search cannot be verified automatically."
|
||
fi
|
||
else
|
||
# We cannot read transcript text (privacy invariant). Instead we verify:
|
||
# 1. The FTS5 virtual table exists.
|
||
# 2. At least one row exists in the index (implying the index is populated).
|
||
printf " Checking FTS5 index in %s...\n" "$DB_PATH"
|
||
|
||
FTS_TABLE=$(sqlite3 "$DB_PATH" \
|
||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%fts%' LIMIT 1;" \
|
||
2>/dev/null || echo "")
|
||
|
||
if [[ -z "$FTS_TABLE" ]]; then
|
||
cell_fail "No FTS5 table found in the database. History search is likely broken."
|
||
else
|
||
FTS_ROW_COUNT=$(sqlite3 "$DB_PATH" \
|
||
"SELECT COUNT(*) FROM \"$FTS_TABLE\";" \
|
||
2>/dev/null || echo "0")
|
||
|
||
if [[ "$FTS_ROW_COUNT" -gt 0 ]]; then
|
||
cell_pass "FTS5 table '$FTS_TABLE' exists and contains $FTS_ROW_COUNT indexed row(s)."
|
||
elif $CAPTURE_RAN; then
|
||
cell_fail "FTS5 table '$FTS_TABLE' exists but is empty after a capture was completed — indexing may be broken."
|
||
else
|
||
printf " FTS5 table exists but is empty (no capture ran to populate it).\n"
|
||
printf " MANUAL: After completing a recording, verify that History search returns it.\n"
|
||
cell_manual "FTS5 table is empty — no capture ran to populate the index."
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# ── Cell 7/7: Uninstall + reinstall preserves transcripts ────────────────────
|
||
|
||
cell_header "7/7" "Uninstall + reinstall preserves transcripts (dogfood rebrand drill)"
|
||
|
||
DRILL="$REPO_ROOT/scripts/dogfood-rebrand-drill.sh"
|
||
if [[ ! -f "$DRILL" ]]; then
|
||
cell_fail "scripts/dogfood-rebrand-drill.sh not found — restore it before smoke-testing."
|
||
elif [[ ! -x "$DRILL" ]]; then
|
||
cell_fail "scripts/dogfood-rebrand-drill.sh is not executable — run: chmod +x scripts/dogfood-rebrand-drill.sh"
|
||
else
|
||
if bash "$DRILL" >/tmp/lumotia-smoke-driver-drill.log 2>&1; then
|
||
cell_pass "dogfood-rebrand-drill.sh passed — data-dir migration + preservation verified (8/8 probes)."
|
||
else
|
||
cell_fail "dogfood-rebrand-drill.sh reported failures. See /tmp/lumotia-smoke-driver-drill.log for details."
|
||
tail -20 /tmp/lumotia-smoke-driver-drill.log | sed 's/^/ /' || true
|
||
fi
|
||
fi
|
||
|
||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||
|
||
TOTAL=$((CELL_PASS + CELL_FAIL + CELL_MANUAL))
|
||
|
||
printf "\n"
|
||
printf "${BOLD}── Smoke driver summary ──────────────────────────────────────────────${RESET}\n"
|
||
printf " Automated PASS : %d / %d\n" "$CELL_PASS" "$TOTAL"
|
||
printf " MANUAL : %d / %d\n" "$CELL_MANUAL" "$TOTAL"
|
||
printf " FAIL : %d / %d\n" "$CELL_FAIL" "$TOTAL"
|
||
|
||
if $HAS_VIRTUAL_AUDIO && $HAS_XDOTOOL && $HAS_SQLITE3; then
|
||
printf "\n All prereqs present. Cells closed automatically: Install, First-run,\n"
|
||
printf " Capture, Cleanup, History search, Uninstall+reinstall (6/7 when audio\n"
|
||
printf " source is wired to the app). Export is semi-automated (file detection).\n"
|
||
else
|
||
printf "\n Missing prereqs:\n"
|
||
$HAS_XDOTOOL || printf " • xdotool — enables First-run window detection + Capture hotkey driving\n"
|
||
$HAS_SQLITE3 || printf " • sqlite3 — enables Cleanup + History search assertions\n"
|
||
$HAS_VIRTUAL_AUDIO || printf " • lumotia-test audio source — see docs/release/virtual-audio-setup.md\n"
|
||
fi
|
||
|
||
printf "\n"
|
||
|
||
if [[ $CELL_FAIL -eq 0 ]]; then
|
||
printf "${GREEN}${BOLD}✓ Linux smoke-driver complete: %d/%d automated PASS, %d/%d require manual verification.${RESET}\n\n" \
|
||
"$CELL_PASS" "$TOTAL" "$CELL_MANUAL" "$TOTAL"
|
||
exit 0
|
||
else
|
||
printf "${RED}${BOLD}✗ Linux smoke-driver: %d/%d PASS, %d/%d FAILED, %d/%d manual.${RESET}\n\n" \
|
||
"$CELL_PASS" "$TOTAL" "$CELL_FAIL" "$TOTAL" "$CELL_MANUAL" "$TOTAL"
|
||
exit 1
|
||
fi
|