Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
f3e5ebd
chore: clean Tauri package metadata
Air000000 Sep 8, 2026
b04ca82
test: define database lifecycle regression coverage
Air000000 Sep 8, 2026
970267c
test: enable database lifecycle regression suite
Air000000 Sep 8, 2026
cd6797a
test: define database initialization integration coverage
Air000000 Sep 8, 2026
80ed2ba
ci: add reproducible Windows release workflow
Air000000 Sep 8, 2026
212543e
docs: document Windows release gate
Air000000 Sep 8, 2026
8d820af
docs: define real product media capture gate
Air000000 Sep 8, 2026
76fcdc9
test: fix database regression helper lifetime
Air000000 Sep 8, 2026
c8b9810
test: remove duplicate database regression file
Air000000 Sep 8, 2026
5c28cb8
ci: modernize Windows checks and cache Rust builds
Air000000 Sep 8, 2026
db427f7
ci: use current GitHub action runtimes for releases
Air000000 Sep 8, 2026
3c79357
docs: expose distribution and product evidence gates
Air000000 Sep 8, 2026
38bc4a0
ci: pass installer bundle flags directly to Tauri CLI
Air000000 Sep 8, 2026
44ad6bc
refactor: expose legacy database file migration seam
Air000000 Sep 8, 2026
8b4f1b4
refactor: extract connection-based database initializer
Air000000 Sep 8, 2026
1f48690
refactor: preserve legacy migration short-circuit semantics
Air000000 Sep 8, 2026
48de5c2
ci: cancel superseded pull-request runs
Air000000 Sep 8, 2026
1f14230
ci: move validation to Node 24 LTS
Air000000 Sep 8, 2026
cee67e6
ci: use Node 24 LTS for Windows releases
Air000000 Sep 8, 2026
78986c5
chore: declare Node 24 development baseline
Air000000 Sep 8, 2026
0f0d591
docs: align prerequisites with Node 24 LTS
Air000000 Sep 8, 2026
89a72fe
docs: align testing strategy with current coverage
Air000000 Sep 8, 2026
56657a5
test: expose corrupted root category seed names
Air000000 Sep 8, 2026
4a785ba
fix: repair corrupted root category seed names
Air000000 Sep 8, 2026
da7dcc4
ci: validate Windows installers on packaging PRs
Air000000 Sep 8, 2026
e54b9fd
chore: retrigger packaging workflow discovery
Air000000 Sep 8, 2026
b84d1c3
fix: pass Windows bundle targets as separate arguments
Air000000 Sep 8, 2026
9f06e8e
docs: document automated installer packaging gate
Air000000 Sep 8, 2026
5d8aa34
docs: separate packaging and installer smoke gates
Air000000 Sep 8, 2026
f6f5c1a
docs: specify first-run tracking activation
Air000000 Sep 8, 2026
5a878a0
docs: plan first-run tracking implementation
Air000000 Sep 8, 2026
c2e1b8d
docs: tighten first-run tracking implementation plan
Air000000 Sep 8, 2026
97406b1
test(db): specify first-run tracking defaults
Air000000 Sep 8, 2026
9920dd4
feat(db): persist first-run tracking defaults
Air000000 Sep 8, 2026
8feb50d
feat(tracking): add tracking state contract
Air000000 Sep 8, 2026
38001ce
test(tracking): specify persisted state invariants
Air000000 Sep 8, 2026
8d8e346
feat(tracking): expose tracking domain
Air000000 Sep 8, 2026
11ee4a9
test(tracking): compile tracking invariant tests
Air000000 Sep 8, 2026
6b64c27
feat(tracking): add persisted tracking state invariant
Air000000 Sep 8, 2026
7676da0
test(startup): require manual product window creation
Air000000 Sep 8, 2026
9ba30c5
test(startup): compile window creation gate
Air000000 Sep 8, 2026
705a997
fix(startup): require manual webview creation
Air000000 Sep 8, 2026
895300a
feat(startup): add configured window factory
Air000000 Sep 8, 2026
d6fe7c3
feat(startup): expose configured window factory
Air000000 Sep 8, 2026
e905a1d
fix(startup): initialize database before webviews
Air000000 Sep 8, 2026
4490e50
fix(reminders): preserve snooze duration argument
Air000000 Sep 8, 2026
30999d9
test(frontend): add tracking API type contract
Air000000 Sep 8, 2026
24e53f8
test(frontend): move capture policy out of sampler contract
Air000000 Sep 8, 2026
75085a3
feat(frontend): add persisted tracking state type
Air000000 Sep 8, 2026
89bfacc
feat(frontend): add tracking command client
Air000000 Sep 8, 2026
934c2e9
feat(frontend): export tracking commands
Air000000 Sep 8, 2026
7a3dc9b
feat(frontend): export tracking state
Air000000 Sep 8, 2026
cf3071f
refactor(frontend): make capture sampler policy-free
Air000000 Sep 8, 2026
75b484f
refactor(frontend): keep legacy sampler option inert
Air000000 Sep 8, 2026
3ff8317
test(frontend): define tracking runtime contract
Air000000 Sep 8, 2026
df250fd
test(frontend): require persisted guard capture toggle
Air000000 Sep 8, 2026
b6428b4
test(frontend): require tracking gate before main runtime
Air000000 Sep 8, 2026
3fae87e
feat(frontend): centralize persisted tracking state
Air000000 Sep 8, 2026
f456736
feat(frontend): persist focus guard capture toggle
Air000000 Sep 8, 2026
7a21d91
feat(frontend): load persisted tracking before runtime startup
Air000000 Sep 8, 2026
eb3cf2a
feat(frontend): gate capture runtime on persisted tracking state
Air000000 Sep 8, 2026
5cef416
test(frontend): require typed pet summon command
Air000000 Sep 8, 2026
6cf5098
test(frontend): require tracking onboarding view
Air000000 Sep 8, 2026
cde638f
test(frontend): define onboarding UI state gate
Air000000 Sep 8, 2026
45efdbb
feat(frontend): add typed pet window command
Air000000 Sep 8, 2026
ed1bf8a
feat(frontend): export pet window command
Air000000 Sep 8, 2026
a53a4ab
feat(frontend): centralize tracking UI state gate
Air000000 Sep 8, 2026
4111054
feat(frontend): add first-run tracking privacy onboarding
Air000000 Sep 8, 2026
4be1390
feat(frontend): gate first-run UI and activate tracking without restart
Air000000 Sep 8, 2026
2183bde
fix(frontend): expose runtime tracking UI overload
Air000000 Sep 8, 2026
8850a97
test(frontend): forbid capture policy in sampler API
Air000000 Sep 8, 2026
7ca48a4
refactor(frontend): remove capture policy from sampler API
Air000000 Sep 8, 2026
cce1dcf
test(frontend): pin sampler policy rejection to property
Air000000 Sep 8, 2026
67e323e
test(pet): require persisted tracking mood contract
Air000000 Sep 8, 2026
94a318e
feat(pet): show persisted tracking status
Air000000 Sep 8, 2026
aff2660
test(ui): require compare-and-clear error ownership
Air000000 Sep 8, 2026
56f305b
fix(ui): add compare-and-clear error API
Air000000 Sep 8, 2026
9f3aeb1
fix(ui): preserve newer errors during home recovery
Air000000 Sep 8, 2026
ebf6593
fix(ui): clear only recovered home read errors
Air000000 Sep 8, 2026
1970a49
docs: add first-run tracking smoke gates
Air000000 Sep 8, 2026
c34410a
docs: add first-run tracking release gates
Air000000 Sep 8, 2026
f297c1b
test(idle): require previous-app attribution contract
Air000000 Sep 11, 2026
8062ce1
feat(api): expose idle attribution candidate
Air000000 Sep 11, 2026
0264cd2
feat(api): accept idle app attribution decision
Air000000 Sep 11, 2026
2875b7c
test(idle): require app-attributed interval persistence
Air000000 Sep 11, 2026
229b463
feat(idle): persist app-attributed idle intervals
Air000000 Sep 11, 2026
60325f1
feat(idle): carry previous-app attribution candidate
Air000000 Sep 11, 2026
a60f0eb
test(idle): carry attribution field through usage fixtures
Air000000 Sep 11, 2026
27d8f66
feat(idle): capture and resolve previous-app attribution
Air000000 Sep 11, 2026
bdbdc08
test(idle): require app attribution in guard decisions
Air000000 Sep 11, 2026
fe5d44a
feat(idle): add app attribution guard decision
Air000000 Sep 11, 2026
23f7061
feat(idle): expose app attribution to view contexts
Air000000 Sep 11, 2026
7c165be
feat(idle): pass app name formatter to idle banner
Air000000 Sep 11, 2026
6558b02
refactor(idle): keep banner context minimal
Air000000 Sep 11, 2026
d102728
refactor(idle): keep banner formatter local
Air000000 Sep 11, 2026
6ee3ff4
feat(idle): add progressive idle attribution banner
Air000000 Sep 11, 2026
acda1d8
feat(idle): simplify attribution choices in guard
Air000000 Sep 11, 2026
75a40f0
feat(pet): carry idle attribution candidate
Air000000 Sep 11, 2026
8f72ebb
feat(pet): simplify idle attribution prompt
Air000000 Sep 11, 2026
6e95509
feat(pet): route idle attribution other choice to guard
Air000000 Sep 11, 2026
03e472f
test(idle): forbid attribution privacy bypass
Air000000 Sep 11, 2026
b17e633
fix(idle): enforce privacy before app attribution
Air000000 Sep 11, 2026
b5791e1
fix(idle): restore prompt when attribution persistence fails
Air000000 Sep 11, 2026
3ba2267
docs(ui): explain idle app attribution semantics
Air000000 Sep 11, 2026
4712ee4
docs: add idle attribution smoke coverage
Air000000 Sep 11, 2026
4f9da17
fix(idle): anchor attribution to last user input
Air000000 Sep 11, 2026
19320df
Track idle attribution provenance
Air000000 Sep 11, 2026
cb95d2a
Fix pet prompt clipping on high-DPI displays
Air000000 Sep 13, 2026
1aa5fd3
Fix pet scaling across mixed-display sizes
Air000000 Sep 13, 2026
421aed0
Handle mixed-resolution pet scaling
Air000000 Sep 13, 2026
eea12aa
Restore editable Home goal duration
Air000000 Sep 13, 2026
1dbfac5
Strengthen Home goal editor regression coverage
Air000000 Sep 13, 2026
758b4d0
Make Home goal contract line-ending agnostic
Air000000 Sep 13, 2026
b85bca5
feat: reuse Yin Yue pet spritesheet
Air000000 Sep 14, 2026
53ed39c
fix: use resting poses when pet is docked
Air000000 Sep 14, 2026
23b880e
feat: animate Yin Yue dragging and docking
Air000000 Sep 14, 2026
511b980
fix: refine Yin Yue docking and drag motion
Air000000 Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,38 @@ on:
- main
- "refactor/**"

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
checks:
name: Typecheck, Vite build, Rust check/test
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 20
node-version: 24

- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust build output
uses: Swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
cache-on-failure: true

- name: Install frontend dependencies
run: pnpm install --frozen-lockfile

Expand All @@ -35,9 +48,9 @@ jobs:
- name: Build frontend to temporary output
run: pnpm run build:check

- name: Check Rust backend
- name: Check all Rust targets
working-directory: src-tauri
run: cargo check
run: cargo check --all-targets

- name: Test Rust backend
working-directory: src-tauri
Expand Down
111 changes: 111 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Windows Release

# Packaging PRs build real Windows installers without publishing a Release.
on:
pull_request:
branches:
- main
paths:
- ".github/workflows/release.yml"
- "src-tauri/tauri.conf.json"
- "src-tauri/Cargo.toml"
- "src-tauri/Cargo.lock"
- "package.json"
- "pnpm-lock.yaml"
workflow_dispatch:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
windows:
name: Build Windows installers
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: 24

- name: Enable pnpm
run: |
corepack enable
corepack prepare pnpm@9.15.9 --activate

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust build output
uses: Swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"

- name: Install frontend dependencies
run: pnpm install --frozen-lockfile

- name: Validate synchronized versions
shell: pwsh
run: |
$tauriVersion = (Get-Content src-tauri/tauri.conf.json | ConvertFrom-Json).version
$packageVersion = (Get-Content package.json | ConvertFrom-Json).version
$cargoMetadata = cargo metadata --manifest-path src-tauri/Cargo.toml --no-deps --format-version 1 | ConvertFrom-Json
$cargoVersion = ($cargoMetadata.packages | Where-Object { $_.name -eq 'timeprism' } | Select-Object -First 1).version

