# `AshNeo4j.Unknown`
[🔗](https://github.com/diffo-dev/ash_neo4j/blob/v0.10.1/lib/unknown.ex#L5)

A sentinel value for *"the data layer tried to determine this and couldn't, in
its current view of the graph"* — complementary to `Ash.NotLoaded`.

`Ash.NotLoaded` marks a value that simply hasn't been fetched **yet** — ask
again and it resolves. `AshNeo4j.Unknown` marks a value the read genuinely
reached for and could **not** determine — e.g. a reached node whose labels
don't resolve to a known resource, or a value a future store reports as
indeterminate. Both are explicit values: consumers pattern-match `Unknown`,
`nil`, and concrete values as distinct outcomes, and never read `nil`-meaning-
absent into an `Unknown`.

It deliberately implements no `String.Chars`/`Inspect`-coercion that would let
it pass quietly as a value — use it where a value is expected and it stays
loud; acknowledge it and it's calm.

## Shape

    %AshNeo4j.Unknown{
      world:   MyApp.SomeResource,   # the (Domain, Resource) frame of the original query
      reason:  :reached_unresolved,  # a leaf atom, OR a nested unknown from a deeper layer
      context: %{label: :Foo, …}     # local diagnostics, not load-bearing
    }

* `:world` — the **(Domain, Resource) frame the query was asked in**, stored as
  the resource module (the Domain is derivable via `Ash.Resource.Info.domain/1`).
  It is the frame the caller is standing in — *not* the thing that failed to
  resolve (that belongs in `:reason`/`:context`). The Domain alone is
  insufficient: within one Domain multiple resources can share a base label,
  so Domain + Resource together identify the frame.

* `:reason` — **either a leaf `atom()` or a nested unknown** from the layer
  beneath. The cause of not-knowing can *be* a deeper not-knowing: an
  `AshNeo4j.Unknown` raised at the data-layer boundary can carry, as its
  reason, a store's own indeterminate value, and an outer world (e.g. a
  consumer's `Diffo.Unknown`) can in turn carry this one. Outer holds inner
  **by value**, following the dependency direction — no module coupling, no
  cycles. The chain bottoms out in an atom. The atom vocabulary is
  library-structural and documented where each Unknown is produced; there is
  no central registry.

* `:context` — `term()`, local diagnostics for *this* layer (the unresolved
  label, the hop chain, …). Never the place the cause lives.

## Frame-lifting at the boundary

ash_neo4j is the layer that turns a frameless or store-native unknown into an
Ash-framed one: it stamps `:world` with the resource being read and, where the
unknown originated beneath it, carries that inner unknown as `:reason`. A store
has no concept of a Domain/Resource pair, so its `:world` may be store-native
or absent — this struct never assumes the thing it nests shares its notion of
a world.

# `t`

```elixir
@type t() :: %AshNeo4j.Unknown{
  context: term(),
  reason: atom() | struct(),
  world: module() | nil
}
```

# `domain`

```elixir
@spec domain(t()) :: module() | nil
```

The Ash Domain of the query frame, derived from `:world` via
`Ash.Resource.Info.domain/1`. `nil` when there is no resolvable world.

# `new`

```elixir
@spec new(module() | nil, atom() | struct(), term()) :: t()
```

Builds an `AshNeo4j.Unknown` stamped with the query `world` (the resource the
read was asked in), a `reason` (a leaf atom or a nested unknown), and optional
local `context`.

# `root_reason`

```elixir
@spec root_reason(t()) :: term()
```

Walks the nested `:reason` chain to its leaf — the root-cause atom (or, if a
nested unknown bottoms out in a non-atom, that value). For a single-layer
Unknown this is just its `:reason`.

# `unknown?`

```elixir
@spec unknown?(term()) :: boolean()
```

True for an `AshNeo4j.Unknown`, false for anything else.

# `world_chain`

```elixir
@spec world_chain(t()) :: [module()]
```

The stack of `:world` frames the unknown passed through, outermost first —
one entry per nested unknown layer that carries a world. Layers without a
world (e.g. a store-native leaf) contribute nothing.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
