Originally created by: Akarsh-Hegde
Promotes pre-main → main: the culmination of the Next-fold and the self-contained-DMG / setup re-platform initiative. Meridian moves from a Node-server + external-screenpipe-process topology to a single self-contained Tauri .app — the dashboard runs inside the webview as a static export, every /api route is ported to Rust, capture runs in-process, and the MLX runtime is provisioned/supervised by the tray.
This bundles 13 already-reviewed feature PRs (#298, [#314]–#329) plus post-merge review + CI fixes.
| Metric | Value |
|---|---|
| Commits | 186 |
| Files changed | 223 (83 added, 49 deleted) |
| Diff | +25,162 / −8,814 |
| New shared crate | meridian-core — 23 files, ~6,050 LOC |
| Commit types | 72 feat, 49 fix, 11 chore, 9 refactor, 8 docs, 7 build, 6 test, 6 ci |
| Resulting version | v1.64.0 (minor — see Versioning) |
| DMG size | 19.6 MB (Meridian.app.tar.gz 19.3 MB) |
| Release build | ~5 min (macOS runner: build + sign + notarize-ready + package) |
| Tests | 516 Rust + 7 JS/TS suites — all green |
The core shift: delete the Node UI server and the external screenpipe process; collapse everything into one signed .app where the tray owns capture + the embedded dashboard, the daemon owns ETL, and meridian-core is the shared data layer both link against.
Before (v1.63.0)
screenpipe runs as its own launchd process → writes ~/.screenpipe/db.sqlite; the daemon reads it cross-DB.com.meridiona.ui launchd agent) serves the dashboard and ~32 /api/* routes, reading meridian.db over better-sqlite3./api routes over HTTP.After (v1.64.0)
screenpipe-screen + screenpipe-a11y) → writes capture_frames / capture_ui_events into meridian.db. No screenpipe DB / process / pool.invoke / events through ui/lib/bridge.ts. No Node server, no /api routes.DB reads live in meridian-core/src/readers/ (shared by daemon + tray); file/env/process actions are tray commands. The four SSE streams became Tauri events off the tray poll loop.
:::mermaid
flowchart TB
subgraph BEFORE["BEFORE — v1.63.0 (multi-process)"]
direction TB
sp1[screenpipe process]:::ext --> spdb[(~/.screenpipe/db.sqlite)]:::ext
spdb --> d1[meridian daemon · ETL]
d1 --> mdb1[(meridian.db)]
node[Next.js Node server
com.meridiona.ui]:::ext --> mdb1
node -->|serves| dash1[Dashboard + /api routes]
tray1[Tauri tray] -->|HTTP poll /api| node
mlx1[MLX Python server]:::py --> d1
end
subgraph AFTER["AFTER — v1.64.0 (single self-contained .app)"]
direction TB
subgraph app["Meridian.app"]
cap[Tray · in-process capture
screenpipe-screen/-a11y]:::new
core[[meridian-core
shared readers + commands]]:::new
webview[Embedded dashboard
static export]:::new
d2[meridian daemon · ETL]
end
cap -->|capture_frames / capture_ui_events| mdb2[(meridian.db)]
d2 -->|reads capture tables| mdb2
d2 -->|app_sessions| mdb2
webview -->|Tauri invoke / events| core
core --> mdb2
mlx2[MLX Python server
provisioned + supervised by tray]:::py --> d2
end
classDef ext fill:#fde,stroke:#b66;
classDef new fill:#dfe,stroke:#6b6;
classDef py fill:#eef,stroke:#66b;
/api → Rust (#298, [#314])output: 'export') — no Node server./api/* routes ported: DB reads → meridian-core/src/readers/ (re-exported by the daemon), file/env/process → tray/src-tauri/src/commands/.com.meridiona.ui plist, ui-start.sh, pinned Node + better-sqlite3 ABI dance). Dashboard now ships embedded in the tray binary..app — daemon + a11y staged to stable ~/.meridian/bin/, plists rendered, launchctl bootstrap (#321).meridian setup commands + native setup UI (#315, [#326]).meridian uninstall [--purge] [--dry-run] [--yes] command for clean teardown.capture_frames / capture_ui_events into meridian.db.run_etl(meridian) instead of run_etl(screenpipe, meridian)).tauri-plugin-updater staging channel wired into the release pipeline (#328); staging private-fork auth fix (#329). Minisign trust, independent of Apple Dev ID. Proven end-to-end (ad-hoc bundle relaunch)./code-review of [#298] surfaced 4 correctness divergences from the original TS routes — all fixed (b54ec04): multibyte log-tail drop, leaked tasks-sync child, lenient quiet-hours parser, divergent DB-path resolver.screenpipe-fork git dep (#331).Audited across four surfaces (DB schema, public interfaces, install/upgrade path, runtime behavior). No hard breaking changes for existing installs — the version-affecting changes are additive or documented v1 degradations.
✅ Safe / non-breaking
capture_frames, 047 capture_ui_events, 048 ETL-cursor reset) — no modified/renamed/deleted migrations (CI "Migrations append-only" guard enforced), no DROP/ALTER/destructive changes. Existing meridian.db upgrades cleanly; app_sessions history preserved.settings.json key, or MCP tool removed or renamed (only additions). The deleted /api/* routes were internal-only (consumed solely by the bundled dashboard, which moved to Tauri invoke in lockstep).busy_timeout(5s) on the meridian pool for the new tray↔daemon concurrent-write topology — backward compatible.⚠️ Documented v1 degradations (intentional, not regressions)
get_audio_snippets stubbed empty (in-process capture is text/OCR/a11y only). Classifier runs on text, so accuracy is unaffected; audio_snippets is simply always empty.system_sleep (capture_trigger is NULL).Semantic note: the
/apiremoval and capture cutover are large architectural changes but not user-contract breaks — nothing a user, script, or external integration depends on was removed. Hence the minor bump is honest (see below).
Meridian.dmg), updater bundle Meridian.app.tar.gz 19.3 MB + minisign .sig + latest.json.release-staging.yml: build → sign → updater-sign → package → publish + mirror)..app carries the daemon, a11y helper, embedded dashboard, and in-process capture; the MLX runtime provisions on first run.meridian-core 63, tray 27, integration 64 (tests/etl_*.rs, task_linker_smoke, tray_assets, worklog_provider, + meridian-core/tests/readers.rs, today_smoke.rs).ui/__tests__/: intervals, format, category-colors, cursor-pointer, redesign, lib, meridian-bin)./api routes.-D warnings + test) 2m52s, UI build 41s, Migrations-append-only guard, screenpipe MIT-pin guard, CodeQL (rust/js/python/actions), CodeRabbit.cargo test + UI build + UI tests + security audit).v1.63.0 → v1.64.0 (minor). semantic-release derives the bump from conventional commits: 72 feat: force at least a minor, and zero BREAKING CHANGE: footers / ! markers mean no major. Empirically confirmed — the staging channel already cut v1.64.0-staging.{1,2,3} from these exact commits.
week.rs UTC bucketing, refresh.rs poll-loop efficiency, file-size splits (incl. mlx_server.rs 1183 lines), table_exists() DRY extraction.Every constituent change already merged via its own reviewed PR into pre-main; this PR is the promotion gate to main, not new unreviewed work. Each feature is independently revertable at its merge commit. CI is fully green.
🤖 Generated with Claude Code
Tickets: #298
Tickets: #314
Tickets: #318
Tickets: #319
Tickets: #320
Tickets: #321
Tickets: #324
Tickets: #326
Tickets: #329
Tickets: #330
Tickets: #331
Tickets: #332
Tickets: #333
Originally posted by: coderabbitai[bot]
✨ Finishing Touches
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests - [ ] Commit unit tests in branch `pre-main`Comment
@coderabbitai helpto get the list of available commands.Related
Commit: [72a2a6]
Originally posted by: Akarsh-Hegde
Review findings resolved → PR [#333]
All findings from @adityaharishch's review have been addressed in PR [#333] (
fix/pr-330-review → pre-main). Summary:Fixed
meridian doctorexits non-zero on every healthy post-fold install —ui_service()+ui::checks()probe the retiredcom.meridiona.uiNode serverInfochecks: "dashboard embedded in the Tauri binary (no separate service)". Removed the HTTP probe, plist check,.nextcheck,serve_mode_check, and their unit tests.supervise_mlxnever resetsattemptswhen/healthkeeps failing, so the zombie holding port 7823 is never clearedMLX_COOLING_TICKS = 10(~10 min).attemptsnow increments through the cooling window pastMLX_MAX_RESTARTS; atMAX + COOLINGticks it resets to0sosupervise()runs again (with its existingKilledWedgedpath) and a fresh restart cycle begins.MERIDIAN_RUNTIME_MANIFEST_URLenv-var redirectable in production#[cfg(debug_assertions)]— release binary cannot be redirected via a compromised env var. The self-referential SHA-256 issue (manifest + tarball on the same origin) requires an out-of-band trust root (minisign, like the updater); design tracked as a follow-up.capture_coveragedatetime format mismatch —datetime('now', ?1)produces'…T…Z'vs' '-separated stored timestamps; 'T' > ' ' so every row passes the filterjulianday(timestamp) > julianday('now', ?1)following theframe_freshnesspattern in the same file.subscribe()snapshot prime silently swallows errors on cold-DB startupconsole.warn(...)on second failure — visible in DevTools.sudo npm installruns lifecycle scripts as root--ignore-scriptsto both npm install lines ininstall-from-bundle.sh.Deferred (acknowledged, not blocking)
install.shOO sha256 andbuild-mlx-runtime.shPBS pin: both need real hash values from the upstream release pages — straightforward follow-ups.pub_key). Tracked separately.Related
Tickets:
#333Originally posted by: Akarsh-Hegde
Review findings — status update (PR [#333] merged to pre-main)
All inline threads updated. Summary of what's resolved vs. still open:
poll/mod.rs— MLX restart-budget exhaustion permanently wedgesMLX_COOLING_TICKS = 10added;attemptscounts through a ~10-min cooling window then resets, lettingsupervise()kill any zombie and start a fresh restart cycleui/lib/bridge.ts—subscribe()snapshot prime silently swallows errorsconsole.warnon second failuresrc/health/ui.rs+platform::ui_service()—doctorCRITICAL on healthy installsInfo: dashboard embedded in the Tauri binarysrc/health/capture.rs—capture_coveragedatetime format mismatchjulianday(timestamp) > julianday('now', ?1)in both subqueriesinstall-from-bundle.sh—sudo npm installruns lifecycle scripts as root--ignore-scriptsadded to both npm install linesmlx_server.rs—MERIDIAN_RUNTIME_MANIFEST_URLredirectable in production#[cfg(debug_assertions)]; self-referential SHA-256 (manifest + tarball from same origin) requires an out-of-band trust root (minisign embedded key), tracked as a follow-upbuild-mlx-runtime.sh— PBSreleases/latest+ no checksumservices/agents/server.py— emptyexceptclausePR [#333] is merged into pre-main. The remaining two open items are tracked as follow-ups and are not blocking the
pre-main → mainpromotion.Related
Tickets:
#333Originally posted by: Akarsh-Hegde
The three deferred items are now tracked in the Obsidian tech backlog (Architecture/Tech Backlog.md):
build-mlx-runtime.shPBS release pin + sha256 verificationservices/agents/server.pyemptyexceptclauseTicket changed by: Akarsh-Hegde