Summary
Two additions that finish the symbol layer, both using node kinds that already exist in NODE_KINDS — no new kinds, no schema migration:
- A.
parameter nodes for VBA procedure parameters (4,747 in the corpus).
- B. Real table nodes with their fields, parsed from the Access structure export (
ERD/*.md) — 81 tables, 451 fields, and 15 linked tables pointing at 6 external backends.
They are independent. Land them as two PRs.
Half A — parameter nodes
Source of truth
#250's metadata.params. That issue already parses the signature — name, type, ByRef, Optional, isArray, hasDefault — after line-continuation joining, which matters: the corpus has 7,753 line continuations and multi-line signatures are common.
This half is emission only. Do not write a second signature parser. Land #250 first.
Shape
id generateNodeId(filePath, 'parameter', `${procName}.${paramName}`, declLine)
kind 'parameter'
name the parameter name
qualifiedName '<ModuleOrClass>.<Procedure>.<param>'
filePath the .bas / .cls
startLine the procedure's declaration line (parameters have no line of their own)
metadata { position, byRef, optional, isArray, hasDefault, declaredType }
Edges:
| Edge |
Source |
Target |
Kind |
| procedure owns its parameter |
function |
parameter |
contains |
| parameter has a project type |
parameter |
the type's node |
type_of |
type_of fires only when the declared type is a project class, using the existing Dim … As Foo resolution machinery in vba/dims.ts. Primitives (Long, String, Variant, …) emit nothing — PRIMITIVE_TYPES is already the gate.
Budget
|
|
| Procedures |
4,815 |
| …with at least one parameter |
2,561 |
| Total parameters |
4,747 |
| Largest signature |
11 parameters |
So ~4,700 nodes and ~4,700 contains edges, plus type_of only for project-typed parameters.
Exclusions
- Do not add
'parameter' to HIGH_VALUE_NODE_KINDS (src/context/index.ts) — it is the default node filter for context results, and 4,700 parameters would flood every response.
- Do not add it to
CONTAINER_NODE_KINDS (src/mcp/tools.ts).
Half B — table structure from the Access ERD export
The input, and why this is declaration rather than inference
Table references in the graph today are synthetic class placeholders created from SQL text: a name, nothing else. There is no column information anywhere, and inferring columns from SELECT lists would be incomplete by construction.
But there is an authoritative export. 00_GESTION_RIESGOS/ERD/Estructura_Datos.md is a generated structure dump of the backend:
# Estructura de Datos: Gestion_Riesgos_Datos.accdb
## Tabla: TbAnexos
| Campo | Tipo | Longitud |
| :--- | :--- | :--- |
| IDAnexo | 4 | 4 |
| Titulo | 10 | 255 |
## Tabla: TbAplicaciones
> (LINKED) **Tabla Vinculada**
> *Origen:* TbAplicaciones
> *Conexión:* MS Access;PWD=***;DATABASE=\\datoste\...\0Lanzadera\Lanzadera_Datos.accdb
| Campo | Tipo | Longitud |
...
Contents of that one file: 81 tables, 451 fields, 15 linked tables resolving to 6 distinct external backends — Lanzadera, EXPEDIENTES, AGEDO, AGEDYS, No Conformidades and the project's own datos file. That is the cross-application data topology of this estate, and none of it is in the graph.
Only 1 of the 3 corpus projects has this file. The extractor must treat it as optional and degrade silently when absent — no warning, no error, no empty node.
Shape
New extractor src/extraction/access-erd-extractor.ts, routed on a path match for ERD/*.md plus a content-shape gate (# Estructura de Datos: on the first non-empty line). Gate on both, exactly as the .sql route gates on a sibling queries.json — an ordinary ERD folder in a non-Access repo must never be picked up.
| Emitted |
Kind |
Notes |
| One node per table |
class |
Same kind the SQL placeholders already use — see the convergence note below |
| One node per field |
type_member |
contains from its table; metadata: { accessType, length, position } |
| Linked-table origin |
references edge |
synthesizedBy: 'vba-linked-table', metadata: { originTable, backendPath } |
The convergence problem — read this before writing code
vba-sql-table (in vba/sql-wrapper.ts) and sql-query-table (in sql-query-extractor.ts) already create class placeholder nodes for table names, keyed on generateNodeId(filePath, 'class', tableName, 0) — keyed on the referencing file. The ERD extractor keys on the ERD file.
Left alone, TbAnexos from the ERD and TbAnexos from a SELECT in a .cls are two unrelated nodes, and the whole feature buys nothing: fields on one node, references on the other.
Decided strategy: resolve it in src/resolution/, reusing the mechanism that already repoints VBA call stubs (resolveVbaCallStubs in src/resolution/index.ts).
- The ERD extractor emits table nodes exactly like any other extractor — per-file ids, no special id scheme. Do not invent a global key; that would break the per-file node identity every other extractor relies on.
- A post-extraction pass promotes the ERD table node to canonical when one exists for that name, and repoints the SQL placeholders' references onto it, stamping the outcome the way
repointDecision already does for call stubs.
- When two ERD files declare the same table name (two backends, same table), that is
declined-ambiguous — repoint nothing and leave both. Do not guess which backend a SELECT meant.
- When no ERD exists, the pass is a no-op and today's placeholder behaviour is unchanged.
This reuses a proven mechanism, keeps extraction ignorant of resolution, and degrades correctly on the two corpus projects that ship no ERD.
Budget
81 table nodes, 451 field nodes, ~530 contains edges, 15 references edges — on the one project that ships an ERD. Negligible.
Secondary win
The linked-table origins give the cross-backend edge that the SQL-scanner issue (#256) calls out as missing for the IN "other.accdb" clause. The two should agree on how an external backend file is represented; whichever lands second adopts the first one's shape.
Acceptance criteria
Half A
Half B
Context
Task T16 of docs/vba-node-discovery-plan.md. Half A depends on #250. Half B is independent of everything else in the roadmap and coordinates with #256 on how an external backend file is represented.
Summary
Two additions that finish the symbol layer, both using node kinds that already exist in
NODE_KINDS— no new kinds, no schema migration:parameternodes for VBA procedure parameters (4,747 in the corpus).ERD/*.md) — 81 tables, 451 fields, and 15 linked tables pointing at 6 external backends.They are independent. Land them as two PRs.
Half A —
parameternodesSource of truth
#250's
metadata.params. That issue already parses the signature — name, type,ByRef,Optional,isArray,hasDefault— after line-continuation joining, which matters: the corpus has 7,753 line continuations and multi-line signatures are common.This half is emission only. Do not write a second signature parser. Land #250 first.
Shape
Edges:
functionparametercontainsparametertype_oftype_offires only when the declared type is a project class, using the existingDim … As Fooresolution machinery invba/dims.ts. Primitives (Long,String,Variant, …) emit nothing —PRIMITIVE_TYPESis already the gate.Budget
So ~4,700 nodes and ~4,700
containsedges, plustype_ofonly for project-typed parameters.Exclusions
'parameter'toHIGH_VALUE_NODE_KINDS(src/context/index.ts) — it is the default node filter for context results, and 4,700 parameters would flood every response.CONTAINER_NODE_KINDS(src/mcp/tools.ts).Half B — table structure from the Access ERD export
The input, and why this is declaration rather than inference
Table references in the graph today are synthetic
classplaceholders created from SQL text: a name, nothing else. There is no column information anywhere, and inferring columns fromSELECTlists would be incomplete by construction.But there is an authoritative export.
00_GESTION_RIESGOS/ERD/Estructura_Datos.mdis a generated structure dump of the backend:Contents of that one file: 81 tables, 451 fields, 15 linked tables resolving to 6 distinct external backends — Lanzadera, EXPEDIENTES, AGEDO, AGEDYS, No Conformidades and the project's own datos file. That is the cross-application data topology of this estate, and none of it is in the graph.
Only 1 of the 3 corpus projects has this file. The extractor must treat it as optional and degrade silently when absent — no warning, no error, no empty node.
Shape
New extractor
src/extraction/access-erd-extractor.ts, routed on a path match forERD/*.mdplus a content-shape gate (# Estructura de Datos:on the first non-empty line). Gate on both, exactly as the.sqlroute gates on a siblingqueries.json— an ordinaryERDfolder in a non-Access repo must never be picked up.classtype_membercontainsfrom its table;metadata: { accessType, length, position }referencesedgesynthesizedBy: 'vba-linked-table',metadata: { originTable, backendPath }The convergence problem — read this before writing code
vba-sql-table(invba/sql-wrapper.ts) andsql-query-table(insql-query-extractor.ts) already createclassplaceholder nodes for table names, keyed ongenerateNodeId(filePath, 'class', tableName, 0)— keyed on the referencing file. The ERD extractor keys on the ERD file.Left alone,
TbAnexosfrom the ERD andTbAnexosfrom aSELECTin a.clsare two unrelated nodes, and the whole feature buys nothing: fields on one node, references on the other.Decided strategy: resolve it in
src/resolution/, reusing the mechanism that already repoints VBA call stubs (resolveVbaCallStubsinsrc/resolution/index.ts).repointDecisionalready does for call stubs.declined-ambiguous— repoint nothing and leave both. Do not guess which backend aSELECTmeant.This reuses a proven mechanism, keeps extraction ignorant of resolution, and degrades correctly on the two corpus projects that ship no ERD.
Budget
81 table nodes, 451 field nodes, ~530
containsedges, 15referencesedges — on the one project that ships an ERD. Negligible.Secondary win
The linked-table origins give the cross-backend edge that the SQL-scanner issue (#256) calls out as missing for the
IN "other.accdb"clause. The two should agree on how an external backend file is represented; whichever lands second adopts the first one's shape.Acceptance criteria
Half A
parameternode per parameter,qualifiedNamescoped to the procedurecontainsedge from the owningfunctionnodetype_ofedge only for project-typed parameters; primitives emit nothing_line continuation) produces the same nodes as the one-line formParamArrayandOptional … = defaultcarry the right metadata'parameter'absent fromHIGH_VALUE_NODE_KINDSandCONTAINER_NODE_KINDS, asserted by testHalf B
ERD/Estructura_Datos.mdyields 81 table nodes and 451 field nodesreferencesedges carrying their backend pathERD/directory extracts exactly as before — no warning, no empty nodeERD/folder that is not an Access structure export is ignored (content-shape gate)SELECT * FROM TbAnexosin a.clsand the ERD'sTbAnexosresolve to one node that has both the fields and the referenceContext
Task T16 of
docs/vba-node-discovery-plan.md. Half A depends on #250. Half B is independent of everything else in the roadmap and coordinates with #256 on how an external backend file is represented.