if ($tauriVersion -ne $packageVersion -or $tauriVersion -ne $cargoVersion) {
throw "Version mismatch: tauri=$tauriVersion package=$packageVersion cargo=$cargoVersion"
}

if ($env:GITHUB_REF_TYPE -eq 'tag') {
$expectedTag = "v$tauriVersion"
if ($env:GITHUB_REF_NAME -ne $expectedTag) {
throw "Tag/version mismatch: pushed=$env:GITHUB_REF_NAME expected=$expectedTag"
}
}

"TIMEPRISM_VERSION=$tauriVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

- name: Typecheck frontend
run: pnpm run typecheck

- name: Test Rust backend
working-directory: src-tauri
run: cargo test

- name: Build installers for validation
if: github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
run: pnpm tauri build --bundles nsis msi

- name: Upload validation installers
if: github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
uses: actions/upload-artifact@v7
with:
name: TimePrism-${{ env.TIMEPRISM_VERSION }}-windows-installers-${{ github.event_name }}
if-no-files-found: error
path: |
src-tauri/target/release/bundle/nsis/*-setup.exe
src-tauri/target/release/bundle/msi/*.msi

- name: Build and create draft GitHub Release
if: github.event_name == 'push' && github.ref_type == 'tag'
uses: tauri-apps/tauri-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tagName: v__VERSION__
releaseName: TimePrism v__VERSION__
releaseBody: |
Windows-first, local-first TimePrism desktop release.

Download either the NSIS setup executable or MSI installer from the release assets.
This project is currently unsigned, so Windows SmartScreen may warn about an unknown publisher.
releaseDraft: true
prerelease: false
generateReleaseNotes: true
args: --bundles nsis msi
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</p>

<p align="center">
<a href="https://github.com/Air000000/timeprism/releases"><strong>Windows Releases</strong></a> |
<a href="#what-it-demonstrates">What It Demonstrates</a> |
<a href="#architecture">Architecture</a> |
<a href="#engineering-highlights">Engineering Highlights</a> |
Expand All @@ -31,6 +32,8 @@ Rather than relying on manual start/stop timers, it samples the foreground appli

The project is intentionally scoped as a Windows-first, local-first desktop application. It does not depend on cloud accounts, remote sync, or external services for its core workflow.

> **Release status:** the currently published `v0.1.0-preview` is an early portable build and predates the latest architecture/reliability work. The repository now includes a gated Windows release pipeline for NSIS `-setup.exe` and MSI installers; packaging changes are validated on Windows before merge, while public releases remain draft-first and require an interactive installer smoke test before publication.

## What It Demonstrates

TimePrism is primarily a desktop full-stack engineering project. The repository demonstrates:
Expand Down Expand Up @@ -144,7 +147,7 @@ SQLite persistence
| Backend | Rust |
| Storage | SQLite / rusqlite |
| Platform integration | Windows + Tauri window APIs |
| Validation | GitHub Actions, `vue-tsc`, Vite build, `cargo check`, `cargo test` |
| Validation | GitHub Actions, `vue-tsc`, Vite build, `cargo check --all-targets`, `cargo test` |

## Project Structure

Expand Down Expand Up @@ -178,18 +181,32 @@ timeprism/

## Validation

Every push to `main` and `refactor/**`, plus pull requests, runs the repository CI workflow on Windows:
Every push to `main` and `refactor/**`, plus pull requests, runs the repository CI workflow on Windows. Rust build output is cached between runs and all test targets are compiled before the test phase:

```text
pnpm run typecheck
pnpm run build:check
cargo check
cargo check --all-targets
cargo test
```

Release-related pull requests also run an independent Windows packaging gate that builds both NSIS and MSI installers and uploads them as workflow artifacts. This verifies that the current tree can reach real Tauri installer artifacts without publishing a Release.

The current desktop flow has also been manually smoke-tested on Windows for startup behavior, main views, reminder workflows, pet interactions, and full-app quit behavior.

CI and smoke tests are used as project-level regression evidence; they are not a claim of production-grade hardening.
CI, packaging validation, and smoke tests are used as project-level regression evidence; they are not a claim of production-grade hardening.

## Distribution

`.github/workflows/release.yml` separates installer generation from publication on `windows-latest`:

- **Packaging PR:** release-related pull requests validate synchronized versions, run frontend/Rust checks, build both NSIS and MSI installers, and upload the real installer artifacts without publishing anything.
- **Manual dry run:** `workflow_dispatch` performs the same installer build on demand after merge and uploads the installers as workflow artifacts without creating a Release.
- **Version tag:** requires the tag to exactly match the synchronized Cargo/Node/Tauri app version, then creates a **draft** GitHub Release with both installer formats.

Automated packaging proves that the installers can be generated; it does not replace the interactive Windows gate. At least one generated installer must still be downloaded, installed, launched, and smoke-tested before the draft Release is made public.

The project is currently unsigned, so Windows SmartScreen may warn about an unknown publisher. See [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md) for the full publication gate.

## Privacy

Expand All @@ -210,7 +227,7 @@ Because active-window data can be sensitive, privacy is treated as part of the d

Recommended local development environment:

- Node.js 20
- Node.js 24 LTS
- pnpm 9
- Rust toolchain
- Tauri v2 system prerequisites
Expand Down Expand Up @@ -242,7 +259,7 @@ pnpm dev
pnpm run typecheck
pnpm run build:check
cd src-tauri
cargo check
cargo check --all-targets
cargo test
```

Expand Down Expand Up @@ -277,8 +294,12 @@ Engineering notes and design rationale live under `docs/`, including:
- database and privacy notes
- smoke-test catalog
- testing strategy
- release checklist
- product-media capture guide
- ADRs and refactor traceability

Real product screenshots/GIF are only added from sanitized current Windows builds; the capture/privacy requirements are documented in [`docs/media/README.md`](docs/media/README.md) so generated or mock UI is not presented as implementation evidence.

## License

TimePrism is licensed under the MIT License.
137 changes: 137 additions & 0 deletions docs/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Release Checklist

TimePrism publishes Windows installers from GitHub Actions. Releases are created as **drafts** first so the generated installers can be smoke-tested before they become public.

## Release artifacts

The Windows release workflow builds both Tauri-supported installer formats:

- NSIS setup executable: `*-setup.exe`
- WiX/MSI installer: `*.msi`

The current project is unsigned. Windows SmartScreen can therefore show an unknown-publisher warning until code signing is added.

## Two-layer installer gate

Installer validation is deliberately split into two layers:

1. **Automated packaging gate:** release-related pull requests build both NSIS and MSI installers on `windows-latest` and upload them as workflow artifacts. This proves the current tree can reach real installer artifacts before merge.
2. **Interactive Windows smoke:** a generated installer is downloaded to a real Windows desktop, installed, launched, exercised, and checked for restart/data/lifecycle behavior. CI cannot replace this interaction-level gate.

A green packaging job is therefore necessary evidence, but it is not sufficient by itself to publish a Release.

## Mandatory first-run and upgrade acceptance

Before a public release, both tracking-state migration paths must pass on a real Windows machine using the exact release candidate build.

### Fresh-install gate

Use a clean or sandboxed TimePrism app-data directory and complete `S-050 Fresh Install And First-Run Tracking` in `docs/SMOKE_TESTS.md`.

Release-blocking assertions include:

- SQLite is initialized before product WebViews query it; no `no such table` startup failure appears.
- No foreground capture occurs before the user accepts the first-run tracking disclosure.
- The disclosure accurately describes foreground app/window metadata, local SQLite storage, browser privacy processing, no screenshot/keystroke-content capture, and the distinction between observed activity and classified `LEARN / REST` time.
- Activation begins tracking without an application restart.
- Pause persists across full quit/reopen; resume also persists and restores automatic tracking after restart.
- The desktop pet reports active versus paused tracking while keeping `学 / 休` counters classification-based.

### Upgrade gate

Use a backed-up recognizable TimePrism database created before the tracking-state keys existed and complete `S-060 Upgrade From A Pre-Tracking-State Database`.

Release-blocking assertions include:

- existing rows survive migration;
- onboarding does not unexpectedly reappear for an existing user;
- the legacy database starts onboarded with capture enabled;
- a later explicit pause is preserved across reinitialization and restart.

Do not publish a release, mark a release candidate accepted, or promote a tracking-state PR solely from automated CI evidence if either interactive gate has not passed.

## Before tagging

1. Confirm `main` CI is green.
2. Confirm the latest relevant packaging PR produced both installer formats successfully.
3. Confirm both mandatory acceptance paths above passed on the release candidate:
- fresh install (`S-050`);
- pre-tracking-state upgrade (`S-060`).
4. Update the same semantic version in:
- `package.json`
- `src-tauri/Cargo.toml`
- `src-tauri/tauri.conf.json`
5. Run or verify:

```text
pnpm install --frozen-lockfile
pnpm run typecheck
pnpm run build:check
cargo check --manifest-path src-tauri/Cargo.toml --all-targets
cargo test --manifest-path src-tauri/Cargo.toml
```

6. Smoke-test the remaining desktop flows listed in `docs/SMOKE_TESTS.md`.
7. Commit the synchronized version bump to `main`.

The release workflow rejects mismatched Node/Cargo/Tauri versions. Tag-triggered runs also reject a tag that does not equal `v<app-version>`.

## Pull-request packaging validation

When a pull request changes release/package inputs, **Windows Release** runs automatically.

The pull-request path:

1. validates version synchronization;
2. runs frontend typecheck and Rust tests;
3. builds both NSIS and MSI installers on `windows-latest`;
4. uploads both installers as one workflow artifact;
5. does **not** create a GitHub Release.

The packaging job uses `if-no-files-found: error`, so a green upload step also verifies that both configured installer paths resolved to real files.

## Manual dry-run installer build

After the workflow is on `main`, **Windows Release** can also be started manually from GitHub Actions before creating a tag.

The `workflow_dispatch` path performs the same version checks, tests, installer build, and artifact upload without creating a GitHub Release. Use this when you want a fresh installer candidate from the default branch even if no packaging-related pull request is open.

Download the generated artifact and perform the interactive Windows smoke before publishing a version tag.

## Interactive installer smoke

At minimum, verify one generated installer end to end on Windows:

- install from the NSIS `-setup.exe` or MSI artifact;
- run the fresh-install tracking acceptance with a clean/sandboxed app-data directory;
- separately run the upgrade acceptance against a backed-up pre-tracking-state database;
- verify Home, Insights, Focus Guard, reminders, privacy settings, and desktop pet flows;
- verify main-window close/background behavior and full-app quit behavior;
- restart the app and confirm local data and tracking pause/resume state survive as expected;
- uninstall the tested installer and confirm the uninstall flow completes normally;
- retain the unsigned/SmartScreen note until Windows code signing exists.

If the installer or installed application fails this gate, fix the issue and produce a fresh artifact before tagging.

## Publish a release draft

Create and push a tag that exactly matches the app version, for example:

```text
git tag v0.2.0
git push origin v0.2.0
```

The tag-triggered workflow validates the tag/version match and uses `tauri-apps/tauri-action` to create a draft GitHub Release with the NSIS and MSI assets attached.

## Verify the draft

Before publishing the draft Release:

- confirm the expected NSIS and MSI assets are attached;
- install at least one draft asset if it differs from the previously smoke-tested artifact;
- confirm both fresh-install and upgrade acceptance evidence corresponds to the same code/release candidate;
- confirm Release notes describe any known limitations;
- explicitly retain the unsigned/SmartScreen note until Windows code signing exists.

Only after these checks should the GitHub Release be published.
Loading