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
Eres la IA mantenedora de @aroman22/codegraph-vba. Repo: https://github.com/ardelperal/codegraph-vba. Branch sugerida: fix/stub-resolver-both-flags (sugerida para consolidar con round-4 si querés). Versión actual: 1.6.3.
Contexto del round
Round 5 = ampliación de scope del round-4 (#109): la evidencia reunida por el consumer gestion_riesgos indica que el post-extraction resolver no solo debe cubrir stub=false — también stub=true requiere el mismo tratamiento. 248/248 qualified names distintos extraídos de edges con stub=true YA EXISTEN como nodes.qualified_name en el codebase.
Rounds previos en codegraph-vba (NO repetir huecos cerrados):
Este round (#X): el mismo resolver debe actuar también sobre metadata.stub=true. La causa raíz es la MISMA — el extractor crea edges sintéticos sin intentar lookup contra nodes antes de decidir el flag stub.
Lo que YA funciona (NO tocar)
Schema edges y nodes (round-4 ya los documenta; no los repito).
VBA extractor (src/extraction/vba/calls.ts, call-sweep.ts, controls.ts, declarations.ts, etc.) está intacto y emite los synthetic stubs correctamente.
El maintainer YA distingue entre stubs y edge kinds (los kinds del schema edges cubren calls, contains, raises-event, etc.). No romper ese surface.
Lo que falta en este round
Bug único: post-extraction resolver cubre stub=false pero NO stub=true
Síntoma verificado
Consumer corre esto contra .codegraph-vba/codegraph.db del codebase gestion_riesgos (26 MB):
SELECTCOUNT(*) AS n
FROM (
SELECT DISTINCT
json_extract(metadata, '$.receiverType') ||'.'||
json_extract(metadata, '$.member') AS q
FROM edges
WHERE kind='calls'AND json_extract(metadata, '$.synthesizedBy')='vba-name-resolution'AND json_extract(metadata, '$.stub')=1
) synthetic
WHERE EXISTS (
SELECT1FROM nodes WHERE qualified_name =synthetic.q
);
Devuelve literal: n = 248.
Y el total de qualified names distintos emitidos como stub=true también es 248. Es decir: el 100% de los synthetic-stub calls con stub=true tienen target declarado en nodes — codegraph-vba nunca los enlazó.
Sample adicional sobre los 12 qualified names más frecuentes:
qualified (stub=true)
n occurrences
¿Existe en nodes? (exact match)
VBA.DoEvents
1309
✅ (function:d52a64f1...)
Collection.Add
874
✅
DAO.Execute
202
✅
fso.GetFileName
68
✅
DAO.OpenRecordset
51
✅
fso.FileExists
51
✅
ListBox.Column
35
✅
riesgo.SetPropiedad
33
✅
err.Raise
26
✅
ListBox.Selected
25
✅
correo.EnviarCorreo
19
✅ (function:e918589ec8...)
ListBox.AddItem
18
✅
Cada uno tiene nodes.qualified_name = <qualified> y un nodo function: o class: que el codegraph-vba no logró enlazar.
Consecuencia práctica
El consumer gestion_riesgos estuvo a punto de shippear tools/lint-missing-callees.mjs como un workaround consumer-side para detectar missing callees. Eso producía findings=0 porque TODO está declarado — solo que el codegraph no lo sabía reportar. El PR se cerró (PR #117 rolled-back) reconociendo que el fix correcto pertenece al extractor, no al consumer.
Tras este round-5 fix, consumer-side lints que filtren por metadata.stub=true empezarán a tener señal real: dejarán de reportar findings=0 (false negative) y empezarán a reportar los typos reales del dominio que quedan.
Diagnóstico preliminar (NO verificado internamente — confirmar/descartar)
El extractor produce el synthetic stub al final de scanCallSites y/o emitQualifiedStatementCallEdge cuando findFunctionNodeByName no matchea. Inicialmente marca metadata.stub=true como "no sé si existe".
Hipótesis: El parser actual NUNCA consulta nodes para decidir si flag con stub=true o stub=false — es un solo camino "stub=true hasta que alguien demuestre lo contrario". Round-4 (#109) introdujo el camino stub=false para casos donde se confirma la existencia pre-extracción, pero falta el paso post-extracción que mira nodes.qualified_name y repointa cualquier stub (true o false) cuyo target exista.
Forma esperada tras el fix
El acceptance criteria ampliado:
-- post-fix esperado:SELECT
(SELECTCOUNT(*) FROM (
SELECT DISTINCT
json_extract(metadata, '$.receiverType') ||'.'||
json_extract(metadata, '$.member') AS q
FROM edges
WHERE kind='calls'AND json_extract(metadata, '$.synthesizedBy')='vba-name-resolution'
) synthetic
WHERE EXISTS (
SELECT1FROM nodes WHERE qualified_name =synthetic.q
)
) AS declared_targets,
(SELECTCOUNT(*) FROM edges
WHERE kind='calls'AND json_extract(metadata, '$.synthesizedBy')='vba-name-resolution'AND json_extract(metadata, '$.stub')=1
) AS stub_true_count;
-- Antes: declared_targets=248, stub_true_count=3201-- Después esperado: stub_true_count <500 (solo casos genuinos missing) y todas las-- edges que apuntan a declared_targets están repointadas (sus edges.target = nodes.id real,-- no = synthetic function hash).
Adicional: la distinción semántica stub=true (genuino missing) vs stub=false (declared but parser couldn't link pre-extraction) deja de ser útil porque tras el repointing todos los edges declaran correctamente. Mantener el campo stub=true SOLO para targets NO encontrados en nodes.
Tests RED sugeridos
Extender los tests del round-4 (__tests__/extraction-vba-stub-resolver.test.ts):
import{describe,it,expect}from'vitest';import{openTestDb}from'./helpers/db';import{extractFromSource}from'../src/extraction/vba-extractor';describe('extraction-vba: post-extraction stub resolver covers BOTH flags (round-5)',()=>{// Mismos tests que round-4 — el comportamiento esperado es IDENTICO// independientemente del flag stub inicial.it('repoints stub:true cross-module call when target exists elsewhere',async()=>{constsrcA=`Public Sub Helper()\n Debug.Print "x"\nEnd Sub\n`;// Call site en modulo B sin import: parser emite synthetic stub=true.// Tras resolver: la edge debe repointar al function node real de A.constsrcB=`Public Sub Caller()\n Helper\nEnd Sub\n`;const{ db }=awaitopenTestDb();awaitextractFromSource(db,'src/modules/A.bas',srcA);awaitextractFromSource(db,'src/modules/B.bas',srcB);constedges=db.prepare(` SELECT e.target, e.metadata FROM edges e WHERE e.source IN (SELECT id FROM nodes WHERE name='Caller') AND e.kind='calls' `).all();expect(edges.length).toBe(1);consthelper=db.prepare(`SELECT id FROM nodes WHERE name='Helper' AND kind='function'`).get();expect(helper).toBeDefined();expect(edges[0].target).toBe(helper.id);// repointed, NO synthetic stubexpect(JSON.parse(edges[0].metadata).stub??null).not.toBe(true);});it('keeps stub:true ONLY when target truly does NOT exist anywhere',async()=>{constsrc=`Public Sub Caller()\n DoesNotExistSub(42)\nEnd Sub\n`;const{ db }=awaitopenTestDb();awaitextractFromSource(db,'src/modules/Z.bas',src);constedges=db.prepare(` SELECT metadata FROM edges WHERE source IN (SELECT id FROM nodes WHERE name='Caller') AND kind='calls' `).all();expect(edges.length).toBe(1);expect(JSON.parse(edges[0].metadata).stub).toBe(true);});it('NO regresion: 167 fixture VBA tests siguen pasando',async()=>{const{ runFixture }=awaitimport('./fixtures/vba/run');constresult=awaitrunFixture();expect(result.passed).toBeGreaterThan(150);});it('integration smoke: declared_targets vs stub_true debe colapsar tras el fix',async()=>{// Smoke benchmark contra el codebase del consumer (si se prueba contra el// .codegraph-vba/codegraph.db real del consumer gestion_riesgos).// Esperado post-fix: COUNT(declared_targets) == stub_true_count (ambos colapsan// cerca de 0 en codebase limpio) y los repointed edges apuntan a nodes.id reales.const{ DatabaseSync }=awaitimport('node:sqlite');// (omitido: path real al .db). Solo el shape del query importa para tests.});});
Riesgo
Si el maintainer quiere MANTENER el flag stub=true para false semantics (e.g. "realmente no estoy seguro"), entonces el fix debe repointar edge pero NO modificar el flag. Decisión del maintainer — la opción que recomiendo es: para targets que existen, repoint edge + flip stub a false; para targets que no existen, mantener stub=true. Documentar la elección en CHANGELOG.
Round-4 ya cubre el caso stub=false pre-extracción. Este round-5 cubre el caso stub=true (y por extensión unifica el comportamiento).
Performance: hoy el post-extraction resolver no existe (round-4 lo introduce). Este round-5 lo AMPLÍA para tratar ambos flags. Diff pequeño. Sin regresión.
Disciplina
TDD: los 4 tests RED antes de tocar el resolver.
Conventional commits con scope vba-resolver (consistente con round-4).
NO modificar la lógica de CANCELACIÓN del flag — solo el repointing.
Mantener backwards compat: unresolved_refs de round-3 + edges.stub=false de round-4 + edges.stub=true de este round-5 → todos conviven.
Mantener el campo metadata JSON — solo modificamos el campo stub (boolean) y target (ref a nodes.id) cuando corresponda.
NO romper vitest run vba (167 verde en 1.6.3).
Acceptance output
PR con los 4 tests RED → GREEN (los 2-4 de round-4 más este nuevo declared_targets vs stub_true count).
CHANGELOG.md: extractor(vba): post-extraction resolver now repoints BOTH stub:true AND stub=false synthetic call edges to existing nodes (round-5 amplification of #109) — combine con el changelog de round-4 si se fusionan los PRs.
Bump a 1.7.0 (minor — ampliando el surface del resolver).
Smoke benchmark documentado: el query declared_targets == 0 post-fix contra codebase consumer gestion_riesgos debería pasar.
server-instructions.md: aclarar que stub=true ahora significa EXCLUSIVAMENTE "target does not exist in nodes.qualified_name".
Quick start
git clone https://github.com/ardelperal/codegraph-vba
cd codegraph-vba
# Si round-4 (#109) ya está mergeado, basarse en su branch.# Si no, partir de main y aplicar ambos rounds juntos.
git checkout -b fix/stub-resolver-both-flags
pnpm install
pnpm test# baseline 167 verde + nuevos tests RED
pnpm test __tests__/extraction-vba-stub-resolver.test.ts
Test repro contra codebase real (consumer gestion_riesgos):
node --input-type=module -e "import {DatabaseSync} from 'node:sqlite';const db = new DatabaseSync('.codegraph-vba/codegraph.db', {readOnly:true});const r = db.prepare(\` SELECT (SELECT COUNT(DISTINCT json_extract(metadata, '\$.receiverType')||'.'||json_extract(metadata, '\$.member')) FROM edges WHERE kind='calls' AND json_extract(metadata, '\$.synthesizedBy')='vba-name-resolution') AS distinct_targets, (SELECT COUNT(*) FROM edges WHERE kind='calls' AND json_extract(metadata, '\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata, '\$.stub')=1) AS stub_true, (SELECT COUNT(*) FROM ( SELECT DISTINCT json_extract(metadata, '\$.receiverType')||'.'||json_extract(metadata, '\$.member') AS q FROM edges WHERE kind='calls' AND json_extract(metadata, '\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata, '\$.stub')=1 ) s WHERE EXISTS (SELECT 1 FROM nodes WHERE qualified_name = s.q)) AS declared_but_stub_true\`).all();console.log(JSON.stringify(r, null, 2));"# Antes: [{"distinct_targets":248,"stub_true":3201,"declared_but_stub_true":248}]# Después: distinct_targets cerca de 0 AND declared_but_stub_true=0
Reinforcement
Regla cross-consumer: "un consumer que use el codegraph como fuente de missing callees debe poder confiar en metadata.stub=true como target genuinely does not exist. Si el codegraph emite stub=true para un target que SÍ existe, el guardrail falla silenciosamente (false negative). Este round-5 cierra esa puerta."
El consumer gestion_riesgos rolled-back su PR #117 (workaround) precisamente porque este gap no se arregla consumer-side. El fix correcto es en el extractor.
Anti-pattern explícito: no emitir stub=true cuando nodes.qualified_name ya matchea el target.
Output contract esperado
{
"tool": "codegraph-vba",
"mode": "bug-hunt",
"round": "round-5",
"variant": "medium",
"prompt_path": "docs/prompts/prompt-ia-mantenedora-codegraph-vba-round-5-2026-07-13.md",
"prompt_bytes": <size>,"verification_queries": [
"node -e \"const {DatabaseSync}=require('node:sqlite');const db=new DatabaseSync('.codegraph-vba/codegraph.db',{readOnly:true});console.log(JSON.stringify(db.prepare(\\\"SELECT (SELECT COUNT(*) FROM edges WHERE kind='calls' AND json_extract(metadata,'\\\\\\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata,'\\\\\\$.stub')=1) AS stub_true, (SELECT COUNT(*) FROM (SELECT DISTINCT json_extract(metadata,'\\\\\\$.receiverType')||'.'||json_extract(metadata,'\\\\\\$.member') AS q FROM edges WHERE kind='calls' AND json_extract(metadata,'\\\\\\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata,'\\\\\\$.stub')=1) s WHERE EXISTS (SELECT 1 FROM nodes WHERE qualified_name = s.q)) AS declared_but_stub_true\\\").all()))\"",
"pnpm test __tests__/extraction-vba-stub-resolver.test.ts"
],
"cross_session_safe": true,
"amplifies_round": "round-4 / issue #109"
}
Eres la IA mantenedora de
@aroman22/codegraph-vba. Repo:https://github.com/ardelperal/codegraph-vba. Branch sugerida:fix/stub-resolver-both-flags(sugerida para consolidar con round-4 si querés). Versión actual: 1.6.3.Contexto del round
Round 5 = ampliación de scope del round-4 (
#109): la evidencia reunida por el consumergestion_riesgosindica que el post-extraction resolver no solo debe cubrirstub=false— tambiénstub=truerequiere el mismo tratamiento. 248/248 qualified names distintos extraídos de edges constub=trueYA EXISTEN comonodes.qualified_nameen el codebase.Rounds previos en codegraph-vba (NO repetir huecos cerrados):
unresolved_refs.reference_kindpor forma sintáctica — independiente de este round (cubre la tablaunresolved_refs, noedges).edges.kind='calls' AND metadata.synthesizedBy='vba-name-resolution' AND metadata.stub=false. El cuerpo de fix(extractor-vba): post-extraction resolver repoints stub:false synthetic call edges (round-4) #109 propone los tests RED y los acceptance criteria pero acotados al bucketstub=false.metadata.stub=true. La causa raíz es la MISMA — el extractor crea edges sintéticos sin intentar lookup contranodesantes de decidir el flagstub.Lo que YA funciona (NO tocar)
edgesynodes(round-4 ya los documenta; no los repito).src/extraction/vba/calls.ts,call-sweep.ts,controls.ts,declarations.ts, etc.) está intacto y emite los synthetic stubs correctamente.edgescubrencalls,contains,raises-event, etc.). No romper ese surface.Lo que falta en este round
Bug único: post-extraction resolver cubre
stub=falsepero NOstub=trueSíntoma verificado
Consumer corre esto contra
.codegraph-vba/codegraph.dbdel codebasegestion_riesgos(26 MB):Devuelve literal:
n = 248.Y el total de qualified names distintos emitidos como
stub=truetambién es 248. Es decir: el 100% de los synthetic-stub calls constub=truetienen target declarado ennodes— codegraph-vba nunca los enlazó.Sample adicional sobre los 12 qualified names más frecuentes:
qualified(stub=true)VBA.DoEventsfunction:d52a64f1...)Collection.AddDAO.Executefso.GetFileNameDAO.OpenRecordsetfso.FileExistsListBox.Columnriesgo.SetPropiedaderr.RaiseListBox.Selectedcorreo.EnviarCorreofunction:e918589ec8...)ListBox.AddItemCada uno tiene
nodes.qualified_name = <qualified>y un nodofunction:oclass:que el codegraph-vba no logró enlazar.Consecuencia práctica
El consumer
gestion_riesgosestuvo a punto de shippeartools/lint-missing-callees.mjscomo un workaround consumer-side para detectar missing callees. Eso producíafindings=0porque TODO está declarado — solo que el codegraph no lo sabía reportar. El PR se cerró (PR #117 rolled-back) reconociendo que el fix correcto pertenece al extractor, no al consumer.Tras este round-5 fix, consumer-side lints que filtren por
metadata.stub=trueempezarán a tener señal real: dejarán de reportarfindings=0(false negative) y empezarán a reportar los typos reales del dominio que quedan.Diagnóstico preliminar (NO verificado internamente — confirmar/descartar)
El extractor produce el synthetic stub al final de
scanCallSitesy/oemitQualifiedStatementCallEdgecuandofindFunctionNodeByNameno matchea. Inicialmente marcametadata.stub=truecomo "no sé si existe".Hipótesis: El parser actual NUNCA consulta
nodespara decidir si flag constub=trueostub=false— es un solo camino "stub=true hasta que alguien demuestre lo contrario". Round-4 (#109) introdujo el caminostub=falsepara casos donde se confirma la existencia pre-extracción, pero falta el paso post-extracción que miranodes.qualified_namey repointa cualquier stub (true o false) cuyo target exista.Forma esperada tras el fix
El acceptance criteria ampliado:
Adicional: la distinción semántica
stub=true(genuino missing) vsstub=false(declared but parser couldn't link pre-extraction) deja de ser útil porque tras el repointing todos los edges declaran correctamente. Mantener el campostub=trueSOLO para targets NO encontrados ennodes.Tests RED sugeridos
Extender los tests del round-4 (
__tests__/extraction-vba-stub-resolver.test.ts):Riesgo
stub=trueparafalsesemantics (e.g. "realmente no estoy seguro"), entonces el fix debe repointar edge pero NO modificar el flag. Decisión del maintainer — la opción que recomiendo es: para targets que existen, repoint edge + flipstubafalse; para targets que no existen, mantenerstub=true. Documentar la elección en CHANGELOG.stub=falsepre-extracción. Este round-5 cubre el casostub=true(y por extensión unifica el comportamiento).gestion_riesgosya rolled-backeó su PR docs(vba): document repointDecision semantics shipped in v1.7.0 (#115) #117. No habrá conflicto en el árbol consumer. Solo este upstream PR y uno nuevo tests file.Disciplina
vba-resolver(consistente con round-4).unresolved_refsde round-3 +edges.stub=falsede round-4 +edges.stub=truede este round-5 → todos conviven.metadataJSON — solo modificamos el campostub(boolean) ytarget(ref a nodes.id) cuando corresponda.vitest run vba(167 verde en 1.6.3).Acceptance output
declared_targets vs stub_true count).CHANGELOG.md:extractor(vba): post-extraction resolver now repoints BOTH stub:true AND stub=false synthetic call edges to existing nodes (round-5 amplification of #109)— combine con el changelog de round-4 si se fusionan los PRs.declared_targets == 0post-fix contra codebase consumergestion_riesgosdebería pasar.server-instructions.md: aclarar questub=trueahora significa EXCLUSIVAMENTE "target does not exist innodes.qualified_name".Quick start
Test repro contra codebase real (consumer
gestion_riesgos):Reinforcement
Regla cross-consumer: "un consumer que use el codegraph como fuente de missing callees debe poder confiar en
metadata.stub=truecomotarget genuinely does not exist. Si el codegraph emitestub=truepara un target que SÍ existe, el guardrail falla silenciosamente (false negative). Este round-5 cierra esa puerta."El consumer
gestion_riesgosrolled-back su PR #117 (workaround) precisamente porque este gap no se arregla consumer-side. El fix correcto es en el extractor.Anti-pattern explícito: no emitir
stub=truecuandonodes.qualified_nameya matchea el target.Output contract esperado
{ "tool": "codegraph-vba", "mode": "bug-hunt", "round": "round-5", "variant": "medium", "prompt_path": "docs/prompts/prompt-ia-mantenedora-codegraph-vba-round-5-2026-07-13.md", "prompt_bytes": <size>, "verification_queries": [ "node -e \"const {DatabaseSync}=require('node:sqlite');const db=new DatabaseSync('.codegraph-vba/codegraph.db',{readOnly:true});console.log(JSON.stringify(db.prepare(\\\"SELECT (SELECT COUNT(*) FROM edges WHERE kind='calls' AND json_extract(metadata,'\\\\\\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata,'\\\\\\$.stub')=1) AS stub_true, (SELECT COUNT(*) FROM (SELECT DISTINCT json_extract(metadata,'\\\\\\$.receiverType')||'.'||json_extract(metadata,'\\\\\\$.member') AS q FROM edges WHERE kind='calls' AND json_extract(metadata,'\\\\\\$.synthesizedBy')='vba-name-resolution' AND json_extract(metadata,'\\\\\\$.stub')=1) s WHERE EXISTS (SELECT 1 FROM nodes WHERE qualified_name = s.q)) AS declared_but_stub_true\\\").all()))\"", "pnpm test __tests__/extraction-vba-stub-resolver.test.ts" ], "cross_session_safe": true, "amplifies_round": "round-4 / issue #109" }