chore: bootstrap trellis workspace and sync deployment settings
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
# Database Guidelines
|
||||
|
||||
> Database patterns and conventions for this project.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Current backend data layer is **SQLAlchemy Async ORM + Alembic**.
|
||||
|
||||
Evidence:
|
||||
|
||||
- Async engine/session: `backend/src/core/db/session.py`
|
||||
- ORM base/mixins: `backend/src/core/db/base.py`
|
||||
- Generic repository soft-delete behavior: `backend/src/core/db/base_repository.py`
|
||||
- Migration entry: `backend/alembic/env.py`
|
||||
- Migration files: `backend/alembic/versions/*.py`
|
||||
|
||||
Transaction boundary convention (actual implementation):
|
||||
|
||||
- **Repository** does query/flush only
|
||||
- **Service** owns `commit()` / `rollback()`
|
||||
|
||||
Examples:
|
||||
|
||||
- `backend/src/v1/todo/repository.py` uses `flush()` and never commits
|
||||
- `backend/src/v1/todo/service.py` commits/rolls back around repository calls
|
||||
- `backend/src/v1/schedule_items/service.py` commits/rolls back in service methods
|
||||
|
||||
---
|
||||
|
||||
## Query Patterns
|
||||
|
||||
Observed query conventions:
|
||||
|
||||
1. Use SQLAlchemy expression API (`select`, `update`, `delete`) with async session.
|
||||
2. Default to soft-delete-safe reads (`deleted_at IS NULL`) for models supporting soft delete.
|
||||
3. Many repositories log and re-raise DB exceptions; service layer converts DB failures into stable API/domain errors.
|
||||
|
||||
Examples:
|
||||
|
||||
- `backend/src/core/db/base_repository.py`
|
||||
- `_apply_soft_delete_filter(...)`
|
||||
- `get_by_id(...)` and `get_one(...)` auto-apply `deleted_at IS NULL` when column exists
|
||||
- `update_by_id(...)`/`soft_delete_by_id(...)` use `flush()` and do not own commit/rollback
|
||||
- `backend/src/v1/todo/repository.py`
|
||||
- `list_by_owner(...)` explicitly applies `.where(Todo.deleted_at.is_(None))`
|
||||
- `backend/src/v1/schedule_items/repository.py`
|
||||
- `list_by_date_range(...)` combines owner filter + soft-delete filter + time window
|
||||
|
||||
---
|
||||
|
||||
## Migrations
|
||||
|
||||
Alembic is the schema migration source of truth.
|
||||
|
||||
Evidence:
|
||||
|
||||
- `backend/alembic/alembic.ini`
|
||||
- `backend/alembic/env.py`
|
||||
- `backend/alembic/versions/20260226_0001_initial_schema.py`
|
||||
|
||||
Project command entry points:
|
||||
|
||||
- `./infra/scripts/dev-migrate.sh migrate`
|
||||
- `./infra/scripts/dev-migrate.sh init-data`
|
||||
- `./infra/scripts/dev-migrate.sh bootstrap`
|
||||
|
||||
The script executes runtime CLI with `uv` and `PYTHONPATH=backend/src`.
|
||||
|
||||
---
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
Observed naming patterns in models and migrations:
|
||||
|
||||
- Table names: plural snake_case (`todos`, `profiles`, `schedule_items`, `friendships`)
|
||||
- Examples in `backend/src/models/todos.py`, `backend/src/models/profile.py`, `backend/src/models/friendships.py`
|
||||
- Common audit columns: `created_at`, `updated_at`, `deleted_at`
|
||||
- Mixins in `backend/src/core/db/base.py`
|
||||
- Key/index/constraint names in Alembic are explicit and prefixed by type:
|
||||
- `ix_*` (index), e.g. `ix_llms_factory_id`
|
||||
- `fk_*` (foreign key), e.g. `fk_profiles_id`
|
||||
- `uq_*` (unique), e.g. `uq_automation_jobs_id_owner`
|
||||
- `chk_*` (check), e.g. `chk_automation_job_schedule_type`
|
||||
- Evidence in `backend/alembic/versions/20260226_0001_initial_schema.py`
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns / Forbidden Patterns
|
||||
|
||||
- Do not commit/rollback inside repository classes.
|
||||
- Repositories should only `flush` and raise (`backend/src/v1/todo/repository.py`).
|
||||
- Do not bypass soft-delete filtering for regular read paths when model has `deleted_at`.
|
||||
- Base behavior exists in `backend/src/core/db/base_repository.py`.
|
||||
- Do not introduce schema changes without Alembic migration.
|
||||
- Current schema lifecycle is Alembic-based (`backend/alembic/env.py`, `backend/alembic/versions/*`).
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties (documented, not invented)
|
||||
|
||||
- There is no repository-wide automated check that every query path includes soft-delete filtering; this is a convention enforced by base repository usage and review.
|
||||
- Some repository method signatures still use broad `dict` input (`backend/src/v1/schedule_items/repository.py`), and a stricter typed policy is not fully codified in docs yet.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Directory Structure
|
||||
|
||||
> How backend code is organized in this project.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Backend code is centered in `backend/src` and follows a route-module layout under `v1/` with shared infrastructure in `core/`.
|
||||
|
||||
- **HTTP entry**: `backend/src/app.py` and `backend/src/v1/router.py`
|
||||
- **Feature modules**: `backend/src/v1/<domain>/`
|
||||
- **Cross-cutting infrastructure**: `backend/src/core/`
|
||||
- **ORM models**: `backend/src/models/`
|
||||
- **Shared/domain schemas**: `backend/src/schemas/`
|
||||
- **Global/base service providers**: `backend/src/services/`
|
||||
- **Migrations**: `backend/alembic/`
|
||||
- **Tests**: `backend/tests/{unit,integration,e2e}`
|
||||
|
||||
---
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── app.py
|
||||
│ ├── core/
|
||||
│ │ ├── config/
|
||||
│ │ ├── db/
|
||||
│ │ ├── http/
|
||||
│ │ ├── logging/
|
||||
│ │ └── taskiq/
|
||||
│ ├── models/
|
||||
│ ├── schemas/
|
||||
│ ├── services/
|
||||
│ └── v1/
|
||||
│ ├── router.py
|
||||
│ ├── auth/
|
||||
│ ├── users/
|
||||
│ ├── todo/
|
||||
│ ├── schedule_items/
|
||||
│ └── ...
|
||||
├── alembic/
|
||||
│ ├── env.py
|
||||
│ └── versions/
|
||||
└── tests/
|
||||
├── unit/
|
||||
├── integration/
|
||||
└── e2e/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module Organization
|
||||
|
||||
Observed feature-module pattern (evidence from multiple `v1/*` modules):
|
||||
|
||||
1. `router.py` contains FastAPI routes and request/response wiring
|
||||
2. `dependencies.py` contains DI providers
|
||||
3. `service.py` contains business logic, authz checks, transaction boundaries
|
||||
4. `repository.py` contains query/CRUD logic only
|
||||
5. `schemas.py` contains API DTOs (Pydantic)
|
||||
|
||||
Real examples:
|
||||
|
||||
- `backend/src/v1/todo/{router.py,dependencies.py,service.py,repository.py,schemas.py}`
|
||||
- `backend/src/v1/schedule_items/{router.py,dependencies.py,service.py,repository.py,schemas.py}`
|
||||
- `backend/src/v1/friendships/{router.py,dependencies.py,service.py,repository.py,schemas.py}`
|
||||
|
||||
Cross-module composition examples:
|
||||
|
||||
- `backend/src/v1/router.py` includes all domain routers under `/api/v1`
|
||||
- `backend/src/app.py` mounts the v1 router and global exception handlers
|
||||
- `backend/src/v1/todo/dependencies.py` composes DB session + user dependency + repositories into `TodoService`
|
||||
|
||||
---
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
Conventions observed in current codebase:
|
||||
|
||||
- Package and file names use **snake_case** (`schedule_items`, `inbox_messages`, `registration_bootstrap.py`)
|
||||
- Route package names are domain nouns (`todo`, `users`, `auth`, `agent`)
|
||||
- Common file names are stable across modules:
|
||||
- `router.py`
|
||||
- `service.py`
|
||||
- `repository.py`
|
||||
- `schemas.py`
|
||||
- `dependencies.py`
|
||||
- Logger names usually follow module path semantics, e.g.:
|
||||
- `get_logger("v1.todo.service")`
|
||||
- `get_logger("v1.schedule_items.repository")`
|
||||
- `get_logger("api.app")`
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns / Forbidden Patterns
|
||||
|
||||
Grounded by repository rules and existing structure:
|
||||
|
||||
- Do not place backend feature code under `apps/**` or non-backend folders; backend runtime code stays in `backend/src/**`.
|
||||
- Do not bypass module layering by putting SQL and transaction commits directly in router files.
|
||||
- Current positive references: `backend/src/v1/todo/router.py` (routing only), `backend/src/v1/todo/service.py` (commit/rollback), `backend/src/v1/todo/repository.py` (query layer).
|
||||
- Do not introduce ad-hoc folder layouts for new domains when `v1/<domain>/{router,service,repository,schemas,dependencies}` already exists.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties (documented, not invented)
|
||||
|
||||
- There is no explicit convention doc yet for when logic should go to `backend/src/services/**` versus `backend/src/v1/**`; both exist and are currently used for different scopes.
|
||||
- `backend/src/v1/analytics/` includes `tasks.py` and static web content usage from `backend/src/app.py` (`v1/analytics/web`), which is slightly different from CRUD-oriented modules.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Error Handling
|
||||
|
||||
> How errors are handled in this project.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Backend error transport is RFC7807-style `application/problem+json` with stable `code` and optional `params` extensions.
|
||||
|
||||
Evidence:
|
||||
|
||||
- Error class: `backend/src/core/http/errors.py` (`ApiProblemError`)
|
||||
- Problem details builder: `backend/src/core/http/response.py`
|
||||
- Global exception mapping: `backend/src/app.py`
|
||||
- Error code registry: `docs/protocols/common/http-error-codes.md`
|
||||
|
||||
---
|
||||
|
||||
## Error Types
|
||||
|
||||
### 1) `ApiProblemError` (preferred for business/API errors)
|
||||
|
||||
Used to carry `status_code`, `detail`, `code`, `params`.
|
||||
|
||||
Examples:
|
||||
|
||||
- `backend/src/v1/todo/service.py` (`_todo_error(...)` returns `ApiProblemError`)
|
||||
- `backend/src/v1/schedule_items/service.py` (raises `ApiProblemError` with `problem_payload(...)`)
|
||||
- `backend/src/v1/users/dependencies.py` (auth failures mapped to `AUTH_UNAUTHORIZED`, `JWT_VERIFIER_NOT_CONFIGURED`)
|
||||
|
||||
### 2) Infra/DB exceptions (caught at boundary and converted)
|
||||
|
||||
- `SQLAlchemyError` is commonly caught in service/repository boundaries.
|
||||
- Many repository methods log and re-raise DB exceptions (for example in todo/schedule_items repositories); generic helpers in `core/db/base_repository.py` may just propagate exceptions.
|
||||
- Service layer usually maps infra failures to stable codes like `*_SERVICE_UNAVAILABLE` / `*_STORE_UNAVAILABLE`.
|
||||
|
||||
Examples:
|
||||
|
||||
- `backend/src/v1/todo/repository.py`
|
||||
- `backend/src/v1/todo/service.py`
|
||||
- `backend/src/v1/schedule_items/service.py`
|
||||
|
||||
---
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
Observed patterns:
|
||||
|
||||
1. **Service methods**: catch DB/infrastructure errors, rollback when needed, then raise typed API problem.
|
||||
- `backend/src/v1/todo/service.py`
|
||||
- `backend/src/v1/schedule_items/service.py`
|
||||
2. **Repository methods**: log exception context and re-raise.
|
||||
- `backend/src/v1/todo/repository.py`
|
||||
- `backend/src/v1/schedule_items/repository.py`
|
||||
3. **Global handlers**: convert unhandled errors and framework exceptions into problem+json responses.
|
||||
- `backend/src/app.py`
|
||||
|
||||
---
|
||||
|
||||
## API Error Responses
|
||||
|
||||
Expected transport:
|
||||
|
||||
- Media type: `application/problem+json`
|
||||
- Fields: RFC7807 core (`type`, `title`, `status`, `detail`, `instance`) + extension fields (`code`, `params`)
|
||||
|
||||
Evidence:
|
||||
|
||||
- Response builder model in `backend/src/core/http/response.py`
|
||||
- Exception handlers in `backend/src/app.py`
|
||||
- Registry and compatibility notes in `docs/protocols/common/http-error-codes.md`
|
||||
|
||||
Practical rule:
|
||||
|
||||
- For business branches, return stable `UPPER_SNAKE_CASE` codes and keep `detail` human-readable.
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns / Forbidden Patterns
|
||||
|
||||
- Do not return free-text-only errors for business branches (missing `code`).
|
||||
- Protocol source: `docs/protocols/common/http-error-codes.md`
|
||||
- Do not swallow exceptions with `except ...: log; return default` in new code.
|
||||
- Project-wide rule from root `AGENTS.md`: no error swallowing.
|
||||
- Do not add new service/repository branches that raise `HTTPException` directly.
|
||||
- Backend target rule in `backend/AGENTS.md` prefers `ApiProblemError` in non-router layers.
|
||||
|
||||
Existing legacy evidence to be aware of:
|
||||
|
||||
- `backend/src/core/db/base_service.py` still raises `HTTPException(401, "Unauthorized")`.
|
||||
- `backend/src/v1/users/dependencies.py` has fallback branches returning `None` after catching exceptions in `_verify_user_with_supabase`.
|
||||
|
||||
These are current-state observations, not patterns to copy for new code.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties (documented, not invented)
|
||||
|
||||
- `backend/src/core/logging/middleware.py` defines `register_exception_handlers(...)` returning `{ "detail": "Internal Server Error" }` JSON (non-problem+json); main app currently defines its own handlers in `backend/src/app.py`. The single enforced runtime path should be clarified before broad refactors.
|
||||
- There is no automated CI check in this repo yet that verifies every endpoint always includes `code` in all error branches; compliance is currently convention + review driven.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Backend Development Guidelines
|
||||
|
||||
> Best practices for backend development in this project.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This directory captures backend conventions observed from the current repository (not aspirational rules).
|
||||
|
||||
---
|
||||
|
||||
## Guidelines Index
|
||||
|
||||
| Guide | Description | Status |
|
||||
|-------|-------------|--------|
|
||||
| [Directory Structure](./directory-structure.md) | Module organization and file layout | Completed |
|
||||
| [Database Guidelines](./database-guidelines.md) | ORM patterns, queries, migrations | Completed |
|
||||
| [Error Handling](./error-handling.md) | Error types, handling strategies | Completed |
|
||||
| [Quality Guidelines](./quality-guidelines.md) | Code standards, forbidden patterns | Completed |
|
||||
| [Logging Guidelines](./logging-guidelines.md) | Structured logging, log levels | Completed |
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Notes
|
||||
|
||||
When behavior changes, update the corresponding guideline file in the same task/PR:
|
||||
|
||||
1. Keep statements grounded in repository evidence (code/config/tests/docs)
|
||||
2. Add or refresh concrete file-path examples
|
||||
3. Keep anti-patterns tied to existing rules or existing legacy code evidence
|
||||
@@ -0,0 +1,109 @@
|
||||
# Logging Guidelines
|
||||
|
||||
> How logging is done in this project.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Backend uses `structlog` integrated with stdlib logging configuration.
|
||||
|
||||
Evidence:
|
||||
|
||||
- Logger factory: `backend/src/core/logging/logger.py`
|
||||
- Logging config and processors: `backend/src/core/logging/config.py`
|
||||
- Formatter/JSON pipeline: `backend/src/core/logging/formatters.py`
|
||||
- Request context middleware: `backend/src/core/logging/middleware.py`
|
||||
- Runtime usage examples: `backend/src/app.py`, `backend/src/v1/todo/service.py`
|
||||
|
||||
---
|
||||
|
||||
## Log Levels
|
||||
|
||||
Observed level usage:
|
||||
|
||||
- `debug`: low-level diagnostic events (e.g., JWT success branch)
|
||||
- `backend/src/v1/users/dependencies.py`
|
||||
- `info`: normal lifecycle and business events
|
||||
- `backend/src/app.py` (startup/initialized)
|
||||
- `backend/src/v1/todo/service.py` (`todo_created`, `todo_updated`, etc.)
|
||||
- `warning`: recoverable issues, unauthorized attempts, validation problems
|
||||
- `backend/src/app.py` (HTTP and validation warnings)
|
||||
- `backend/src/v1/todo/service.py` (unauthorized operations)
|
||||
- `error` / `exception`: failures needing investigation
|
||||
- `backend/src/app.py` (`logger.exception("Unhandled error")`)
|
||||
- repositories with `logger.exception(...)` before re-raise
|
||||
|
||||
---
|
||||
|
||||
## Structured Logging
|
||||
|
||||
Conventions from current setup:
|
||||
|
||||
1. Always obtain logger through `core.logging.get_logger(...)`.
|
||||
2. Use structured fields (keyword args / bound context), not string interpolation only.
|
||||
3. Request-level context can be attached by `RequestContextMiddleware` (`request_id`, `method`, `path`, `client_ip`, `user_id`) when the middleware is mounted.
|
||||
4. JSON output is enabled by default (`runtime.log_json = True`), with fallback plain formatter supported.
|
||||
|
||||
Examples:
|
||||
|
||||
- `backend/src/core/logging/middleware.py` binds and propagates request context (middleware implementation)
|
||||
- `backend/src/core/logging/config.py` configures processors and handlers
|
||||
- `backend/tests/integration/test_fastapi_logging_integration.py` mounts the middleware in test FastAPI app and asserts context fields in emitted logs
|
||||
|
||||
Current wiring note:
|
||||
|
||||
- `backend/src/app.py` currently does **not** call `app.add_middleware(RequestContextMiddleware)`; treat middleware context fields as opt-in wiring, not guaranteed global behavior.
|
||||
|
||||
---
|
||||
|
||||
## What to Log
|
||||
|
||||
Log these consistently:
|
||||
|
||||
- Service lifecycle events (startup/shutdown/init failures)
|
||||
- `backend/src/app.py`
|
||||
- `backend/src/services/base/service_interface.py`
|
||||
- Security/auth failures and unauthorized access attempts
|
||||
- `backend/src/v1/users/dependencies.py`
|
||||
- `backend/src/v1/todo/service.py`
|
||||
- Database or external dependency failures with stable context fields
|
||||
- `backend/src/v1/*/repository.py` patterns
|
||||
- `backend/src/services/base/{redis.py,supabase.py}`
|
||||
|
||||
---
|
||||
|
||||
## What NOT to Log
|
||||
|
||||
Sensitive data should be redacted; avoid raw secrets/tokens/credentials.
|
||||
|
||||
Evidence:
|
||||
|
||||
- Sensitive field defaults in `backend/src/core/config/settings.py` (`password`, `token`, `authorization`, etc.)
|
||||
- Redaction processor tests:
|
||||
- `backend/tests/unit/test_logging_filters.py`
|
||||
- `backend/tests/unit/test_logging_config.py`
|
||||
|
||||
Avoid logging:
|
||||
|
||||
- Raw access tokens / refresh tokens / cookies
|
||||
- Passwords, API keys, secret values
|
||||
- Full untrusted payload dumps containing PII
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns / Forbidden Patterns
|
||||
|
||||
- Do not use `print()` in backend runtime code; use project logger APIs.
|
||||
- Rule source: `backend/AGENTS.md`
|
||||
- Do not bypass logging config by ad-hoc logger setup in feature modules.
|
||||
- Use `configure_logging(...)` from `backend/src/core/logging/config.py`.
|
||||
- Do not log sensitive fields without redaction.
|
||||
- Do not assume request context fields exist unless `RequestContextMiddleware` is explicitly mounted.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties (documented, not invented)
|
||||
|
||||
- This repo has tests for logging behavior and redaction, but no single documented required schema for every log event across all modules (beyond context fields and processor output).
|
||||
- `core.logging.middleware.register_exception_handlers(...)` exists but app-level error handling is primarily in `backend/src/app.py`; effective production wiring should remain explicit per app bootstrap.
|
||||
@@ -0,0 +1,130 @@
|
||||
# Quality Guidelines
|
||||
|
||||
> Code quality standards for backend development.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Backend quality gates are centered around:
|
||||
|
||||
1. Static checks (`ruff`, `basedpyright`) via pre-commit
|
||||
2. Pytest suites under `backend/tests`
|
||||
3. Layering and contract discipline from `backend/AGENTS.md`
|
||||
|
||||
Evidence:
|
||||
|
||||
- `.pre-commit-config.yaml` (`basedpyright` + `ruff`, scoped to `^backend/`)
|
||||
- `pyproject.toml` (`pytest` config, test paths, live marker)
|
||||
- `backend/AGENTS.md` (layering, logging, error contract, DB and env rules)
|
||||
|
||||
---
|
||||
|
||||
## Forbidden Patterns
|
||||
|
||||
### 1) Error swallowing
|
||||
|
||||
- Forbidden by project rule: do not catch/log and silently continue.
|
||||
- Root rule source: `AGENTS.md`.
|
||||
|
||||
Legacy examples to avoid extending:
|
||||
|
||||
- `backend/src/v1/users/dependencies.py` (`_verify_user_with_supabase`) catches broad exceptions and returns `None` fallback.
|
||||
- `backend/src/services/base/service_interface.py` (`initialize_registered_services` / `close_registered_services`) catches broad exceptions and continues lifecycle flow.
|
||||
|
||||
### 2) Runtime `print()` usage
|
||||
|
||||
- Backend runtime must use `core.logging`, not `print()`.
|
||||
- Rule source: `backend/AGENTS.md`.
|
||||
|
||||
### 3) Unstructured HTTP error contracts
|
||||
|
||||
- Business/API errors must not rely only on free-text `detail`; use stable `code` (+ optional `params`).
|
||||
- Sources:
|
||||
- `backend/AGENTS.md`
|
||||
- `docs/protocols/common/http-error-codes.md`
|
||||
|
||||
### 4) Reading env by ad-hoc `os.getenv` in runtime
|
||||
|
||||
- Runtime configuration must go through `core.config.settings`.
|
||||
- Source: `backend/AGENTS.md` and implementation in `backend/src/core/config/settings.py`.
|
||||
|
||||
---
|
||||
|
||||
## Required Patterns
|
||||
|
||||
### Layering discipline
|
||||
|
||||
- Use `schema -> repository -> service` flow.
|
||||
- Repository: query/CRUD + `flush`; no transaction ownership.
|
||||
- Service: authz/business rules + commit/rollback boundary.
|
||||
|
||||
Evidence:
|
||||
|
||||
- `backend/src/v1/todo/repository.py` vs `backend/src/v1/todo/service.py`
|
||||
- `backend/src/v1/schedule_items/repository.py` vs `backend/src/v1/schedule_items/service.py`
|
||||
- `backend/src/core/db/session.py` docs explicitly state caller (service) owns commit/rollback
|
||||
|
||||
### Typed boundary models
|
||||
|
||||
- Prefer Pydantic models with strict extras handling for API/request contracts.
|
||||
- Evidence across schemas:
|
||||
- `backend/src/v1/todo/schemas.py`
|
||||
- `backend/src/v1/schedule_items/schemas.py`
|
||||
- `backend/src/v1/users/schemas.py`
|
||||
|
||||
### Protocol alignment
|
||||
|
||||
- If API contract changes (error codes/data formats), update corresponding `docs/protocols/**` docs in same change.
|
||||
- Source: root `AGENTS.md` + backend rules.
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
Observed baseline:
|
||||
|
||||
- Unit tests: `backend/tests/unit/**`
|
||||
- Integration tests: `backend/tests/integration/**`
|
||||
- Live e2e tests separated by marker `live`: `backend/tests/e2e/README-live.md`
|
||||
|
||||
Pytest setup evidence:
|
||||
|
||||
- `pyproject.toml`
|
||||
- `testpaths = ["backend/tests"]`
|
||||
- `markers = ["live: ..."]`
|
||||
|
||||
Practical expectations for backend changes:
|
||||
|
||||
- Add/adjust tests for changed behavior where possible (especially contract/business logic).
|
||||
- Keep live tests opt-in unless task explicitly requires real dependency runs.
|
||||
|
||||
---
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
Use this checklist for backend PR review:
|
||||
|
||||
1. **Layering**
|
||||
- Router stays thin (`backend/src/v1/*/router.py`)
|
||||
- Service owns transaction and business decisions
|
||||
- Repository does not own commit/rollback
|
||||
2. **Error contract**
|
||||
- Responses align with RFC7807 + stable `code`
|
||||
- New/changed codes reflected in `docs/protocols/common/http-error-codes.md`
|
||||
3. **Logging**
|
||||
- Uses `core.logging.get_logger`
|
||||
- No raw sensitive values in logs
|
||||
4. **Config/secrets**
|
||||
- Reads configuration via `core.config.settings`
|
||||
- No hardcoded secrets
|
||||
5. **Tests and checks**
|
||||
- Relevant pytest scope updated if behavior changed
|
||||
- `ruff` + `basedpyright` remain passing for backend scope
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties (documented, not invented)
|
||||
|
||||
- Backend runtime still contains some legacy fallback exception patterns in specific infra/dependency paths (for example in `backend/src/v1/users/dependencies.py` fallback helper). New code should avoid expanding these patterns unless explicitly required.
|
||||
- No dedicated, repository-wide automated rule currently enforces every layering guideline; enforcement is primarily pre-commit + review discipline.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Component Guidelines
|
||||
|
||||
> Widget composition and UI reuse conventions for `apps/lib/**`.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. Prefer shared widgets before creating new feature-local widgets.
|
||||
2. Use semantic theme slots (`Theme.of(context).colorScheme.*`) and project tokens (`AppSpacing`, `AppRadius`).
|
||||
3. Keep feature screens focused on composition; move reusable sections into `shared/widgets/**`.
|
||||
4. Use project-consistent feedback components (`Toast` / `AppBanner` / `AppLoadingIndicator`).
|
||||
|
||||
---
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- Shared base components:
|
||||
- `apps/lib/shared/widgets/app_button.dart`
|
||||
- `apps/lib/shared/widgets/banner/app_banner.dart`
|
||||
- `apps/lib/shared/widgets/app_loading_indicator.dart`
|
||||
- Feature screen composition using shared widgets/tokens:
|
||||
- `apps/lib/features/auth/presentation/screens/login_screen.dart`
|
||||
- `apps/lib/features/settings/presentation/widgets/settings_page_scaffold.dart`
|
||||
- `apps/lib/features/home/presentation/widgets/home_composer_stack.dart`
|
||||
- Design system sources:
|
||||
- `apps/lib/core/theme/design_tokens.dart`
|
||||
- `apps/lib/core/theme/app_theme.dart`
|
||||
|
||||
---
|
||||
|
||||
## UI Contract Rules
|
||||
|
||||
1. **Color usage**
|
||||
- Use `colorScheme` for semantic colors.
|
||||
- Use `Theme.extension<AppColorPalette>()` only for brand palette/event/avatar domains.
|
||||
2. **Spacing & radius**
|
||||
- Use `AppSpacing.*` / `AppRadius.*` tokens as defaults.
|
||||
3. **Page actions & confirmations**
|
||||
- Reuse shared action/confirmation surfaces where available (for consistent interaction style).
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with evidence)
|
||||
|
||||
- Duplicating near-identical controls per feature instead of extending shared widgets.
|
||||
- Existing shared baseline in `apps/lib/shared/widgets/**` should be checked first.
|
||||
- Hardcoding user-facing feedback patterns outside project primitives.
|
||||
- Existing patterns: `AppBanner` in `login_screen.dart`, `AppLoadingIndicator` in `shared/widgets/app_loading_indicator.dart`.
|
||||
- Bypassing theme semantics by introducing ad-hoc `Colors.*` in feature widgets.
|
||||
- Current style source of truth is `apps/lib/core/theme/app_theme.dart` + `design_tokens.dart`.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- There is no automated lint rule in this repo that forbids all hardcoded color constants in feature widgets; consistency relies on code review and AGENTS guidance.
|
||||
- Some older UI files may still contain local one-off style values where token extraction is incomplete.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Directory Structure
|
||||
|
||||
> How frontend (Flutter) code is organized in this repository.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. `apps/lib/main.dart` is the single root entry file.
|
||||
2. Keep second-level folders fixed to: `app/`, `core/`, `data/`, `features/`, `shared/`, `l10n/`.
|
||||
3. Put feature business code under `features/<feature>/...`; keep cross-feature infra/protocol in `core/` and shared infra in `data/`.
|
||||
4. Shared reusable UI belongs to `shared/widgets/**`, not duplicated under each feature.
|
||||
|
||||
---
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- App bootstrap and routing composition:
|
||||
- `apps/lib/main.dart`
|
||||
- `apps/lib/app/app.dart`
|
||||
- `apps/lib/app/router/app_router.dart`
|
||||
- Shared infrastructure (non-feature business):
|
||||
- `apps/lib/data/network/api_client.dart`
|
||||
- `apps/lib/data/cache/cache_store.dart`
|
||||
- `apps/lib/core/theme/app_theme.dart`
|
||||
- Feature-bounded implementation:
|
||||
- `apps/lib/features/todo/data/apis/todo_api.dart`
|
||||
- `apps/lib/features/todo/data/repositories/todo_repository.dart`
|
||||
- `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`
|
||||
|
||||
---
|
||||
|
||||
## Placement Checklist (before adding a file)
|
||||
|
||||
1. Does this logic belong to exactly one feature flow?
|
||||
- Yes -> `features/<feature>/...`
|
||||
2. Is it cross-feature protocol/orchestration (auth/session/notification/ui schema)?
|
||||
- Yes -> `core/**`
|
||||
3. Is it shared storage/network/cache infra?
|
||||
- Yes -> `data/**`
|
||||
4. Is it reusable widget/presentation helper with no feature business orchestration?
|
||||
- Yes -> `shared/widgets/**` (or `shared/forms/**`)
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with evidence)
|
||||
|
||||
- Creating feature business repositories/models under shared `apps/lib/data/**`.
|
||||
- Existing correct pattern is feature-local repositories under `apps/lib/features/**/data/repositories/`.
|
||||
- Letting UI code bypass repositories for consistency-sensitive data flow.
|
||||
- Correct references: `apps/lib/features/todo/data/repositories/todo_repository.dart`, `apps/lib/features/messages/data/repositories/inbox_repository.dart`.
|
||||
- Deep redundant nesting without clear ownership.
|
||||
- Current tree generally keeps flat-by-concern organization (e.g., `data/apis`, `data/repositories`, `presentation/screens`, `presentation/widgets`).
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- Some modules blend domain-specific and infrastructure concerns (for example, notification rewrite boundaries) and rely on AGENTS rules rather than strict automated folder enforcement.
|
||||
- No script currently validates folder placement rules automatically; enforcement is mostly review + convention.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Hook Guidelines (Bloc/Cubit State Flows)
|
||||
|
||||
> This repository is Flutter-based. This file defines state-flow conventions using Bloc/Cubit.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. Treat `AuthBloc` as the global auth source of truth; auth invalidation should flow through its event chain.
|
||||
2. Keep Cubit/Bloc states immutable and updated via `copyWith`.
|
||||
3. Catch-and-handle branches should log errors with module-scoped logger names.
|
||||
4. For async flow handoff (auth/session switch, SSE stream lifecycle), ensure race ordering is explicit (queues/epoch tokens/cancel semantics).
|
||||
|
||||
---
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- Auth state source of truth and transitions:
|
||||
- `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`
|
||||
- `apps/lib/features/auth/presentation/bloc/auth_event.dart`
|
||||
- `apps/lib/features/auth/presentation/bloc/auth_state.dart`
|
||||
- Feature-local form/interaction state:
|
||||
- `apps/lib/features/auth/presentation/cubits/login_cubit.dart`
|
||||
- `apps/lib/features/settings/presentation/cubits/automation_jobs_cubit.dart`
|
||||
- Multi-step async orchestration with explicit stream/session control:
|
||||
- `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
|
||||
- `apps/lib/core/chat/ag_ui_service.dart`
|
||||
- `apps/lib/app/app.dart`
|
||||
|
||||
---
|
||||
|
||||
## State-Flow Checklist
|
||||
|
||||
Before adding/changing state logic:
|
||||
|
||||
1. Is state ownership clear (global vs feature-local)?
|
||||
2. Are transitions serialized when order matters?
|
||||
3. Are recoverable failures surfaced to UI state and logged?
|
||||
4. Are side effects (cache reset, prewarm, SSE cancel/restart) bound to lifecycle events, not random UI callbacks?
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with evidence)
|
||||
|
||||
- Feature-level auth reset/navigation bypassing global auth flow.
|
||||
- Correct path: `ApiClient` auth-failure callback -> `AuthSessionInvalidated` event (`apps/lib/app/di/injection.dart`, `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`).
|
||||
- Silent exception handling in async orchestration.
|
||||
- Existing weak branches to avoid copying:
|
||||
- `apps/lib/app/di/injection.dart` refresh callback catches and returns `false` without structured logging.
|
||||
- `apps/lib/core/chat/ag_ui_service.dart` ignores malformed SSE payload parsing errors by design.
|
||||
- Emitting mutable/partially-updated state objects.
|
||||
- Current state classes use immutable fields + `copyWith` (`LoginState`, `ChatState`, `AutomationJobsState`).
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- The codebase uses both Bloc and Cubit; exact selection criteria (when to pick Bloc vs Cubit) are not formally codified beyond existing patterns.
|
||||
- Some cancellation/retry branches return error strings directly (e.g., chat flow) and may need future standardization if UX/error taxonomy requirements tighten.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Frontend Development Guidelines (Flutter App)
|
||||
|
||||
> Scope: `apps/**` (Flutter mobile app). This index links to project-grounded guidelines only.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions (Quick Rules)
|
||||
|
||||
1. Keep `apps/lib` second-level layout stable: `app/`, `core/`, `data/`, `features/`, `shared/`, `l10n/`.
|
||||
2. Use feature-bounded modules under `features/<domain>/...` and keep shared infra in `data/` + `core/`.
|
||||
3. Parse backend errors as RFC7807 (`code`/`params`) and keep mapping synced with protocol docs.
|
||||
4. Prefer shared UI widgets (`shared/widgets/**`) and design tokens (`AppSpacing`/`AppRadius` + `Theme.colorScheme`).
|
||||
|
||||
---
|
||||
|
||||
## Repository Evidence (entry files to read first)
|
||||
|
||||
- `apps/lib/main.dart` - bootstrap entry (`configureDependencies`, `Env.init`, `runApp`)
|
||||
- `apps/lib/app/app.dart` - app shell + theme + auth lifecycle orchestration
|
||||
- `apps/lib/app/di/injection.dart` - DI registration and cross-feature singleton wiring
|
||||
- `apps/AGENTS.md` - enforced frontend domain rules
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- `apps/lib/main.dart`
|
||||
- `apps/lib/app/di/injection.dart`
|
||||
- `apps/lib/data/network/error_code_mapper.dart`
|
||||
|
||||
---
|
||||
|
||||
## Guidelines Index
|
||||
|
||||
| Guide | Focus |
|
||||
|---|---|
|
||||
| [Directory Structure](./directory-structure.md) | Module boundaries and placement rules |
|
||||
| [Component Guidelines](./component-guidelines.md) | Widget composition, shared components, design system usage |
|
||||
| [Hook Guidelines (Bloc/Cubit State Flows)](./hook-guidelines.md) | Flutter state flow conventions using Bloc/Cubit |
|
||||
| [Type Safety](./type-safety.md) | DTO parsing, API boundary typing, protocol alignment |
|
||||
| [Quality Guidelines](./quality-guidelines.md) | Lint/test/logging expectations and review checklist |
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns Observed (do not copy)
|
||||
|
||||
- Silent/weak failure branches still exist in some paths and should not be expanded:
|
||||
- `apps/lib/app/di/injection.dart` (`setRefreshCallback` catch branch returns `false` without logging)
|
||||
- `apps/lib/core/chat/ag_ui_service.dart` (malformed SSE payload catch branch intentionally ignores parse error)
|
||||
- Directly hardcoding API contract assumptions outside protocol docs/mappers leads to drift:
|
||||
- Keep `docs/protocols/common/http-error-codes.md` and `apps/lib/data/network/error_code_mapper.dart` synchronized.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- The frontend workflow doc (`.trellis/workflow.md`) still uses the historical filename `hook-guidelines.md`; this index keeps the filename for compatibility while the content is Flutter Bloc/Cubit specific.
|
||||
- There is no repository-level automated check yet proving every protocol-code change updates all three sides (backend + protocol docs + frontend mapping); this is currently review-driven.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Quality Guidelines
|
||||
|
||||
> Frontend quality baseline for `apps/**` changes.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. Run Flutter analyzer/tests for touched scope before finalizing changes.
|
||||
2. Keep error logging explicit at catch sites using project logger (`getLogger(...)`).
|
||||
3. Prioritize regression tests for high-risk modules: auth/session, home viewport, cache/repository, agent SSE flows.
|
||||
4. Keep docs/protocol/frontend mappings aligned for API-contract changes.
|
||||
|
||||
---
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- Lint/static analysis configuration:
|
||||
- `apps/analysis_options.yaml`
|
||||
- `apps/pubspec.yaml` (dev dependencies: `flutter_test`, `bloc_test`, `mocktail`)
|
||||
- Test suites for high-risk modules:
|
||||
- `apps/test/app/router/app_router_redirect_test.dart`
|
||||
- `apps/test/core/chat/ag_ui_service_test.dart`
|
||||
- `apps/test/data/cache/cached_repository_test.dart`
|
||||
- Logging usage examples:
|
||||
- `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`
|
||||
- `apps/lib/features/auth/presentation/cubits/login_cubit.dart`
|
||||
- `apps/lib/features/settings/presentation/cubits/automation_jobs_cubit.dart`
|
||||
|
||||
---
|
||||
|
||||
## Review Checklist
|
||||
|
||||
1. **Layering and ownership**
|
||||
- No feature business logic moved into shared `data/**` infra.
|
||||
2. **Error handling**
|
||||
- Exceptions are either propagated or converted to typed user-safe errors.
|
||||
- Catch branches include context logging where actionable.
|
||||
3. **Contract alignment**
|
||||
- If API/error/event contracts changed, protocol docs and mapping updates are in the same change.
|
||||
4. **Regression coverage**
|
||||
- Add/adjust tests for changed behavior in critical flows.
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with evidence)
|
||||
|
||||
- Error swallowing branches that silently downgrade behavior.
|
||||
- Existing examples to avoid extending:
|
||||
- `apps/lib/app/di/injection.dart` refresh callback catch returns `false`.
|
||||
- `apps/lib/core/chat/ag_ui_service.dart` malformed SSE payload parse catch ignores payload.
|
||||
- Feature code bypassing shared feedback/loading primitives.
|
||||
- Project standard primitives exist in `apps/lib/shared/widgets/toast/`, `apps/lib/shared/widgets/banner/`, `apps/lib/shared/widgets/app_loading_indicator.dart`.
|
||||
- Protocol changes implemented only in one layer.
|
||||
- Contract source of truth is under `docs/protocols/**` and must stay synchronized.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- The repository does not currently enforce all frontend rules via CI (for example, no strict automated checker for protocol-mapper parity).
|
||||
- `analysis_options.yaml` is currently close to Flutter defaults; stricter lint rules may still be introduced incrementally.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Type Safety
|
||||
|
||||
> API/data boundary typing and protocol alignment rules for Flutter frontend.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. Parse remote payloads into typed models at API/repository boundaries, not in UI widgets.
|
||||
2. Keep request/response key names aligned with backend/protocol snake_case fields.
|
||||
3. Parse backend errors as RFC7807 (`detail` + `code` + optional `params`) and map by `code` first.
|
||||
4. When protocol changes, update protocol docs and frontend mapping in the same change.
|
||||
|
||||
---
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- Error transport parsing and localization mapping:
|
||||
- `apps/lib/data/network/api_exception.dart`
|
||||
- `apps/lib/data/network/error_code_mapper.dart`
|
||||
- `docs/protocols/common/http-error-codes.md`
|
||||
- Typed todo contract across frontend/backend/protocol:
|
||||
- `apps/lib/features/todo/data/apis/todo_api.dart`
|
||||
- `backend/src/v1/todo/schemas.py`
|
||||
- `docs/protocols/models/todo.md`
|
||||
- AG-UI/SSE contract consumption:
|
||||
- `apps/lib/core/chat/ag_ui_service.dart`
|
||||
- `apps/lib/features/chat/data/apis/chat_api_impl.dart`
|
||||
- `docs/protocols/agent/sse-events.md`
|
||||
|
||||
---
|
||||
|
||||
## Contract Checkpoints (frontend side)
|
||||
|
||||
1. Endpoint path + method alignment:
|
||||
- Example: `/api/v1/todos` in both `todo_api.dart` and backend `todo/router.py`.
|
||||
2. Field naming and shape alignment:
|
||||
- Example: `schedule_item_ids`, `created_at`, `updated_at` in todo DTOs.
|
||||
3. Error code alignment:
|
||||
- Any new backend `code` must be added to `http-error-codes.md` and `error_code_mapper.dart`.
|
||||
4. SSE lifecycle alignment:
|
||||
- Frontend stream handling expects terminal event (`RUN_FINISHED` or `RUN_ERROR`) per run.
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with evidence)
|
||||
|
||||
- Using free-text `detail` as the only user-facing error contract.
|
||||
- Correct implementation uses `code -> l10n key` mapping first in `api_exception.dart` + `error_code_mapper.dart`.
|
||||
- Parsing untyped `Map` payloads directly inside UI pages.
|
||||
- Current typed parsing is concentrated in API/model/repository files (e.g., `TodoResponse.fromJson`, `HistorySnapshot.fromJson`).
|
||||
- Changing backend error codes without frontend mapping update.
|
||||
- Registry and sync rule explicitly documented in `docs/protocols/common/http-error-codes.md`.
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- The repository currently has no automatic script that validates full parity between `http-error-codes.md` and `error_code_mapper.dart`.
|
||||
- Some API/model parsing remains manual (not fully generated), so contract drift risk is controlled mainly via tests/review.
|
||||
@@ -0,0 +1,105 @@
|
||||
# Code Reuse Thinking Guide
|
||||
|
||||
> **Purpose**: Stop and think before creating new code - does it already exist?
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
**Duplicated code is the #1 source of inconsistency bugs.**
|
||||
|
||||
When you copy-paste or rewrite existing logic:
|
||||
- Bug fixes don't propagate
|
||||
- Behavior diverges over time
|
||||
- Codebase becomes harder to understand
|
||||
|
||||
---
|
||||
|
||||
## Before Writing New Code
|
||||
|
||||
### Step 1: Search First
|
||||
|
||||
```bash
|
||||
# Search for similar function names
|
||||
grep -r "functionName" .
|
||||
|
||||
# Search for similar logic
|
||||
grep -r "keyword" .
|
||||
```
|
||||
|
||||
### Step 2: Ask These Questions
|
||||
|
||||
| Question | If Yes... |
|
||||
|----------|-----------|
|
||||
| Does a similar function exist? | Use or extend it |
|
||||
| Is this pattern used elsewhere? | Follow the existing pattern |
|
||||
| Could this be a shared utility? | Create it in the right place |
|
||||
| Am I copying code from another file? | **STOP** - extract to shared |
|
||||
|
||||
---
|
||||
|
||||
## Common Duplication Patterns
|
||||
|
||||
### Pattern 1: Copy-Paste Functions
|
||||
|
||||
**Bad**: Copying a validation function to another file
|
||||
|
||||
**Good**: Extract to shared utilities, import where needed
|
||||
|
||||
### Pattern 2: Similar Components
|
||||
|
||||
**Bad**: Creating a new component that's 80% similar to existing
|
||||
|
||||
**Good**: Extend existing component with props/variants
|
||||
|
||||
### Pattern 3: Repeated Constants
|
||||
|
||||
**Bad**: Defining the same constant in multiple files
|
||||
|
||||
**Good**: Single source of truth, import everywhere
|
||||
|
||||
---
|
||||
|
||||
## When to Abstract
|
||||
|
||||
**Abstract when**:
|
||||
- Same code appears 3+ times
|
||||
- Logic is complex enough to have bugs
|
||||
- Multiple people might need this
|
||||
|
||||
**Don't abstract when**:
|
||||
- Only used once
|
||||
- Trivial one-liner
|
||||
- Abstraction would be more complex than duplication
|
||||
|
||||
---
|
||||
|
||||
## After Batch Modifications
|
||||
|
||||
When you've made similar changes to multiple files:
|
||||
|
||||
1. **Review**: Did you catch all instances?
|
||||
2. **Search**: Run grep to find any missed
|
||||
3. **Consider**: Should this be abstracted?
|
||||
|
||||
---
|
||||
|
||||
## Gotcha: Asymmetric Mechanisms Producing Same Output
|
||||
|
||||
**Problem**: When two different mechanisms must produce the same file set (e.g., recursive directory copy for init vs. manual `files.set()` for update), structural changes (renaming, moving, adding subdirectories) only propagate through the automatic mechanism. The manual one silently drifts.
|
||||
|
||||
**Symptom**: Init works perfectly, but update creates files at wrong paths or misses files entirely.
|
||||
|
||||
**Prevention checklist**:
|
||||
- [ ] When migrating directory structures, search for ALL code paths that reference the old structure
|
||||
- [ ] If one path is auto-derived (glob/copy) and another is manually listed, the manual one needs updating
|
||||
- [ ] Add a regression test that compares outputs from both mechanisms
|
||||
|
||||
---
|
||||
|
||||
## Checklist Before Commit
|
||||
|
||||
- [ ] Searched for existing similar code
|
||||
- [ ] No copy-pasted logic that should be shared
|
||||
- [ ] Constants defined in one place
|
||||
- [ ] Similar patterns follow same structure
|
||||
@@ -0,0 +1,116 @@
|
||||
# Cross-Layer Thinking Guide
|
||||
|
||||
> Purpose: prevent contract drift across `docs/protocols` -> backend -> frontend.
|
||||
|
||||
---
|
||||
|
||||
## Practical Conventions
|
||||
|
||||
1. Treat `docs/protocols/**` as the contract source of truth before code changes.
|
||||
2. For any API/event/error contract change, verify all three sides in one pass:
|
||||
- Protocol doc
|
||||
- Backend transport/output
|
||||
- Frontend parsing/mapping
|
||||
3. Prefer explicit boundary checks over implicit assumptions (status/code/date/run lifecycle).
|
||||
4. When touching cross-layer behavior, keep a short written checkpoint list in task notes (aligned with `.opencode/commands/trellis/finish-work.md` cross-layer verification items).
|
||||
|
||||
---
|
||||
|
||||
## Project-Specific Boundary Map
|
||||
|
||||
| Boundary | Contract Source | Backend Evidence | Frontend Evidence |
|
||||
|---|---|---|---|
|
||||
| HTTP errors | `docs/protocols/common/http-error-codes.md` | `backend/src/core/http/response.py`, `backend/src/app.py` | `apps/lib/data/network/api_exception.dart`, `apps/lib/data/network/error_code_mapper.dart` |
|
||||
| Todo model | `docs/protocols/models/todo.md` | `backend/src/v1/todo/schemas.py`, `backend/src/v1/todo/router.py` | `apps/lib/features/todo/data/apis/todo_api.dart` |
|
||||
| Agent SSE events | `docs/protocols/agent/sse-events.md` | `backend/src/v1/agent/router.py` | `apps/lib/features/chat/data/apis/chat_api_impl.dart`, `apps/lib/core/chat/ag_ui_service.dart` |
|
||||
|
||||
## Real File Path Examples
|
||||
|
||||
- `docs/protocols/common/http-error-codes.md`
|
||||
- `backend/src/v1/agent/router.py`
|
||||
- `apps/lib/core/chat/ag_ui_service.dart`
|
||||
|
||||
---
|
||||
|
||||
## Contract Checkpoints (Required for cross-layer work)
|
||||
|
||||
### 1) Endpoint and payload shape
|
||||
|
||||
- Confirm method/path/query/body on both sides.
|
||||
- Example pair:
|
||||
- Backend: `backend/src/v1/todo/router.py` (`/api/v1/todos`, query `status/priority`)
|
||||
- Frontend: `apps/lib/features/todo/data/apis/todo_api.dart` (`getTodos`, `createTodo`, `reorderTodos`)
|
||||
|
||||
### 2) Error code transport and mapping
|
||||
|
||||
- Backend should return RFC7807 + stable `code`/`params`.
|
||||
- Frontend should localize by `code`, not by free-text `detail`.
|
||||
- Example triplet:
|
||||
- `docs/protocols/common/http-error-codes.md`
|
||||
- `backend/src/core/http/response.py`
|
||||
- `apps/lib/data/network/error_code_mapper.dart`
|
||||
|
||||
### 3) Event lifecycle completeness (SSE)
|
||||
|
||||
- Ensure lifecycle expectations are symmetric:
|
||||
- backend stream semantics (filter + terminal event)
|
||||
- frontend stream completion/recovery logic
|
||||
- Example pair:
|
||||
- `backend/src/v1/agent/router.py` (target run filtering, terminal events)
|
||||
- `apps/lib/core/chat/ag_ui_service.dart` (terminal event required for stream completion)
|
||||
|
||||
### 4) Date/time and ID boundary parsing
|
||||
|
||||
- Check datetime serialization/parsing and UUID/string assumptions across layers.
|
||||
- Example:
|
||||
- `backend/src/v1/todo/schemas.py` (`datetime`, `UUID`)
|
||||
- `apps/lib/features/todo/data/apis/todo_api.dart` (`DateTime.parse`, string IDs)
|
||||
|
||||
---
|
||||
|
||||
## Project Examples
|
||||
|
||||
### Example A: Todo contract alignment
|
||||
|
||||
1. Protocol defines field names and ordering rules: `docs/protocols/models/todo.md`
|
||||
2. Backend enforces schema and route behavior: `backend/src/v1/todo/schemas.py`, `backend/src/v1/todo/router.py`
|
||||
3. Frontend maps to model parsing and request payloads: `apps/lib/features/todo/data/apis/todo_api.dart`
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] field names (`schedule_item_ids`, `created_at`, `updated_at`) match
|
||||
- [ ] enums/ranges (`status`, `priority`) match
|
||||
- [ ] reorder payload shape (`items: [{id, priority, order}]`) matches
|
||||
|
||||
### Example B: Agent SSE run lifecycle alignment
|
||||
|
||||
1. Protocol lifecycle doc: `docs/protocols/agent/sse-events.md`
|
||||
2. Backend stream filtering/termination: `backend/src/v1/agent/router.py`
|
||||
3. Frontend consumption/recovery: `apps/lib/core/chat/ag_ui_service.dart`
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] frontend sends `runId` query for `/events`
|
||||
- [ ] backend only emits target run to subscriber
|
||||
- [ ] frontend treats `RUN_FINISHED`/`RUN_ERROR` as terminal for stream completion
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns (with current evidence)
|
||||
|
||||
- Changing backend error codes without updating protocol registry and frontend mapper.
|
||||
- Synchronization points: `http-error-codes.md` + `error_code_mapper.dart`.
|
||||
- Inferring user-facing behavior from `detail` free text.
|
||||
- Frontend already supports code-first mapping in `api_exception.dart`; bypassing this creates localization drift.
|
||||
- Swallowing boundary failures in stream/auth paths.
|
||||
- Existing weak branches (do not extend):
|
||||
- `apps/lib/app/di/injection.dart` refresh callback catch returns `false`
|
||||
- `apps/lib/core/chat/ag_ui_service.dart` malformed SSE payload parse catch ignores payload
|
||||
- `backend/src/v1/agent/router.py` SSE slot acquire/release failure falls back to warning + continue
|
||||
|
||||
---
|
||||
|
||||
## Uncertainties / Gaps
|
||||
|
||||
- No repository-wide automated checker currently validates full cross-layer parity (protocol doc <-> backend implementation <-> frontend mapping/event consumer).
|
||||
- Some fallback branches exist for runtime resilience in legacy paths; whether to hard-fail vs degrade is not yet uniformly specified across all modules.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Thinking Guides
|
||||
|
||||
> **Purpose**: Expand your thinking to catch things you might not have considered.
|
||||
|
||||
---
|
||||
|
||||
## Why Thinking Guides?
|
||||
|
||||
**Most bugs and tech debt come from "didn't think of that"**, not from lack of skill:
|
||||
|
||||
- Didn't think about what happens at layer boundaries → cross-layer bugs
|
||||
- Didn't think about code patterns repeating → duplicated code everywhere
|
||||
- Didn't think about edge cases → runtime errors
|
||||
- Didn't think about future maintainers → unreadable code
|
||||
|
||||
These guides help you **ask the right questions before coding**.
|
||||
|
||||
---
|
||||
|
||||
## Available Guides
|
||||
|
||||
| Guide | Purpose | When to Use |
|
||||
|-------|---------|-------------|
|
||||
| [Code Reuse Thinking Guide](./code-reuse-thinking-guide.md) | Identify patterns and reduce duplication | When you notice repeated patterns |
|
||||
| [Cross-Layer Thinking Guide](./cross-layer-thinking-guide.md) | Think through data flow across layers | Features spanning multiple layers |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Thinking Triggers
|
||||
|
||||
### When to Think About Cross-Layer Issues
|
||||
|
||||
- [ ] Feature touches 3+ layers (API, Service, Component, Database)
|
||||
- [ ] Data format changes between layers
|
||||
- [ ] Multiple consumers need the same data
|
||||
- [ ] You're not sure where to put some logic
|
||||
|
||||
→ Read [Cross-Layer Thinking Guide](./cross-layer-thinking-guide.md)
|
||||
|
||||
### When to Think About Code Reuse
|
||||
|
||||
- [ ] You're writing similar code to something that exists
|
||||
- [ ] You see the same pattern repeated 3+ times
|
||||
- [ ] You're adding a new field to multiple places
|
||||
- [ ] **You're modifying any constant or config**
|
||||
- [ ] **You're creating a new utility/helper function** ← Search first!
|
||||
|
||||
→ Read [Code Reuse Thinking Guide](./code-reuse-thinking-guide.md)
|
||||
|
||||
---
|
||||
|
||||
## Pre-Modification Rule (CRITICAL)
|
||||
|
||||
> **Before changing ANY value, ALWAYS search first!**
|
||||
|
||||
```bash
|
||||
# Search for the value you're about to change
|
||||
grep -r "value_to_change" .
|
||||
```
|
||||
|
||||
This single habit prevents most "forgot to update X" bugs.
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Directory
|
||||
|
||||
1. **Before coding**: Skim the relevant thinking guide
|
||||
2. **During coding**: If something feels repetitive or complex, check the guides
|
||||
3. **After bugs**: Add new insights to the relevant guide (learn from mistakes)
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Found a new "didn't think of that" moment? Add it to the relevant guide.
|
||||
|
||||
---
|
||||
|
||||
**Core Principle**: 30 minutes of thinking saves 3 hours of debugging.
|
||||
Reference in New Issue
Block a user