Query reachability between entities
Ask whether one entity reaches another over the declared edges with aontu reaches, and get the path as the answer.
Rendered from
docs/how-to/query-reachability.md
in the engine repository, where a correction belongs, and where the
test suite executes every example on this page.
“If the extract job stops, what downstream goes stale?” is a question
about the graph’s closure, and no amount of staring at one edge at a
time answers it. aontu reaches asks it directly: does anything
from links to, at any remove, end up at to?
It runs over the same declared edges as aontu relations. The model below is a trimmed version
of use-cases/12-relations: jobs feed
jobs, both directions written out. The vocabulary goes in spec.aon:
spec: hide({
Job: {
kind: job
feeds?: rel($.spec.JobShape) & acyclic() & inverse(fedBy)
fedBy?: rel($.spec.JobShape)
}
JobShape: kind: job
})
and a four-job topology in pipeline.aon, where transform fans out
to two consumers:
@"./spec.aon"
jobs: { &: $.spec.Job }
jobs: extract: feeds: [path($.jobs.transform)]
jobs: transform: {
fedBy: [path($.jobs.extract)]
feeds: [path($.jobs.load) path($.jobs.audit)]
}
jobs: load: fedBy: [path($.jobs.transform)]
jobs: audit: fedBy: [path($.jobs.transform)]
Now ask whether the extract job’s output ends up in the warehouse:
$ aontu reaches $.jobs.extract $.jobs.load --relation feeds pipeline.aon
verdict: reaches
$.jobs.extract -> $.jobs.transform -> $.jobs.load
$ echo $?
0
The path is the answer, not decoration: an operator asking what a failure takes out acts on the chain, and a bare “yes” gives them nothing to act on. It is a shortest path, and among shortest paths the first in code-point order, so both engines print the same one.
Direction matters
Along feeds, the DAG answers directionally: downstream yes,
upstream no:
$ aontu reaches $.jobs.load $.jobs.extract --relation feeds pipeline.aon
verdict: unreachable
$.jobs.load does not reach $.jobs.extract
$ echo $?
1
An unreachable pair is a failed check, and carries no path: there is
no evidence for a negative answer. Exit 1 (rather than 4) says
the question was answered, and the answer was no, which is exactly
what a containment policy wants to assert in CI (“nothing in the
public tier may reach the ledger”).
With and without --relation
Omit --relation and every declared edge counts, fedBy included.
load and audit are siblings, so no chain of feeds connects
them, but the unrestricted graph does:
$ aontu reaches $.jobs.load $.jobs.audit pipeline.aon
verdict: reaches
$.jobs.load -> $.jobs.transform -> $.jobs.audit
$ aontu reaches $.jobs.load $.jobs.audit --relation feeds pipeline.aon
verdict: unreachable
$.jobs.load does not reach $.jobs.audit
$ echo $?
1
That is the difference between “are these connected at all” and “can
this reach that this way”, and in a model that writes out its
inverses, the unrestricted graph connects nearly everything, so
--relation is what makes direction mean anything. The query is
transitive but never reflexive for free: reaches $.jobs.extract $.jobs.extract is true only when a path of one or more edges returns
home, which reports a cycle rather than reporting nothing.
An endpoint that names nothing refuses
An endpoint that names no node would make unreachable a lie about
the model, so the verb refuses instead, and lists what it does know:
$ aontu reaches $.jobs.extract $.jobs.laod pipeline.aon
verdict: error
$: refer_unresolved [reference]
$.jobs.laod names no node in this document.
note: nodes with links: $.jobs.audit, $.jobs.extract, $.jobs.load, $.jobs.transform
$ echo $?
4
A policy check that silently passed because its target was misspelled
is the failure mode this exit code exists to prevent. Treat exit 4
as a broken check, and fix the spelling.
The verb is specified under aontu reaches, including the library
forms (reachCheck / Aontu.Reach). The live version is
use-cases/12-relations, whose
check.sh asserts the directional answers above. To validate the
edge set itself before querying it, start with Check that components
agree about their relations.