mirror of
https://github.com/nexu-io/open-design.git
synced 2026-07-06 22:31:53 +08:00
* feat(packaged): main-process telemetry for pre-daemon startup failures The daemon hosts the PostHog client, so when daemon/web sidecars die before reporting status (issue #4638: better-sqlite3 vanished from the bundle -> ERR_MODULE_NOT_FOUND), nothing emits telemetry and the whole pre-daemon crash class is invisible on every dashboard. Add a main-process direct capture on the fatal-exit path: classify daemon-vs-web by log-path segment, parse the real error code + missing module from the sidecar log tail, scrub home dirs, and POST one packaged_runtime_failed event over plain fetch (no posthog-node SDK, no new dependency). Zero happy-path side-effects by construction: all logic runs only inside main().catch, fetch is wrapped in Promise.race with a hard timeout, errors are swallowed, and it is a no-op without a PostHog key. Crash telemetry follows the existing captureSafety policy (stability data is retained even for opted-out users); Settings -> Privacy copy follow-up tracked in the PR. Slice B of the crash-governance spec (PR #4694). * fix(packaged): Windows web-path classify + main-process installationId (review) Address PerishCode review on #4696: - classifyStartupFailure normalizes backslashes before the /web/ segment check so Windows web-sidecar failures aren't misreported as daemon-start. - resolveStartupDistinctId now takes an explicit installationRoot (threaded from paths.installationRoot); OD_INSTALLATION_DIR is only set in the daemon child env, so the main process previously always fell back to the synthetic id. Adds a Windows web-path fixture and an installationId-resolution test (18 total). * fix(packaged): raise crash-telemetry flush timeout 1.5s -> 5s (e2e finding) Real-machine e2e (local packaged build -> delete better-sqlite3 to reproduce issue #4638 -> launch -> query PostHog) showed the 1.5s cap silently DROPPED the event: a cold DNS+TLS handshake to us.i.posthog.com from a fresh main process exceeds 1.5s, so Promise.race timed out and process.exit(1) killed the in-flight POST. Confirmed with a standalone repro (1.5s never landed, 6s did). 5s lets the single request complete on a cold connection; an offline machine still exits fast because fetch rejects on DNS failure (send resolves early), so only a black-hole network pays the full bound — acceptable on a crash exit. * feat(packaged): stamp shared safety-event envelope on crash telemetry (review) Address PerishCode review on #4696: the direct PostHog emitter only sent the startup props + $os, omitting the envelope captureSafety always stamps. Mirror it so env/schema-filtered dashboards bucket packaged_runtime_failed beside the rest of the stream: event_schema_version, env, device_id, client_type (packaged_main), capture_source (packaged/startup), ui_version, event_id and $insert_id (dedup key). EVENT_SCHEMA_VERSION + the env resolver are replicated (packaged doesn't depend on contracts) with lockstep comments. Adds a typed PackagedRuntimeFailedProps + AnalyticsEventPayload entry in contracts, and a test asserting the envelope wire fields (19 tests). * fix(contracts): restore packaged_runtime_failed in AnalyticsEventName (review) The name union member was clobbered when the envelope change synced events.ts; only the AnalyticsEventPayload entry survived. PerishCode caught the regression — restore the canonical AnalyticsEventName member so the event name and its typed payload stay in lockstep.