Files
social-app/.trellis/spec/guides/cross-layer-thinking-guide.md

4.9 KiB

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.