# 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.