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 viaAsh.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 leafatom()or a nested unknown from the layer beneath. The cause of not-knowing can be a deeper not-knowing: anAshNeo4j.Unknownraised at the data-layer boundary can carry, as its reason, a store's own indeterminate value, and an outer world (e.g. a consumer'sDiffo.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.
Summary
Functions
The Ash Domain of the query frame, derived from :world via
Ash.Resource.Info.domain/1. nil when there is no resolvable world.
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.
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.
True for an AshNeo4j.Unknown, false for anything else.
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.
Types
Functions
The Ash Domain of the query frame, derived from :world via
Ash.Resource.Info.domain/1. nil when there is no resolvable world.
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.
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.
True for an AshNeo4j.Unknown, false for anything else.
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.