Files
Lumotia/scripts/parse-diagnostic-bundle.sh
Jake 3770815fbf
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
agent: lumotia — v0.1 release-completion run
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.
2026-05-15 06:59:08 +01:00

154 lines
6.4 KiB
Bash
Executable File
Raw Permalink 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
# parse-diagnostic-bundle.sh — summarise a Lumotia diagnostic bundle zip.
# Usage: ./scripts/parse-diagnostic-bundle.sh path/to/tester-bundle.zip
# Requires: unzip (required), jq (preferred; graceful grep fallback).
# Privacy: never prints transcript text, audio content, or .db content.
set -euo pipefail
BOLD='\033[1m'; GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[0;33m'; RESET='\033[0m'
pass() { printf "${GREEN}${RESET} %s\n" "$*"; }
fail() { printf "${RED} ×${RESET} %s\n" "$*"; OVERALL_FAIL=1; }
warn() { printf "${YELLOW} !${RESET} %s\n" "$*"; }
info() { printf " %s\n" "$*"; }
header(){ printf "\n${BOLD}%s${RESET}\n" "$*"; }
OVERALL_FAIL=0
# Argument + file validation
[[ $# -lt 1 ]] && { echo "Usage: $0 path/to/tester-bundle.zip" >&2; exit 1; }
BUNDLE="$1"; BUNDLE_NAME="$(basename "$BUNDLE")"
[[ ! -f "$BUNDLE" ]] && { echo "ERROR: file not found: $BUNDLE" >&2; exit 2; }
if ! file "$BUNDLE" 2>/dev/null | grep -qi 'zip'; then
(dd if="$BUNDLE" bs=2 count=2 2>/dev/null | grep -q 'PK') \
|| { echo "ERROR: $BUNDLE does not appear to be a zip archive." >&2; exit 2; }
fi
# jq check
HAS_JQ=0
command -v jq &>/dev/null && HAS_JQ=1 \
|| echo "WARNING: jq not found — falling back to grep-based extraction. Install jq for best results." >&2
# Extract
TMPDIR_WORK="$(mktemp -d)"; trap 'rm -rf "$TMPDIR_WORK"' EXIT
unzip -q "$BUNDLE" -d "$TMPDIR_WORK" 2>/dev/null \
|| { echo "ERROR: failed to extract $BUNDLE" >&2; exit 3; }
# Header
TITLE="LUMOTIA DIAGNOSTIC BUNDLE SUMMARY: $BUNDLE_NAME"
printf "\n${BOLD}%s${RESET}\n" "$TITLE"
printf '%0.s=' $(seq 1 ${#TITLE}); printf '\n'
# metadata.json → version + timestamps
META="$TMPDIR_WORK/metadata.json"; GEN_AT=""; VERSION=""
if [[ -f "$META" ]]; then
if [[ $HAS_JQ -eq 1 ]]; then
GEN_AT="$(jq -r '.generated_at // empty' "$META" 2>/dev/null)"
VERSION="$(jq -r '.lumotia_version // empty' "$META" 2>/dev/null)"
else
GEN_AT="$(grep -o '"generated_at":[^,}]*' "$META" | grep -o '[0-9]*' | head -1)"
VERSION="$(grep -o '"lumotia_version":"[^"]*"' "$META" | cut -d'"' -f4)"
fi
fi
GEN_HUMAN=""
if [[ -n "$GEN_AT" && "$GEN_AT" =~ ^[0-9]+$ ]]; then
GEN_HUMAN="$(date -u -d "@$GEN_AT" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
|| date -u -r "$GEN_AT" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
|| echo "$GEN_AT (epoch)")"
fi
info "Generated: ${GEN_HUMAN:-unknown}"
info "Lumotia version: ${VERSION:-unknown}"
# system_info.txt → platform
SYSINFO="$TMPDIR_WORK/system_info.txt"
if [[ -f "$SYSINFO" ]]; then
OS_LINE="$(grep -i '^OS:' "$SYSINFO" | head -1 | sed 's/^OS: *//')"
ARCH_LINE="$(grep -i '^Arch:' "$SYSINFO" | head -1 | sed 's/^Arch: *//')"
info "Platform: ${OS_LINE:-unknown} / ${ARCH_LINE:-unknown}"
fi
# Content inventory
header "CONTENT INVENTORY:"
check_path() {
[[ -e "$TMPDIR_WORK/$2" ]] && pass "$1" || warn "$1 (not present)"
}
check_path "system_info.txt" "system_info.txt"
check_path "preferences-redacted.json" "preferences-redacted.json"
check_path "metadata.json" "metadata.json"
LOG_DIR="$TMPDIR_WORK/logs"; LOG_COUNT=0
if [[ -d "$LOG_DIR" ]]; then
LOG_COUNT="$(find "$LOG_DIR" -type f | wc -l)"
pass "logs/ ($LOG_COUNT files, $(du -sh "$LOG_DIR" 2>/dev/null | cut -f1))"
else warn "logs/ (directory not present)"; fi
CRASH_DIR="$TMPDIR_WORK/crashes"
if [[ -d "$CRASH_DIR" ]]; then
CRASH_COUNT="$(find "$CRASH_DIR" -type f | wc -l)"
[[ "$CRASH_COUNT" -eq 0 ]] \
&& pass "crashes/ (0 files) — no crash dumps, good" \
|| warn "crashes/ ($CRASH_COUNT files) — crash dumps present, review carefully"
else pass "crashes/ (directory absent) — no crash dumps, good"; fi
# Redaction check — bundle MUST NEVER contain these
header "REDACTION CHECK (bundle MUST NEVER include these):"
DENY_CLEAN=1
check_absent() {
local hits; hits="$(find "$TMPDIR_WORK" -type f | { grep -iE "$2" 2>/dev/null || true; })"
[[ -z "$hits" ]] && pass "$1" \
|| { fail "$1 — FOUND: $(printf '%s' "$hits" | head -3 | tr '\n' ' ')"; DENY_CLEAN=0; }
}
check_absent "no .wav files" '\.wav$'
check_absent "no .mp3/.opus/.ogg/.flac files" '\.(mp3|opus|ogg|flac)$'
check_absent "no transcripts/ paths" '/transcripts/'
check_absent "no captures/ paths" '/captures/'
check_absent "no .db files" '\.(db|db-wal|db-shm)$'
check_absent "no .env files" '(^|/)\.env(\.|$)'
if [[ $DENY_CLEAN -eq 1 ]]; then
printf "\n${GREEN} PASS: bundle passed redaction check.${RESET}\n"
else
printf "\n${RED} FAIL: bundle contains DENIED content — do not share this bundle.${RESET}\n"
OVERALL_FAIL=1
fi
# Log analysis
header "LOG ANALYSIS (last 7 days):"
if [[ -d "$LOG_DIR" && "$LOG_COUNT" -gt 0 ]]; then
ALL_LOGS="$(find "$LOG_DIR" -type f -exec cat {} \;)"
ERR_COUNT="$(printf '%s' "$ALL_LOGS" | { grep -iE '(ERROR|ERRO|\bERR\b)' 2>/dev/null || true; } | wc -l)"
WARN_COUNT="$(printf '%s' "$ALL_LOGS" | { grep -iE '(WARN|WARNING)' 2>/dev/null || true; } | wc -l)"
info "Errors: $ERR_COUNT"
info "Warnings: $WARN_COUNT"
if [[ "$ERR_COUNT" -gt 0 ]]; then
info ""; info "TOP 3 ERROR PATTERNS:"
printf '%s' "$ALL_LOGS" \
| { grep -iE '(ERROR|ERRO|\bERR\b)' || true; } \
| sed 's/^[0-9TZ:. -]*//' | sort | uniq -c | sort -rn | head -3 \
| while IFS= read -r line; do info " × $line"; done
fi
else warn "No log files found."; fi
# Preferences (non-secret values)
PREFS="$TMPDIR_WORK/preferences-redacted.json"
header "PREFERENCES (redacted secrets):"
if [[ ! -f "$PREFS" ]]; then
warn "preferences-redacted.json not found"
elif [[ $HAS_JQ -eq 1 ]]; then
jq -r 'paths(scalars) as $p | getpath($p)
| select(. != "[redacted]" and . != null and . != "")
| "\($p | join(".")): \(.)"' "$PREFS" 2>/dev/null | head -30 \
| while IFS= read -r line; do info " $line"; done
REDACTED_COUNT="$(jq '[.. | strings | select(. == "[redacted]")] | length' "$PREFS" 2>/dev/null || echo '?')"
info " ($REDACTED_COUNT field(s) redacted by bundler)"
else
{ grep -oE '"[^"]+": *("[^"]*"|[0-9.]+|true|false)' "$PREFS" || true; } \
| grep -v '\[redacted\]' | head -30 | while IFS= read -r line; do info " $line"; done
fi
# Verdict
header "VERDICT:"
if [[ $OVERALL_FAIL -eq 0 ]]; then
printf "${GREEN} PASS: bundle looks healthy. Safe to inspect for bug triage.${RESET}\n\n"
else
printf "${RED} FAIL: one or more checks failed — review items marked × above.${RESET}\n\n"
exit 1
fi