feat(resolution): bridge MediatR Send/Publish to its IRequestHandler.Handle

MediatR decouples a _mediator.Send(x)/.Publish(x) call from the Handle method
that runs it, linked by the request/notification TYPE (the IRequestHandler<X,…>
generic), usually across files in a Clean Architecture layout — so flows
dead-end at the mediator call and the agent reads to find the handler.
mediatrDispatchEdges bridges each dispatch -> the matching handler's Handle.

Same two-pass, type-keyed shape as the Spring synthesizer, with two C#-specific
twists found by probing:

- C# method nodes carry NO signature (csharp.ts defines no getSignature), so
  Pass 1 reads the request type from the handler CLASS base-list source
  (`: IRequestHandler<X,…>` first generic arg) and binds the class's Handle.
- The dominant .NET idiom is VARIABLE-passed, not inline `Send(new X)` — eShop
  has zero genuine inline MediatR sends. So Pass 2 resolves the sent type from
  the argument three ways within the enclosing method: inline `new X(…)`, a
  local `var v = new X(…)` (backward scan), or a parameter/local declared `X v`.

Two precision gates: the receiver must be mediator-ish (mediator/sender/
publisher — excludes MAUI MessagingCenter.Send, HttpClient.Send) AND the
resolved type must have a handler (so a same-named non-request DTO is never
bridged). Handles the IdentifiedCommand<T,R> wrapper and void IRequestHandler<T>.

Surfaces as `dynamic: mediatr dispatch` via the generic synth-edge fallback.

Validated 100% precision on two grep-confirmed repos: jasontaylordev/
CleanArchitecture (small, 9 edges, inline + param forms) and dotnet/eShop
(medium, 9 edges, 0 false positives, variable-passed + IdentifiedCommand +
the CancelOrderCommand DTO-collision correctly avoided); 0 on the
Newtonsoft.Json control. Node-stable (pure edge synth). Suite 1619 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-06-20 22:34:24 -05:00
co-authored by Claude Opus 4.8
parent 8591ea5993
commit d1381e11f6
4 changed files with 249 additions and 2 deletions
+1
View File
@@ -17,6 +17,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `codegraph_explore` now connects React data-fetching flows built on **RTK Query** (Redux Toolkit's `createApi`). An endpoint defined inside `createApi({ endpoints })` and the `useGetXQuery` / `useUpdateYMutation` hook it generates were both invisible to analysis — so "what does this component fetch?" or "where does `useGetThingQuery` get its data?" dead-ended, because the hook, the endpoint, and the component had nothing linking them. CodeGraph now indexes each endpoint and each generated hook as real symbols and wires the path `component → useGetXQuery → getX → queryFn`, so the flow resolves in one explore call instead of reading the API slice by hand. Both the arrow (`endpoints: build => ({ … })`) and method (`endpoints(builder) { return { … } }`) styles are recognized, along with the `useLazyGetXQuery` variant; hand-written hooks of a similar name are left untouched.
- `codegraph_explore` now follows **Celery** task dispatch in Python. A `send_email.delay(...)` or `send_email.apply_async(...)` call now links to the `@shared_task` / `@app.task` function it runs — typically defined in a different module (`tasks.py`) from where it's triggered (a view or service) — so "what actually happens when this is dispatched?" traces from the call site straight into the task body instead of stopping at the `.delay()` line. Both decorator dialects are recognized (bare `@shared_task` and the arg'd `@app.task(bind=True, …)` form), including the module-qualified `tasks.invalidate_cache.apply_async()` call style. It stays precise: a `.delay()` on something that isn't a Celery task is never mislinked, so a project that doesn't use Celery is unaffected.
- `codegraph_explore` now follows **Spring application events** in Java. A `publishEvent(new OrderShippedEvent(...))` call now links to every `@EventListener` that handles that event — usually in a different class — so "what reacts when this is published?" traces from the publisher straight into each listener method instead of dead-ending at `publishEvent(...)`. The link is by event type, and all the common listener styles are recognized: a `@EventListener` typed on its parameter, the `@EventListener(SomeEvent.class)` form, `@TransactionalEventListener`, and the older `implements ApplicationListener<SomeEvent>`. One event fans out to all its listeners, and a plain Spring app with no event bus is unaffected.
- `codegraph_explore` now follows **MediatR** request and notification dispatch in C#/.NET. A `_mediator.Send(command)` or `_mediator.Publish(notification)` call now links to the `Handle` method of the matching `IRequestHandler<>` / `INotificationHandler<>` — usually in a different file in a Clean Architecture layout — so "what handles this command?" traces from the controller straight into the handler instead of stopping at the mediator call. The sent type is recognized whether it's constructed inline (`Send(new GetFooQuery())`), built into a local first (`var cmd = new …; Send(cmd)`), or passed in as a parameter, and it's matched by type — so a `MessagingCenter.Send(...)` or a same-named DTO that isn't a request is never mislinked, and a project without MediatR is unaffected.
- `codegraph_explore` now surfaces the right code in large multi-layer projects. When you ask a backend-flow question in a repo that pairs an API server with a big frontend that mirrors the same domain words — say an `app/` admin UI sitting over an `api/` server — the server-side file that genuinely matches several of your query's terms is no longer pushed out of the results by the larger, more interconnected frontend layer. A file corroborated by two or more distinct query terms is now kept in the answer even when a denser unrelated layer would otherwise crowd it out, so "how does X read items / handle the request" returns the service or handler that does the work instead of a wall of frontend views. Single-layer projects are unaffected; set `CODEGRAPH_RANK_NO_MULTITERM=1` to revert to the previous ranking.
- Impact and blast-radius analysis for TypeScript, JavaScript, Go, Python, Rust, Ruby, C, Java, C#, PHP, Scala, Kotlin, Swift, Dart, and Pascal/Delphi now understands the readers of a constant. When you change a file-scope, package-level, module-level, or class-level constant — a config object, a lookup table, a shared constant — the other symbols in that file that read it now show up as affected, where before they were invisible (impact only followed calls, imports, and inheritance, so a constant's consumers looked like "nothing depends on this"). This makes `codegraph impact`, and the impact trail in `codegraph_explore`/`codegraph_node`, catch the "change this table, break its readers" class of change. It's on by default and adds no nodes to your graph; bundled/minified files and ambiguously-shadowed names are skipped to keep results precise. Set `CODEGRAPH_VALUE_REFS=0` to turn it off.