From 3ec546be628848b5baf2c3e0df5fdef1e0bf5dd4 Mon Sep 17 00:00:00 2001 From: Garrett Griffin-Morales Date: Sun, 26 Apr 2026 19:47:34 -0400 Subject: [PATCH] feat(graph): add staticGated edge property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an optional `staticGated?: boolean` field to `GraphRelationship` that flags edges originating in code branches known at index time to be unreachable in production — e.g. `if (CONST_FALSE)` blocks where the condition reduces to a comptime-known `false`. Schema + persistence wiring: - `gitnexus-shared/src/graph/types.ts` — additive optional field on `GraphRelationship`; absent edges read identically to live ones. - `gitnexus/src/core/lbug/schema.ts` — `staticGated BOOLEAN` column on the `CodeRelation` REL table. - `gitnexus/src/core/lbug/csv-generator.ts` — appends a `staticGated` column (0/1) to the `relations.csv` written for bulk COPY ingest. - `gitnexus/src/core/lbug/lbug-adapter.ts` — fallback per-row `MATCH ... CREATE` insert reads the optional column and threads it into the relationship properties. No language has populated this field yet — the Zig hookup lands in the next commit. Existing DBs need a re-index for the new column to appear; existing readers are unchanged because the field is optional and absent on every other language's edges. Co-Authored-By: Claude Opus 4.7 (1M context) (cherry picked from commit c8cd5efe27a77a5d1b3f05e3f4e89069b5c6b19e) --- gitnexus-shared/src/graph/types.ts | 14 ++++++++++++++ gitnexus/src/core/lbug/csv-generator.ts | 6 +++++- gitnexus/src/core/lbug/lbug-adapter.ts | 12 +++++++++--- gitnexus/src/core/lbug/schema.ts | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/gitnexus-shared/src/graph/types.ts b/gitnexus-shared/src/graph/types.ts index 8b46113df..60466b3e3 100644 --- a/gitnexus-shared/src/graph/types.ts +++ b/gitnexus-shared/src/graph/types.ts @@ -306,4 +306,18 @@ export interface GraphRelationship { readonly weight: number; readonly note?: string; }[]; + /** + * When `true`, the edge originates inside a code branch known at + * index time to be unreachable in production — e.g. a Zig + * `if (CONST_FALSE)` block where the condition reduces to a + * comptime-known `false`. Consumers of impact / context queries + * can filter these edges out to suppress paper-tiger callers + * caused by feature-flag-gated dead code paths. + * + * Optional and additive: edges from languages that don't compute + * static gating leave this `undefined`, which existing readers + * treat identically to "live". Currently set only by the Zig + * call-extractor. + */ + staticGated?: boolean; } diff --git a/gitnexus/src/core/lbug/csv-generator.ts b/gitnexus/src/core/lbug/csv-generator.ts index 83b630207..76d1b56be 100644 --- a/gitnexus/src/core/lbug/csv-generator.ts +++ b/gitnexus/src/core/lbug/csv-generator.ts @@ -396,7 +396,7 @@ class BufferedCSVWriter { /** Canonical relationship CSV header — shared by the emit pass and the * `splitRelCsvByLabelPair` differential oracle. */ -export const REL_CSV_HEADER = 'from,to,type,confidence,reason,step'; +export const REL_CSV_HEADER = 'from,to,type,confidence,reason,step,staticGated'; /** Build the escaped CSV row (no trailing newline) for one relationship. * Single source of the relationship row bytes — used by the emit pass and by @@ -409,6 +409,10 @@ export const buildRelRow = (rel: GraphRelationship): string => escapeCSVNumber(rel.confidence, 1.0), escapeCSVField(rel.reason), escapeCSVNumber(rel.step, 0), + // `staticGated` persists as 0/1 (LadybugDB BOOLEAN COPY accepts both). + // Every edge that does not carry the flag writes 0, so readers treat + // absence and `false` identically. + rel.staticGated === true ? '1' : '0', ].join(','); /** Canonical BasicBlock node CSV header — taint/PDG substrate (issue #2080). diff --git a/gitnexus/src/core/lbug/lbug-adapter.ts b/gitnexus/src/core/lbug/lbug-adapter.ts index 40ccdb8e0..97dda7673 100644 --- a/gitnexus/src/core/lbug/lbug-adapter.ts +++ b/gitnexus/src/core/lbug/lbug-adapter.ts @@ -1466,22 +1466,28 @@ export const fallbackRelationshipInserts = async ( for (let i = 1; i < validRelLines.length; i++) { const line = validRelLines[i]; try { - const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)/); + // CSV layout: from,to,type,confidence,reason,step[,staticGated] + // The trailing `staticGated` column (0/1) is optional so we remain + // tolerant of legacy CSVs written before the column existed. + const match = line.match( + /"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)",([0-9-]+)(?:,([01]))?/, + ); if (!match) continue; - const [, fromId, toId, relType, confidenceStr, reason, stepStr] = match; + const [, fromId, toId, relType, confidenceStr, reason, stepStr, gatedStr] = match; const fromLabel = getNodeLabel(fromId); const toLabel = getNodeLabel(toId); if (!validTables.has(fromLabel) || !validTables.has(toLabel)) continue; const confidence = parseFloat(confidenceStr) || 1.0; const step = parseInt(stepStr) || 0; + const staticGated = gatedStr === '1'; await queryAndDrain( conn, ` MATCH (a:${escapeLabel(fromLabel)} {id: ${formatCypherValue(fromId)} }), (b:${escapeLabel(toLabel)} {id: ${formatCypherValue(toId)} }) - CREATE (a)-[:${REL_TABLE_NAME} {type: ${formatCypherValue(relType)}, confidence: ${confidence}, reason: ${formatCypherValue(reason)}, step: ${step}}]->(b) + CREATE (a)-[:${REL_TABLE_NAME} {type: ${formatCypherValue(relType)}, confidence: ${confidence}, reason: ${formatCypherValue(reason)}, step: ${step}, staticGated: ${staticGated}}]->(b) `, ); } catch { diff --git a/gitnexus/src/core/lbug/schema.ts b/gitnexus/src/core/lbug/schema.ts index 946f729e6..d8c66d906 100644 --- a/gitnexus/src/core/lbug/schema.ts +++ b/gitnexus/src/core/lbug/schema.ts @@ -657,7 +657,8 @@ ${generatedPairDdl()}, type STRING, confidence DOUBLE, reason STRING, - step INT32 + step INT32, + staticGated BOOLEAN )`; // ============================================================================