agent: lumotia — Phase A.6 dogfood drill for rebrand migration on real OS paths
scripts/dogfood-rebrand-drill.sh — end-to-end probe that launches the real
lumotia binary against synthetic legacy magnotia state on disk, then
verifies both migration paths produced the expected outcome:
1. paths.rs: ~/.local/share/magnotia/ -> ~/.local/share/lumotia/, including
magnotia.db -> lumotia.db rename + non-DB companion files carried along
by the directory rename.
2. tauri_app_data_migration.rs: ~/.local/share/uk.co.corbel.magnotia/
copied via atomic staging to ~/.local/share/consulting.corbel.lumotia/,
with legacy preserved as a backup and staging dir cleaned up.
Closes the last gap in Phase A: every other test (paths::tests + storage
integration test + localStorageMigration.test.ts) uses synthetic in-process
state. The drill is the only verification that the real binary's startup
hook calls migrate_legacy_data_dir + migrate_tauri_app_data_dir_with_paths
against real OS path resolution.
Two modes:
(default) Sandbox: HOME=<tempdir>, faithful on Linux. NOT
faithful on macOS — Tauri 2 uses
NSSearchPathForDirectoriesInDomains which ignores
HOME overrides. Drill refuses to start in sandbox
mode on macOS rather than silently writing to the
user's real Application Support tree.
--against-real-home Real $HOME. Refuses to start if any lumotia data
already exists at the real paths (no clobbering
real user data). Cleans up planted state on exit
unless --keep is passed.
Eight probes covering: data-dir rename outcome, db file rename, legacy
removal, companion file survival, Tauri app_data_dir copy, legacy-backup
preservation, staging-dir cleanup, and lumotia_startup log line presence.
README: documents the drill alongside cargo test + npm test in the
Testing section, with the macOS caveat clearly flagged.
Not run as part of this commit — the drill launches a Tauri WebView
window for a few seconds. Jake to invoke when ready to dogfood.
This commit is contained in:
19
README.md
19
README.md
@@ -321,6 +321,25 @@ Frontend test files live alongside source (`src/**/*.test.ts`) and run in
|
|||||||
jsdom by default. See [vite.config.js](vite.config.js) for the vitest
|
jsdom by default. See [vite.config.js](vite.config.js) for the vitest
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
|
#### Rebrand-migration dogfood drill
|
||||||
|
|
||||||
|
End-to-end probe that launches the real `target/debug/lumotia` binary
|
||||||
|
against synthetic legacy magnotia state planted on disk, then verifies
|
||||||
|
both migration paths produced the expected on-disk outcome.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build -p lumotia # need the binary first
|
||||||
|
scripts/dogfood-rebrand-drill.sh # sandbox mode (Linux only)
|
||||||
|
scripts/dogfood-rebrand-drill.sh --keep # leave sandbox dir for inspection
|
||||||
|
scripts/dogfood-rebrand-drill.sh --against-real-home # run against real $HOME
|
||||||
|
```
|
||||||
|
|
||||||
|
Sandbox mode is faithful only on Linux — Tauri 2 on macOS uses
|
||||||
|
`NSSearchPathForDirectoriesInDomains` which ignores `HOME` overrides. The
|
||||||
|
drill refuses to start in sandbox mode on macOS. Real-home mode refuses
|
||||||
|
to start if any lumotia data already exists at your real paths, so it
|
||||||
|
can roll back cleanly on exit.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Project documentation
|
## Project documentation
|
||||||
|
|||||||
272
scripts/dogfood-rebrand-drill.sh
Executable file
272
scripts/dogfood-rebrand-drill.sh
Executable file
@@ -0,0 +1,272 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Lumotia rebrand-migration dogfood drill.
|
||||||
|
#
|
||||||
|
# Launches the real lumotia binary against synthetic legacy magnotia state
|
||||||
|
# planted on disk, then probes the post-startup state to confirm both
|
||||||
|
# migration paths ran:
|
||||||
|
# 1. paths.rs: ~/.local/share/magnotia/ -> ~/.local/share/lumotia/
|
||||||
|
# magnotia.db -> lumotia.db
|
||||||
|
# 2. tauri_app_data_migration.rs:
|
||||||
|
# ~/.local/share/uk.co.corbel.magnotia/ copied via staging to
|
||||||
|
# ~/.local/share/consulting.corbel.lumotia/ (legacy preserved
|
||||||
|
# as a backup)
|
||||||
|
#
|
||||||
|
# Modes:
|
||||||
|
# (default) Sandbox. Sets HOME=<tempdir>, plants legacy state
|
||||||
|
# inside, launches the binary, verifies, tears down.
|
||||||
|
# Faithful on Linux. NOT faithful on macOS (Tauri 2
|
||||||
|
# uses NSSearchPathForDirectoriesInDomains which
|
||||||
|
# ignores HOME overrides and would write to your
|
||||||
|
# real Application Support tree).
|
||||||
|
# --against-real-home Real $HOME. Refuses to run if any lumotia data
|
||||||
|
# already exists at the real paths. Backs up the
|
||||||
|
# legacy planting before running so cleanup can
|
||||||
|
# restore your real-home tree to its pre-drill
|
||||||
|
# state.
|
||||||
|
#
|
||||||
|
# Flags:
|
||||||
|
# --keep Keep the sandbox dir / preserved backups after the
|
||||||
|
# run for manual inspection.
|
||||||
|
# --timeout SECS How long to wait for the binary to come up and run
|
||||||
|
# the migration. Default 20s. Bump on slow hardware
|
||||||
|
# or when running under a debugger.
|
||||||
|
# --binary PATH Override the default ./target/debug/lumotia.
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 all probes passed
|
||||||
|
# 1 a probe failed (migration did not produce the expected on-disk state)
|
||||||
|
# 2 argument error
|
||||||
|
# 3 preflight check failed (real-home already has lumotia data, binary
|
||||||
|
# missing, unsupported platform for sandbox mode, ...)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MODE="sandbox"
|
||||||
|
KEEP=false
|
||||||
|
TIMEOUT_SECS=20
|
||||||
|
BINARY="./target/debug/lumotia"
|
||||||
|
|
||||||
|
# ---- arg parsing -----------------------------------------------------------
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--against-real-home) MODE="real-home"; shift ;;
|
||||||
|
--keep) KEEP=true; shift ;;
|
||||||
|
--timeout) TIMEOUT_SECS="$2"; shift 2 ;;
|
||||||
|
--binary) BINARY="$2"; shift 2 ;;
|
||||||
|
-h|--help)
|
||||||
|
sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
|
exit 0 ;;
|
||||||
|
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---- preflight -------------------------------------------------------------
|
||||||
|
|
||||||
|
if [[ ! -x "$BINARY" ]]; then
|
||||||
|
echo "FAIL: binary not found at $BINARY. Run 'cargo build -p lumotia' first." >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
PLATFORM="$(uname -s)"
|
||||||
|
if [[ "$MODE" == "sandbox" && "$PLATFORM" == "Darwin" ]]; then
|
||||||
|
echo "FAIL: sandbox mode is not faithful on macOS." >&2
|
||||||
|
echo " Tauri 2 uses NSSearchPathForDirectoriesInDomains which ignores HOME overrides." >&2
|
||||||
|
echo " Either run on Linux or use --against-real-home (with backup discipline)." >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Real-home mode: refuse to run if the user already has lumotia data on
|
||||||
|
# disk. We'd risk merging fake planted state into real data on cleanup.
|
||||||
|
if [[ "$MODE" == "real-home" ]]; then
|
||||||
|
REAL_HOME="$HOME"
|
||||||
|
REAL_LUMOTIA_DATA="${XDG_DATA_HOME:-$REAL_HOME/.local/share}/lumotia"
|
||||||
|
REAL_LUMOTIA_TAURI="${XDG_DATA_HOME:-$REAL_HOME/.local/share}/consulting.corbel.lumotia"
|
||||||
|
for p in "$REAL_LUMOTIA_DATA" "$REAL_LUMOTIA_TAURI" "$REAL_HOME/.lumotia"; do
|
||||||
|
if [[ -e "$p" ]]; then
|
||||||
|
echo "FAIL: $p already exists. Refusing to run in real-home mode against existing data." >&2
|
||||||
|
echo " Either back it up manually and remove the original, or run in sandbox mode." >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- sandbox setup ---------------------------------------------------------
|
||||||
|
|
||||||
|
if [[ "$MODE" == "sandbox" ]]; then
|
||||||
|
SANDBOX="$(mktemp -d -t lumotia-dogfood-XXXXXX)"
|
||||||
|
export HOME="$SANDBOX"
|
||||||
|
unset XDG_DATA_HOME
|
||||||
|
PLANT_ROOT="$SANDBOX/.local/share"
|
||||||
|
echo "Sandbox: $SANDBOX (HOME overridden)"
|
||||||
|
else
|
||||||
|
SANDBOX="" # signal: don't tear down a sandbox at the end
|
||||||
|
PLANT_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||||
|
echo "Real home: planting under $PLANT_ROOT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOG_FILE="$(mktemp -t lumotia-dogfood-binary-XXXXXX.log)"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n "${BINARY_PID:-}" ]] && kill -0 "$BINARY_PID" 2>/dev/null; then
|
||||||
|
kill -TERM "$BINARY_PID" 2>/dev/null || true
|
||||||
|
sleep 0.5
|
||||||
|
kill -KILL "$BINARY_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ "$KEEP" == "false" && -n "$SANDBOX" && -d "$SANDBOX" ]]; then
|
||||||
|
rm -rf "$SANDBOX"
|
||||||
|
fi
|
||||||
|
if [[ "$KEEP" == "false" && "$MODE" == "real-home" ]]; then
|
||||||
|
# Real-home cleanup: remove planted legacy state AND any
|
||||||
|
# post-migration artefacts we created. We refused to start if
|
||||||
|
# any of these existed, so removing them now is safe.
|
||||||
|
rm -rf \
|
||||||
|
"$PLANT_ROOT/magnotia" \
|
||||||
|
"$PLANT_ROOT/uk.co.corbel.magnotia" \
|
||||||
|
"$PLANT_ROOT/lumotia" \
|
||||||
|
"$PLANT_ROOT/consulting.corbel.lumotia"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
# ---- plant synthetic legacy state -----------------------------------------
|
||||||
|
|
||||||
|
mkdir -p "$PLANT_ROOT/magnotia/recordings/2026-05-13"
|
||||||
|
# Plant a sentinel non-DB file inside the legacy data dir to confirm the
|
||||||
|
# migration sweeps the whole tree, not just magnotia.db.
|
||||||
|
echo "fake-wav-bytes-for-dogfood-drill" > "$PLANT_ROOT/magnotia/recordings/2026-05-13/clip.wav"
|
||||||
|
# An empty file at magnotia.db is enough for the file-level rename probe.
|
||||||
|
# The Rust integration test (crates/storage/tests/legacy_db_migration.rs)
|
||||||
|
# covers the "DB still openable after rename" path with a real SQLite;
|
||||||
|
# this drill is about the LIVE BINARY at startup, not schema integrity.
|
||||||
|
: > "$PLANT_ROOT/magnotia/magnotia.db"
|
||||||
|
|
||||||
|
mkdir -p "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb"
|
||||||
|
echo "sentinel-leveldb-bytes" > "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb/000003.log"
|
||||||
|
echo '{"main":{"x":100,"y":200,"width":1280,"height":720}}' > "$PLANT_ROOT/uk.co.corbel.magnotia/window-state.json"
|
||||||
|
|
||||||
|
echo "Planted synthetic legacy state. Launching $BINARY..."
|
||||||
|
|
||||||
|
# ---- launch binary in background, give it time to run migrations ----------
|
||||||
|
|
||||||
|
# RUST_LOG forces lumotia_startup tracing events to disk regardless of any
|
||||||
|
# default filter. The setup hook logs `migrated legacy magnotia data dir`
|
||||||
|
# at info level on a successful rename.
|
||||||
|
RUST_LOG="lumotia_startup=info,lumotia=info" \
|
||||||
|
"$BINARY" >"$LOG_FILE" 2>&1 &
|
||||||
|
BINARY_PID=$!
|
||||||
|
|
||||||
|
# Wait for either: the migration log line to appear, OR the timeout.
|
||||||
|
DEADLINE=$(( $(date +%s) + TIMEOUT_SECS ))
|
||||||
|
MIGRATED_DATA_DIR_SEEN=false
|
||||||
|
MIGRATED_TAURI_DIR_SEEN=false
|
||||||
|
while (( $(date +%s) < DEADLINE )); do
|
||||||
|
if grep -q "migrated legacy magnotia data dir to lumotia" "$LOG_FILE" 2>/dev/null; then
|
||||||
|
MIGRATED_DATA_DIR_SEEN=true
|
||||||
|
fi
|
||||||
|
if grep -qE "Migrated|migrated.*tauri.*app_data|copied legacy Tauri" "$LOG_FILE" 2>/dev/null; then
|
||||||
|
MIGRATED_TAURI_DIR_SEEN=true
|
||||||
|
fi
|
||||||
|
if [[ "$MIGRATED_DATA_DIR_SEEN" == "true" ]]; then
|
||||||
|
# Give the Tauri side another moment after the data-dir migration.
|
||||||
|
sleep 1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
# SIGTERM the binary cleanly. We don't need it running for the probes —
|
||||||
|
# all probes inspect the post-migration filesystem state.
|
||||||
|
if kill -0 "$BINARY_PID" 2>/dev/null; then
|
||||||
|
kill -TERM "$BINARY_PID" || true
|
||||||
|
fi
|
||||||
|
wait "$BINARY_PID" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ---- probes ----------------------------------------------------------------
|
||||||
|
|
||||||
|
PROBES_PASSED=0
|
||||||
|
PROBES_FAILED=0
|
||||||
|
report_probe() {
|
||||||
|
local name="$1"; local outcome="$2"; local detail="${3:-}"
|
||||||
|
if [[ "$outcome" == "PASS" ]]; then
|
||||||
|
printf ' PASS %s\n' "$name"
|
||||||
|
PROBES_PASSED=$(( PROBES_PASSED + 1 ))
|
||||||
|
else
|
||||||
|
printf ' FAIL %s %s\n' "$name" "$detail"
|
||||||
|
PROBES_FAILED=$(( PROBES_FAILED + 1 ))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
printf '\nProbe results:\n'
|
||||||
|
|
||||||
|
# 1. Data-dir migration: target exists at the new path.
|
||||||
|
if [[ -d "$PLANT_ROOT/lumotia" ]]; then
|
||||||
|
report_probe "data-dir rename produced ~/.local/share/lumotia/" PASS
|
||||||
|
else
|
||||||
|
report_probe "data-dir rename produced ~/.local/share/lumotia/" FAIL "(missing)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Data-dir migration: lumotia.db is at the new path.
|
||||||
|
if [[ -f "$PLANT_ROOT/lumotia/lumotia.db" ]]; then
|
||||||
|
report_probe "magnotia.db renamed to lumotia.db at new path" PASS
|
||||||
|
else
|
||||||
|
report_probe "magnotia.db renamed to lumotia.db at new path" FAIL "(missing)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Data-dir migration: legacy magnotia tree is gone (rename moved it).
|
||||||
|
if [[ ! -d "$PLANT_ROOT/magnotia" ]]; then
|
||||||
|
report_probe "legacy ~/.local/share/magnotia/ removed by rename" PASS
|
||||||
|
else
|
||||||
|
report_probe "legacy ~/.local/share/magnotia/ removed by rename" FAIL "(still on disk)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Data-dir migration: non-DB companion file carried along.
|
||||||
|
if [[ -f "$PLANT_ROOT/lumotia/recordings/2026-05-13/clip.wav" ]]; then
|
||||||
|
report_probe "non-DB companion file carried along by directory rename" PASS
|
||||||
|
else
|
||||||
|
report_probe "non-DB companion file carried along by directory rename" FAIL "(missing)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Tauri migration: webview/localStorage copied to new bundle id path.
|
||||||
|
if [[ -f "$PLANT_ROOT/consulting.corbel.lumotia/localStorage/leveldb/000003.log" ]]; then
|
||||||
|
report_probe "Tauri app_data_dir copied to consulting.corbel.lumotia/" PASS
|
||||||
|
else
|
||||||
|
report_probe "Tauri app_data_dir copied to consulting.corbel.lumotia/" FAIL "(localStorage missing)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Tauri migration: legacy uk.co.corbel.magnotia preserved as backup.
|
||||||
|
if [[ -f "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb/000003.log" ]]; then
|
||||||
|
report_probe "legacy Tauri dir preserved as backup" PASS
|
||||||
|
else
|
||||||
|
report_probe "legacy Tauri dir preserved as backup" FAIL "(missing — migration should NOT delete legacy)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. Tauri migration: staging dir cleaned up.
|
||||||
|
if [[ ! -d "$PLANT_ROOT/consulting.corbel.lumotia.migrating" ]]; then
|
||||||
|
report_probe "staging dir consulting.corbel.lumotia.migrating cleaned up" PASS
|
||||||
|
else
|
||||||
|
report_probe "staging dir consulting.corbel.lumotia.migrating cleaned up" FAIL "(staging leaked)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 8. Log line: lumotia_startup migration event appeared.
|
||||||
|
if [[ "$MIGRATED_DATA_DIR_SEEN" == "true" ]]; then
|
||||||
|
report_probe "lumotia_startup logged the data-dir migration" PASS
|
||||||
|
else
|
||||||
|
report_probe "lumotia_startup logged the data-dir migration" FAIL "(no log line within ${TIMEOUT_SECS}s)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- summary --------------------------------------------------------------
|
||||||
|
|
||||||
|
printf '\nLog file: %s\n' "$LOG_FILE"
|
||||||
|
if [[ "$KEEP" == "true" && -n "$SANDBOX" ]]; then
|
||||||
|
printf 'Sandbox preserved at: %s\n' "$SANDBOX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\nPassed: %s / Failed: %s\n' "$PROBES_PASSED" "$PROBES_FAILED"
|
||||||
|
|
||||||
|
if (( PROBES_FAILED > 0 )); then
|
||||||
|
printf '\nDrill FAILED. Inspect %s for clues.\n' "$LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\nDrill PASSED. Rebrand migration runs end-to-end against real OS paths.\n'
|
||||||
Reference in New Issue
Block a user