gitnexus 1.6.10-rc.67 → 1.6.10-rc.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -110,6 +110,24 @@ export function emitJavaScopeCaptures(sourceText, filePath, cachedTree) {
110
110
  grouped['@reference.receiver'] !== undefined) {
111
111
  continue;
112
112
  }
113
+ // Normalize a `new`-expression receiver to its constructed type's simple
114
+ // name: `new Local().inner()` binds the WHOLE `object_creation_expression`
115
+ // as `@reference.receiver`, so its raw text is `"new Local()"` — a string
116
+ // that can never match a scope binding, so the compound-receiver resolver
117
+ // silently falls through to name-only fallback resolution and picks the
118
+ // wrong same-named method on a collision (#2564). Rewriting the text to
119
+ // just `Local` lets Case 2 (class-name / static receiver) in
120
+ // receiver-bound-calls.ts resolve it via its normal MRO walk. Mirrors the
121
+ // established `normalizePhpReceiver` precedent (php/captures.ts) — a
122
+ // language-local capture rewrite, no shared-pipeline change.
123
+ if (grouped['@reference.receiver'] !== undefined) {
124
+ const receiverNode = nodeIfType(nodeMap['@reference.receiver'], 'object_creation_expression');
125
+ const typeNode = receiverNode?.childForFieldName('type');
126
+ const simpleName = typeNode ? javaBaseSimpleNameOf(typeNode) : undefined;
127
+ if (simpleName !== undefined) {
128
+ grouped['@reference.receiver'] = syntheticCapture('@reference.receiver', receiverNode, simpleName);
129
+ }
130
+ }
113
131
  // Filter read.member when it's a child of method_invocation or assignment.
114
132
  // `@reference.read.member` is captured directly on the `field_access` node.
