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)
- No testing by yourself — All testing is done by the team.
- No assumptions about code or domain logic — Always confirm and be sure before making changes.
- 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:
- Domain-Driven Design (DDD) — Bounded contexts as domains
- Layered architecture — Clear separation of concerns
- 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
- Logic package — Uses
neverthrow(ResultAsync,okAsync,errAsync) for async operations that may fail. @pkg/result— ProvidesResult<T, Err>,ERROR_CODES, andtryCatch(). TheResulttype is legacy; as the project is moving toward neverthrow. UseERROR_CODESfor consistent error codes.getError()— From@pkg/logger. Use at boundaries when converting a thrown error to anErrobject:
return getError({ code: ERROR_CODES.XXX, message: "...", description: "...", detail: "..." }, e).- Domain errors — Each domain has an
errors.tsthat exports error constructors built withgetError. Use these instead of ad-hoc error objects. - Check before use — Always check
result.isOk()/result.isErr()before usingresult.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.tsclasses. 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.tsand 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/logiccontrollers 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.tsper domain. - Use
v.InferOutput<typeof Schema>for TypeScript types. - Routers use
sValidatorfrom@hono/standard-validatorwith 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)