# Lodging & rooming (Phase 06)

Selling a lodging type/modality and physically assigning a passenger are related
but **independent** processes. Hotels are separate from the commercial supplier
(wired in phase 07).

## Structure

`hotel → tour_lodging_segment → contracted_room_inventory → room → room_spaces →
passengers`. Room codes (`DBL-01`) are internal; the hotel's physical number is
optional and mapped later.

## Availability by spaces and rooms

- **Space** states: `available | held | occupied | blocked`.
- **Room** states are derived: `empty | partial | full` (plus manual `blocked` /
  `out_of_service`).
- A **shared** double with one occupant keeps a space available (1/2).
- A **private/full** room with one captured passenger is **0 commercially
  available**, even if a physical space is empty.
- Blocking a space reduces commercial availability without faking physical
  capacity; `out_of_service` rooms are 0.

## Atomic assign / move / swap (no silent deletes)

`RoomingService` runs each action in a transaction, **locks the involved spaces
FOR UPDATE**, validates (available, serviceable, no double-booking in the
segment), then applies. If a space was taken meanwhile, it **conflicts** rather
than overwriting. Move and swap **end** the previous assignment (kept for
history) and create new ones. Every change writes a `room_assignment_event`
(before/after, actor, reason). An ordinary rooming change never touches the
commercial ledger, so booking totals are unaffected.

## Holds & consecutive stays

A temporary space hold reserves a space for an expected companion and releases
**exactly once** on expiry (idempotent). Consecutive lodging segments let the
same passenger sit in different rooms per city/date.

## Pairing

No automatic matching on sensitive attributes: explicit `roommate_requests`,
authorized notes, and human decision only. Suggestions (later) are explainable
and never mandatory.

## API

`GET /tenant/tours/{uuid}/rooming` (board), and
`POST /tenant/rooming/spaces/{uuid}/{assign|release|block}` (permission
`rooming.manage`). The SPA renders a visual board (space chips show
position/occupant and capacity text like 1/2, not colour alone).
