ek9_query_construct_neighbourhood
Answers first: nodeCount, nodes[] (name, fullyQualifiedName, module, genus, file, line, distance), edges[] (from, to, kind) and edgesByKind - the N-hop typed ego graph merging inheritance, calls, composition, references and DI.
When to use it
If I change this construct, what is structurally affected - one call with symbol:'Order' and direction:'incoming' (the blast radius), no file needed.
When not to use it
- ONLY inheritance/trait ancestry — use
ek9_query_type_hierarchy. - You want ONLY call edges — use
ek9_query_call_graph. - You want the exhaustive per-site reference list (not the typed structural shape) — use
ek9_query_references. - You want module-altitude coupling, not one construct's neighbourhood — use
ek9_query_module_coupling.
Details
Edge kinds: extends/implements/composes(field types)/references(param/return types)/calls(phase-5 call graph)/injects(DI fields)/dispatches(dispatcher overload group) — all types generic-unwrapped, only user-construct↔user-construct edges kept, each node carries 'distance' from the target. A virtual call THROUGH an injected abstract field is a 'calls' edge to the abstract type, which has a 'calls' edge on to each implementation that is constructed somewhere (a register in the application counts) - one hop more, never none. A function's implicit generic super ('Consumer of Address') gives 'references', not 'extends', to its type argument. Also emits 'requirements[]' (whose anchoring tests can REACH this construct) over the FULL transitive closure — never limited by 'depth', over-inclusive by construction (reach is change-impact, NOT what this construct implements); reachResolved=false means no call graph to answer from; test nodes carry isTest/requirementRef.
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
depth | integer | How many edges out from the target (default 1, max 5). | |
direction | string: both | incoming | outgoing | 'outgoing' = what it depends on, 'incoming' = what depends on it (the blast radius), 'both' (default). | |
edgeKinds | array of string: extends | implements | composes | references | calls | injects | dispatches | Restrict to these relationships. Omit for all. | |
file | string | The EK9 source file - OPTIONAL when symbol is a construct name; required to anchor on a member or a use site. | |
line | integer | 1-based line of the declaration or a reference - OMIT it and pass symbol:'<name>' to let the tool find the line (a model counting lines in a file miscounts); if you do count, use ek9_read_file(lineNumbers:true). | |
symbol | string | The construct: a bare name ('Order') or a qualified name ('com.acme.orders::Order') - no file needed, the tool finds it workspace-wide; 'Type.member' anchors on the member's construct. |
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it. 3 of the 4 recorded are shown.
Parameterised super references its argument
Request
{
"direction" : "incoming",
"symbol" : "sample.arch.model::Address"
}
Response
{
"depth" : 1,
"direction" : "incoming",
"edgeCount" : 1,
"edges" : [ {
"from" : "sample.arch.model::stamp",
"kind" : "references",
"to" : "sample.arch.model::Address"
} ],
"edgesByKind" : {
"references" : 1
},
"meta" : {
"serverMs" : "<timing>"
},
"nodeCount" : 2,
"nodes" : [ {
"distance" : 0,
"file" : "model.ek9",
"fullyQualifiedName" : "sample.arch.model::Address",
"genus" : "RECORD",
"line" : 6,
"module" : "sample.arch.model",
"name" : "Address"
...
Cut short here: 24 more lines. The whole recorded call.
Requirements beyond hop budget
Request
{
"depth" : 1,
"direction" : "incoming",
"symbol" : "com.example.trace::deepCore"
}
Response
{
"depth" : 1,
"direction" : "incoming",
"edgeCount" : 1,
"edges" : [ {
"from" : "com.example.trace::mid",
"kind" : "calls",
"to" : "com.example.trace::deepCore"
} ],
"edgesByKind" : {
"calls" : 1
},
"meta" : {
"serverMs" : "<timing>"
},
"nodeCount" : 2,
"nodes" : [ {
"distance" : 0,
"file" : "main.ek9",
"fullyQualifiedName" : "com.example.trace::deepCore",
"genus" : "FUNCTION",
"line" : 6,
"module" : "com.example.trace",
"name" : "deepCore"
...
Cut short here: 27 more lines. The whole recorded call.
Test node carries anchor
Request
{
"depth" : 1,
"direction" : "incoming",
"symbol" : "com.example.trace::outer"
}
Response
{
"depth" : 1,
"direction" : "incoming",
"edgeCount" : 1,
"edges" : [ {
"from" : "com.example.trace.tests::DeepTest",
"kind" : "calls",
"to" : "com.example.trace::outer"
} ],
"edgesByKind" : {
"calls" : 1
},
"meta" : {
"serverMs" : "<timing>"
},
"nodeCount" : 2,
"nodes" : [ {
"distance" : 0,
"file" : "main.ek9",
"fullyQualifiedName" : "com.example.trace::outer",
"genus" : "FUNCTION",
"line" : 12,
"module" : "com.example.trace",
"name" : "outer"
...
Cut short here: 29 more lines. The whole recorded call.