- Add 3 new test fixtures: swift-if-let-guard-let, swift-await-try, swift-for-loop-inference
- Add integration tests for if let/guard let binding resolution (4 assertions)
- Add integration tests for await/try expression unwrapping (3 assertions)
- Add for-loop-inference fixture (documented as known gap — type-env infrastructure
is in place but call-processor re-parse path doesn't propagate the binding yet)
- Fix cross-chunk Swift implicit imports: standard processImports path now passes
allFileList instead of chunk-only files to addSwiftImplicitImports, matching
the fast-path behavior
- Add Swift type_annotation fallback in type-env declarationTypeNodes population
(handles [User] array sugar where childForFieldName('type') returns null)
- Handle Swift 'pattern' node in extractVarName fallback (pattern wraps simple_identifier)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`import models as m` aliases were stored in namedImportMap (a symbol-binding
map) then cross-referenced in pipeline.ts — semantic misuse and inefficient.
Refactored: NamedBinding gains `isModuleAlias` flag. applyImportResult routes
tagged bindings directly to moduleAliasMap at import time. Removes the
pipeline.ts post-processing loop entirely.
Added test fixture and 5 integration tests for `import X as Y` with
multi-module disambiguation (both models.py and auth.py export User).
`import numpy as np` and `from models import User as U` previously
generated no CALLS edges because:
1. `import_statement` with an `aliased_import` child was not captured
by the tree-sitter query for Python imports.
2. `extractPythonNamedBindings()` only handled `import_from_statement`,
ignoring plain `import X as Y` forms.
Changes:
- `tree-sitter-queries.ts`: add query pattern for
`(import_statement name: (aliased_import name: (dotted_name)))` so
the import path is captured before named-binding extraction runs.
- `named-binding-extraction.ts`: extend `extractPythonNamedBindings()`
to handle `import_statement` nodes carrying `aliased_import` children.
Records `{ local: "np", exported: "numpy" }` so call-sites using the
alias resolve to the real module.
- `test/fixtures/lang-resolution/python-alias-imports/`: update fixtures
used by `python.test.ts` to exercise `from models import User as U`.
Existing tests in `test/integration/resolvers/python.test.ts`
(suite "Python alias import resolution") cover this path.
Previously, Python was added to WILDCARD_IMPORT_LANGUAGES which expanded
all exported symbols into namedImportMap using first-seen wins. This caused
`auth.User()` to incorrectly resolve to `models.py:User` when both modules
exported a class named User.
Root cause: Python `import models` is a namespace import, not wildcard
symbol expansion. Expanding all symbols produces ambiguous bindings that
cannot be disambiguated later.
Fix:
- Remove Python from WILDCARD_IMPORT_LANGUAGES
- Add ModuleAliasMap (callerFile → alias → sourceFile) to ResolutionContext
- In synthesizeWildcardImportBindings, build moduleAliasMap for Python
using the filename stem as the module alias
- In resolveCallTarget, add module-alias disambiguation step: when multiple
candidates survive filtering and the receiver name matches a module alias,
narrow candidates to the aliased file
Result: `models.User()` → models.py:User, `auth.User()` → auth.py:User
even when both modules export a class named User.
Adds regression test for the ambiguity case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix timeout detection: AbortSignal.timeout() throws TimeoutError, not
AbortError. Timeouts are no longer retried (30s fail, not 93s).
- Validate embedding dimensions in both httpEmbed and httpEmbedQuery
against config.dimensions or the 384d schema default. When DIMS is
unset, the error says 'Set GITNEXUS_EMBEDDING_DIMS=N' to guide users.
- Centralize test env var cleanup in afterEach via savedEnv snapshot.
- Test mocks use 384d vectors matching schema default.
- 4 new tests: timeout not retried, network retry success, query path
dim mismatch, unset-dims hint. 23 total, all pass.
Python repos were producing 0 CALLS edges for module-qualified constructor
calls like `models.User()` where `import models` is a bare module import.
Root causes:
1. `SupportedLanguages.Python` was absent from `WILDCARD_IMPORT_LANGUAGES`,
so `synthesizeWildcardImportBindings` never ran for Python files — bare
module imports never received per-symbol namedImportMap bindings.
2. Synthesis only ran in the Phase 14 pre-pass, after all chunks had already
been call-resolved. When `models.User()` was processed in Phase 3+4,
`namedImportMap` was empty for Python → Tier 2a-named fell through to
Tier 2a which found both `models.py:User` and `auth.py:User` (ambiguous).
3. `filterCallableCandidates` with `callForm='member'` excluded `Class` nodes
(only `CALLABLE_SYMBOL_TYPES` = Function/Method/Constructor/…). With 2
ambiguous Class candidates both were dropped, producing 0 CALLS edges.
Fixes:
- Add `SupportedLanguages.Python` to `WILDCARD_IMPORT_LANGUAGES` so that
`import models` expands to per-symbol namedImportMap entries (first-seen
semantics: `User→models.py:User`, `Admin→auth.py:Admin`).
- Call `synthesizeWildcardImportBindings` inline in the chunk loop, after
`processImportsFromExtracted` but BEFORE `processCallsFromExtracted`. This
ensures Tier 2a-named can disambiguate `module.ClassName()` at initial
call-resolution time. The Phase 14 pre-pass remains as a final safety net.
- Add a fallback in `resolveCallTarget`: if `callForm='member'` yields 0
filtered candidates, retry with `callForm='constructor'`. This handles the
case where a module-qualified class instantiation (e.g. `models.User()`)
is syntactically an attribute-access call but semantically a constructor
call. The fallback only triggers for 0-candidate member calls, so it
cannot over-eagerly promote normal member calls.
Tests: add `python-module-import` fixture (models.py/auth.py/app.py) with
4 regression tests covering IMPORTS edges, name-collision disambiguation
for `models.User()`, and `auth.Admin()`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers all high and medium impact gaps from the Swift feature coverage
analysis:
1. if let / guard let bindings: add if_statement and guard_statement
to DECLARATION_NODE_TYPES, extract varName and value for Tier 2
return-type propagation (callResult, copy, fieldAccess, methodCallResult)
2. await / try expression unwrapping: add unwrapSwiftExpression() that
strips await_expression and try_expression wrappers before checking
for call_expression. Applied in extractPendingAssignment,
extractInitializer, and scanConstructorBinding.
3. for item in collection: add extractForLoopBinding for Swift with
extractSwiftElementTypeFromTypeNode that handles [User] array sugar
and Array<User> generic types. Registered in typeConfig.
4. Multiple inheritance specifiers: already working — tree-sitter
queries match all inheritance_specifier occurrences automatically.
Verified, no code changes needed.
5. Enum case extraction: add (enum_entry (simple_identifier) @name)
@definition.property query to SWIFT_QUERIES.
6. self/super resolution: unskipped both describe.skip test suites
(tree-sitter-swift 0.7.1 ships prebuilds, Node 22 build issue
resolved). Both pass — 5 previously-skipped tests now running.
7. Optional chaining obj?.method(): already working — tree-sitter-swift
parses the ? transparently. Verified, no code changes needed.
Tests: 3,603 → 3,608 (5 unskipped self/super tests)
Swift tests: 23 → 28 passing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses reviewer feedback: the new Swift behaviors (implicit imports,
constructor fallback, extension dedup, export detection) had no dedicated
integration tests. Adds 4 fixture directories and 11 new test assertions:
1. swift-implicit-imports: two files, no explicit import, cross-file
constructor + member call resolves via addSwiftImplicitImports
2. swift-extension-dedup: extension creates duplicate Class node,
constructor still resolves to primary definition
3. swift-constructor-fallback: ClassName() without `new` resolves as
constructor via free→constructor retry
4. swift-export-visibility: internal symbols visible cross-file,
public/open visible, private/fileprivate noted as Tier 3 limitation
All 3,603 tests pass (11 new, 0 regressions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix assignment query: tree-sitter-swift 0.7.1 uses named fields
(target:/result:/suffix:) instead of positional children
- Update export detection tests: Swift `internal` (default) is now
correctly treated as exported (module-scoped visibility)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allow RFC 1918 private network ranges in the CORS origin allowlist so
users running GitNexus on their home or office LAN can access the web UI
from another device on the same network.
Permitted private ranges:
10.0.0.0/8 (10.x.x.x)
172.16.0.0/12 (172.16.x.x – 172.31.x.x)
192.168.0.0/16 (192.168.x.x)
The origin check is extracted into an exported isAllowedOrigin() helper
so it can be unit-tested in isolation. A new test file covers:
- No origin (curl / server-to-server)
- localhost and 127.0.0.1 variants
- All three RFC 1918 ranges including boundary values
- The deployed gitnexus.vercel.app site
- Public / untrusted origins that must be rejected
The server bind address (127.0.0.1 by default) is unchanged; this PR
only affects which cross-origin browser requests are accepted.
Closes#390
Instead of hardcoding confidence: 1.0, compute it at ingestion time using
the same resolution tier system that CALLS edges already use.
Heritage edges (EXTENDS, IMPLEMENTS):
- resolveHeritageId now returns { id, confidence } using TIER_CONFIDENCE
- Same-file → 0.95, import-scoped → 0.9, global → 0.5
- Edge confidence = geometric mean of source and target confidence
(principled for partially-correlated cross-scope estimates, per
Dillig et al. POPL 2011 and Dempster-Shafer theory)
MRO edges (OVERRIDES):
- MRO-ordered → 0.9, class method wins → 0.95
- Single interface → 0.85, ambiguous/unresolved → 0.5
IMPORTS and CONTAINS intentionally keep 1.0 (deterministic).
Closes#412
When LadybugDB throws a BUSY/lock error (e.g. CLI and server running
concurrently), withLbugDb retries up to 3 times with linear backoff.
Addresses review feedback:
1. **Race condition fix**: Connection cleanup (close + state reset) now
runs inside runWithSessionLock, preventing another operation from
acquiring the lock between cleanup steps and having its connection
closed from under it.
2. **Tests call withLbugDb directly**: Replaced simulateWithRetry helper
with tests that invoke the real withLbugDb implementation, catching
regressions in retry count, backoff, and lock interaction.
Closes#325
Addresses all review items from @magyargergo and Copilot:
1. **Rename --no-git to --skip-git**: Commander.js treats --no-X flags
as negation of --X (stores as options.git = false, not options.noGit).
--skip-git maps correctly to options.skipGit.
2. **Fix false " Already up to date\ on non-git folders**: When
currentCommit is empty string, skip the cache check — we cannot
detect changes without git, so always rebuild.
3. **Replace isGitRepo() with hasGitDir()**: Use filesystem check
(statSync on .git) instead of shelling out to git CLI. Consistent,
faster, and works when git is not installed.
4. **Fix misleading warning**: Message now only fires when .git
directory is actually absent (not when git CLI fails).
5. **Add CLI integration tests**: Verify Commander maps --skip-git
correctly and that non-git folders are rejected without the flag.
Previously gitnexus analyze exited with an error on any directory that
lacked a .git entry, making it impossible to index generated code,
vendored libraries, or monorepo sub-trees that are not git roots.
Changes:
storage/git.ts
- Add hasGitDir(dirPath): boolean — a lightweight synchronous check for
the presence of a .git file or directory. Works for git worktrees
(.git file pointing at the real repo) as well as standard repos.
cli/analyze.ts
- Add noGit?: boolean to AnalyzeOptions.
- When the explicit inputPath resolves to a non-git folder (or the cwd
is not inside any git repo), respect --no-git instead of hard-failing.
- Print an actionable tip pointing at --no-git when git is absent and the
flag was not supplied.
- currentCommit defaults to an empty string for non-git folders so the
up-to-date check still functions (empty string never matches a real
commit hash, so the index is always rebuilt).
- Skip addToGitignore() when no .git is present — there is nothing to
update and the function would create a stale .gitignore at the root.
Git-dependent features that remain disabled for non-git folders:
- Incremental update (always rebuilds from scratch)
- Commit tracking in metadata
- .gitignore update
Closes#384
- Enhance C++ tree-sitter queries to support inline class method declarations and return types.
- Introduce `importedRawReturnTypes` in `BuildTypeEnvOptions` for cross-file raw return type handling.
- Add `FileTypeEnvBindings` interface to capture file-scope type bindings for exported symbols.
- Implement logic in `parse-worker.ts` to extract and serialize file-scope type bindings for cross-file type resolution.
- Create test fixtures for C++, Go, Ruby, and Rust to validate cross-file binding propagation.
- Update integration tests to verify correct resolution of method calls across files for C++, Go, Ruby, and Rust.
- Document Phase 14: Cross-File Binding Propagation in the type resolution roadmap and system documentation.
* feat: add markdown file indexing (headings + cross-links)
Parse .md/.mdx files using regex (no tree-sitter dependency) to extract:
- Section nodes from headings (h1-h6) with hierarchy via CONTAINS edges
- Cross-file IMPORTS edges from markdown links to other repo files
Ported from #286 to resolve conflicts with kuzu→lbug rename.
Co-Authored-By: Dennis Palatov <dp-web4@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add Section to NODE_TABLES and NODE_SCHEMA_QUERIES
The Section schema was defined but not registered in NODE_TABLES or
NODE_SCHEMA_QUERIES, so the table was never created in the database.
Also adds missing FROM File TO Section relation entry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update schema test counts for Section node type
NODE_TABLES: 27→28, NODE_SCHEMA_QUERIES: 27→28, SCHEMA_QUERIES: 29→30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add diagnostic output to skills-e2e idempotency test
Show stdout/stderr in assertion message so CI failures reveal
why the second analyze --skills run exits with code 1.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add Section COPY query with level column in lbug-adapter
Section table has 8 columns (includes level) but getCopyQuery fell
through to the default 7-column multi-language path. Adds explicit
Section cases to getCopyQuery and insertNodeToLbug/upsertNodeToLbug.
Error was: COPY failed for Section: Number of columns mismatch. Expected 7 but got 8.
---------
Co-authored-by: Dennis Palatov <dp-web4@users.noreply.github.com>
All 4 test blocks now share one DB lifecycle to avoid cross-block
"Database is closed" errors caused by LadybugDB's shared global DB
in a single vitest fork. Staleness detection (which triggers closeLbug)
runs last to avoid invalidating connections for other blocks.
11/11 tests pass on macOS, Ubuntu, and Windows.
Add ExportedTypeMap infrastructure to propagate resolved type bindings
across file boundaries. When file A exports `const user = getUser()`
(resolved to `User`), file B importing `user` now gets seeded with
`user → User`, enabling `user.save()` to produce CALLS edges.
Key components:
- `importedBindings` option on BuildTypeEnvOptions with scopeEnv seeding
AFTER walk() to respect first-writer-wins (local declarations win)
- `collectExportedBindings()` in call-processor using graph node
isExported flag (no SymbolDefinition changes needed)
- Inline Kahn's algorithm topological sort with level grouping for
parallel-safe file ordering and cycle detection
- Re-resolution pass in pipeline.ts: topological order, 3% skip
threshold, path validation, per-file export caps (500)
- 32 new tests: 11 topological sort, 6 seeding, 15 integration
(simple cross-file, re-export chain, circular imports)
All 3454 tests pass (32 net new, 0 regressions).
Address code review findings from PR #392 senior compiler review:
- Fix Java "Yes" → "No" in optional-param-arity matrix (Java has no defaults)
- Simplify Kotlin hasDefaultValue while-as-if to direct const/if check
- Update OPTIONAL_PARAM_TYPES comment to include Ruby
- Replace per-declaration Set allocation with size-based Map iteration skip
- Add 11 unit tests for multi-declarator type association and constructorTypeMap
Add requiredParameterCount to SymbolDefinition and MethodSignature,
enabling range-based arity filtering in filterCallableCandidates.
Calls with omitted optional/default arguments now resolve correctly.
Supported: TS, Python, Kotlin, C#, C++, PHP, Ruby (7 languages).
Detection via OPTIONAL_PARAM_TYPES set + hasDefaultValue helper.
9 integration tests added across all 7 languages.
- Updated AGENTS.md and CLAUDE.md to reflect new indexing metrics.
- Enhanced call-processor.ts to support cross-file inheritance tracking and improved virtual dispatch resolution.
- Added support for TypeScript overload signatures in tree-sitter queries.
- Improved type extraction for C++, C#, and Kotlin to handle smart pointers and constructor types.
- Introduced inferLiteralType for overload disambiguation across multiple languages.
- Added tests for C++ smart pointer dispatch and Kotlin virtual dispatch scenarios.
- Updated type-resolution-roadmap.md to reflect completion of phases P.1 to P.3 and outline future work on covariant return types.
- Add AbortSignal.timeout(30s) on all fetch calls
- Add retry with backoff for 429/5xx (core: 2 retries, MCP: 1 retry)
- Guard initEmbedder() and getEmbedder() to throw in HTTP mode
- Discard cached embeddings on dimension mismatch during incremental re-index
- Add MCP embedQuery retry for transient failures
- Add 16 unit tests covering both core and MCP HTTP paths
- Fix README: concise, accurate env var docs
The fileIndex Map stored SymbolDefinition per name, silently dropping
earlier overloads via Map.set(). Changed to SymbolDefinition[] so all
same-name methods (e.g., Java overloads) survive in same-file resolution.
Added lookupExactAll() for resolution-context to pass all same-file
candidates through to candidate filtering.
- Remove dead replayPendingItems array and inert if-block in type-env.ts
- Add nullable_type fallback in extractKotlinDeclaration for val x: User? local vars
- Tighten isCSharpNullableDecl to avoid substring false positives on type names
- Add missing scope boundaries: function_expression (TS), constructor_declaration/
local_function_statement/lambda_expression (C#) in null-check narrowing walkers
- Extend null-check narrowing fixtures and add 4 integration tests covering:
Kotlin local variable nullable, C# constructor + lambda, TS function expression
Adds 17 new fixture directories and 23 new describe blocks covering every
feature in Milestone D (Phases A, B, C) with full cross-language integration
test coverage:
Phase A — Fixpoint Completeness:
- TS/JS object destructuring (const { field } = obj → fieldAccess resolution)
- TS/JS post-fixpoint for-loop replay (iterable var resolved by fixpoint)
- Rust struct_pattern destructuring (let Point { x, y } = p)
Phase B — Inheritance & Receivers:
- Grandparent MRO (depth-2 C→B→A) for all 9 OOP languages:
TS, Kotlin, C#, C++, Java, PHP, Python, Ruby, JS
- Go inc/dec write access (obj.Field++/-- emit ACCESSES write edges)
Phase C — Branch-Sensitive Narrowing:
- Null-check narrowing for TS (!==null, !=null, !==undefined),
C# (!=null, is not null), and Kotlin (!=null)
Bug fix — Kotlin null-check narrowing (3 issues in jvm.ts):
1. patternBindingNodeTypes registered 'comparison_expression' but
tree-sitter-kotlin produces 'equality_expression' for !=
2. Handler checked for 'null_literal' named child but 'null' is an
anonymous node in the Kotlin grammar
3. extractKotlinParameter only searched for 'user_type' direct child,
missing 'nullable_type' wrapper (so x: User? never got a base binding)
17 fixtures, 23 describe blocks, 705 new lines of test code, 0 failures.
Replace the sequential Tier 2b/2a propagation with a unified fixpoint
loop that handles four binding kinds: callResult, copy, fieldAccess,
and methodCallResult. The loop iterates until no new bindings are
produced (max 10 iterations), enabling arbitrary-depth mixed chains:
const user = getUser(); // callResult → User
const addr = user.address; // fieldAccess → Address
const city = addr.getCity(); // methodCallResult → City
city.save(); // resolves to City#save
Infrastructure:
- PendingAssignment union extended with fieldAccess and methodCallResult
- resolveFieldType helper: typeName → class nodeId → lookupFieldByOwner
- resolveMethodReturnType helper: typeName → class nodeId → lookupFuzzyCallable filtered by ownerId
- Fixpoint also resolves reverse-order copy chains that single-pass missed
Languages: TS, JS, Java, Kotlin, C#, Go, Rust, Python, PHP, Ruby, C++.
Each gets field access and/or method-call-with-receiver detection in
extractPendingAssignment, plus method-chain-binding test fixtures.
Activate the dormant Tier 2b pendingCallResults infrastructure in
type-env.ts by extending each language's extractPendingAssignment to
emit { kind: 'callResult', lhs, callee } when the RHS of an untyped
variable declaration is a simple function call.
This enables `var user = getUser(); user.save()` to resolve at TypeEnv
build time. Tier 2b now runs before Tier 2a copy-propagation, enabling
mixed chains like `const user = getUser(); const alias = user;
alias.save()`.
Languages: TS, JS, Java, Kotlin, C#, Go, Rust, Python, PHP, Ruby, C++.
Swift excluded. Each language gets a call-result-binding test fixture
and integration tests.
Conservative: only simple calls (no method calls with receivers), only
when exactly one callable matches, first-writer-wins.
* feat: upgrade @ladybugdb/core to 0.15.2 and remove segfault workarounds
The upstream fix (ladybug-nodejs#1) resolves the child QueryResult lifetime
segfault, making .close() safe on all platforms. This removes 6 workaround
sites:
- Remove `dangerouslyIgnoreUnhandledErrors` from vitest config
- Remove platform-conditional .close() guards in global-setup and test helper
- Delete test/setup.ts (process._getActiveHandles unref hack)
- Replace no-op cleanup in test-indexed-db.ts with real adapter close
- Fix pool adapter closeOne() to properly close connections with shared
Database refcount guard and orphaned connection handling in checkin()
- Update segfault-related comments across the codebase
Also bumps @ladybugdb/wasm-core to ^0.15.2 in gitnexus-web for consistency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: keep dangerouslyIgnoreUnhandledErrors for macOS N-API exit crash
The N-API destructor ordering crash during worker fork exit on macOS is
independent of the QueryResult lifetime fix in 0.15.2. Tests pass, but
the exit triggers a crash. Keep the flag with an updated comment
explaining the actual cause. Can be removed once LadybugDB fixes all
destructor ordering issues upstream.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: unify test run for single-pass coverage
- Update `npm test` to run all tests (unit + integration + lbug-db)
via `vitest run` instead of `vitest run test/unit`
- Add `test:unit` script for running unit tests only
- Remove `ci-integration.yml` — the per-file lbug-db process isolation
is no longer needed with `dangerouslyIgnoreUnhandledErrors` and
`fileParallelism: false` handling fork exit issues
- Update `ci-unit-tests.yml` to run all tests with build + coverage
- Simplify `ci.yml` gate (two jobs: quality + tests)
- Simplify `ci-report.yml` (single coverage artifact, no merge step)
* fix: update cli-commands test for renamed test:all → test:unit script
* fix: set USERPROFILE in setup-skills test for Windows compatibility
os.homedir() checks USERPROFILE on Windows, not HOME.
* fix: add isolate: false to lbug-db project to prevent fork crashes
On macOS, N-API destructors crash fork workers on exit. With
isolate: true (default), vitest recycles the fork between files,
triggering the crash after each file. After several crashes, the
remaining lbug-db files never execute.
isolate: false keeps all 8 lbug-db files in a single fork — the
fork only exits once after all files complete, and that single exit
crash is caught by dangerouslyIgnoreUnhandledErrors.
* fix: add unique sequence.groupOrder to vitest projects
Vitest v4 requires unique groupOrder when projects have different
maxWorkers (lbug-db has fileParallelism: false → maxWorkers: 1).
* fix: await async close() in global-setup and remove isolate: false
global-setup.ts called conn.close() and db.close() without await —
these return Promise<void> in @ladybugdb/core 0.15.2. The setup
function returned before the DB was fully closed, so vitest forks
hit a stale file lock when opening the same DB path, crashing the
lbug-db worker before any test ran.
isolate: false caused native state corruption after 2-3 open/close
cycles in the same fork (vitest-specific, not reproducible in plain
Node.js). Without it, each file gets its own module scope and the
N-API destructor crash at fork exit is caught by
dangerouslyIgnoreUnhandledErrors.
Also fixes fire-and-forget close() calls in the pool adapter —
try/catch around an async close() never catches rejections; changed
to .catch(() => {}) for proper unhandled-rejection prevention.
Before: 0/8 lbug-db files ran on macOS CI (fork crash).
After: 8/8 pass, 84 files, 3077 tests, zero errors.
* fix: update project index references in AGENTS.md and CLAUDE.md to reflect correct symbol counts and relationships
* feat: enhance lbug adapter with external database support and write operation validation
* feat: create ci-tests workflow for comprehensive test coverage across platforms
* ci: move PR report inline to ci.yml, delete ci-report.yml
The old ci-report.yml used workflow_run which always runs code from
the default branch (main). This meant the PR comment used main's
stale report template that still referenced the old unit/integration
split architecture — causing "Merge coverage reports" failures.
Moving the report inline to ci.yml means it runs from the PR branch
and uses the current report template. The report now shows:
- per-platform status (Ubuntu/Windows/macOS columns)
- unified test counts from the single vitest run
- coverage with base branch (main) delta comparison
- commit SHA for traceability
Also removes the save-pr-meta job since the report no longer needs
a separate workflow_run trigger.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>