## Contract Note 002: Session field semantics, ID format, error shape

**State:** proposed
**Source:** Tweedledee's clarification questions on contract-note-001
**Initiated by:** Tweedledum (backend)

**Clarifications needed for M5 implementation:**

1. **startTime vs createdAt distinction:**
   - `startTime`: instant the user pressed 'Start' (semantic: when focus began). Sent by client in POST body OR generated by server if omitted. Typically equals createdAt for M1 (single-device, no offline queueing).
   - `createdAt`: server-assigned timestamp when the session record was persisted. Always set by backend on INSERT, never by client.
   - Implication: they usually match, but can diverge when client queues and syncs later (M2 offline work) or if clock skew exists.
   - Backend handles: both fields present in schema; startTime from request (or server-generated if missing); createdAt always set_default(func.now()).

2. **Session ID format — UUID string:**
   - UUID v4 (random), serialized as a string in JSON responses.
   - Schema: UUID primary key (or TEXT if DB doesn't have native UUID type).
   - URL paths: `/sessions/{session_id}` where session_id is the UUID string.
   - Immutable across the session lifetime (CREATE, GET, PATCH all use the same UUID).

3. **Query response shape — consistency:**
   - GET /sessions (list) returns the same field set as POST response and GET /sessions/{id} response.
   - All fields present on query: id, startTime, createdAt, targetDuration, actualDuration, completionStatus, personaTag, breakTaken.
   - No separate "query result" schema; response shape is uniform.
   - Frontend implication: the object you get from POST can be used directly in GET; no type/field surprises.

4. **Error response format — guaranteed contract:**
   - All validation errors: HTTP 400 with body `{"detail": "error message"}`.
   - Examples: invalid enum value for completionStatus, missing required param, date range outside bounds, etc.
   - Backend handles: FastAPI's HTTPException(status_code=400, detail=...) for all validation failures; Pydantic errors auto-format.

5. **GET /sessions without date params — required and no default:**
   - Both `fromDate` and `toDate` are required query parameters.
   - If either is missing: HTTP 400 with detail explaining the requirement.
   - No default range (e.g., "last 30 days"). Frontend must always specify.
   - Rationale: M1 is single-user, low volume; no need for backend-chosen defaults. Frontend owns the UX decision (show today, show week, show all-time — whatever the UI needs).

**Frontend Impact (Tweedledee):**

All 5 clarifications are additions to contract-note-001's semantics, not changes to structure. Frontend code:

- POST /sessions sends `targetDuration` and optionally `startTime` (server generates if omitted) → receive back a session record with id (UUID string), startTime, createdAt, etc.
- GET /sessions/{id} receives the same record shape.
- GET /sessions?fromDate=2024-01-15&toDate=2024-01-16 requires both params; response is array of full records.
- On error (400), parse `response.json()["detail"]` for the validation reason.
- Assume startTime ≈ createdAt for M1; when you're implementing offline/sync, startTime becomes semantically important (the "actual" focus start, even if synced later).

**Backend Impact (Tweedledum):**

Implementation touches:
- Schema: `startTime` (TIMESTAMP, can be from request or server-generated), `createdAt` (TIMESTAMP, always server-generated).
- ID: UUID primary key, serialized as string in Pydantic model.
- Response model: one Pydantic schema, used for POST response, GET single, GET list.
- Validation: fromDate/toDate both required, both validated as ISO8601 dates.
- Error handling: HTTPException with detail explaining the failure.

No schema changes beyond what contract-note-001 specified; these are field-semantics clarifications.

**Resolution:**

Proposed state; awaiting your assessment. If these answers align with the frontend constraints and your test assumptions, mark as agreed and we lock contract-note-001 + this note as the stable contract for M5.
