From 9c6361f61557d358b83f1fd3bcd85af5dd9614ce Mon Sep 17 00:00:00 2001 From: Heitor Rosa Date: Mon, 20 Apr 2026 17:18:48 -0300 Subject: [PATCH 1/2] docs: update AGENTS.md to reflect current project state --- AGENTS.md | 542 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 318 insertions(+), 224 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f627ee4..6a51091 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,350 +2,444 @@ ## 1. Project Overview -This project is a backend service for a **barbershop reservation system** built with strong architectural discipline. -The goal is to implement a maintainable and scalable backend using **clean layered architecture**, prioritizing domain modeling and long-term maintainability over rapid feature development. +This project is a backend service for a **barbershop reservation system** designed with strong architectural discipline. -The system manages: +The primary goal is **long-term maintainability and correctness**, not rapid prototyping. +The architecture follows **strict layered separation** and **domain-driven modeling**. -* Users -* Barbers (a specialization of users) -* Reservations between users and barbers +The system currently supports: -At the current stage the project already contains **domain logic, application use cases, database infrastructure, and integration tests**. The **HTTP layer is being implemented** as the external interface. +* User registration +* Authentication using **JWT access tokens and refresh tokens** +* Barber management (modeled as a specialization of users) +* Reservation scheduling between users and barbers +* Reservation lifecycle management (create, cancel, reschedule) +* Reservation conflict prevention and concurrency protection + +The project already includes: + +* Fully implemented **domain layer** +* Application **use cases** +* Database **repositories** +* **HTTP interface** +* **Authentication system** +* **Integration and concurrency tests** + +The system prioritizes **correct domain invariants**, **transaction safety**, and **explicit architecture boundaries**. --- -## 2. Tech Stack - -| Concern | Tool | -|---|---| -| Language | TypeScript | -| Runtime | Node.js 20 | -| Framework | NestJS | -| Database | PostgreSQL | -| ORM / Query Builder | Drizzle ORM (query builder and mapping layer, not schema authority) | -| Migrations | SQL-first manual migrations | -| Password & Token hashing | argon2 | -| Testing | Jest | -| Package manager | pnpm 9 | -| Containerization | Docker | -| Linting | ESLint | -| Formatting | Prettier | -| Task runner | Taskfile (task) | - -Testing approach emphasizes **integration tests and concurrency validation** rather than heavy mocking. +# 2. Tech Stack + +| Concern | Tool | +| ----------------------- | ----------------------------- | +| Language | TypeScript | +| Runtime | Node.js | +| Framework | NestJS | +| Database | PostgreSQL | +| Query Builder / Mapping | Drizzle ORM | +| Migrations | SQL-first manual migrations | +| Authentication | JWT (stateless access tokens) | +| Password hashing | argon2 | +| Refresh token hashing | argon2 | +| Logging | Pino | +| Testing | Jest | +| Package manager | pnpm | +| Containerization | Docker | +| Task runner | go-task | + +Important design principle: + +**The database schema is authoritative and defined via SQL migrations, not via ORM models.** --- -## 3. Project Structure +# 3. Project Structure ``` src/ - domain/ # Pure business logic. Entities, value objects, domain errors, repository interfaces. No framework dependencies allowed. - application/ # Use cases and orchestration. Commands, repository tokens for DI. - infrastructure/ # Drizzle repositories, database provider, PostgreSQL, argon2, migration runner, schema definitions. - interfaces/http/ # Controllers, DTOs, validation, exception filters, domain-to-HTTP error mapping. + domain/ # Pure business logic + application/ # Use cases and orchestration + infrastructure/ # Database, repositories, auth, logging + interfaces/http/ # Controllers, DTOs, filters, decorators + +migrations/ # Versioned SQL migrations -migrations/ # Versioned SQL migrations. All schema changes go here. docs/ - adr/ # Architecture Decision Records (ADRs 0001–0010). Consult before proposing structural changes. + adr/ # Architecture Decision Records testing.md use-cases.md -test/ # Integration tests, concurrency tests, test utilities, and factories. -docker/ # docker-compose.dev.yml, docker-compose.prod.yml, docker-compose.test.yml + +test/ + e2e tests + integration tests + concurrency tests + test utilities + factories + +docker/ + docker-compose.dev.yml + docker-compose.prod.yml + docker-compose.test.yml ``` --- -## 4. Code Conventions +# 4. Layer Responsibilities -Favor **explicit, readable code over abstraction**. Avoid premature generalization. +### Domain -| Element | Convention | Example | -|---|---|---| -| Entities | PascalCase | `User`, `Reservation` | -| Use cases | Verb-based | `CreateUserUseCase` | -| Commands | Verb + domain object | `CreateReservationCommand` | -| Repository interface | Domain name | `UserRepository` | -| Repository impl | Name + Drizzle | `UserDrizzleRepository` | -| DTOs | Suffix with DTO | `CreateUserDTO` | -| Files | kebab-case | `create-user.usecase.ts` | -| Errors | Explicit error classes | `UserAlreadyExistsError` | +Pure business logic. -Avoid: +Contains: + +* Entities +* Value Objects +* Domain Errors +* Repository Interfaces + +Restrictions: -* Generic utility abstractions -* Shared base classes for controllers -* Leaking infrastructure logic into domain or application layers +* No framework imports +* No database code +* No HTTP code --- -## 5. Task Runner +### Application -All operations must be run via `go-task`, never by invoking scripts or docker compose directly. +Coordinates domain logic. -| Task | Purpose | -|---|---| -| `task dev-up` | Start DEV Postgres container | -| `task dev-stop` | Stop DEV containers | -| `task dev-down` | Remove DEV containers (keep volumes) | -| `task dev-reset` | Remove DEV containers and volumes | -| `task test-up` | Start TEST Postgres container (waits for healthy) | -| `task test-down` | Remove TEST containers | -| `task test-reset` | Remove TEST containers and volumes | -| `task migrate` | Run SQL migrations (requires DATABASE_URL) | -| `task lint` | Run ESLint | -| `task typecheck` | Run TypeScript type check (no emit) | -| `task ci-test` | Full CI test run: reset → up → migrate → jest → down | +Contains: -Never invoke `docker compose` directly. Always use the Taskfile. +* Use cases +* Commands +* Repository tokens for DI +* Service interfaces (token generator, password hasher) + +Application layer **does not know infrastructure implementations**. --- -## 6. Docker +### Infrastructure + +Implements interfaces defined by domain/application. -Three isolated environments with separate compose files: +Contains: -| Environment | File | Purpose | -|---|---|---| -| dev | `docker/docker-compose.dev.yml` | Local development | -| prod | `docker/docker-compose.prod.yml` | Production | -| test | `docker/docker-compose.test.yml` | Integration/CI tests | +* Drizzle repositories +* PostgreSQL integration +* JWT authentication +* Password hashing (argon2) +* Refresh token persistence +* Logging (Pino) -Never mix environments. Never use the dev or prod compose for tests. +Infrastructure must **never leak into domain**. --- -## 7. Commit & PR Rules +### Interfaces (HTTP) -**Commit format:** +External interface. + +Contains: + +* Controllers +* DTOs +* Validation +* Exception filters +* HTTP ↔ Domain error mapping +* Authentication decorators + +Controllers call **use cases only**. + +--- + +# 5. Code Conventions + +Favor **explicitness over abstraction**. + +| Element | Convention | +| ------------------------- | ----------------------------- | +| Entities | PascalCase | +| Use cases | VerbBasedUseCase | +| Commands | VerbDomainObjectCommand | +| Repository interface | DomainNameRepository | +| Repository implementation | NameDrizzleRepository | +| DTOs | `*.dto.ts` | +| Files | kebab-case | +| Errors | Explicit domain error classes | + +Avoid: + +* Generic repositories +* Base controllers +* Premature abstractions +* Cross-layer shortcuts + +--- + +# 6. Task Runner + +All project operations must be executed through **Taskfile tasks**. + +Never call `docker compose` directly. + +Key tasks: + +| Task | Purpose | +| -------------------------------------- | ----------------------------- | +| `task up ENV=dev` | Start development environment | +| `task down ENV=dev` | Stop development environment | +| `task reset ENV=dev` | Reset development DB | +| `task up` | Start test DB | +| `task migrate` | Run migrations | +| `task test` | Full isolated test run | +| `task test-watch FILES="file.spec.ts"` | Run specific tests | + +Testing pipeline: ``` -type: short description +task test ``` -Allowed types: `feat`, `fix`, `refactor`, `test`, `docs`, `style`, `chore` +This runs: -Commit messages must be imperative, concise, and focused on a single change. +``` +reset → up → migrate → jest → down +``` + +--- -Commits must be atomic: each commit should represent a single, self-contained change that leaves the codebase in a working state. -Never batch unrelated changes in a single commit. -Prefer multiple small commits over one large commit per task. +# 7. Docker Environments -`chore` applies to: tooling changes, configuration updates, dependency management, and developer experience (DX) improvements that do not affect application logic. +Three isolated environments: -Examples: -- chore: add @src path alias -- chore: harden eslint config -- chore: update tsconfig paths +| Environment | File | +| ----------- | ----------------------- | +| dev | docker-compose.dev.yml | +| prod | docker-compose.prod.yml | +| test | docker-compose.test.yml | + +Each environment has its own database. + +Never mix environments. + +--- +# 8. Environment Configuration -**Branch naming:** +Environment variables are managed via **Taskfile dotenv integration**. + +Files: + +| File | Purpose | Versioned | +| ----------- | ------------------- | --------- | +| `.env.dev` | Local development | Yes | +| `.env.test` | Testing environment | Yes | +| `.env.prod` | Production secrets | No | + +Tasks automatically load the correct file. + +Example: ``` -feat/feature-name -fix/bug-description -refactor/refactor-description -test/test-description -docs/doc-description -chore/short-description +task up ENV=dev ``` -Never append generated IDs, timestamps, hashes, or any automatic suffix to branch names. -Branch names must always follow exactly: type/short-description -Correct: refactor/domain-entity-created-at -Wrong: refactor/domain-entity-created-at-12618706006155161774 +loads: -**PR rules:** +``` +.env.dev +``` -PRs must be small and focused. Never mix refactors with features. +--- -PR description structure: +# 9. Testing -```markdown -## Description -One paragraph summarizing the change. +Testing framework: **Jest** -## What was done -- Bullet list of concrete changes made +Test types: -## Why -Explanation of motivation and architectural reasoning. -Why this approach was chosen and what it enables going forward. -``` +* End-to-end tests +* Integration tests +* Concurrency tests -**Git workflow:** +Structure: -The agent must always follow these steps before starting any work: +``` +test/ + auth/ + barber/ + reservation/ + user/ + factories/ + utils/ +``` -1. Switch to main: `git switch main` -2. Fetch remote changes: `git fetch origin` -3. Pull latest: `git pull origin main` -4. Create and switch to the new branch from the updated main +Factories generate valid domain objects. -After completing any task: -1. Run `task ci-test` and confirm all tests pass. If any test fails, fix it before proceeding. -2. Stop and summarize what was done -3. List all files modified -4. Ask for explicit approval before proceeding to open a PR -5. Only open the PR after receiving confirmation +Concurrency-sensitive flows must always have tests. -Never open a PR autonomously. Always wait for human review and approval first. +Reservation creation is heavily validated under concurrent scenarios. --- -## 8. CI Pipeline +# 10. Architecture Decision Records (ADR) -CI runs on all PRs and on pushes to `main`. +Located in: -Pipeline steps (via GitHub Actions): +``` +docs/adr/ +``` -1. `pnpm install --frozen-lockfile` -2. `task lint` -3. `task typecheck` -4. `task ci-test` (reset → up → migrate → jest → down) +These documents define the **architectural rules of the system**. -`DATABASE_URL` is injected from `secrets.TEST_DATABASE_URL`. +Key decisions include: -A PR must pass all CI steps before merging. Never open a PR that is known to break lint, typecheck, or tests. +| ID | Decision | +| ---- | --------------------------------- | +| 0001 | Layered architecture | +| 0003 | PostgreSQL-first strategy | +| 0004 | SQL-first migrations | +| 0005 | Drizzle over Prisma | +| 0010 | UUID identifiers | +| 0011 | Application-controlled timestamps | +| 0013 | Stateless JWT authentication | +| 0014 | Refresh token strategy | +| 0015 | Structured logging with Pino | -When running in a containerless environment (such as Jules), `task ci-test` will fail due to Docker Hub pull rate limits or lack of container access. In this case, run `task typecheck` and `pnpm build` as a fallback. The human reviewer is responsible for running `task ci-test` locally before approving the PR. +Before proposing structural changes, consult ADRs. + +If a proposal contradicts an ADR, **stop and request clarification**. --- -## 9. Testing +# 11. Domain Glossary -Testing framework: Jest +| Term | Meaning | +| -------------------- | -------------------------------------------------- | +| User | System user who creates reservations | +| Barber | Specialized user capable of receiving reservations | +| Reservation | Appointment between user and barber | +| Reservation Conflict | Overlapping reservations for same barber | +| Access Token | Short-lived JWT used for authentication | +| Refresh Token | Stateful token used to renew access tokens | +| Repository | Persistence interface used by application layer | -Test types: integration tests, concurrency tests. +--- -Tests are located in `test/`. Structure example: +# 12. Hard Rules + +### Entity factories + +Entities must expose two factory methods: ``` -test/reservation/reservation-concurrency.int.spec.ts -test/user/create-user.e2e.spec.ts +create() +reconstitute() ``` -Utilities include: test database setup, truncation helpers, concurrency barrier, and factories. +Rules: + +* `create()` + Used when creating new entities + Must internally generate `createdAt`. + +* `reconstitute()` + Used by infrastructure mappers when rebuilding entities from database rows. -Factories generate valid domain objects for tests. +Never mix their usage. -Tests must prioritize **behavioral verification and business invariants**, not implementation details. +--- -| Command | Purpose | -|---|---| -| `pnpm test` | Run all tests | -| `pnpm test ` | Run a specific test file | -| `task ci-test` | Full CI-grade test run with isolated DB | -| `task test-watch` | Run one or more specific test files against the existing test database. Requires test-up and migrate to have been run first. Accepts multiple files via FILES='file1 file2'. | +### Layer dependency rule -Note: prefer `task test-watch` over `task ci-test` when iterating on a specific test during development. Use `task ci-test` only for full verification before opening a PR. +Dependencies must always flow: -Critical domain flows must have integration coverage. Concurrency-sensitive logic (reservations) must always be tested. +``` +interfaces → application → domain +``` + +Infrastructure implements domain interfaces but **must never be imported by domain or application directly**. --- -## 10. Architecture Decision Records (ADRs) - -ADRs are located in `docs/adr/` and document all major technical decisions: - -| ID | Topic | -| ---: | ------------------------------------ | -| 0001 | Layered Architecture | -| 0002 | Architecture Enforcement via Tooling | -| 0003 | PostgreSQL-First Database Strategy | -| 0004 | SQL-First Migrations | -| 0005 | Drizzle Over Prisma | -| 0006 | SQL-First Migration Runner | -| 0007 | Barber as Specialized User | -| 0008 | Drizzle Schema in Database Provider | -| 0009 | Postgres Error Mapper | -| 0010 | UUID as Identifier Type | -| 0011 | Application-Controlled Timestamps | -| 0012 | Entity Factory Method Separation | -| 0013 | Stateless JWT Authentication | -| 0014 | Refresh Token Strategy | - -**Before proposing any structural change, consult the relevant ADR.** If a decision contradicts an existing ADR, flag it explicitly and do not proceed without confirmation. +### Repository access + +Controllers must **never access repositories directly**. + +They must call **application use cases only**. --- -## 11. Domain Glossary +### Database schema + +Never modify database structure without creating a **new migration**. -| Term | Definition | -|---|---| -| User | A system user capable of creating reservations | -| Barber | A specialization of a user who can receive reservations. Modeled as relational specialization, not a role enum | -| Reservation | A scheduled appointment between a user and a barber | -| Reservation Conflict | Occurs when a reservation overlaps an existing reservation for the same barber | -| Invalid Reservation Time | Occurs when reservation start/end times violate domain rules | -| CreatedAt | Timestamp generated by the application layer, not the database | -| Repository | Interface used by the application layer to interact with persistence | -| Access Token | Stateless JWT with a short expiration, used to securely identify the user within the system. | -| Refresh Token | Stateful, database-persisted token mapped to a user, used to securely renew the Access Token. | +Automatic ORM schema generation is forbidden. --- -## 12. Hard Rules +### Authentication -Entities must have two separate static factory methods: -- `create()` — for new entity creation. Never accepts `createdAt` as input. Always sets `createdAt: new Date()` internally. -- `reconstitute()` — exclusively for infrastructure mappers rebuilding entities from database rows. Accepts all fields including `createdAt` and `active` state. +All endpoints are **protected by default** using `JwtAuthGuard`. -Never call `create()` from mappers. Never call `reconstitute()` from use cases or application layer. +Endpoints that must be public must explicitly use: -All routes must require authentication by default (globally protected by `JwtAuthGuard`). Endpoints that legitimately bypass this restriction must be explicitly marked with the `@Public()` decorator. +``` +@Public() +``` -Refresh tokens must never be persisted in plain text. Always persist the token hash (using argon2) in the database. +--- -Never place framework code inside the domain layer. Domain must never depend on NestJS, Drizzle, PostgreSQL, HTTP, or DTOs. +### Security rules -Controllers must never access repositories directly. Controllers must call **application use cases only**. +* Refresh tokens must never be stored in plaintext. +* Always store **argon2 hash**. +* Do not log secrets. +* Never use `console.log` in production code. -Never modify the database schema without creating a **new migration**. Do not use automatic ORM schema generation. +--- -Do not duplicate SQL error handling logic. All PostgreSQL error interpretation must go through **PostgresErrorMapper**. +### Error handling -Do not introduce generic repositories or query abstractions prematurely. +PostgreSQL errors must be interpreted only through: -Do not introduce base controller classes or unnecessary architectural layers. +``` +PostgresErrorMapper +``` -Do not bypass domain entities when creating business objects. +Do not duplicate SQL error handling logic elsewhere. -Do not log using `console.log` in production code. +--- -Avoid premature optimization and speculative abstractions. +### Architecture discipline -**Dependency flow must always be:** +Never introduce: -``` -interfaces/http → application → domain +* Generic repositories +* Base controllers +* Cross-layer shortcuts +* Premature abstractions -infrastructure implements domain interfaces (e.g. repositories) -but is never imported by domain or application directly. -``` +--- -Never invert this dependency direction. +### Branching rule -Never create a branch from a stale or non-main base unless explicitly instructed otherwise. +Always start work from the latest `main`. -## 13. Environment Configuration +Never create branches from stale bases. -The project uses environment-specific `.env` files managed by `task`. +--- -| File | Purpose | Versioned? | -|---|---|---| -| `.env.dev` | Local development variables | Yes | -| `.env.test` | Test environment variables (Default) | Yes | -| `.env.prod` | Production credentials/secrets | **No** | +## Important Principle -**How it works:** -- Tasks use `go-task` native `dotenv` support: `dotenv: [ .env.{{.ENV | default "test"}} ]`. -- Running `task dev-*` automatically sets `ENV=dev` and loads `.env.dev`. -- Running `task test-*` (or default tasks) loads `.env.test`. -- Running `task prod-*` automatically sets `ENV=prod` and loads `.env.prod`. +The architecture intentionally favors: -**Primary Variables:** -- `DATABASE_URL`: Active database connection used by migrations and the app. +* **clarity over abstraction** +* **explicit domain modeling** +* **strict boundaries** +* **long-term maintainability** -Never commit `.env.prod` or any other file containing real secrets. Use `.env.dev` and `.env.test` for non-sensitive, reproducible configurations. +When in doubt, prefer the **simplest solution that preserves architecture rules**. From 8efa23d87d9ee2374b99e056fc1a9b693becfa07 Mon Sep 17 00:00:00 2001 From: Heitor Rosa Date: Mon, 20 Apr 2026 17:28:36 -0300 Subject: [PATCH 2/2] docs: restore commit and PR rules section in AGENTS.md --- AGENTS.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6a51091..47358dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -227,7 +227,87 @@ Never mix environments. --- -# 8. Environment Configuration +## 8. Commit & PR Rules + +**Commit format:** + +``` +type: short description +``` + +Allowed types: `feat`, `fix`, `refactor`, `test`, `docs`, `style`, `chore` + +Commit messages must be imperative, concise, and focused on a single change. + +Commits must be atomic: each commit should represent a single, self-contained change that leaves the codebase in a working state. +Never batch unrelated changes in a single commit. +Prefer multiple small commits over one large commit per task. + +`chore` applies to: tooling changes, configuration updates, dependency management, and developer experience (DX) improvements that do not affect application logic. + +Examples: +- chore: add @src path alias +- chore: harden eslint config +- chore: update tsconfig paths + + +**Branch naming:** + +``` +feat/feature-name +fix/bug-description +refactor/refactor-description +test/test-description +docs/doc-description +chore/short-description +``` + +Never append generated IDs, timestamps, hashes, or any automatic suffix to branch names. +Branch names must always follow exactly: type/short-description + +Correct: refactor/domain-entity-created-at + +Wrong: refactor/domain-entity-created-at-12618706006155161774 + +**PR rules:** + +PRs must be small and focused. Never mix refactors with features. + +PR description structure: + +```markdown +## Description +One paragraph summarizing the change. + +## What was done +- Bullet list of concrete changes made + +## Why +Explanation of motivation and architectural reasoning. +Why this approach was chosen and what it enables going forward. +``` + +**Git workflow:** + +The agent must always follow these steps before starting any work: + +1. Switch to main: `git switch main` +2. Fetch remote changes: `git fetch origin` +3. Pull latest: `git pull origin main` +4. Create and switch to the new branch from the updated main + +After completing any task: +1. Run `task test` and confirm all tests pass. If any test fails, fix it before proceeding. +2. Stop and summarize what was done +3. List all files modified +4. Ask for explicit approval before proceeding to open a PR +5. Only open the PR after receiving confirmation + +Never open a PR autonomously. Always wait for human review and approval first. + +--- + +# 9. Environment Configuration Environment variables are managed via **Taskfile dotenv integration**. @@ -255,7 +335,7 @@ loads: --- -# 9. Testing +# 10. Testing Testing framework: **Jest** @@ -285,7 +365,7 @@ Reservation creation is heavily validated under concurrent scenarios. --- -# 10. Architecture Decision Records (ADR) +# 11. Architecture Decision Records (ADR) Located in: @@ -315,7 +395,7 @@ If a proposal contradicts an ADR, **stop and request clarification**. --- -# 11. Domain Glossary +# 12. Domain Glossary | Term | Meaning | | -------------------- | -------------------------------------------------- | @@ -329,7 +409,7 @@ If a proposal contradicts an ADR, **stop and request clarification**. --- -# 12. Hard Rules +# 13. Hard Rules ### Entity factories