Project Context for AI Agents
This file contains critical rules and patterns that AI agents must follow when implementing code in this project. Focus on unobvious details that agents might otherwise miss.
Technology Stack & Versions
Languages & core frameworks
-
Rust (workspace toolchain 1.96; pin via
rust-toolchain.tomlon scaffold). -
Tauri v2 (tauri-cli 2.11.x) — desktop/mobile shell; native barcode scanning (proven in prior Ersthelfer work). Target v2 APIs ONLY; reject v1 snippets. Capabilities/permissions are explicit in v2 — declare them.
-
Leptos (v0.8+) — Rust/WASM frontend. UI is Rust, not JS/TS. Frontend crates must compile to
wasm32; never block the WASM thread — route IO/inference to the Rust core via async Tauri commands. -
Node 26 for Tauri/Leptos/Tailwind build tooling ONLY — no application logic in JS.
Frontend UI layer
-
crates/ui— OUR OWN extractable component library, consumed by both apps. Built using Rust/UI (rust-ui.com) and shadcn/ui as references — nevercargo addrust-ui, never vendor snippets ad-hoc. New components get a typed public API inui; apps depend onui. -
Tailwind CSS — styling system; config + design tokens live ONLY in
crates/ui; apps never redefine them. Tailwindcontentglobs MUST include**/*.rs; computed class names must be literal strings or safelisted, or they get purged in release (silent style loss).
On-device inference
-
CHOSEN (2026-06-13): llama.cpp via
llama-cpp-2, CPU. Benchmarked on-device (SD 8 Gen 2) vs candle, mistral.rs, ort — fastest decode (13.4 tok/s on Qwen2.5-1.5B), lowest RAM, trivial Android build, only engine with GPU/NPU headroom. Default model: Qwen2.5-1.5B-Instruct Q4_K_M (Apache-2.0). Seedocs/research/inference-benchmark-results.md. -
All inference sits behind an
Inferencetrait insubstrate; nollamacpp::/backend types above the substrate boundary. Model load is fallible + cancelable; "model unavailable" is a normal handled state — neverunwrap(). One capable small model for MVP; no router/adapter swarm in v1. -
To ship GPU later: vendor
llama-cpp-sys-2+config.define("GGML_OPENCL","ON")(crate doesn’t expose it). NPU (Hexagon V73 libs present on test device) is a post-MVP spike.
Data & sync
-
Encrypted CRDT sync (Automerge/Yjs-style); server stores ciphertext only (zero-knowledge). Version the CRDT document schema from day one; migrations forward-only and fixture-tested before any entity field change.
-
Open Food Facts local nutrition KB — versioned + replaceable; a barcode miss is a normal fidelity-ladder fallback (L1/L0), not an error.
Repo shape & dependency direction
-
Single Cargo workspace (monorepo) now, built for later extraction:
crates/substrate,crates/domain-core,crates/ui,apps/organizer,apps/nutrition. Exact split ratified in architecture. -
Dependency direction is ONE-WAY:
apps → {ui, domain-core, substrate}. Shared crates never depend on apps or upward on each other. This is what keeps crates extractable. -
Cargo.lockis committed (workspace ships binaries).
Critical Implementation Rules
Privacy & data invariants (NON-NEGOTIABLE)
-
Local-first by default. No user data leaves the device without explicit, per-action opt-in.
-
E2EE: all user entities encrypted at rest; server only ever sees ciphertext (zero-knowledge).
-
All user content is wrapped in a
Private<T>newtype whoseDebug/Displayredacts — so plaintext is structurally unloggable. Key material useszeroizeand is neverDebug. -
NEVER log, telemeter, panic-message, or commit: keys, passphrases, or decrypted user content. No plaintext user data in test fixtures.
-
Ban third-party telemetry/crash-reporting SDKs. Anything leaving the device requires explicit allowlist review.
-
Errors reference IDs, never user values.
-
No plaintext user-derived data (signals, indexes, caches) persisted outside the encrypted store; plaintext exists in memory only while unlocked.
-
Decrypted data lives in memory only while unlocked. Lock-on-background is mandatory and non-configurable; idle-timeout is configurable (conservative default). Keys zeroize on lock; re-derive from passphrase on unlock.
-
Recovery codes are shown once, never persisted in plaintext. Onboarding states honestly: lose the key, lose the data.
-
Optional cloud (post-MVP): the previewed payload IS the serialized bytes sent (one code path; a test asserts
preview_input == send_input). HARD-DISABLED for work-tagged items. UI states it is not E2EE. -
Debug/benchmark builds never contain real user data and never weaken encryption — incl. the Xiaomi
adbinference benchmark (synthetic data only).
Product-soul invariants (must hold in v1 — these define the product)
-
Organizer — move-or-sacrifice: no code path mutates/deletes a protected block except a guarded API that writes a
SacrificeLogentry. Sync conflicts on protected blocks require user resolution — never auto-drop. -
Friction asymmetry: protecting time is one tap; overriding/sacrificing requires a deliberate, logged confirm. Drift surfaces via the Guide’s reflective tone — never an alert/badge/nag. No productivity gamification (no streaks/scores on work).
-
Nutrition — always a band: energy/macros are the typed
Estimate { low, high, confidence }; UI components acceptEstimate, notf64; aggregation sums bands (low+low, high+high); never collapse to a point in UI. (Exact counts stay scalars.) -
Nutrition — no mandatory goal: no-goal is a complete default state; nothing in the UI implies a goal is missing; goal code paths gated off by default; no red/green "over budget".
-
Variable fidelity, no penalty: fidelity is metadata only, never styled as deficient; no completeness/streak score penalizes low fidelity. The lowest-fidelity entry (L0) is the fastest path (one action); nutrition capture never blocks on clarification.
-
Guide reflects, never scolds: triggers fire on patterns, never single events — each
GuideTriggerdeclares awindow(≥2 events or a duration); a unit test asserts no single-event window. A banned-tone test scans copy (over budget,failed,should,lazy,cheat, red/green). Entry shape: Truth · Turn · Do · Footnote; Do is one schedulable action. -
Publish membrane is a code boundary: outbound data is a sanitized DTO; the internal
Item+ raw fields (dump, energy, clarifications, roll-over counts) arePrivate<T>and never serialized to any external sink. Only edited description + minutes + project may cross.
Architecture & extraction discipline
-
Substrate is built ONCE (store, crypto, keys, sync, inference, calendar, Guide) and reused by every app face. Don’t reimplement per app.
-
Dependency direction is ONE-WAY:
apps → {ui, domain-core, substrate}. A test parsescargo metadataand FAILS the build ifsubstrate/domain-core/uidepend on an app crate. -
A meal IS a
DailyBlock/ScheduledBlock— one calendar-block entity, not a parallel timeline. -
Inference, calendar providers, and storage backends sit behind traits in
substrate; concrete backends never leak above that boundary. -
Each crate documents its public API as if it will be published (it may be extracted into its own repo).
Rust / Tauri / Leptos conventions
-
Workspace-level
deny(clippy::unwrap_used, clippy::expect_used); allowed only incfg(test)and build scripts. Fallible ops return typedResults; "unavailable" states (model, calendar, network) are normal and UI-handled. -
Frontend (Leptos/WASM) does no blocking IO/inference — it calls async Tauri commands into the Rust core; frontend crates compile to
wasm32. -
Tauri v2 only; declare capabilities/permissions explicitly.
-
Derived signals (domain shares, drift, meal patterns, calibration) are computed locally; never assume a server computes anything.
Usage Guidelines
For AI agents:
-
Read this file before implementing any code in this repo.
-
Follow every rule exactly; when in doubt, prefer the more restrictive option.
-
The Privacy and Product-soul invariants are non-negotiable — they define the product. Never trade them for convenience or "optimization."
-
Anything marked as an open decision (e.g. the exact crate split, CalDAV-first vs per-provider calendar, Open Food Facts packaging) must not be hardcoded — keep it behind its trait/boundary until architecture ratifies it. (Inference is now resolved — see frontmatter
resolved_decisions.)
For humans:
-
Keep this file lean and agent-focused. Update it when the stack or an invariant changes.
-
Resolve the listed
open_decisions(frontmatter) during the architecture phase and remove them here once decided. -
Review periodically; drop rules that become obvious once the codebase exists.
Last Updated: 2026-06-13