Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
60 changes: 49 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
<!-- OPENSPEC:START -->
# OpenSpec Instructions

These instructions are for AI assistants working in this project.

Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding

Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines

Keep this managed block so 'openspec update' can refresh the instructions.

<!-- OPENSPEC:END -->

# AGENTS.md: SolidStart AI Task List

_Last updated: 2025-11-22_
Expand All @@ -15,10 +34,11 @@ This is a **Local-First AI Task Manager** built with **SolidStart**. It is desig

- **Framework**: SolidStart (SolidJS meta-framework) using `vinxi`.
- **Language**: TypeScript.
- **Data Layer (Local-First)**: **Y.js** for state and CRDTs, persisted via **IndexedDB** (`y-indexeddb`). There is **no** traditional backend database (Postgres/MySQL).
- **Data Layer (Local-First)**: **cr-sqlite** for structured queries with CRDTs, persisted via **OPFS** (Origin Private File System) with IndexedDB fallback. Tasks stored in SQL tables. There is **no** traditional backend database (Postgres/MySQL).
- **AI Layer**: Vercel AI SDK (`ai`, `@ai-sdk/google`) running on Server Actions to interface with Gemini.
- **Styling**: Modern vanilla CSS with CSS Variables, layers, CSS nesting, scoped component CSS, features like View Transitions and modern size values.
- **Ordering**: `lexorank` for efficient drag-and-drop sorting.
- **Sync Architecture**: File-based encrypted changesets stored in **Cloudflare R2**. Sync is pull-to-refresh (no real-time WebSocket). Device-to-device pairing via QR codes with end-to-end encryption.

**Golden Rule**: When unsure about implementation details, architectural choices, or requirements, **ALWAYS consult the developer** rather than making assumptions.

Expand Down Expand Up @@ -46,8 +66,9 @@ This is a **Local-First AI Task Manager** built with **SolidStart**. It is desig
- **Never** destructure props in Solid components (you lose reactivity). Access them via `props.value`.
- **Control Flow**: Use Solid's `<Show>`, `<For>`, `<Switch>`, `<Match>` components instead of `array.map()` or ternary operators for rendering.
- **Data Persistence**:
- All persistent data goes through `src/stores/taskStore.ts`.
- Mutations must happen inside `doc.transact(() => { ... })`.
- All persistent data goes through `src/stores/taskStore.ts` and `src/stores/vaultStore.ts`.
- Task mutations happen via SQL queries to cr-sqlite database.
- Vault keys and sync state stored in IndexedDB.
- **Styling**:
- Use minimal amount of classes, enhance css + html with data attributes for variable styling, (e.g., button[type="submit"] or .container[data-size="large"])
- Design for mobile-first experience, targeting media queries only above certain screen sizes.
Expand All @@ -71,10 +92,10 @@ This is a **Local-First AI Task Manager** built with **SolidStart**. It is desig

## Key Architectural Concepts

### 1. Local-First Data (Y.js + IndexedDB)
- **Concept**: The app works offline. Data is stored in a Y.js `Doc` which syncs to IndexedDB via `y-indexeddb`.
- **State**: The UI reads from a Solid `createStore` that mirrors the Y.js map.
- **Sync**: `yTasks.observe` triggers a `reconcile` on the Solid store to update the UI efficiently.
### 1. Local-First Data (cr-sqlite + OPFS)
- **Concept**: The app works offline. Data is stored in cr-sqlite WASM database persisted via OPFS (Origin Private File System) with IndexedDB fallback.
- **State**: The UI reads from a Solid `createStore` that mirrors SQLite queries.
- **Sync**: Changes trigger reactive updates to the Solid store to update the UI efficiently.
- **Implication**: Do not try to fetch tasks from an API endpoint. Read them from `tasks()` in `taskStore.ts`.

