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.
This commit is contained in:
153
scripts/parse-diagnostic-bundle.sh
Executable file
153
scripts/parse-diagnostic-bundle.sh
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user