diff --git a/docs/adr/0007-sentry-posthog-telemetry.md b/docs/adr/0007-sentry-posthog-telemetry.md new file mode 100644 index 00000000..8c4cb5d8 --- /dev/null +++ b/docs/adr/0007-sentry-posthog-telemetry.md @@ -0,0 +1,30 @@ +--- +type: ADR +id: "0007" +title: "Opt-in telemetry via Sentry and PostHog" +status: active +date: 2026-03-25 +--- + +## Context + +Laputa had no visibility into crashes or usage patterns. Bug reports from users were the only signal, often missing reproduction steps or environment details. We needed crash reporting and basic analytics while respecting user privacy — this is a personal knowledge management tool with sensitive data. + +## Decision + +**Integrate Sentry for crash reporting and PostHog for usage analytics, both strictly opt-in. No telemetry is sent without explicit user consent via a first-launch dialog.** + +## Options considered + +- **Option A** (chosen): Sentry + PostHog, opt-in with consent dialog — pros: industry-standard tools, granular control (crash reporting and analytics toggle independently), path scrubbing prevents vault content leakage / cons: two third-party dependencies, requires DSN/key management +- **Option B**: Self-hosted error tracking — pros: full data control / cons: infrastructure overhead, not justified at current scale +- **Option C**: No telemetry — pros: simplest, zero privacy risk / cons: flying blind on crashes, no usage data for prioritization + +## Consequences + +- First launch shows `TelemetryConsentDialog` — accept generates a local `anonymous_id` (UUID), decline means zero network requests +- Users can toggle crash reporting and analytics independently in Settings +- `beforeSend` hooks in both Rust and JS scrub file paths and note titles from payloads +- PostHog configured with `autocapture: false`, `persistence: 'memory'`, no cookies +- Dual-layer initialization: Rust-side Sentry in `lib.rs` setup, JS-side Sentry + PostHog via `useTelemetry` hook +- Re-evaluate if we need server-side event processing or if client-side scrubbing is insufficient diff --git a/docs/adr/0008-canary-release-channel.md b/docs/adr/0008-canary-release-channel.md new file mode 100644 index 00000000..fa72f9e0 --- /dev/null +++ b/docs/adr/0008-canary-release-channel.md @@ -0,0 +1,30 @@ +--- +type: ADR +id: "0008" +title: "Canary release channel for early testing" +status: active +date: 2026-03-25 +--- + +## Context + +All users received the same builds from `main`. There was no way to test new features with a subset of users before a full rollout, and no mechanism to ship experimental builds without risking stability for everyone. + +## Decision + +**Support two release channels — Stable (default, from `main`) and Canary (from `canary` branch) — with independent CI/CD pipelines and update manifests.** + +## Options considered + +- **Option A** (chosen): Two-channel release (stable + canary) — pros: early feedback loop, canary users opt in explicitly, independent build pipelines, semver prerelease tags (`-canary`) / cons: two CI workflows to maintain, canary updates are manual download (not auto-update) +- **Option B**: Single channel with feature flags only — pros: one build pipeline / cons: no way to test platform/build-level changes, feature flags don't cover Rust changes +- **Option C**: Beta program via TestFlight — pros: native distribution / cons: Apple-only, review delays, doesn't match the direct-download model + +## Consequences + +- `release.yml` builds from `main` → `latest.json` on GitHub Pages (Tauri auto-update) +- `release-canary.yml` builds from `canary` → `latest-canary.json` (manual download via release page) +- `update_channel` stored in Settings, configurable in Settings panel +- Canary versions use semver prerelease: `0.YYYYMMDD.N-canary` +- Stable users get seamless auto-update via Tauri updater plugin; canary users get a notification with a link to download +- Re-evaluate if canary adoption grows enough to justify auto-update support for canary channel diff --git a/docs/adr/0009-local-feature-flags.md b/docs/adr/0009-local-feature-flags.md new file mode 100644 index 00000000..a27d9e33 --- /dev/null +++ b/docs/adr/0009-local-feature-flags.md @@ -0,0 +1,30 @@ +--- +type: ADR +id: "0009" +title: "Local feature flags (no remote dependency)" +status: active +date: 2026-03-25 +--- + +## Context + +We needed a way to gate unfinished features behind flags during development and QA without shipping them to all users. Remote feature flag services (LaunchDarkly, PostHog flags) add network dependencies, privacy concerns, and complexity disproportionate to a single-developer desktop app. + +## Decision + +**Use a local-only feature flag system based on compile-time defaults with `localStorage` overrides. No remote fetching, no external dependencies.** + +## Options considered + +- **Option A** (chosen): Local flags with `localStorage` override — pros: zero network requests, zero privacy concerns, instant toggle for dev/QA, type-safe via TypeScript union, trivial implementation / cons: no remote rollout control, no gradual rollout percentage +- **Option B**: PostHog feature flags — pros: remote control, gradual rollout, A/B testing / cons: adds network dependency, privacy implications, overkill for single-developer project +- **Option C**: Build-time flags only (no runtime override) — pros: simplest / cons: requires rebuild to toggle, bad for QA + +## Consequences + +- `useFeatureFlag(name)` hook checks `localStorage` key `ff_`, falls back to `FLAG_DEFAULTS` +- `FeatureFlagName` TypeScript union type enforces valid flag names at compile time +- QA can toggle flags via browser DevTools without rebuilding +- API surface is designed to be compatible with future migration to remote flags if needed +- No gradual rollout capability — flags are binary (on/off) per installation +- Re-evaluate if user base grows enough to need remote control or percentage-based rollouts diff --git a/docs/adr/0010-codescene-code-health-gates.md b/docs/adr/0010-codescene-code-health-gates.md new file mode 100644 index 00000000..6436c943 --- /dev/null +++ b/docs/adr/0010-codescene-code-health-gates.md @@ -0,0 +1,31 @@ +--- +type: ADR +id: "0010" +title: "CodeScene code health gates in CI and git hooks" +status: active +date: 2026-03-24 +--- + +## Context + +Code complexity was creeping up as features were added rapidly. Manual code review could catch some issues, but there was no automated enforcement of code quality standards. Large functions, deep nesting, and god components were appearing in hotspot files (frequently edited files). + +## Decision + +**Enforce CodeScene code health scores as automated gates in pre-commit and pre-push hooks: hotspot health >= 9.5, average health >= 9.31. Both gates block commit/push on failure.** + +## Options considered + +- **Option A** (chosen): CodeScene automated gates in git hooks — pros: catches complexity at commit time, objective thresholds, MCP integration for in-editor feedback, continuous improvement via Boy Scout Rule / cons: can slow down commits, external API dependency, thresholds need periodic tuning +- **Option B**: Manual code review only — pros: no tooling overhead / cons: subjective, inconsistent, doesn't scale with rapid development +- **Option C**: ESLint complexity rules only — pros: free, fast, no API / cons: doesn't measure structural complexity, no cross-language support (Rust), misses higher-level patterns + +## Consequences + +- Pre-commit hook checks both hotspot and average code health before allowing commits +- Pre-push hook runs the same checks as a safety net +- Developers must fix complexity regressions before committing — even in files they didn't directly modify if their changes affected the average +- `.codesceneignore` excludes test files, scripts, and tool directories from scoring +- CodeScene MCP tools available for checking scores before committing +- Thresholds have been tuned upward over time (8.8 → 8.9 → 9.31 → current) as the codebase improved +- Re-evaluate if CodeScene API latency becomes a bottleneck for commit workflow diff --git a/docs/adr/README.md b/docs/adr/README.md index e6d17c20..60c455f9 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -61,3 +61,8 @@ proposed → active → superseded | [0003](0003-single-note-model.md) | Single note open at a time (no tabs) | active | | [0004](0004-vault-vs-app-settings-storage.md) | Vault vs app settings for state storage | active | | [0005](0005-tauri-ios-for-ipad.md) | Tauri v2 iOS for iPad support (vs SwiftUI rewrite) | active | +| [0006](0006-flat-vault-structure.md) | Flat vault structure (no type-based folders) | active | +| [0007](0007-sentry-posthog-telemetry.md) | Opt-in telemetry via Sentry and PostHog | active | +| [0008](0008-canary-release-channel.md) | Canary release channel for early testing | active | +| [0009](0009-local-feature-flags.md) | Local feature flags (no remote dependency) | active | +| [0010](0010-codescene-code-health-gates.md) | CodeScene code health gates in CI and git hooks | active |