This file defines mandatory development rules for AI Agents in this project. Unless the user explicitly requests a deviation, these rules must be followed.
This section is an executable refinement of the layering rules: each layer must do only what belongs to that layer. Data should flow around DTOs, GORM details should be concentrated in repositories, transaction boundaries should be concentrated in services, and response assembly should be concentrated in builders/handlers.
-`models` -> must not depend on any business layer
-`repositories` -> may depend on `models` and base libraries (`gorm`/`simple/sqls`)
-`services` -> may depend on `repositories`, `models`, and `enums/errorsx/utils`; responsible for transactions and business orchestration
-`builders` -> may depend on `models` and `dto/response`; if necessary, may depend on a small number of `services` to supplement display fields, but aggregation in the service layer is preferred
-`handlers` -> may depend on `services`, `builders`, `pkg/dto/request`, `pkg/httpx/params`, and `pkg/httpx` response wrappers
1. The **handler** reads parameters (`query/body/form/path`), performs permission checks, and calls the **service**
2. The **service** executes business rules (validation, idempotency, state machines, aggregation), starts a transaction when needed, and calls the **repository**
3. The **repository** only performs data reads/writes (`CRUD + queries`) and returns `models` or necessary aggregate structures
4.**builders** map `models`/aggregate results into `response DTO`
- Prefer `internal/pkg/httpx/params` for parameter binding
- Use `internal/pkg/httpx.WriteJSON` for all HTTP responses
- Write transaction boundaries must follow **4.1.4 Transaction Best Practices** (avoid slogan-style rules such as "always open a transaction even for a single write SQL statement")
This project uses explicit Gin routes and does not use framework automatic routing. Handler method names are only for code organization; final URLs are determined by the paths registered in `internal/bootstrap/*_routes.go`.
- With the registration above, the resource base path is determined by the outer `dashboardGroup.Group("/quick-reply")`; the final full paths are `/api/dashboard/quick-reply/list`, `/api/dashboard/quick-reply/{id}`, etc.
- Handler names should keep the existing readable prefixes: `XxxList`, `XxxGetBy`, `XxxPostCreate`, `XxxPostUpdate`, `XxxPostDelete`
- Handler names do not create routes; before adding an endpoint, the corresponding `register...Routes` function must be modified
- The HTTP method must be determined by the Gin registration method:
- List queries: prefer `group.Any("/list", XxxList)`, used by the frontend as `GET /list`
- Do not assume adding an `XxxList` method automatically creates a `/list` route; it must be explicitly registered in the routes file
- Do not register detail endpoints as `/detail`; the current convention is `GET /:id`
- Do not casually add deeply nested routes; subordinate resources should preferably be filtered by ordinary parameters such as `projectId` and `conversationId`
- If an API contract requires an underscore path, write the underscore path directly in the Gin route, for example `group.POST("/send_message", ...)`
- Before adding a handler method, first write the corresponding Gin route registration and confirm that the final URL matches the frontend contract
- Detail endpoints should preferably return `httpx.WriteJSON(ctx, dto)`
- Delete endpoints should preferably return `httpx.WriteJSON(ctx, nil)`
- JSON bodies should preferably be read with `params.ReadJSON`
- Form parameters should preferably be read with `params.ReadForm`
- Single parameters may be retrieved with `params.GetInt64`, `params.GetInt64Arr`, `params.Get`, etc.
- Pagination and query parameters should preferably use `params.NewPagedSqlCnd`
- Authenticated users should be retrieved through `services.AuthService.GetAuthPrincipal(ctx)` or `RequirePermission(ctx, ...)`
- Permission checks should consistently use `services.AuthService.HasPermission(...)` or `RequirePermission(...)`
- Authentication/authorization failures should consistently return `httpx.WriteJSON(ctx, err)`
- Errors such as `gorm.ErrRecordNotFound` should be converted into clear business messages
- When returning backend data, logic that converts data into response DTOs may be placed under `internal/builders`
### 8.7 Enum Definitions
- System constants should be defined uniformly under `/internal/pkg/enums`
- Model statuses should preferably use `Status` from `/internal/pkg/enums/enums.go`; only add a new status enum when it does not meet the requirement
- Enums shared by backend and frontend must follow [docs/design/specs/backend-frontend-enum-ast-spec.md](docs/design/specs/backend-frontend-enum-ast-spec.md)
- Shared backend/frontend enums may only be defined in the backend; the frontend must generate results with `make enums`, and handwritten duplicate business enums are forbidden
## 9. Go Code Standards
- Logs must consistently use the standard library `log/slog`
- New logs must not introduce other logging libraries
- Log fields should preferably use structured key-value pairs
- New Go code must consistently use `any`; do not add new `interface{}`
- Run `gofmt` after modifying Go code
## 10. Frontend Standards
### 10.1 Project Facts
- Frontend directory: `web`
- Framework: `Next.js 16` + App Router
- Page directory: `web/app/*`
- Component directory: `web/components/*`
- shadcn/ui base component directory: `web/components/ui/*`
- Utility directories: `web/lib/*`, `web/hooks/*`
- Alias: `@/*`
- Style entry: `web/app/globals.css`
- shadcn config: `web/components.json`
### 10.2 Components and Pages
- Prefer base components from `shadcn/ui`
- If an existing `shadcn/ui` component covers the use case, do not duplicate an equivalent base component
- If missing base components such as `dialog`, `textarea`, or `select` are truly needed for business logic, install them according to the standard process instead of hand-writing substitutes
- Do not modify `web/components/ui/*`
- Business components should live in `web/components/*` or the corresponding business directory
- API calls must be uniformly encapsulated in the service layer; do not scatter raw `fetch` calls in pages
- Frontend business APIs must be called through service methods under `web/lib/api/*`; raw `fetch` must not be used directly in `page.tsx`, business components, or stores
-`web/lib/api/client.ts` is the default request entry point; new business APIs should preferably reuse `request()` instead of implementing another request client
- When the backend returns the unified `JsonResult`, the frontend must handle `success`, `errorCode`, `message`, and `data` consistently; success must not be determined only by HTTP status
- Business code must not parse `JsonResult.data`, assemble generic error handling, or hand-write auth-refresh logic by itself; these concerns must be centralized in the common request wrapper
- Requests that require login state must reuse the unified wrapper with auth headers, `3000/3002` token refresh, and login-expiration cleanup; do not handle these separately at the page layer
- Direct use of low-level `fetch` is allowed only for third-party external services, binary downloads, SSE/streaming responses, WebSocket handshakes, or other cases not yet supported by the unified wrapper; such usage must include a code comment explaining the reason
### 10.3 shadcn Usage Process
- First confirm that `web/components.json` exists; if it exists, do not run `init` again
- Default to a two-layer structure: `page.tsx` manages the list and state, `_components/edit.tsx` manages the dialog form
- Forms should default to: `react-hook-form` + `zod` + `web/components/ui/field.tsx`
- API calls should stay in the page layer or service layer; form components should not call APIs directly
- After adding or modifying dashboard list/form pages, the AI Agent must first self-check compliance with that document, then run `cd web && pnpm typecheck`
- All frontend display times must be formatted as `yyyy-MM-dd HH:mm:ss`; preferably use `formatDateTime` from `web/lib/utils.ts`
- Dropdown components should not use the shadcn `select` component; use the shadcn `combobox` component instead. The project has a general dropdown wrapper at `web/components/option-combobox.tsx`; use it where possible.
- If data is used inside a component, the component should load it itself as much as possible instead of receiving it from outside. Preserve component independence.