You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
vba-event-tracer (skill shipped in #33) traces an event end-to-end at query time by walking the graph: declaration site → raises-event edges → subscribes-event edges → handler Subs named by the WithEvents variable convention (<varName>_<EventName>). Every codegraph_explore call repeats this work because the GRAPH doesn't pre-compute the connection.
WithEvents m_X As ClassName → references + subscribes-event edges (subscriber → class, with metadata.variableName: "m_X") — already emitted
<varName>_<EventName> Sub → exists as a function node when declared
What's missing: a direct edge from the RaiseEvent site to the handler Sub.
Goal
Materialize one event-handler edge (reusing the existing kind) from every RaiseEvent <EventName> site to its m_X_<EventName> handler Sub, when resolvable by the same WithEvents naming convention that vba-event-tracer uses at query time. Edge provenance: heuristic, metadata.synthesizedBy: 'vba-event-handler'.
Why now
The graph data exists, the trace is deterministic, and pre-computing it turns a 4-step query (skill) into a 1-step codegraph_explore call — measurable token + tool-call savings on every event-trace request.
Same structural pattern as the existing callback-synthesizer.ts Phase 1 (field-backed observer): registrar + dispatcher paired by field convention. The pair here is the WithEvents field convention rather than property name.
Two-phase post-extraction pass (mirrors resolveVbaCallStubs in src/resolution/index.ts):
Phase 1 — collect the pairs (per file):
For every WithEvents m_X As ClassName in file F:
Resolve ClassName to the real class node (or skip if shadow)
For every Public Event <EventName> in ClassName: locate the handler Sub in F named m_X_<EventName>
If found, register a pending pair (eventNode, handlerNode, variableName: "m_X")
Phase 2 — connect at RaiseEvent sites (whole project, after extraction):
For every raises-event edge in the DB:
Locate the event node, find its declaring class
Look up the handler Sub from the pairs registered for that class
Emit an event-handler edge from the raiser Sub to the handler Sub
Stamp metadata.synthesizedBy: 'vba-event-handler' and metadata.eventName
Failure modes (silent, no error):
WithEvents variable not found in any subscriber (the subscribes-event graph edge is the source of truth — Phase 1 reads it, not source text)
Handler Sub m_X_<EventName> not declared (typical for unimplemented events; vba-event-tracer would warn NO_HANDLER — the synthesis just emits no edge)
Event declared in a class that has no WithEvents (skip — there's no one listening)
Acceptance criteria
On a fixture with: 1 class declaring 2 events, 1 form with WithEvents+handlers for both events, 1 raise site per event → 2 event-handler edges materialized
No new edge when the handler Sub m_X_<EventName> is not declared
No new edge when the WithEvents variable name doesn't match any Sub in the same file
codegraph_explore query for the event returns the handler Sub in ONE call (no skill walk needed) — verified by hand on a real Dysflow-exported project
vba-event-tracer skill still works (backward compatible — the skill walks the graph, the new edge is a faster path, not the only path)
No new false positives on the FORMS-* real fixture suite (__tests__/extraction-vba-form.test.ts, __tests__/extraction-vba-control-modeling.test.ts)
New tests in __tests__/extraction-vba-event-synth.test.ts
Out of scope
Cyclic event chains (ModuleA.EventA → ModuleB.EventB → ModuleA.EventA) — vba-event-tracer already handles cycle_detected at query time; the synthesis just emits the FIRST hop, the query layer decides whether to follow further
Set x = New ClassName late-instantiation subscriptions (these don't produce a subscribes-event edge today; tracked separately)
Modifying vba-event-tracer itself (the skill stays; the new edge is an optimization, not a replacement)
Problem
vba-event-tracer(skill shipped in #33) traces an event end-to-end at query time by walking the graph: declaration site →raises-eventedges →subscribes-eventedges → handler Subs named by the WithEvents variable convention (<varName>_<EventName>). Everycodegraph_explorecall repeats this work because the GRAPH doesn't pre-compute the connection.The graph has all the ingredients:
RaiseEvent Foo→raises-eventedge (caller → event node) — already emitted byscanRaiseEventsWithEvents m_X As ClassName→references+subscribes-eventedges (subscriber → class, withmetadata.variableName: "m_X") — already emitted<varName>_<EventName>Sub → exists as afunctionnode when declaredWhat's missing: a direct edge from the
RaiseEventsite to the handler Sub.Goal
Materialize one
event-handleredge (reusing the existing kind) from everyRaiseEvent <EventName>site to itsm_X_<EventName>handler Sub, when resolvable by the same WithEvents naming convention thatvba-event-traceruses at query time. Edge provenance:heuristic,metadata.synthesizedBy: 'vba-event-handler'.Why now
codegraph_explorecall — measurable token + tool-call savings on every event-trace request.callback-synthesizer.tsPhase 1 (field-backed observer): registrar + dispatcher paired by field convention. The pair here is theWithEventsfield convention rather than property name.Me.<Control>references; the event wiring is the last big gap in the form code-behind story.Approach
Two-phase post-extraction pass (mirrors
resolveVbaCallStubsinsrc/resolution/index.ts):Phase 1 — collect the pairs (per file):
For every
WithEvents m_X As ClassNamein file F:ClassNameto the real class node (or skip if shadow)Public Event <EventName>in ClassName: locate the handler Sub in F namedm_X_<EventName>(eventNode, handlerNode, variableName: "m_X")Phase 2 — connect at RaiseEvent sites (whole project, after extraction):
For every
raises-eventedge in the DB:event-handleredge from the raiser Sub to the handler Submetadata.synthesizedBy: 'vba-event-handler'andmetadata.eventNameFailure modes (silent, no error):
subscribes-eventgraph edge is the source of truth — Phase 1 reads it, not source text)m_X_<EventName>not declared (typical for unimplemented events;vba-event-tracerwould warnNO_HANDLER— the synthesis just emits no edge)WithEvents(skip — there's no one listening)Acceptance criteria
WithEvents+handlers for both events, 1 raise site per event → 2event-handleredges materializedm_X_<EventName>is not declaredWithEventsvariable name doesn't match any Sub in the same filecodegraph_explorequery for the event returns the handler Sub in ONE call (no skill walk needed) — verified by hand on a real Dysflow-exported projectvba-event-tracerskill still works (backward compatible — the skill walks the graph, the new edge is a faster path, not the only path)__tests__/extraction-vba-form.test.ts,__tests__/extraction-vba-control-modeling.test.ts)__tests__/extraction-vba-event-synth.test.tsOut of scope
ModuleA.EventA→ModuleB.EventB→ModuleA.EventA) —vba-event-traceralready handlescycle_detectedat query time; the synthesis just emits the FIRST hop, the query layer decides whether to follow furtherSet x = New ClassNamelate-instantiation subscriptions (these don't produce asubscribes-eventedge today; tracked separately)vba-event-traceritself (the skill stays; the new edge is an optimization, not a replacement)Related
vba-event-tracerskill (doesn't replace it, just makes the common case a single explore call)resolveVbaCallStubslifecycle atsrc/index.ts:535src/resolution/callback-synthesizer.tsPhase 1 (field-backed observer)