### 2. Server Actions for AI
Expand All @@ -84,22 +105,39 @@ This is a **Local-First AI Task Manager** built with **SolidStart**. It is desig
2. `breakdownTask` action (in `src/actions/taskActions.ts`) runs on the server.
3. Server calls Google Gemini via Vercel AI SDK.
4. Action returns structured data (JSON) to the client.
5. Client receives data and updates the **local** Y.js store.
5. Client receives data and updates the **local** SQLite database.

### 3. Lexorank Ordering
- **Concept**: To support drag-and-drop without re-indexing the whole list, we use `lexorank`.
- **Usage**: Every task has a `rank` string. When moving a task, calculate the new rank between the previous and next sibling.

### 4. Vault-Based Sync (Cloudflare R2 + cr-sqlite CRDTs)
- **Concept**: Optional sync via encrypted changesets stored in Cloudflare R2. Device pairing via QR codes.
- **Flow**:
1. First device = local-only (no sync)
2. Adding first paired device generates vault key and stores in IndexedDB
3. QR code contains vault key + device ID
4. cr-sqlite generates changesets on local changes
5. Changesets encrypted with vault key (AES-GCM) before upload
6. Upload to R2 with path = SHA256(vaultKey) (prevents enumeration)
7. Sync on page load: fetch changesets since last sync, decrypt, apply to local DB
8. Offline queue for failed uploads with retry on next sync
- **Zero-Knowledge**: Server (R2 proxy) never sees unencrypted data. All encryption happens client-side.
- **CRDT Merger**: cr-sqlite handles conflicts automatically with last-write-wins per column.

---

## Common Pitfalls (SolidJS Specific)

- **Destructuring Props**: `const { title } = props;` breaks reactivity. **Always** use `props.title`.
- **Dependency Arrays**: `createEffect` tracks dependencies automatically. Do not pass a dependency array like in React.
- **Class vs ClassName**: Solid uses `class="..."`, not `className`.
- **Server vs Client**:
- Files with `"use server"` run only on the server.
- Components using browser APIs (like `IndexedDB` or `document`) must be wrapped in `clientOnly` or checked with `isServer` / `onMount`.
- **Server vs Client**:
- Files with `"use server"` run only on the server.
- Components using browser APIs (like `IndexedDB`, `document`, or `crypto`) must be wrapped in `clientOnly` or checked with `isServer` / `onMount`.
- **SQL Database**:
- When working with cr-sqlite, always use camelCase for TypeScript types (e.g., `parentId`, `dueAt`) but snake_case for SQL queries (e.g., `parent_id`, `due_at`).
- Convert between types using helper functions (e.g., `dbRowToTask`).

---

Expand Down
14 changes: 7 additions & 7 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { defineConfig } from "@solidjs/start/config";
export default defineConfig({
server: {
experimental: {
websocket: true
websocket: false
}
},
vite: {
optimizeDeps: {
exclude: ["@vlcn.io/crsqlite-wasm"]
},
assetsInclude: ["**/*.wasm"]
}
}).addRouter({
name: "ws",
type: "http",
handler: "./src/ws.ts",
target: "server",
base: "/ws",
});

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,25 @@
},
"dependencies": {
"@ai-sdk/google": "^2.0.42",
"@aws-sdk/client-s3": "^3.972.0",
"@aws-sdk/s3-request-presigner": "^3.972.0",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.4",
"@solidjs/start": "^1.2.0",
"@thisbeyond/solid-dnd": "^0.7.5",
"@types/qrcode": "^1.5.6",
"@vlcn.io/crsqlite-wasm": "^0.16.0",
"@vlcn.io/ws-client": "^0.2.0",
"ai": "^5.0.100",
"dotenv": "^17.2.3",
"level": "^10.0.0",
"html5-qrcode": "^2.3.8",
"lexorank": "^1.0.5",
"lucide-solid": "^0.554.0",
"nanoid": "^5.1.6",
"qrcode": "^1.5.4",
"solid-js": "^1.9.10",
"ufo": "^1.6.1",
"vinxi": "^0.5.8",
"y-indexeddb": "^9.0.12",
"yjs": "^13.6.27",
"zod": "^4.1.12"
},
"devDependencies": {
Expand Down
Loading
Loading