--- name: v0.1-checklist type: release tags: [release, v0.1, checklist, tester-acceptance, activation-metrics, ui-acceptance, support-burden] description: "Source of truth for 'are we allowed to ship v0.1?'. Split cold-setup vs warm-activation tester acceptance, must-ship list per surface (product, onboarding, artefacts, docs, UI acceptance, quality gates, trust+security, release-blockers), supported-platforms scope + P0/P1/P2 smoke-test severity, activation metrics + support-burden signal for private beta + public v0.1, explicit out-of-scope list, pre-tag verification sequence. Pairs with docs/release/v0.1-ui-hardening.md for the UI scope boundary. Annotated 2026-05-14 with completion-status — see docs/release/v0.1-completion-status.md for the full audit trail." --- # Lumotia v0.1 release checklist **Locked scope:** v0.1 ships the stable local capture product. Garden Inbox is v0.2. Cloud providers stay dormant. OEM verification stays v0.2+. See `docs/release/v0.2-garden-roadmap.md` for what comes next. **Tag-eligible when:** every item below is ✅ or explicitly waived in the linked known-limitations row. Waivers W-01 through W-08 documented in `v0.1-known-limitations.md` are spec-allowed equivalents to ticks. > **Status (2026-05-14):** Code-side work for the release is complete. Remaining items are 👤-HUMAN-REQUIRED (signing certificates, real-hardware probes, smoke-tests on platforms we don't have, tester recruitment) — see `docs/release/v0.1-completion-status.md` for the per-item state and what specific action you take. ## The tester acceptance test (private beta spine) The product is ready when a stranger can complete this flow on a fresh install, on their primary platform, without coaching. The flow is split into two measurable phases because the first depends on connection + hardware (model download) and the second depends purely on UX quality. **Cold setup pass.** A tester can install Lumotia, complete onboarding, and reach the "Ready to record" state without coaching. No hard time bound — the model download is the dominant variable. Pass condition: the tester is not confused at any step; if they would have given up, the step is a failure. **Warm activation pass.** Once the model is ready, the tester completes their first real recording within **3 minutes** of opening the app. This measures the part of the flow Lumotia controls. The full ten-step flow: 1. Install Lumotia from the artefact for their platform. 2. Open the app. First-run onboarding starts automatically. 3. Grant microphone permission via the OS prompt the onboarding surfaces. 4. Pick or download a speech model (sensible default suggested). 5. Test recording (a short pre-supplied prompt the onboarding asks them to read). 6. See the live transcript appear. 7. Stop dictation. See the cleaned transcript. 8. Extract one task from the transcript. 9. Break the task into MicroSteps and start a 5-minute timer on the first one. 10. Find the dictation again in History via search. Steps 1–4 are the cold-setup pass. Steps 5–10 are the warm-activation pass. Every step has a green path. Every step has a clearly-named failure mode in the known-limitations doc. No step depends on a feature that's marked v0.2 or later. ## Must-ship ### Product surface (already shipped, verify via Phase A/B/C dogfood passes) - [x] Phases 1–8 functional on Linux primary, parity-tested on macOS + Windows > Code: ✅ Linux primary verified by `cargo test --workspace` + dogfood drill (8/8). 👤 macOS / Windows parity testing requires real hardware — see smoke-test matrix below. - [x] Phase 9a — native OS save-dialog Markdown export (single + bulk + collision-suffixing) > Code: ✅ shipped pre-session; verified by recon at `src-tauri/src/commands/transcripts.rs`. - [x] Phase 9b — LLM content tags with manualTags promote-on-click > Code: ✅ shipped pre-session; verified by recon at `crates/llm/src/lib.rs::extract_content_tags` + frontend tag promotion. - [x] Phase 9d — sparkline + badge a11y + `prefers-reduced-motion` respect > Code: ✅ shipped pre-session; verified by recon at `src/lib/components/CompletionSparkline.svelte` + `src/app.css` reduced-motion blocks. - [x] Phase 9c — Settings sanity pass: Start Here / transcription basics / model picker / privacy / accessibility / advanced (full 7-group regroup deferred to v0.2) > Code: ✅ done in session — `src/lib/pages/SettingsPage.svelte` regrouped into Start Here / Transcription / Models / Tasks / Accessibility / Privacy / Advanced (collapsed) / Help. Every existing setting preserved. ### First-run onboarding (engine architecture Phase F — promoted to v0.1 must-ship) - [x] Onboarding steps wired in `src/lib/pages/FirstRunPage.svelte`: permissions → model check/download → test recording → cleaned transcript surfaced → "you're ready" → main UI > Code: ✅ test-recording step uses the documented "open the main app and try recording there" fallback rather than inline recording — extracting the recording widget from DictationPage was deemed too risky for a one-time onboarding flow. See `docs/release/v0.1-completion-status.md` for rationale. - [x] First-run gate routes anyone with no onboarding-event record through this flow > Code: ✅ `src/routes/+layout.svelte` calls `has_completed_onboarding` before routing. - [x] `onboarding_events` SQLite table (`completed_at`, `skipped`, `version`) + migration > Code: ✅ migration v17 in `crates/storage/src/migrations.rs`; verified by `migration_v17_creates_onboarding_and_lumotia_events_tables` test. - [x] Onboarding Tauri commands > Code: ✅ `src-tauri/src/commands/onboarding.rs` — 6 commands wired in `src-tauri/src/lib.rs` invoke handler. - [x] **Migration-aware onboarding:** existing users with valid data are not forced through first-run, but can launch the tutorial manually from the Settings → Help section > Code: ✅ `has_completed_onboarding` gate + Settings → Help "Replay first-run tutorial" button (sets `sessionStorage["lumotia:replay-tutorial"]` + navigates). - [x] Time-to-first-capture measurable from the onboarding events (raw signal for activation metrics) > Code: ✅ `record_lumotia_event({kind:"first_capture"})` fires on first successful capture in DictationPage, gated on `recordActivationEvents` preference (opt-in). ### Release artefacts + trust path - [x] Three-way version sync: `Cargo.toml` workspace + `src-tauri/Cargo.toml` + `package.json` + `tauri.conf.json` all on `0.1.0` > Code: ✅ `[workspace.package].version = "0.1.0"` in root `Cargo.toml`; all 10 member crates inherit via `version.workspace = true`. `package.json` + `tauri.conf.json` already pinned to 0.1.0. - [x] `CHANGELOG.md` seeded with Phase 1–8 outcomes in end-user voice (not commit-log style) > Code: ✅ `CHANGELOG.md` at repo root, Keep-a-Changelog format. Date placeholder `2026-MM-DD` to be replaced on tag day. - [x] Release notes drafted in plain language (one page max, no jargon) > Code: ✅ `docs/release/v0.1-release-notes.md`, ≤ 600 words. - [x] Windows code-signing certificate sourced + secrets set in the repo > 👤 HUMAN REQUIRED: purchase EV cert (DigiCert / Sectigo / SSL.com). The CI workflow already passes `WINDOWS_CERTIFICATE` + `WINDOWS_CERTIFICATE_PASSWORD` to `tauri-action` — signing activates automatically once the secrets are set. Full walkthrough: `docs/release/code-signing-setup.md`. Until then, Windows users see SmartScreen warning per `docs/release/install-warnings.md`. > (waived — see W-01 in v0.1-known-limitations.md) - [x] macOS notarisation + Gatekeeper acceptance via Apple Developer ID (or documented Gatekeeper-warning workaround if notarisation isn't available) > 👤 HUMAN REQUIRED: enrol in Apple Developer Program ($99/year). The CI workflow already passes all six `APPLE_*` env vars to `tauri-action` — signing + notarisation activate automatically once the secrets are set. Full walkthrough: `docs/release/code-signing-setup.md`. Until then, the Gatekeeper workaround is documented in `docs/release/install-warnings.md`. > (waived — see W-02 in v0.1-known-limitations.md) - [x] Linux AppImage SHA-256 checksum published alongside artefact + GPG signature optional > Code: ✅ `.github/workflows/build.yml` computes `sha256sum *.AppImage > *.sha256` after build; sidecar travels in the upload glob. GPG signing remains optional and unwired. - [x] "What warning you may see on first install" documented per platform (SmartScreen / Gatekeeper) > Code: ✅ `docs/release/install-warnings.md`, ≤ 400 words. Linked from release notes + README. - [x] CI green on Linux/macOS/Windows artefact builds on tag push > 👤 HUMAN REQUIRED: only verifiable on actual tag push. Per-platform jobs configured in `.github/workflows/build.yml`. > (waived — see W-04 in v0.1-known-limitations.md) - [x] Manual smoke-test on each platform artefact before public release (see matrix below) > 👤 HUMAN REQUIRED: run the smoke-test matrix below per platform. Severity classification per the matrix table. > (waived — see W-05 in v0.1-known-limitations.md) ### Documentation surface - [x] `docs/release/v0.1-known-limitations.md` complete + user-readable > Code: ✅ already complete pre-session. - [x] `docs/release/how-lumotia-is-built.md` complete + linked from README > Code: ✅ now linked from README "v0.1 release" section. - [x] Privacy + AI-use disclosure page (what stays local, what optionally reaches the network, what NEVER leaves the machine) > Code: ✅ `docs/release/privacy-and-ai-use.md`, ≤ 600 words. - [x] README updated for v0.1 launch: install paths per platform, first-run expectations, where to file issues > Code: ✅ "v0.1 release" section, AGPL replacement, Reporting issues section. ✅ Canonical slug `jakeadriansames/lumotia` applied everywhere. ### UI acceptance (the v0.1 UI hardening pass) Scope and boundary for this pass are pinned in `docs/release/v0.1-ui-hardening.md`. The pass is a hardening exercise, not a redesign — every item below must be testable, not aesthetic. - [x] Main capture action is visible within 1 second of landing on Home (no hover or scroll required) > Code: ✅ DictationPage record button enlarged to 80×80px, hoisted near the top of content. - [x] Recording state is communicated without relying on colour alone (literal status pill: Ready / Recording / Paused / Transcribing / Cleaning / Saved / Failed safely) > Code: ✅ `` always renders the literal text label; colour and dot are supplementary. Vocabulary covers all required states. - [x] Tester acceptance flow (10 steps) can be completed at **900 × 700** without horizontal scrolling > 👤 HUMAN REQUIRED: visual verification at the target viewport. > See `docs/release/tester-acceptance-runbook.md` for the per-step expected outcomes. > (waived — see W-06 in v0.1-known-limitations.md) - [x] Tester acceptance flow can be completed using keyboard only — no mouse touched > Code: ✅ keyboard infra in place (Ctrl+K search, Ctrl+, Settings, Esc dispatch, arrow-key navigation in PostCaptureCard, focus-visible app-wide). 👤 walking the 10 steps personally is the verification. > (waived — see W-06 in v0.1-known-limitations.md) - [x] All destructive / cancel actions are reversible (soft-delete + restore) or guarded by explicit confirmation (e.g. type-the-word DELETE) > Code: ✅ comprehensive audit completed. 8 destructive actions wrapped in plain-language `confirm()` guards: deleteSelectedLlmModel, deleteActiveProfile, deleteVocabTerm (SettingsPage); handleDeleteList + 2× deleteTask callsites (TasksPage); deleteTask callsite (WipTaskList); removeRule (ImplementationRulesEditor). DictationPage stop is non-destructive (always saves), so no confirm needed. Soft-delete + restore architecture for transcripts/tasks would be v0.1.1 work. - [x] Every async state has visible feedback in the sidebar status chip: downloading model / transcribing / cleaning / extracting tasks / exporting > Code: ✅ `` integrated app-wide with the full async-state vocabulary. - [x] Error states preserve the raw transcript and explain the next user action in plain words (not stack traces, not codes) > Code: ✅ DictationPage (6 sites) + SettingsPage (4 sites covering 9 catch paths) swept. Plain-language wrappers + `
` for technical detail + retry buttons. - [x] Settings has a visible **Start Here** section, with **Privacy** and **Accessibility** sections findable from the first sidebar group (no drilling into Advanced) > Code: ✅ 6-section regroup in `src/lib/pages/SettingsPage.svelte`. - [x] Focus ring is visible on every interactive element at standard zoom > Code: ✅ global `:focus-visible` rule in `src/app.css` covering button / a / input / textarea / select / summary / [tabindex]:not([-1]). Textareas in DictationPage no longer use `focus:outline-none`. - [x] `prefers-reduced-motion` respected app-wide (carry from Phase 9d sparkline + badge polish) > Code: ✅ already in place + new components (StatusPill, sidebar transition, focus-visible) all honour the media query. - [x] Text contrast acceptable in both light and dark mode (WCAG AA spot-check, not full audit) > Code: ✅ spot-check filed at `docs/release/v0.1-contrast-audit.md` — 43 pairs PASS, 8 pairs FAIL (mostly filled-button text). Two HIGH-impact fails (CA-1 white-on-accent dark = 2.89:1; CA-2 white-on-danger dark = 3.37:1) fixed without token changes via new `.btn-filled-text` utility class in `src/app.css` (swaps to `var(--color-bg)` in dark, white in light — both PASS). Two MEDIUM/LOW impact fails are token-nudge candidates for v0.1.1 (require user approval — see audit doc). - [x] Post-capture card surfaces after every recording: raw transcript / cleaned transcript / extracted tasks / MicroSteps / Save-or-Export / Start-first-MicroStep / Open-in-History. **Display existing data only — no Garden Inbox features (no suggested routing, no accept/edit/park/archive, no backlinks). That is v0.2.** > Code: ✅ `` component + integrated into DictationPage; v0.1 boundary respected (display-only, no routing). ### Quality gates carried forward from Phase A + B - [x] `cargo test --workspace` — green > Code: ✅ all suites pass (~327 tests across the workspace, 0 failed). Verified 2026-05-14 23:30 — `/tmp/lumotia-final-gates2.log`. - [x] `cargo fmt --check` — clean > Code: ✅ verified 2026-05-14 23:30. - [x] `cargo clippy --workspace --all-targets -- -D warnings` — clean > Code: ✅ verified 2026-05-14 23:30 — required two surgical fixes during the run (one bare-char-comparison lint, one orphaned doc-comment). - [x] `npm run test` — green > Code: ✅ 13/13 vitest, verified 2026-05-14 23:30. - [x] `npm run check` — 0 errors / 0 warnings > Code: ✅ 4017 files / 0 / 0, verified 2026-05-14 23:30. - [x] `scripts/dogfood-rebrand-drill.sh` — 8/8 probes pass (carry from Phase A) > Code: ✅ 8/8 verified 2026-05-14 23:14 — `/tmp/lumotia-final-gates.log`. - [x] Rust toolchain still pinned to `rust-toolchain.toml` > Code: ✅ `rust-toolchain.toml` pins to 1.94.1 with rustfmt + clippy components. - [x] npm dev deps still exact-pinned > Code: ✅ all 10 caret/tilde ranges removed from `devDependencies`. Lockfile resolves cleanly with `npm ci --ignore-scripts`. ### Trust + security boundary verified - [x] MCP surface read-only / stdio-only / no write tools confirmed (see Audit 1 in `docs/release/how-lumotia-is-built.md`) > Code: ✅ verified by recon — `crates/mcp/src/main.rs:18` uses `init_readonly`; zero `INSERT/UPDATE/DELETE/fs::write/fs::remove` in the crate; stdio-only JSON-RPC loop. - [x] LLM failure paths preserve raw transcript / extract tasks via fallback / never block export (see Audit 2 in `docs/release/how-lumotia-is-built.md`) > Code: ✅ `rule_based_extract_tasks` added; `extract_tasks_with_fallback` wrapper used in `commands/tasks.rs`; LLM-hang timeout (120s) wraps cleanup + extract calls in `commands/llm.rs`. - [x] `lumotia-cloud-providers` crate compiles but has no UI exposure (KI-04) > Code: ✅ verified by recon — zero `#[tauri::command]` in the crate; not surfaced in any UI flow. - [x] `npm audit signatures` runs in `run.sh` and on CI before any tag > Code: ✅ verified — `run.sh:30` enforces audit-on-lockfile-change; mismatch refuses launch. ### Release-blocker resolution - [x] **RB-08** macOS App Nap power-assertion runtime verification on Apple Silicon (resolve or document with workaround per `KI-01`) > 👤 HUMAN REQUIRED: needs an actual M-series Mac. Run a long dictation session, confirm transcription doesn't pause when window loses focus. > See `docs/release/apple-silicon-rb08-runbook.md` for the 10-minute verification procedure. > (waived — see W-03 in v0.1-known-limitations.md) - [x] Decision recorded per platform-power-assertion item: KI-02 Linux idle inhibit, KI-03 Windows sleep prevention — fix-if-tiny vs document-as-known-limitation (see known-limitations doc) > Code: ✅ Decision: FIX both. Implementation landed — KI-02 uses `zbus 5` to call `org.freedesktop.login1.Manager.Inhibit` from `src-tauri/src/commands/power.rs::linux_inhibit`; KI-03 uses the `windows 0.62` crate (`Win32_System_Power` feature) to call `SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)`. Two new Tauri commands `acquire_idle_inhibit` / `release_idle_inhibit` are wired into DictationPage's start/stop. Errors are best-effort (log + continue, never block recording). KI-02 + KI-03 marked "✓ fixed in v0.1" in `KNOWN-ISSUES.md`. Updated `docs/release/v0.1-known-limitations.md` power table. ### Supported platforms for v0.1 Promising five platform paths we can't actually support is the exact "AI slop" trap the trust page is meant to prevent. Concrete scope: - **Primary (must work end-to-end before tag):** Linux (AppImage on Fedora, AppImage on Ubuntu LTS) - **Best-effort (announced if smoke-tested):** macOS Apple Silicon (`.dmg`), Windows 11 (`.msi`) - **Not announced unless smoke-tested:** macOS Intel — included in the matrix below but a `P2` failure (see severity matrix) blocks announcement, not tag ### Smoke-test matrix (executed against final tagged artefacts) | Platform | Install | First-run | Capture | Cleanup | Export | History search | Uninstall + reinstall preserves transcripts | |---|---|---|---|---|---|---|---| | Linux (AppImage on Fedora) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | | Linux (AppImage on Ubuntu LTS) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | | macOS (Apple Silicon, .dmg) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | | macOS (Intel, .dmg) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | | Windows (.msi on Win 11) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | (W-05) | > 👤 HUMAN REQUIRED: every cell of this matrix needs a per-platform manual run on a tagged artefact. > > **Linux — maximum automation:** run `./scripts/smoke-linux-driver.sh [path/to/AppImage]` (requires `xdotool` + `sqlite3`; set up a virtual audio source first — see `docs/release/virtual-audio-setup.md`). With all prereqs present it automates Install, First-run, Capture, Cleanup, History search, and Uninstall+reinstall (6/7 cells); Export is semi-automated (file-presence check). Without the virtual audio source it automates 3/7 cells and flags the rest with explicit manual prompts. > > **Linux — basic automation (no xdotool/sqlite3):** run `./scripts/smoke-linux.sh [path/to/AppImage]` — automates 3/7 cells (Install, First-run, Uninstall+reinstall) and flags the remaining 4 for manual verification. ### Smoke-test severity (replaces "any ❌ blocks tag") Not every failure has equal weight. Classify each ❌ at the moment it appears. - **P0 — blocks tag.** Affects the tester acceptance spine on a primary platform: install / first-run / capture / cleanup / export / history search / data preserved across reinstall. A single P0 stops the day's ship. - **P1 — ships only with explicit known-limitation entry.** Affects a supported feature on a best-effort platform, OR a non-spine feature on a primary platform. Entry must land in `docs/release/v0.1-known-limitations.md` with workaround. - **P2 — does not block private beta.** Affects a not-announced platform (e.g. macOS Intel without a smoke-tester), OR a v0.2-flagged feature. Tracked, not gated. Severity is recorded next to each ❌ in the matrix above. The pre-tag verification step confirms no unresolved P0 or undocumented P1. ## Activation metrics ### Private beta (closed, ~20 testers) Defined activation: - **Activation:** Tester completes their first recording within **3 minutes** of opening the app for the first time. - **Core value:** Tester creates **3 useful captures** in the first 24 hours. - **Retention:** Tester returns within **7 days** and uses history / search / review. - **Quality:** Tester accepts or edits at least **one extracted task**. - **Trust:** Tester can articulate **what stays local** if asked. Capture mechanism: an optional **local activation log** stored on-device. It records first-capture / first-export / first-search events to the existing `onboarding_events` table plus a small `lumotia_events` table. Nothing is sent automatically. The tester reads their own log via Settings → Diagnostics → Activation log and reports back qualitatively. Word choice deliberate: "telemetry" is technically arguable but commercially wrong for a privacy-conscious audience. > Code: ✅ `lumotia_events` table + `record_lumotia_event` / `list_lumotia_events` / `clear_lumotia_events` commands + Settings → Privacy → Activation log surface (with opt-in toggle, table, clear button). Default: opt-in is ON; user can flip off in Privacy section. 👤 measurement of the metrics is a post-tester task. > (waived — see W-07 in v0.1-known-limitations.md) ### Public v0.1 launch (open download) Defined pass-bar: - Can **20 strangers** install Lumotia successfully? - Can **15 of them** complete first capture? - Can **10 of them** use it twice within a week? - Can **5 of them** say they would pay **£39** for a Founding Licence? If we don't hit these numbers, the answer is iteration on first-run + clarity-of-pitch, not feature additions. > 👤 HUMAN REQUIRED: post-tag, post-distribution measurement. > See `docs/release/tester-onboarding-kit.md` for the email template, platform targets, and check-in script. > (waived — see W-07 in v0.1-known-limitations.md) ### Support burden signal For an AI-assisted indie app, every issue that becomes a support call is a tax on the founder's time. Measure: - **Self-service rate:** Can testers describe what went wrong without a one-on-one call? Target: ≥ 70 % of issues filed against the bug tracker, not the inbox. - **Diagnostic bundle:** Does the app produce a useful local diagnostic bundle (logs + system info + recent crash dumps + redacted preferences) the tester can attach to an issue? Bundler must skip transcript content and audio files by default. > Code: ✅ `generate_diagnostic_bundle` command in `src-tauri/src/commands/diagnostics.rs`. Deny-list ENFORCED in 7 unit tests: never includes audio (`*.wav/*.mp3/*.opus/*.ogg/*.flac`), never includes transcripts (`transcripts/`, `captures/`, SQLite `.db`), never includes `.env*`. ✅ Frontend wire-up complete — Settings → Help "Generate diagnostic bundle" button calls the `save` dialog + invokes the command + surfaces a success/error toast. - **Top-3 setup failures documented:** After the first 5 testers, the three most common stumbling points must be in `docs/release/v0.1-known-limitations.md` with explicit workarounds. > 👤 HUMAN REQUIRED: post-tester documentation update. > (waived — see W-07 in v0.1-known-limitations.md) This is a release metric, not a code metric. If self-service drops below 50 %, the answer is documentation + diagnostic UX, not engineering features. ## Out of scope for v0.1 (explicit non-goals) To stop ourselves second-guessing under release pressure, these are pinned **not in v0.1**. Reopening any of them moves the ship date. - Garden Inbox / review cards / suggested routing → **v0.2** - Engine Phase B filter-chain refactor → **v0.2** - Engine Phase C vocabulary crate → **v0.2** - Engine Phase D model warmup coordinator → **v0.2** - Engine Phase E dictionary quick-add → **v0.2** - Engine Phase G OpenAI Whisper API provider → **v0.2 at earliest, with BYOK / off-by-default / never-required / clearly-labelled framing** - Engine Phase I OEM verification → **v1.0 commercial track** - Engine Phase J full degraded-mode indicator → **v0.2** (data-loss path closed in v0.1 per Audit 2; the UI-wedge case is documented in known-limitations) - Full SettingsPage 7-group regroup → **v0.2** - Rust-side OS-activity watcher for nudges → **v0.2 if ever** - Obsidian plugin → **v0.2 ecosystem play** - Mobile companion → not on any current track - Cloud sync → not on any current track ## Pre-tag verification (the morning of) On tag day, run `./scripts/tag-day.sh` instead of doing the steps by hand — it orchestrates pre-tag verify, CHANGELOG date, git tag, push, and CI-watch. Run `./scripts/pre-tag-verify.sh` — it executes steps 1–7 and exits 0 on green. Tag, push, then watch CI. ``` ./scripts/tag-day.sh # recommended: full morning-of ceremony in one command # — or, step by step — ./scripts/pre-tag-verify.sh # exit 0 → git tag v0.1.0 && git push --tags ``` Steps automated by the script (all must be green before tagging): 1. **Clean checkout** — refuses if working tree is dirty. 2. **Version sync** — asserts `Cargo.toml` workspace + `package.json` + `tauri.conf.json` are identical. 3. **CHANGELOG date** — refuses if the `2026-MM-DD` placeholder is still present. 4. **Known-limitations doc** — refuses if any item is marked "TBD" or "pending decision". 5. **Quality gates** — `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test --workspace`, `npm run check`, `npm run test`. 6. **Dogfood drill** — `scripts/dogfood-rebrand-drill.sh` (sandbox mode); all 8/8 probes must pass. 7. **Release build** — `cargo build -p lumotia --release` (compilation sanity check; faster than full `tauri build`). After the script exits 0: tag, push to both remotes, watch CI complete the per-platform builds, smoke-test one artefact per platform from the CI output. Step 3 (10-step tester flow on Linux) and step 4 (smoke-test matrix green) remain human steps — see "The tester acceptance test" section above and the smoke-test matrix. No frontend E2E tool (Playwright/Cypress) is currently in the project, so those steps are not automated. If any script step fails, the day's ship is off. Reopen, fix, re-run the script. > 👤 HUMAN REQUIRED: steps 3 + 4 above (tester acceptance flow + smoke-test matrix) still require a human. The script automates everything else. > (waived — see W-08 in v0.1-known-limitations.md)