Pure scope-hydration contract shared between Sigra.Plug.LoadActiveOrganization
(Plug pipeline) and the generated UserAuth.on_mount callback (LiveView).
Given a (scope, config, session), returns a fully populated %Scope{} or
a fail-closed error tuple. This module is the SINGLE place scope hydration
lives. Any future scope augmentation — impersonation (v1.2), feature flags,
passkey context — extends this function.
Fail-closed contract
On stale pointer (:not_a_member) or deleted org (:org_not_found),
callers MUST clear session.active_organization_id and re-run
Sigra.Organizations.select_active_organization/3. See
Sigra.Plug.LoadActiveOrganization (Plan 14-02) for the orchestration.
The hydrator NEVER raises. It calls the non-raising
Sigra.Organizations.fetch_organization/2 (not get_organization!/2)
specifically to keep Ecto.NoResultsError out of the request pipeline
(PITFALLS O-6).
Purity
This module performs only the reads necessary to resolve the active
organization and the calling user's membership in it. It writes nothing:
no session mutation, no audit event, no cache update. Calling hydrate/3
twice with the same inputs yields structurally-equal scopes.
Source of truth: Phase 14 CONTEXT.md D-01 / D-14 / D-23.
Summary
Functions
Hydrates the given scope with :active_organization and :membership
based on session.active_organization_id.
Types
Functions
@spec hydrate(scope :: struct(), config :: map(), session :: Sigra.Session.t()) :: {:ok, struct()} | {:error, hydrate_error()}
Hydrates the given scope with :active_organization and :membership
based on session.active_organization_id.
Returns {:ok, scope} unchanged when the session has no active organization
pointer. Returns {:ok, scope} with the org and membership populated on the
happy path. Returns {:error, :not_a_member} when the session points at an
org the user was removed from. Returns {:error, :org_not_found} when the
session points at a hard-deleted or soft-deleted org.
The config argument is the Sigra.Organizations config map (the same shape
produced by the private __validate_config__! helper in Sigra.Organizations),
NOT the top-level
Sigra.Config.t(). Callers in the plug/on_mount paths resolve this from
the generated wrapper module.