# PubScript Conventions Guidelines every chunk follows so a human and a strictly-instructed coding agent stay in lockstep. ## Language & tooling - **TypeScript `strict`** everywhere, with `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` on. No `any` in new code. - **pnpm workspaces** with a committed lockfile. Never run `pnpm install` without `--frozen-lockfile` in CI. - **ESM throughout** (`"type": "module"`). Import sibling modules with a `.js` extension even though the source is `.ts` (bundler module resolution). - **Prettier** (100-col, double quotes) + **ESLint flat config** (`typescript-eslint` recommended, `no-explicit-any` off, unused-vars on). ## File layout ``` PubScript/ ├── PLAN.md ← single source of truth for *what* and *why* ├── CHUNK#n.md ← executable work orders (this chunk's steps) ├── apps/web/ ← React 19 + Vite app (runs in browser AND Tauri) ├── packages/ │ ├── core/ ← domain types, IDs, time, token budget, StorageAdapter contract │ ├── ui/ ← design tokens, theme engine, component kit │ ├── editor/ boards/ exporters/ ai/ ← later chunks ├── server/ ← Fastify + SQLite: auth, multi-tenant, WS, admin, AI proxy ├── desktop/ ← Tauri 2 shell (later chunk) ├── docker/ ← Dockerfile + compose └── docs/ ← ADRs, data model, role prompts, parity matrix ``` ## The golden rule: code against `packages/core`, never against a backend Every feature codes against the **`StorageAdapter` contract** in `packages/core/src/storage.ts`, never against `ServerAdapter` or `LocalAdapter` directly. This is what makes web, desktop, and local modes interchangeable and lets a single contract-test suite enforce parity. ## Domain conventions - **IDs are ULIDs** (sortable, collision-free). See `packages/core/src/ids.ts`. - **Timestamps are UTC ISO-8601** strings. See `packages/core/src/time.ts`. - **All domain types live in `packages/core/src/types.ts`** and are shared by app *and* server. Do not redefine them locally. ## Testing - **Vitest** for unit tests colocated as `src/**/*.test.ts(x)`. - **Contract suite** (`packages/core` `contract` script) runs the same assertions against every `StorageAdapter` implementation. - Acceptance criteria in each `CHUNK#n.md` are the gate to the next chunk. ## Commits Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`, `build:`). One logical change per commit; reference the chunk in the body when relevant. ## Risks & fallbacks - Tailwind v4 + React 19 friction → pin exact versions and record them here. - `better-sqlite3` native build in Docker → `node:22-alpine` with `python3 make g++` in the build stage; fall back to `node:22-slim` if needed (record in an ADR).