115
133
  if (grouped['@reference.read.member'] !== undefined) {
@@ -1,7 +1,7 @@
1
1
  export declare const TYPESCRIPT_QUERIES: string;
2
2
  export declare const JAVASCRIPT_QUERIES: string;
3
3
  export declare const PYTHON_QUERIES = "\n(class_definition\n name: (identifier) @name) @definition.class\n\n(function_definition\n name: (identifier) @name) @definition.function\n\n(import_statement\n name: (dotted_name) @import.source) @import\n\n; import numpy as np \u2192 aliased_import captures the module name so the\n; import path is resolved and named-binding extraction stores \"np\" \u2192 \"numpy\".\n(import_statement\n name: (aliased_import\n name: (dotted_name) @import.source)) @import\n\n(import_from_statement\n module_name: (dotted_name) @import.source) @import\n\n(import_from_statement\n module_name: (relative_import) @import.source) @import\n\n(call\n function: (identifier) @call.name) @call\n\n(call\n function: (attribute\n attribute: (identifier) @call.name)) @call\n\n; Class attribute type annotations \u2014 PEP 526: address: Address or address: Address = Address()\n; Both bare annotations (address: Address) and annotated assignments (name: str = \"test\")\n; are parsed as (assignment left: ... type: ...) in tree-sitter-python.\n(expression_statement\n (assignment\n left: (identifier) @name\n type: (type)) @definition.property)\n\n; Plain variable assignments without type annotation: x = 5, MAX_SIZE = 100\n; Overlap with @definition.property (typed) is handled by parse-worker dedup.\n(expression_statement\n (assignment\n left: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Write access: obj.field += value (compound assignment)\n(augmented_assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Python HTTP clients: requests.get('/path'), httpx.post('/path'), session.get('/path')\n(call\n function: (attribute\n attribute: (identifier) @http_client.method)\n arguments: (argument_list\n (string (string_content) @http_client.url))) @http_client\n\n; Python decorators: @app.route, @router.get, etc.\n; The first positional argument is captured three ways (#2391): a string literal\n; path via @decorator.arg (quote-free, the fast path); a bare constant name or a\n; plus-concatenation via @decorator.arg_expr (resolved cross-file by the constant\n; resolver). The anchored optional alternation pins to the FIRST arg and stays\n; optional, so no-arg decorators (@app.tool(), etc.) and non-path first args still\n; match.\n(decorator\n (call\n function: (attribute\n object: (identifier) @decorator.receiver\n attribute: (identifier) @decorator.name)\n arguments: (argument_list\n .\n [\n (string (string_content)? @decorator.arg) @decorator.arg_str\n (identifier) @decorator.arg_expr\n (binary_operator) @decorator.arg_expr\n ]?))) @decorator\n";
4
- export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Anonymous class bodies: new Runnable() { ... } \u2014 no @name capture; the\n; class extractor synthesizes the javac-style Worker$N name (#2550)\n(object_creation_expression (class_body)) @definition.class\n\n; Enum constant bodies: enum E { A { ... } } \u2014 javac's other anonymous\n; shape, synthesized as E$N by the same naming authority (#2555)\n(enum_constant body: (class_body)) @definition.class\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
4
+ export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Records, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(record_declaration name: (identifier) @name) @definition.record\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Anonymous class bodies: new Runnable() { ... } \u2014 no @name capture; the\n; class extractor synthesizes the javac-style Worker$N name (#2550)\n(object_creation_expression (class_body)) @definition.class\n\n; Enum constant bodies: enum E { A { ... } } \u2014 javac's other anonymous\n; shape, synthesized as E$N by the same naming authority (#2555)\n(enum_constant body: (class_body)) @definition.class\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
5
5
  export declare const C_QUERIES = "\n; Functions (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n\n; Functions returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n\n; Functions returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n\n; Structs, Unions, Enums, Typedefs\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(union_specifier name: (type_identifier) @name) @definition.union\n(enum_specifier name: (type_identifier) @name) @definition.enum\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n\n; Variable declarations: int x = 5; or int x;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n";
6
6
  export declare const GO_QUERIES = "\n; Functions & Methods\n(function_declaration name: (identifier) @name) @definition.function\n(method_declaration name: (field_identifier) @name) @definition.method\n(method_elem name: (field_identifier) @name) @definition.method\n\n; Types\n(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @definition.struct\n(type_declaration (type_spec name: (type_identifier) @name type: (interface_type))) @definition.interface\n\n; Imports\n(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import\n(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import\n\n; Struct fields \u2014 named field declarations inside struct types\n(field_declaration_list\n (field_declaration\n name: (field_identifier) @name) @definition.property)\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (selector_expression field: (field_identifier) @call.name)) @call\n\n; Const/var declarations\n(const_declaration (const_spec (identifier) @name)) @definition.const\n(var_declaration (var_spec (identifier) @name)) @definition.variable\n(var_declaration (var_spec_list (var_spec (identifier) @name))) @definition.variable\n\n; Short variable declaration: x := 5\n(short_var_declaration left: (expression_list (identifier) @name)) @definition.variable\n\n; Struct literal construction: User{Name: \"Alice\"}\n(composite_literal type: (type_identifier) @call.name) @call\n\n; Write access: obj.field = value\n(assignment_statement\n left: (expression_list\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property))\n right: (_)) @assignment\n\n; Write access: obj.field++ / obj.field--\n(inc_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n(dec_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n";
7
7
  export declare const CPP_QUERIES = "\n; Classes, Structs, Namespaces\n(class_specifier name: (type_identifier) @name) @definition.class\n(class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.class\n; Out-of-line nested definition: class Outer::Inner { ... } / struct Outer::Inner { ... }.\n; Key the node by the full qualified_identifier text so the def materializes a\n; node that matches the HAS_METHOD owner id (also the full qualified text) and\n; stays distinct from a same-tail type in another scope (#1975, #1978).\n(class_specifier name: (qualified_identifier) @name) @definition.class\n(struct_specifier name: (qualified_identifier) @name) @definition.struct\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(struct_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.struct\n(namespace_definition name: (namespace_identifier) @name) @definition.namespace\n(enum_specifier name: (type_identifier) @name) @definition.enum\n\n; Typedefs and unions (common in C-style headers and mixed C/C++ code)\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n(union_specifier name: (type_identifier) @name) @definition.union\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Functions & Methods (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (operator_name) @name))) @definition.method\n\n; Functions/methods returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n\n; Functions/methods returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))))) @definition.method\n\n; Functions/methods returning references (reference_declarator wraps function_declarator)\n(function_definition declarator: (reference_declarator (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (operator_name) @name)))) @definition.method\n\n; Destructors (destructor_name is distinct from identifier in tree-sitter-cpp)\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (destructor_name) @name))) @definition.method\n\n; Function declarations / prototypes (common in headers)\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n\n; Class/struct data member fields (Address address; int count;)\n; Uses field_identifier to exclude method declarations (which use function_declarator)\n(field_declaration\n declarator: (field_identifier) @name) @definition.property\n\n; Pointer member fields (Address* address;)\n(field_declaration\n declarator: (pointer_declarator\n declarator: (field_identifier) @name)) @definition.property\n\n; Reference member fields (Address& address;)\n(field_declaration\n declarator: (reference_declarator\n (field_identifier) @name)) @definition.property\n\n; Inline class method declarations (inside class body, no body: void save();)\n; tree-sitter-cpp uses field_identifier (not identifier) for names inside class bodies\n(field_declaration declarator: (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name)) @definition.method\n\n; Inline class method declarations returning a pointer (User* lookup();)\n(field_declaration declarator: (pointer_declarator declarator: (function_declarator declarator: [(field_identifier) (identifier)] @name))) @definition.method\n\n; Inline class method declarations returning a reference (User& lookup();)\n(field_declaration declarator: (reference_declarator (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method\n\n; Inline class method definitions (inside class body, with body: void Foo() { ... })\n(field_declaration_list\n (function_definition\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name) (destructor_name)] @name)) @definition.method)\n\n; Inline class methods returning a pointer type (User* lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (pointer_declarator\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Inline class methods returning a reference type (User& lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (reference_declarator\n (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Templates\n(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template\n(template_declaration\n (class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments))) @definition.template\n(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call\n(call_expression function: (template_function name: (identifier) @call.name)) @call\n(binary_expression operator: \"+\" @call.name) @call\n(binary_expression operator: \"<<\" @call.name) @call\n\n; Constructor calls: new User()\n(new_expression type: (type_identifier) @call.name) @call\n\n; Variable declarations: int x = 5; or auto x = 5;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n\n; Structured bindings: auto [a, b] = makePair(); (one @name per bound identifier)\n(declaration\n declarator: (init_declarator\n declarator: (structured_binding_declarator\n (identifier) @name))) @definition.variable\n\n; Structured bindings, reference form: auto& [x, y] = tup;\n(declaration\n declarator: (init_declarator\n declarator: (reference_declarator\n (structured_binding_declarator\n (identifier) @name)))) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_expression\n argument: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)\n right: (_)) @assignment\n\n";
@@ -738,10 +738,11 @@ export const PYTHON_QUERIES = `
738
738
  `;
739
739
  // Java queries - works with tree-sitter-java
740
740
  export const JAVA_QUERIES = `
741
- ; Classes, Interfaces, Enums, Annotations
741
+ ; Classes, Interfaces, Enums, Records, Annotations
742
742
  (class_declaration name: (identifier) @name) @definition.class
743
743
  (interface_declaration name: (identifier) @name) @definition.interface
744
744
  (enum_declaration name: (identifier) @name) @definition.enum
745
+ (record_declaration name: (identifier) @name) @definition.record
745
746
  (annotation_type_declaration name: (identifier) @name) @definition.annotation
746
747
 
747
748
  ; Anonymous class bodies: new Runnable() { ... } — no @name capture; the
@@ -519,6 +519,27 @@ export declare const queryFTS: (tableName: string, indexName: string, query: str
519
519
  [key: string]: any;
520
520
  }>>;
521
521
  /**
522
- * Drop an FTS index
522
+ * True for the two benign "nothing to drop" `DROP_FTS_INDEX` failures —
523
+ * both catalog/binder exceptions, LadybugDB's classes for "this name isn't
524
+ * bound to anything right now" (probe-verified end-to-end through
525
+ * `dropFTSIndex`'s real `conn.query()` path against @ladybugdb/core
526
+ * 0.18.x): the named index was never created (`Binder exception: Table <T>
527
+ * doesn't have an index with name <name>.`), or the FTS extension/function
528
+ * isn't registered at all (`Catalog exception: function DROP_FTS_INDEX is
529
+ * not defined...`). A real engine failure — e.g. the `Runtime exception:
530
+ * FTS index '<name>' is inconsistent: ...` class from #2589 — is a
531
+ * DIFFERENT exception class (an execution-time failure, not a catalog/bind
532
+ * lookup miss), so this returns false for it. Anchored to the START of the
533
+ * message (not a bare substring search): every probed LadybugDB error leads
534
+ * with its exception class, and anchoring means a future message that merely
535
+ * mentions "Binder exception" or "Catalog exception" further in in the body
536
+ * of an otherwise-genuine failure can't be misclassified as benign. Pure
537
+ * string logic so it is unit-testable without a native LadybugDB connection.
538
+ */
539
+ export declare const isBenignDropFtsIndexError: (message: string) => boolean;
540
+ /**
541
+ * Drop an FTS index. Tolerates only {@link isBenignDropFtsIndexError} —
542
+ * anything else rethrows instead of being silently masked, which previously
543
+ * let a corrupted index persist across analyze runs undetected.
523
544
  */
524
545
  export declare const dropFTSIndex: (tableName: string, indexName: string) => Promise<void>;
@@ -2576,7 +2576,28 @@ export const queryFTS = async (tableName, indexName, query, limit = 20, conjunct
2576
2576
  }
2577
2577
  };
2578
2578
  /**
2579
- * Drop an FTS index
2579
+ * True for the two benign "nothing to drop" `DROP_FTS_INDEX` failures —
2580
+ * both catalog/binder exceptions, LadybugDB's classes for "this name isn't
2581
+ * bound to anything right now" (probe-verified end-to-end through
2582
+ * `dropFTSIndex`'s real `conn.query()` path against @ladybugdb/core
2583
+ * 0.18.x): the named index was never created (`Binder exception: Table <T>
2584
+ * doesn't have an index with name <name>.`), or the FTS extension/function
2585
+ * isn't registered at all (`Catalog exception: function DROP_FTS_INDEX is
2586
+ * not defined...`). A real engine failure — e.g. the `Runtime exception:
2587
+ * FTS index '<name>' is inconsistent: ...` class from #2589 — is a
2588
+ * DIFFERENT exception class (an execution-time failure, not a catalog/bind
2589
+ * lookup miss), so this returns false for it. Anchored to the START of the
2590
+ * message (not a bare substring search): every probed LadybugDB error leads
2591
+ * with its exception class, and anchoring means a future message that merely
2592
+ * mentions "Binder exception" or "Catalog exception" further in in the body
2593
+ * of an otherwise-genuine failure can't be misclassified as benign. Pure
2594
+ * string logic so it is unit-testable without a native LadybugDB connection.
2595
+ */
2596
+ export const isBenignDropFtsIndexError = (message) => message.startsWith('Binder exception:') || message.startsWith('Catalog exception:');
2597
+ /**
2598
+ * Drop an FTS index. Tolerates only {@link isBenignDropFtsIndexError} —
2599
+ * anything else rethrows instead of being silently masked, which previously
2600
+ * let a corrupted index persist across analyze runs undetected.
2580
2601
  */
2581
2602
  export const dropFTSIndex = async (tableName, indexName) => {
2582
2603
  if (!conn) {
@@ -2585,8 +2606,11 @@ export const dropFTSIndex = async (tableName, indexName) => {
2585
2606
  try {
2586
2607
  await queryAndDrain(conn, `CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
2587
2608
  }
2588
- catch {
2589
- // Index may not exist
2609
+ catch (e) {
2610
+ const msg = e instanceof Error ? e.message : String(e);
2611
+ if (!isBenignDropFtsIndexError(msg)) {
2612
+ throw e;
2613
+ }
2590
2614
  }
2591
2615
  finally {
2592
2616
  ensuredFTSIndexes.delete(ftsIndexKey(tableName, indexName));
@@ -15,7 +15,7 @@ import { resetDegradedParseCounter } from './tree-sitter/safe-parse.js';
15
15
  import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, closeLbugBeforeExit, loadCachedEmbeddings, deleteNodesForFiles, deleteAllCommunitiesAndProcesses, deleteAllInterprocTaintPaths, deleteAllCallSummaries, deleteAllInjects, queryImportersBatch, loadFTSExtension, wipeLbugDbFiles, LbugWipeError, DELETE_FILES_CHUNK_SIZE, } from './lbug/lbug-adapter.js';
16
16
  import { estimateBufferPool, setBufferPoolSizeHint } from './lbug/lbug-config.js';
17
17
  import { escapeCypherString } from './lbug/cypher-escape.js';
18
- import { buildSearchIndexesOrDegrade, createSearchFTSIndexes, initialiseSearchFTSStemmer, verifySearchFTSIndexes, } from './search/fts-indexes.js';
18
+ import { buildSearchIndexesOrDegrade, createSearchFTSIndexes, dropSearchFTSIndexes, initialiseSearchFTSStemmer, verifySearchFTSIndexes, } from './search/fts-indexes.js';
19
19
  import { cjkSegmentationModeMismatch, getSearchFTSCjkSegmentation, initialiseSearchFTSCjkSegmentation, } from './search/cjk-segmentation.js';
20
20
  import { getExtensionCapabilities, resolveAnalyzeInstallPolicy } from './lbug/extension-loader.js';
21
21
  import { diagnoseExtensionLoad } from './lbug/extension-load-error.js';
@@ -1199,7 +1199,20 @@ export async function runFullAnalysis(repoPath, options, callbacks, runnerIdenti
1199
1199
  });
1200
1200
  }
1201
1201
  else {
1202
- // 1a. Remove the write set's existing rows — batched (#2409): one
1202
+ // 1a. Drop every FTS index before touching a single row (#2589).
1203
+ // `deleteNodesForFiles` below DETACH DELETEs rows out of tables
1204
+ // that otherwise still carry the FTS index built at the end of
1205
+ // the PREVIOUS analyze run — Phase 3 doesn't drop+rebuild it
1206
+ // until well after this delete completes. LadybugDB's FTS
1207
+ // extension is not proven to survive DML against an indexed
1208
+ // table (its own docs never demonstrate it), and that ordering
1209
+ // is exactly what produced "FTS index 'file_fts' is
1210
+ // inconsistent: term is missing during delete". Dropping first
1211
+ // removes the hazard outright; Phase 3's createSearchFTSIndexes
1212
+ // rebuilds every index from the final row set regardless, so
1213
+ // this is a no-op on its own drop step there.
1214
+ await dropSearchFTSIndexes();
1215
+ // 1b. Remove the write set's existing rows — batched (#2409): one
1203
1216
  // DETACH DELETE per table per 200-file chunk. The former per-file
1204
1217
  // loop issued a count + delete per table per FILE — ~13k
1205
1218
  // single-row write transactions on a ~700-file write set — which
@@ -24,6 +24,15 @@ export declare function initialiseSearchFTSStemmer(): string;
24
24
  * tests) so validation always applies.
25
25
  */
26
26
  export declare function getSearchFTSStemmer(): string;
27
+ /**
28
+ * Drop every configured FTS index (no-op per index when absent or unloadable
29
+ * — `dropFTSIndex` tolerates both). Callable ahead of any DML that mutates an
30
+ * FTS-indexed table's rows: LadybugDB's FTS extension is not proven to
31
+ * survive a DETACH DELETE against a table that still carries a live index
32
+ * from a prior run (#2589) — dropping first removes that hazard entirely,
33
+ * regardless of whether it also fixed a specific native inconsistency.
34
+ */
35
+ export declare function dropSearchFTSIndexes(): Promise<void>;
27
36
  export declare function createSearchFTSIndexes(options?: CreateSearchFTSIndexesOptions): Promise<void>;
28
37
  export declare function verifySearchFTSIndexes(executeQuery: (cypher: string) => Promise<unknown[]>): Promise<string[]>;
29
38
  export interface BuildSearchIndexesResult {
@@ -103,6 +103,19 @@ export function initialiseSearchFTSStemmer() {
103
103
  export function getSearchFTSStemmer() {
104
104
  return resolvedStemmer ?? resolveFTSStemmer();
105
105
  }
106
+ /**
107
+ * Drop every configured FTS index (no-op per index when absent or unloadable
108
+ * — `dropFTSIndex` tolerates both). Callable ahead of any DML that mutates an
109
+ * FTS-indexed table's rows: LadybugDB's FTS extension is not proven to
110
+ * survive a DETACH DELETE against a table that still carries a live index
111
+ * from a prior run (#2589) — dropping first removes that hazard entirely,
112
+ * regardless of whether it also fixed a specific native inconsistency.
113
+ */
114
+ export async function dropSearchFTSIndexes() {
115
+ for (const { table, indexName } of FTS_INDEXES) {
116
+ await dropFTSIndex(table, indexName);
117
+ }
118
+ }
106
119
  export async function createSearchFTSIndexes(options) {
107
120
  const stemmer = getSearchFTSStemmer();
108
121
  for (const { table, indexName, properties } of FTS_INDEXES) {
@@ -403,8 +403,16 @@ export interface RepoMeta {
403
403
  * `E.hook` to `E$1.hook`, and nested-host anonymous names re-key
404
404
  * (`EnumWrap$1` → `EnumWrap$Mode$1`). Same contract as v8: identities move
405
405
  * on unchanged files; force a full re-analyze.
406
- */
407
- export declare const INCREMENTAL_SCHEMA_VERSION = 9;
406
+ * v10: Java `record_declaration` now emits a first-class `Record` graph node
407
+ * (#2564): a record's container node was previously never created (JAVA_QUERIES
408
+ * had no capture for it), so its methods existed as ownerless Method nodes
409
+ * with no `HAS_METHOD` edge. The incremental write set only covers changed
410
+ * files — a top-up against a pre-v10 index would keep silently omitting the
411
+ * `Record` node and its `HAS_METHOD` edges for every unchanged record file
412
+ * (same v7 contract: new nodes/edges the incremental path would otherwise
413
+ * never backfill); force a full re-analyze instead.
414
+ */
415
+ export declare const INCREMENTAL_SCHEMA_VERSION = 10;
408
416
  export interface IndexedRepo {
409
417
  repoPath: string;
410
418
  storagePath: string;
@@ -124,8 +124,16 @@ export const cloneDirBelongsToEntry = (cloneDir, entryPath) => registryPathEqual
124
124
  * `E.hook` to `E$1.hook`, and nested-host anonymous names re-key
125
125
  * (`EnumWrap$1` → `EnumWrap$Mode$1`). Same contract as v8: identities move
126
126
  * on unchanged files; force a full re-analyze.
127
+ * v10: Java `record_declaration` now emits a first-class `Record` graph node
128
+ * (#2564): a record's container node was previously never created (JAVA_QUERIES
129
+ * had no capture for it), so its methods existed as ownerless Method nodes
130
+ * with no `HAS_METHOD` edge. The incremental write set only covers changed
131
+ * files — a top-up against a pre-v10 index would keep silently omitting the
132
+ * `Record` node and its `HAS_METHOD` edges for every unchanged record file
133
+ * (same v7 contract: new nodes/edges the incremental path would otherwise
134
+ * never backfill); force a full re-analyze instead.
127
135
  */
128
- export const INCREMENTAL_SCHEMA_VERSION = 9;
136
+ export const INCREMENTAL_SCHEMA_VERSION = 10;
129
137
  const GITNEXUS_DIR = '.gitnexus';
130
138
  const GITNEXUS_EXCLUDE_ENTRY = `${GITNEXUS_DIR}/`;
131
139
  export const INDEX_METADATA_FILE = 'gitnexus.json';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.10-rc.67",
3
+ "version": "1.6.10-rc.69",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",