Files
forgery/AGENTS.md
T
google-labs-jules[bot] e66917d716 Refactor schema to use @default(ulid()) and remove manual assignment
- Update prisma/schema.prisma to use @default(ulid()) for all relevant models
- Remove manual ulid() calls from all service files
- Update list APIs to order by id descending
- Update AssetService tests to match new behavior
- Update AGENTS.md with new conventions
- Remove empty migration file (20260217022222_add_ulid_default)
2026-02-17 02:37:20 +00:00

6.0 KiB

Developer Guide

Backend Architecture

The backend is built with:

  • Runtime: Bun
  • Framework: Hono
  • ORM: Prisma (with SQLite)
  • Database: SQLite (local file)
  • AI: Google Generative AI (Gemini)

Layered Architecture

We follow a strict layered architecture:

  1. API Layer (src/api): Handles HTTP requests, validation, and calls Service layer. Do not access DB directly.
  2. Service Layer (src/service): Contains business logic and interacts with the Database via Prisma.
  3. Data Layer (src/db.ts): Exports the Prisma Client instance.

Service Layer Patterns

  • Instance Methods: Define service logic as instance methods on a class, not static methods.
  • Singleton Export: Export a singleton instance of the service class.
    export class MyService {
      async doSomething() { ... }
    }
    export const myService = new MyService()
    
  • Usage: Import and use the exported instance.
    import { myService } from '../service/myService'
    await myService.doSomething()
    

Hono API Definitions

  • Route Chaining: Always define API routes using method chaining on a Hono instance. This ensures that the type definitions for inputs and outputs are correctly inferred and preserved in the exported type.
  • Export Pattern: Export the chained route instance as the default export.
    const app = new Hono()
    const route = app
      .get('/', ...)
      .post('/', ...)
    export default route
    
  • Type Safety: Use zValidator for request validation (query, json, form) to ensure full end-to-end type safety with the Hono RPC client.

Frontend Architecture

The frontend is built with:

  • Runtime: Bun (Bundler & Runner)
  • Framework: React
  • Styling: Tailwind CSS + Shadcn UI (Components)
  • State: React Hooks (useState, useEffect)

Project Structure

  • ui/: Root of the frontend project.
  • ui/index.html: Entry point for the frontend.
  • ui/App.tsx: Main application component.
  • ui/components/: Reusable UI components.
  • ui/index.css: Global styles (Tailwind imports).

Development

  • Run Dev: bun --hot src/index.ts (Runs on port 3000)

Testing

We use bun:test for testing.

Service Tests

  • Located in src/service/*.test.ts.
  • Mock prisma client using mock.module('../db', ...).
  • Important: Due to bun test module caching in parallel execution, service tests must import the service under test dynamically with a cache-busting query parameter to ensure a fresh module instance (and thus fresh mocks).
    // @ts-ignore
    const { MyService } = await import(`./myService?v=${Date.now()}`)
    

API Tests

  • Located in src/api/*.test.ts.
  • Mock the Service layer using mock.module('../service/myService', ...).
  • Verify that the API calls the Service methods correctly.

Submission Rules

  • Strict Requirement: A submission is considered complete only when there is a single final code state in which all of the following pass simultaneously:

    • lint
    • format
    • typecheck
    • test
  • Fixes must be iterated until no check causes any other check to fail.

  • Do not submit intermediate states where some checks pass and others fail, even temporarily.

  • Cleanup Requirement: Remove all verification related files (scripts, screenshots, verification/ folder) before submit.

Prisma Configuration & Migrations

  • Schema: prisma/schema.prisma
  • Generated Client: src/generated/prisma
  • Config: prisma.config.ts (Required for Prisma 7+)
  • Adapter: We use @prisma/adapter-libsql for SQLite compatibility with Bun.

Migration Workflow

Whenever you modify the prisma/schema.prisma file, you must generate a corresponding migration file.

Do not create migration SQL files manually.

Instead, follow this workflow:

  1. Ensure you have a local SQLite database configured (e.g., via .env setting DATABASE_URL="file:./dev.db").
  2. Run the migration command:
    bun x prisma migrate dev --name <migration_name>
    
    This command will automatically:
    • Detect changes in schema.prisma.
    • Generate the SQL migration file in prisma/migrations/.
    • Apply the changes to your local development database.
    • Regenerate the Prisma Client.

Commands

  • Generate Client: bun --bun run prisma generate
  • Push Schema to DB (Prototyping only): bun --bun run prisma db push

ID Generation & Sorting

  • ULID Default: All models (except key-value stores like Setting) must use @default(ulid()) for the id field in schema.prisma.
    model Example {
      id String @id @default(ulid())
    }
    
  • No Manual Assignment: Do not manually generate or assign ULIDs in application code (e.g., src/service/*.ts) for creating database records. Let Prisma handle it via the schema default.
    • Exception: File names generated by FileService may still use ulid() internally, but this is separate from the database ID.
  • Sorting: All list APIs must order results by id descending (orderBy: { id: 'desc' }) to ensure consistent, time-based sorting (since ULIDs are sortable). Do not sort by createdAt as it is not indexed.

Environment Variables

  • .env: Development environment variables (DATABASE_URL="file:./dev.db", API_KEY="...").
  • .env.test: Test environment variables (DATABASE_URL="file:./test.db").

Known Issues

  • bun test module mocking can be sticky across test files running in the same process/worker. Use the dynamic import workaround in Service tests.

Infinite Scroll & Pagination

  • Frontend: Use the useInfiniteScroll hook for all list UIs.
    • Located in src/ui/hooks/use-infinite-scroll.ts.
    • Pass a fetchData function that accepts page and limit.
  • Backend:
    • All list APIs must support pagination via page and limit query parameters.
    • The default limit should be 20.
    • Services should implement pagination logic (Prisma skip/take).
    • For mixed lists (built-in + DB), implement custom pagination logic in the Service layer.