Skip to main content

Architecture Overview

CommitT is a Turborepo monorepo with a strict separation between client applications, shared packages, and native platform code. Every component is designed around a single principle: the user cannot bypass their own commitments.

Monorepo Structure

apps/native

The primary application. React Native + Expo with 7 custom Kotlin native modules, Zustand state management, and a full Expo Router navigation stack.

packages/backend

Convex serverless backend with Domain-Driven Design. Contains API, Core, Execution, and DB layers with a 500+ line typed schema.

apps/web

Tauri desktop dashboard for monitoring commitments from a computer. Shares the Convex backend with the mobile app.

apps/extension

WXT-based browser extension for blocking distracting websites. Syncs blocklist rules from the same Convex backend.

Data Flow Architecture

The entire system revolves around three isolated data environments that must stay synchronized: Every user action that modifies data must write to three independent systems in sequence:

The Provider Tree

The app’s root _layout.tsx establishes a deeply layered provider architecture. The order is critical — each layer depends on the one above it:
The SecurityShield sits above the navigation stack. If a rooted device or jailbreak is detected (via JailMonkey), the entire app tree below it is unmounted and replaced with a violation screen. There is no way to navigate around it.

Resurrection Protocol

The ResurrectionProvider wraps the entire tree and exposes an iteration counter. When a catastrophic failure is detected (database corruption, WebSocket death, unrecoverable state), the system increments the counter. This causes ConvexClientWrapper to destroy the old Convex client and spawn a fresh one:
A useRef stores the previous client and explicitly calls .close() on it before the new client takes over. This prevents orphaned WebSocket connections from corrupting the WAL journal — a bug discovered on Lenovo K12 Note devices with slow eMMC storage.

Key Design Principles

The local SQLite database is a disposable cache. If it becomes corrupted or out of date, the system performs a “Nuke and Pave” — dropping all tables and rebuilding the schema from scratch. Data is then re-downloaded from Convex via the HydrationSync engine (“Amnesia Mode”).
If GPS data is stale, the database is unreadable, or the sync token is missing, the Accessibility Service blocks by default. The system never fails open — ambiguity is treated as a violation.
Task instances (individual occurrences of a commitment) are first-class citizens. They survive even after their parent task is deleted. This prevents users from escaping penalties by deleting a task after failing it. The SQLite schema has zero foreign key constraints by design.
When a task instance is created, the penalty and waiver rules are frozen onto the instance. Even if the user later edits the parent task’s penalty settings, the existing instance enforces the original contract. This prevents retroactive manipulation (e.g., setting a large penalty, failing, then quickly editing it to a minimal one).