Files
illusory-mapp/AGENTS.md
2026-02-28 18:55:52 +02:00

7.4 KiB

AGENTS.md

This document defines the laws, principles, and rule sets that govern this codebase. Any agent or developer making changes has to adhere to these rules.

Before starting off — Read the README.md file to understand the project's goals and objectives.


Agent Rules (Override Everything)

  1. No testing by yourself — All testing is done by the team.
  2. No assumptions about code or domain logic — Always confirm and be sure before making changes.
  3. No running scripts — Do not run build, dev, test, or migrate scripts unless explicitly approved.

More rules are only to be added by the human, in case such a suggestion becomes viable.


1. Project Overview

  • Monorepo: Turbo repo
  • Package Manager: pnpm
  • Language: TypeScript everywhere
  • Node: >= 24

Note

: The monorepo architecture is currently in flux. Some patterns are being refined; follow existing conventions and confirm before introducing new ones.

Applications

App Purpose
apps/main SvelteKit UI — the primary user-facing application
apps/processor Hono web server — intended for asynchronous processing (jobs, workers). Currently minimal; structure is evolving.

Packages

Packages live under packages/ and are standalone, modular pieces consumed by apps:

Package Purpose
@pkg/logic Domain logic (DDD, controllers, repositories)
@pkg/db Drizzle schema, database access
@pkg/logger Logging, getError for error construction
@pkg/result Result type, ERROR_CODES, tryCatch
@pkg/keystore Redis instance (sessions, 2FA, etc.)
@pkg/settings App settings / env config

Data Stores

  • PostgreSQL (Drizzle) — Primary relational data (auth, users, accounts, etc.)
  • Redis (Valkey) — Sessions, 2FA verification state (via @pkg/keystore)

Additional stores (NoSQL DBs, R2, etc.) may be introduced later. Follow existing domain patterns when adding new data access.


2. The Logic Package: DDD + Layered Architecture

The @pkg/logic package contains all domain logic. It follows:

  1. Domain-Driven Design (DDD) — Bounded contexts as domains
  2. Layered architecture — Clear separation of concerns
  3. Result-style error handling — Errors as values; avoid try-catch in domain code

Domain Structure

Each domain is a folder under packages/logic/domains/:

domains/
  <domain-name>/
    data.ts        # Types, schemas (Valibot)
    repository.ts  # Data access
    controller.ts  # Use cases / application logic
    router.ts      # HTTP routes (when exposed via API)
    errors.ts      # Domain-specific error constructors (using getError)

Not every domain has a router (e.g. auth uses config.base.ts with better-auth). Add new domains as needed; mirror existing patterns.

Path Aliases (logic package)

  • @/*./*
  • @domains/*./domains/*
  • @core/*./core/*

Flow Execution Context

Domain operations receive a FlowExecCtx (fctx) for tracing and audit:

type FlowExecCtx = {
  flowId: string;
  userId?: string;
  sessionId?: string;
};

3. Error Handling: Result Pattern (neverthrow)

Errors are values, not exceptions. The codebase uses Result-style handling.

Current Conventions

  1. Logic package — Uses neverthrow (ResultAsync, okAsync, errAsync) for async operations that may fail.
  2. @pkg/result — Provides Result<T, Err>, ERROR_CODES, and tryCatch(). The Result type is legacy; as the project is moving toward neverthrow. Use ERROR_CODES for consistent error codes.
  3. getError() — From @pkg/logger. Use at boundaries when converting a thrown error to an Err object:
    return getError({ code: ERROR_CODES.XXX, message: "...", description: "...", detail: "..." }, e).
  4. Domain errors — Each domain has an errors.ts that exports error constructors built with getError. Use these instead of ad-hoc error objects.
  5. Check before use — Always check result.isOk() / result.isErr() before using result.value; never assume success.

Err Shape

type Err = {
  flowId?: string;
  code: string;
  message: string;
  description: string;
  detail: string;
  actionable?: boolean;
  error?: any;
};

4. Frontend (Main App)

The main app is a SvelteKit application with a domain-driven UI structure.

Structure

  • Routes: File-based routing under src/routes/. Layout groups (e.g. (main), auth) wrap related pages.
  • Domain-driven UI: Feature code lives under src/lib/domains/<domain>/ — each domain has its own folder with view models and components.
  • View Model (VM) pattern: Domain logic and state for a screen live in *.vm.svelte.ts classes. VMs hold reactive state ($state), orchestrate API calls, and expose methods. Pages import and use a VM instance.
  • API layer: A typed Hono client (hc) is used against a Router composed from logic package routers. The API is defined in $lib/api.ts and proxied through SvelteKit route handlers at /api/v1/[...paths]. Auth/session context is injected at the proxy layer.

Context Injection

Logic routers receive user, session, and fCtx via c.env.locals. The shape is defined in HonoContext (@core/hono.helpers). The API proxy builds this context from the session before forwarding requests.

Global Stores

Shared state (e.g. apiClient, user, session, breadcrumbs) lives in $lib/global.stores.ts.

Conventions

  • Pages are thin: they mount a VM, render components, and wire up lifecycle.
  • VMs own async flows, polling, and error handling for their domain.
  • UI components under $lib/components/ are shared; domain-specific components live in $lib/domains/<domain>/.

5. Processor App

The processor is a Hono server intended for background workers and async jobs. Its structure is still evolving.

Current State

  • Minimal Hono server; no BullMQ or job processors yet.
  • When workers are added, processing logic should live under src/domains/<domain>/ and call into @pkg/logic controllers and repositories.
  • HTTP routes will expose internal APIs (e.g. task status, webhooks), secured (e.g. API key), for use by the main app or external services.
  • Async work should be triggered by calling the processor HTTP API, not by importing job queues in the main app or logic package.

Conventions (when implemented)

  • The worker processes jobs; do not block it with long-running HTTP logic.
  • Job payloads should be validated before processing.
  • Follow existing domain patterns for controllers and repositories.

6. Validation & Schemas

  • Valibot is used for schema validation in the logic package.
  • Domain data types are defined in data.ts per domain.
  • Use v.InferOutput<typeof Schema> for TypeScript types.
  • Routers use sValidator from @hono/standard-validator with Valibot schemas for request validation.

7. Package Naming

  • Apps: @apps/* (e.g. @apps/main, @apps/processor)
  • Packages: @pkg/* (e.g. @pkg/logic, @pkg/db, @pkg/logger)