Files
solelog/CLAUDE.md
Bas van Rossem 9ca8a454f4 docs: correct CLAUDE.md after reverse-engineering apps/web backend
The backend is not remote: apps/web is a Next.js 16 app with the API
routes, better-auth, and a Neon Postgres data layer. Document the data
model, the mobile<->web API contract, and the missing /api/logs route.
2026-06-17 10:22:20 +02:00

7.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

A time-tracking app for insole (orthotics) production, built on the "Create" / Anything AI platform and exported to run locally. The UI is in Dutch. A worker picks an insole type (Type zool: Kurk / Berk / 3D), a handling/task (Type handeling), and a count (Aantal zolen, default 2), then runs a stopwatch (start / pause / stop & save / double-press discard). The History tab lists past sessions and exports CSV; the Settings tab manages handelingen per zooltype.

It is a two-app monorepo, not frontend-only — the backend lives in apps/web:

  • apps/mobile — the Expo Router app (iOS / Android / web client). This is what the user runs on their phone via Expo Go / EAS builds.
  • apps/web — a Next.js 16 app that serves the backend HTTP API (/api/*), authentication, and a near-empty web UI. The mobile app talks to it over EXPO_PUBLIC_BASE_URL. In production it is the deployed *.created.app site.

The database is Neon serverless Postgres, reached only from apps/web via DATABASE_URL.

Layout

Yarn 4 (Berry) monorepo, node-modules linker. Workspaces = apps/*apps/mobile and apps/web.

  • publisher/ is NOT a workspace — it's a standalone OpenNext + AWS S3 tool (its own yarn.lock) for building/deploying apps/web. Rarely touched.

Backend: API + data model

API routes live in apps/web/src/app/api/. The DB layer is apps/web/src/app/api/utils/sql.ts — a Neon tagged-template sql\...` (parameterised; interpolations are bind params, not string concatenation). Auth is **better-auth** (apps/web/src/lib/auth.ts): cookie sessions for web, Authorization: Bearer for mobile (token from/api/auth/token`).

Schema, reverse-engineered from the queries (no migrations file ships in the repo):

  • production_tasksid, name, insole_types text[] (subset of Kurk / Berk / 3D)
  • time_logsid, task_id (FK → production_tasks), start_time, end_time, duration_seconds, pair_count, insole_type, notes

API surface the mobile app expects vs what apps/web provides:

Endpoint Method Used by mobile Exists in apps/web
/api/tasks GET, POST yes api/tasks/route.ts
/api/tasks/:id PUT, DELETE yes api/tasks/[id]/route.ts
/api/logs GET, POST yes MISSING
/api/export GET (CSV) yes api/export/route.ts
/api/session, /api/auth/* (auth plumbing)

⚠ Known gap: /api/logs is missing

The mobile app reads the History list (GET /api/logs) and saves a finished session (POST /api/logs), but no such route exists in apps/web — only the CSV /api/export. Against this local backend, Stop & Save and the History tab 404. (The deployed *.created.app instance presumably has it; it just wasn't in this export.) Contract to rebuild it from the callers:

  • POST /api/logs body: { task_id, start_time (ISO), end_time (ISO), duration_seconds, pair_count, insole_type }
  • GET /api/logs returns an array of { id, task_name (join on production_tasks.name), start_time, duration_seconds, pair_count, insole_type }, and (for parity with export) ideally end_time, ordered by start_time.

Commands

Run from the repo root unless noted. The root has no start/lint/test scripts — invoke the tools directly.

yarn install                       # install everything (Yarn 4)
npx oxlint                         # lint (config: .oxlintrc.json) — the real linter
npx oxfmt                          # format (config: .oxfmtrc.json) — 2-space, single-quote, semi, width 100

# Mobile (apps/mobile)
cd apps/mobile && npx expo start            # dev server; press a/i/w or scan QR with Expo Go
cd apps/mobile && npx expo start --web      # web target only
cd apps/mobile && npx tsc --noEmit          # typecheck (strict; @/* -> src/*)
cd apps/mobile && yarn jest                 # tests (jest-expo); add a path or -t "name" for one
cd apps/mobile && eas build --profile <development|preview|production> --platform <android|ios>

# Web / backend (apps/web)
cd apps/web && yarn dev            # Next.js dev on http://localhost:4000
cd apps/web && yarn build          # next build (publisher wraps this for deploy)
cd apps/web && yarn typecheck      # tsc --noEmit  (next.config.js ignores TS errors at build time!)
cd apps/web && npx vitest          # tests (vitest + jsdom)

eslint / typescript-eslint are in devDeps but there is no eslint config in the tree — use oxlint, not eslint.

Environment

Secrets are gitignored (.env). Mobile env is apps/mobile/.env (EXPO_PUBLIC_*): EXPO_PUBLIC_BASE_URL points the client at the web backend; EXPO_PUBLIC_PROJECT_GROUP_ID/HOST feed the patched global fetch. Web needs DATABASE_URL (Neon), plus BETTER_AUTH_URL and the trusted-origin URLs for auth. EXPO_PUBLIC_CREATE_ENV (PRODUCTION/DEVELOPMENT) + __DEV__ gate analytics, Sentry, the in-app "anything-menu", and dev-only native module stubs.

Architecture notes (mobile)

  • Routing: Expo Router, file-based under apps/mobile/src/app/. _layout.tsx gates render on useAuth().initiate() + isReady (loads the persisted session first) and provides the React Query client. (tabs)/ holds index.tsx (Stopwatch), history.tsx (Geschiedenis), tasks.tsx (Instellingen).
  • Entry points split by extension. Native: index.tsxentrypoint.tsApp.tsx. Web: index.web.tsxApp.web.tsx (adds the Create sandbox-iframe plumbing — postMessage handshake, navigation sync, screenshot, healthcheck).
  • Global fetch is monkey-patched. src/__create/polyfills.ts swaps global.fetch for src/__create/fetch.ts, which rewrites first-party (/api/...) URLs onto EXPO_PUBLIC_BASE_URL, injects project/host headers, and attaches the SecureStore JWT. App code calls fetch('/api/...') and relies on this.
  • Web support is Metro module aliasing, not separate code. apps/mobile/metro.config.js resolveRequest swaps many native modules for stubs in polyfills/web/ when platform === 'web'. Adding a native dependency imported on web requires a web polyfill alias here, or the web build breaks.
  • Styling: NativeWind/Tailwind config extends @anythingai/app/tailwind.config, but screens mostly use React Native StyleSheet/inline styles. Inter font.

Conventions & gotchas

  • Do not edit platform-managed files. Files under apps/mobile/__create/ and apps/mobile/src/__create/, the auth files in apps/mobile/src/utils/auth/, and apps/web/src/lib/auth.ts carry ⚠ ANYTHING PLATFORM — DO NOT REWRITE headers. They define the auth surface (signIn/signUp/signOut/auth/isAuthenticated/isReady), the fetch/sandbox plumbing, and the better-auth config (bearer plugin, name backfill, trusted origins, sameSite:'none' cookies). Rewriting them breaks auth or the Create preview. apps/mobile/src/app/_layout.tsx is editable except the <AuthModal /> render and the initiate() + isReady gate.
  • Do not bump patched dependencies. react-native, expo-router, expo-store-review, react-native-purchases(-ui), @expo/cli, @react-native-community/netinfo, etc. use patch: entries backed by .yarn/patches/, pinned further by root resolutions/overrides. Upgrading discards the patch. When running npx expo install --fix, skip any patch: package.
  • This is an exported template: versions are pinned exact (no ^) deliberately; prefer minimal, targeted changes.