export(`%+%`) names the symbol %+%, while the definition node keeps its
spelling `%+%`, so the export check never matched and the function was
flagged as not exported (the same for exportPattern()). Compare the symbol
through one shared helper, rBareName.
The slot pattern matched only list(...) and representation(...), although
the field extractor accepts any call, so setClass("A", slots = c(a =
"numeric")) and setRefClass("R", fields = c(...)) created no Property nodes.
Accept c(...) for both. An unnamed string in slots = c("a", "b") or
fields = c("a", "b") names an untyped member, as in R; an unnamed string in
representation(...) or list(...) is still not a member. The new pattern
captures its callee as @_vecOwner, so the class-naming capture filter does
not mistake these strings for the class name.
attachDeferredROwners resolved the owner hint through the class registry,
which every language shares. A Python class Circle next to an R Circle made
the name ambiguous, so the R methods and slots lost their HAS_METHOD and
HAS_PROPERTY edges; and with no R Circle, an R setMethod attached to the
Python class.
Keep only candidates whose graph node is an R class before the uniqueness
test. The change is inside R's postParse hook, so shared code is untouched.
extractRParameters tested for identifier and argument nodes, but the grammar
emits one parameter node per formal, so it always returned an empty list.
Every R6 and RefClass method therefore had parameterCount 0 and a #0 id, and
a variadic method was not marked variadic.
Read the parameter nodes: the name field gives the formal, a default makes it
optional, and a dots name makes it variadic (such a method has no count and
no arity tag in its id).
Member ids carried no owner, so a second class in one file that shared a
member name with an earlier one (push, items, a setMethod generic, a slot)
minted the same id as the first and lost the member, along with its
HAS_METHOD or HAS_PROPERTY edge.
Register resolveDetachedOwnerName for R. It names the class declared by an
ancestor R6Class assignment, setClass or setRefClass call, or the signature
class of a setMethod call, so ids become Method:<file>:Stack.push#n and
Property:<file>:Stack.items. The owner edge is still attached by postParse
through ownerNameHint. The change is R-only: the hook is optional and
undefined for every other language.
The r-packages characterisation counts now include Child.greet, the member
the id collision used to drop.
Three comments named rProvider.expandsWildcardTo, which does not exist for R:
the pipeline consumes the hook from the ScopeResolver, so point them at
rScopeResolver.expandsWildcardTo. State the \x, \u and \U digit limits in
unescapeRString's comment, which are those of R's own parser.
Comments only.
The @param name pattern [\w.]*\w[\w.]* backtracks quadratically on a long
name followed by anything but a blank (32,000 characters took about half a
second per line), and parseRQualifier's lazy [\s\S]*? does the same on a
long run of blanks. Match [\w.]+ and require a word character separately,
and take the text before the first '::' directly. Accepted inputs and
results are unchanged (checked against the old patterns on two million
generated strings).
Tests cover large adversarial inputs and the dotted formal names.
inSet() scanned a class's ranges from the front on every state visit, but the
search loop charged one unit per visit regardless of class size, so a wide
class multiplied real work the budget never counted. Ranges are sorted,
merged and disjoint for every CharSet (normalise() for written classes,
constants and complement() for escapes), so a binary search is equivalent.
Charged work is unchanged.
Tests pin the reads per visit, the edges of a large class and the charged
work counters.
* fix(routes): reject middleware and route metadata false positives
* fix(routes): invalidate cached phantom routes on upgrade
* test(routes): cover cache upgrades across merged schema versions
Verify that schemas 137 through 140 invalidate both parse stores after merging main into PR #3561 and advancing the schema to 141.
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
parseRoxygenParams read the parameter name with `\w+`, which stops at the first
dot, so `#' @param na.rm Data` documented nothing and the formal got no type
(the old unit test pinned that as "does not bind a dotted parameter name").
Reproduction: four new cases in type-env-r.test.ts (na.rm, .data,
max_n.rows, braced form with a continuation line) returned undefined instead of
'Data' on the unfixed pattern. The name pattern is now `[\w.]*\w[\w.]*`, so a
dotted identifier matches while `...` and `x,y` stay unbound; the former
"does not bind" case is replaced by positive cases plus a not-a-formal control.
expandRWildcardNames walked the flat localDefs of the target file and filtered
only dotted names, so a function nested inside another function
(`outer <- function() { helper <- function() 1 }`) was offered as a
package-level name to every file that calls library()/require() on the package.
Reproduction: two new cases in r-dotted-s3-hooks.test.ts failed on the
unfixed code (['outer', 'helper', 'top'] instead of ['outer', 'top'];
['a', 'b', 'c', 'd'] instead of ['a']). The expansion now keeps only names bound
at module scope (rFileTopLevel().names, which already feeds the dotted-tail
guard); top-level functions and bare-call S4/R6 classes are still listed
(controls added; test/integration/resolvers/r.test.ts unchanged and green).
The R rename guard compared the raw text of each `string_content` node with the
old and new name. An escaped spelling (`"f\u006fo"`, `'f\x6fo'`, octal) keeps
its escape inside string_content, and a raw string (`r"(foo)"`) has no
string_content node at all, so renaming `foo` proceeded while a get() or
do.call() on the escaped or raw spelling still resolved the old name.
Reproduction: 10 cases in the new test/unit/r-rename-string-guard.test.ts
returned 'ok' instead of unsupported_occurrence on the unfixed provider, and the
two new fixtures string-lookup-escaped and string-lookup-raw (rename-provider-r)
renamed successfully on the unfixed build. The guard now decodes each `string`
node (escapes, octal, raw strings) and compares the value.
docs/languages/r-provider.md said "spelled as a string literal"; it now states
the whole-value rule and what is still not detected (substring, paste0(),
non-indexed files).
skipConditionalBody returned just after the `if` of `else if (cond) body`, so
the main scan then read `(cond)` as stray text and emitted the body's
importFrom, S3method, export, exportClasses, exportMethods and exportPattern
directives as unconditional (the same for `if (a) if (b) body`).
Reproduction: 12 new cases in r-namespace-parser.test.ts failed on the
unfixed scanner (e.g. `if (a) {} else if (b) importFrom(x, y)` returned x::y;
`else if (b) export(hidden)` exported `hidden`). The nested `if (cond)` is now
stepped over iteratively (no recursion, tested with 50,000 nested ifs) and the
body that follows it is skipped.
LinearRegex.seed() filtered the closure's character states against the start
character before charging only 1 + (accepting states), so the range scans of
the failed state tests were free. A pattern of a few large character classes
could be probed with distinct start characters (each a filter miss, memoised
only up to 4096 keys) at almost no recorded cost.
Reproduction: for 20 alternated classes of 200 ranges, one miss on a
non-matching name recorded work 1 (expected >= 4000 range checks); a loop of
600 distinct start characters never exhausted a 1,000,000 budget. The filter
now charges 1 + ranges.length per examined state on a cache miss; hits stay
cheap. New tests in r-export-pattern-shared-budget.test.ts.
Review finding: an empty then/else arm loses its cond-true/cond-false edge.
Not reproducible as lost information: an empty `{}` arm (like a missing
`else`) is the header's seq fall-through edge, the same convention as every
other CFG visitor, and control dependence labels it as the complement of the
sibling explicit arm. Probed on the current code: `if (c) {} else work()`
gives c-cond-false->work() with CDG c->work():F, and
`if (c) work() else {}` gives CDG c->work():T.
Adds exact edge-structure and CDG-label tests (all passed on the unmodified
visitor) and documents the fall-through convention in the visitor header.
No behaviour change.
The R6 public/private field queries listed only some expression kinds, so
`public = list(x = NA)` (also Inf, NaN, complex, if/for/while/repeat, a$b,
pkg::v, v[1], v[[1]], {..}, (..)) produced no Property although
RFieldExtractor accepts any value that is not a function_definition.
Reproduction: new test/unit/r-r6-field-values.test.ts failed 46 of 76 cases
on the unfixed query (expected ['x'], received []). The query now enumerates
every named grammar expression kind except function_definition, shared by the
namespaced and bare R6Class patterns.
Restore parse-cache.ts and incremental-parse-cache.test.ts to upstream/main,
so this branch no longer touches either file.
- R files were never parsed before this branch, so no parse-cache entry for
them can be stale: .r files had no language and were in no chunk.
- Chunk keys are a hash of the chunk's file paths and content hashes with no
language or grammar component, so chunks of existing languages keep exactly
the same keys and entries; adding R only adds new R chunks.
- The cache version is `${SCHEMA_BUMP}+${package version}`, so any release
changes the package part and invalidates every entry anyway.
- The bump only protected reviewers re-testing successive heads of this branch
on a single package version, and it kept conflicting with upstream's own
bumps on every merge.
The diff against upstream/main adds no capture or ParseWorkerResult change for
any other language: shared edits are optional provider hooks that only R
defines (postParse, resolveMemberOwnerNode, cfgVisitor, rename).
Plain R functions rename through the scope graph; S3, string-spelled,
S4, R6 and NAMESPACE-exported names are refused. Adds fixtures, the
rename-provider-r test and its cross-platform shard entries.
R now provides a cfgVisitor (if/else, for, while, repeat, break, next,
return(...), nested functions), so the CFG provider gate holds for R and
--pdg emits BasicBlock, CFG and CDG data. There is no def/use harvest, so
reaching definitions report no-facts for R. The v142 parse-cache comment
notes that cached R results lack a CFG side channel.
* fix(ingestion): re-point Go struct member edges when struct is declared in a sibling file (#3558)
findEnclosingClassInfo mints a Go method's owner id from the file
containing the method (Struct:<methodFile>:<Type>), while the Struct
node is keyed by the file declaring the struct (parse-worker uses
declFile). Same package, different file => the HAS_METHOD edge's
sourceId points at a node that does not exist and the edge is silently
dropped at COPY into LadybugDB.
Add reconcileGoStructOwnerEdges, run at the end of the parse phase
after ALL worker chunks are merged so every package's structs are
visible. It rewrites only edges that would otherwise dangle, and only
when exactly one Struct node shares the dangling id's package directory
and simple name; 0 or >=2 candidates keep the old drop behavior (a
wrong owner is worse than a dropped edge).
Index build is lazy: dangling Struct sources are rare (struct fields
never dangle — they live in the struct's own file), so the common
non-Go or fully-same-file case pays two per-type edge scans and no
whole-node pass.
Regression test: test/integration/go-cross-file-struct-owner.test.ts
(fails-before: 1 HAS_METHOD edge; passes-after: 3).
* fix(ingestion): scope Go struct-owner reconcile by language and package
Address PR #3559 review findings:
- Scope the pass to Go: dangling HAS_METHOD/HAS_PROPERTY owner ids whose
Struct:<file>:<name> file is not Go source (Rust, Swift, ...) are never
re-pointed, and only Struct nodes stamped language 'go' (the marker the
worker sets on every declared symbol) are candidates. Negative test pins
a Rust-shaped dangling edge staying untouched beside a same-name Go
struct.
- Key candidate structs by (dir, declared package clause, name) instead of
(dir, name): one directory can carry 'package pkg' and external-test
'package pkg_test' files declaring the same type name, which the old key
saw as ambiguous and dropped fail-closed. Clauses are read via the shared
inferGoPackageName derivation with a call-scoped cache. Integration test
covers the 3-file layout (pkg decl, pkg_test decl, pkg_test methods file)
and asserts the edge lands on the pkg_test.Widget node.
- Keep the direct call in parse-impl with a TODO: the pass needs a
graph-level post-merge hook, which no LanguageProvider/ScopeResolver hook
provides today.
* refactor(ingestion): remove Go graph repair from shared parse phase
* fix(ingestion): resolve Go ownership during ingestion (language-provider.ts)
* Add resolveDetachedOwnerName function to Go config
* Refactor method owner population logic in Go
* Refactor receiver binding to handle unnamed receivers
* Enhance route handler method resolution logic
Refactor route handler to improve ownership resolution and method indexing.
* Add emitPostResolutionEdges to scope resolver
* fix(ingestion): resolve Go ownership during ingestion (ownership.ts)
* fix(ingestion): resolve Go ownership during ingestion (ast-helpers.ts)
* Add resolveDetachedOwnerName to provider methods
* Update SCHEMA_BUMP to 141
Increment schema version to 141 for new features and fixes.
* fix(ingestion): resolve Go ownership during ingestion (decl.go)
* fix(ingestion): resolve Go ownership during ingestion (external_decl_test.go)
* fix(ingestion): resolve Go ownership during ingestion (external_methods_test.go)
* fix(ingestion): resolve Go ownership during ingestion (foreign.rs)
* fix(ingestion): resolve Go ownership during ingestion (go.mod)
* fix(ingestion): resolve Go ownership during ingestion (local.go)
* fix(ingestion): resolve Go ownership during ingestion (methods.go)
* fix(ingestion): resolve Go ownership during ingestion (go-cross-file-struct-owner.test.ts)
* Refactor comments and method registration in tests
Updated comments for clarity and modified method registration to include qualified names and ownership.
* fix(ingestion): resolve Go ownership during ingestion (has-method.test.ts)
* Update SCHEMA_BUMP to 141 in tests
* refactor(ingestion): remove obsolete Go struct owner reconciliation
* fix(go): model build constraints for method ownership
* test(go): cover build constraint grammar and platform variants
* fix(go): resolve method owners across compatible build variants
* test(go): verify variant ownership through ingestion pipeline
This test verifies that build-variant owners are consistent across different workers, the semantic registry, and the graph. It sets up various scenarios to ensure that ownership is correctly resolved and that there are no dangling edges in the ownership graph.
* fix(go): reject impossible known constraints when peer is unknown
Address PR #3559 review feedback. Regression coverage follows in this update.
* feat(ingestion): allow providers to group incremental resolution files
Add the optional language-neutral hook used by the Go ownership writeback fix for PR #3559.
* fix(go): group sibling files for incremental ownership resolution
Use the existing Go package-directory key for the provider hook.
* fix(incremental): collect sibling resolution groups for changed paths
Keep group keys local to each provider and include current siblings of deleted files.
* fix(analyze): persist ownership changes in unchanged Go siblings
Expand changed and deleted resolution groups before importer traversal. Use the same effective file set for deletion and writeback so package-wide ownership changes reach the database.
* test(incremental): cover provider-local resolution groups
Cover changed, added, deleted and unrelated paths for the new sibling expansion helper.
* test(go): compare incremental ownership writes with cold rebuilds
Reproduce ownership changes after deleting and restoring a Windows declaration while the shared method file stays unchanged. Verify incremental writes and persisted ownership parity with forced rebuilds.
* test(go): exclude impossible owners with unknown method constraints
Use real parsing to cover impossible owner edges and valid owners hidden by impossible competitors.
* test(go): pin filename rules and unknown constraint overlap
Document Go first-dot filename behavior and verify impossible known constraints in both operand orders.
* test(go): register incremental ownership coverage across platforms
Include the new database-backed Go ownership suite in the cross-platform test list.
* test(go): schedule incremental ownership suite with native database tests
Complete PR #3559 review fixes. Local validation: 47 focused tests across five suites passed; build and source/test type checks passed. The full suite was interrupted without a result.
* refactor(ingestion): centralize worker owner hook forwarding
Pass the language provider into the private cached owner lookup instead of repeating four owner hooks at five call sites. Keep qualified-name selection explicit and preserve the exported helper signature.
Validation: CLI/shared build and test typecheck pass; repository lint has no errors; 591 tests pass with one skipped across 13 ownership and worker suites. Web typecheck is blocked by uninstalled web dependencies. Graph checks are unavailable because this checkout has no registered index.
* test(rename): add Go cross-file fixture module
Part of the cross-file method ownership regression for PR #3559. The completed fixture uses real indexing, public rename preview/apply, warm cache replay and post-rename reindexing. All 14 Go rename integration cases pass locally.
* test(rename): add cross-file fixture before/types.go
* test(rename): add cross-file fixture before/methods.go
* test(rename): add cross-file fixture before/callers.go
* test(rename): add cross-file fixture after/go.mod
* test(rename): add cross-file fixture after/types.go
* test(rename): add cross-file fixture after/methods.go
* test(rename): add cross-file fixture after/callers.go
* Add test case for renaming pointer receiver method
Complete the real persisted-index regression fixture for PR #3559. The type, pointer receiver method and callers are in separate Go files. Public rename preview/apply must make exactly four edits across the structural interface family while preserving an unrelated same-name method, comments and strings.
The fixture checks six exact ownership, interface and call edges before rename, after warm parse-cache replay and after reindexing the edited sources. All 14 Go rename integration cases pass. Formatting and the CLI typecheck pass. Graph impact/detect-changes could not run because this checkout has no registered index.
---------
Co-authored-by: System administrator <root@nixos-infra.home>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
* fix(lbug): round GITNEXUS_LBUG_MAX_DB_SIZE up to a power of two
The native buffer manager rejects any max DB size that is not a power of
two, but the env parser accepted any positive integer, so a value such as
300000000000 failed at open and was then reported with a misleading
"set a larger limit" hint.
Round a positive safe integer up to the next power of two and warn once
naming the raw and effective values. Values that are not safe integers
now fall back to the 16 GiB default.
Refs #3529
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(lbug): add remedies for the address-space ceiling and past-EOF read
Add two pure classifiers beside bufferPoolExhaustionRemedy. They parse only
what the engine message states, label the byte count as the limit the engine
reported (not the configured value), and name GITNEXUS_ATOMIC_INCREMENTAL=0
only for runs that knob changes. Neither asserts why the failure occurred.
Refs #3529, #3530
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(lbug): add size-evidence formatting for failure diagnostics
formatFileEvidence is a pure formatter over stat-shaped input (apparent
size, allocated blocks, raw statfs f_type), so sparse and regular cases are
testable without a real sparse file. describeFileEvidence wraps it, never
throws, and reports a missing file as unavailable.
Refs #3530
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(analyze): honor GITNEXUS_ATOMIC_INCREMENTAL=0 and classify staged failures
GITNEXUS_ATOMIC_INCREMENTAL was only read as === '1', so the documented =0
mitigation did nothing for watch and auto-sync, which pass atomicIncremental
as an option. An explicit =0 now beats the caller's request. Full rebuilds
and the escalation to staging still stage and are unaffected.
In the analyze catch block, snapshot the live and staging files before the
checkpoint driver stops and the database closes (closing can checkpoint the
staging file), classify the error once, then log the remedy and the held
evidence for a size-ceiling or past-EOF failure. The off-switch is named
only when the failed run was an atomic-incremental staged write. A max-size
message the classifiers do not recognise keeps the generic hint, and the
original error is rethrown unchanged. The atomic-incremental staging log
line now records the staged file size.
Refs #3529, #3530
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs: document max DB size rounding and GITNEXUS_ATOMIC_INCREMENTAL
Note that GITNEXUS_LBUG_MAX_DB_SIZE must be a power of two and is rounded
up, add GITNEXUS_ATOMIC_INCREMENTAL (1 and 0, and which builds 0 does not
affect) to both README env tables, and add a Troubleshooting entry for the
two intermittent embedding write-back failures and the evidence lines GitNexus
now logs for them.
Refs #3529, #3530
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(review): apply review findings
Review fixes:
- Label the GITNEXUS_LBUG_BUFFER_POOL_SIZE=0 sentinel in the failure
evidence line instead of printing "buffer pool 0 B".
- Replace FailureRemedyContext {staged, offSwitchApplies} with a single
StagedBuildMode ('in-place' | 'staged-rebuild' | 'staged-incremental'),
removing the { staged: false, offSwitchApplies: true } illegal state.
- Re-attach the LBUG_MAX_DB_SIZE doc comment to the exported constant.
- Cover the in-place classified failure through runFullAnalysis, and the
buffer-pool sentinel label, with new tests.
Also carries the simplification pass: failure evidence is collected only for
a classified failure, the staged-build flag is reused, the env var is read
once, and the evidence tests share one temp-dir helper.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(lbug): bound failure-evidence stats and record checkpoint activity
Review findings #1 and #3 on #3570.
describeFileEvidence raced nothing: a stat on a stalled 9p/NFS/virtiofs
mount hung the analyze failure handler before the driver stopped, the
database closed and the original error was rethrown. Each stat/statfs now
has a 2000 ms deadline and fails open; a missing file reads "absent"
instead of "unavailable", a stalled one reads "unavailable (timed out ...)".
collectFailureEvidence (new) is the single evidence collector: it reads
the checkpoint activity snapshot and dispatches every stat in one
Promise.all before its first await, covering the live index, the staging
copy and its .wal/.shadow/.wal.checkpoint sidecars. That is what lets a
#3530 report tell a short main file from pages pending in the sidecars or
from a CHECKPOINT GitNexus itself ran just before the failing DELETE.
runCheckpointWithRetry now records completed count, last completion time
and in-flight count. Both the periodic driver tick and checkpointOnce (the
staged window-start CHECKPOINT) go through it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(lbug): state the off-switch cost, pin the rounded max size, isolate ambient env
Review findings #5 (note), #6 (wiring) and #11 on #3570.
offSwitchNote now says that GITNEXUS_ATOMIC_INCREMENTAL=0 can leave the
live index partly written after a failed in-place write, and that the next
run then rebuilds it in full.
A new test stubs GITNEXUS_LBUG_MAX_DB_SIZE=300000000000, reloads
lbug-config and asserts the native Database constructor receives 2 ** 39
and that exactly one rounding warning is logged. The existing tests all
called the parsers directly and matched the constructor argument with
expect.any(Number), so reverting the wiring left the suite green.
vitest.config.ts sets GITNEXUS_ATOMIC_INCREMENTAL to the empty string
(neither '0' nor '1', i.e. unset) like GITNEXUS_MEMORY, so an exported =0
no longer switches off every staging test.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(analyze): carry the failure diagnosis in the error message and label escalated runs
Review findings #2, #4, #5, #7, #8 and the run-analyze half of #6 on #3570.
#4: the remedy and `Failure evidence` lines were only sent to onLog, which
`analyze --watch` wires only with GITNEXUS_VERBOSE=1 and the auto-sync
launcher drops - the only two callers that request atomic-incremental
staging. They are now appended to the rethrown error's message in place
(identity and prototype preserved, native text first so classifiers
anchored on it still match), which every caller prints. A thrown value
that is not an Error falls back to a log. Auto-sync truncated that message
to 240 characters before logging it; the log now allows 2000 while the
persisted summary stays short.
#8: remedy delivery is best effort, so a throwing onLog sink or an
unwritable message can no longer replace the native error.
#2: escalatedFullWrite is declared above the try, and an escalated
atomic-incremental run is classified as a rebuild. Its remedy no longer
points at GITNEXUS_ATOMIC_INCREMENTAL=0, which does not stop the
escalation from staging.
#5: when GITNEXUS_ATOMIC_INCREMENTAL=0 overrides a caller that requested
staging, the run logs one line saying so and what a failed write costs.
#7: the stage-time size line uses the same "staging copy" label as the
failure evidence.
#6: tests pin the evidence-before-driver-stop-before-close order, the exact
configured max DB size in the evidence, and the appended text against the
substring classifiers in the CLI and auto-sync.
The READMEs describe the new evidence lines, ask reporters to include the
stage-time line, and name GITNEXUS_WAL_MANUAL_CHECKPOINT=0 as the bisect
knob for the checkpoint hypothesis.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Address PR review feedback (#3570)
- wal-checkpoint-driver: count a checkpoint as completed only when
tryFlushWAL actually ran one. It returns false when no connection is
open, and the failure evidence renders the count as "N completed".
- auto-sync runner: an oversized native error now logs its head and tail
(1000 characters each) so the appended failure diagnosis, which is always
the tail, survives. The persisted 240-character summary is unchanged.
- lbug-config: the max-size remedy said GITNEXUS_LBUG_MAX_DB_SIZE "must be
a power of 2", but non-powers are rounded up. It now says so, as do the
matching lines in gitnexus/README.md.
- failure-evidence and file-evidence tests use the default registered
temp-dir prefix, so the stale-sidecar sweep recognises their fixtures.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Address PR review feedback (#3570)
- run-analyze: stringify the thrown value through a helper that cannot
throw, so an exotic throw (null-prototype object, throwing toString)
cannot abort the failure handler before it stops the driver, closes the
database and reclaims the staging copy.
- run-analyze: a classified non-Error throw is wrapped in an Error (original
kept as `cause`) so the diagnosis travels in the message like every other
case, instead of in a log that the auto-sync launcher drops. This removes
the separate log fallback.
- auto-sync runner: the oversized-error log keeps head and tail within the
2000-character cap; the separator no longer pushes it to 2003.
- lbug-config: the max-size rounding warning prints the operator's value as
supplied (300000000000.9), not the integer it floored to.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Address PR review feedback (#3570)
- lbug-config: the max-size remedy's rounding example was itself a power of
two. It now shows a value being rounded: 300000000000 becomes 549755813888.
- auto-sync-runner test: the comment said the whole log line is capped at
2000 characters; the assertion covers the error payload after the prefix.
- extension-loader test: pin that the installer child is handed the rounded
max DB size for a non-power-of-two override. The installer prefers that
argument over the env var and has its own non-rounding parser, so this is
what keeps a valid override working there.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Address PR review feedback (#3570)
- Keep the whole failure diagnosis in auto-sync's capped log line: the
tail now reserves the remedy and evidence lines (with a 240-char floor
for the native head) instead of an even head/tail split.
- Count only this run's checkpoints in the failure evidence: snapshot the
process-global activity when the run begins and report the difference,
so an earlier `analyze --watch` refresh no longer leaks in.
- Diagnose classified failures thrown while opening the staging copy, via
helpers shared with the write-path catch.
- Halt checkpoint ticks before measuring failure evidence: start
`walCheckpointDriver.stop()` (which clears its timer synchronously)
before the stats, then await the drain before close.
Note: pre-existing timeouts in run-analyze-fts-repair ("fails repair mode
when no base meta exists", "abandons a pending set that fails
EMBEDDING_RESUME_MAX_ATTEMPTS times in a row") reproduce on the unchanged
PR head on this host and are not addressed by this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(mcp): reject corrupt query identities and refresh index metadata
* fix(mcp): preserve reader and search integrity
Keep replaced readers unknown until matching metadata is published.
Defer shared-handle refresh until other consumers release the old graph
to avoid rebuilding connections against the same stale generation.
Propagate identity corruption after both hybrid search lanes settle,
so the sibling cannot query after its database session is released.
Select the regressions for the OS matrix and retain Python coverage
without optional Swift grammars.
Validation: 393 focused tests, nine native generation tests and ten
race executions across five fresh processes pass. Build and source/test
typechecks pass. The early full run passed 26,096 tests and failed eight:
seven reproduce on baseline; the mixed-source publication case passes
in clean reruns. Windows/macOS native execution remains for CI.
Graph analysis maps all edited production symbols; coverage remains
partial for the anonymous FTS test callback, manually reviewed.
Refs #3571
* fix(lbug): bump @ladybugdb/core to 0.21.2 to stop doubled rows after delete and re-copy
LadybugDB 0.21.0 and 0.21.1 return every row of a table that spans more
than one native vector twice once the incremental writeback has DETACH
DELETEd it and a COPY into an earlier table has run. NodeGroup::scan let a
morsel that ended before the group's first live row wrap an unsigned
subtraction and rescan to the end of the group (LadybugDB #1085, fixed in
0.21.2). No primary key error fires. The identity guard from #3442 stops
the run with "unexpected or duplicate ID", so a Swift edit that pulls every
importer into the write set failed on repos with more than 2048 structs.
Only unreleased main was affected: 1.6.12 ships 0.18.3.
Pin 0.21.2 in package.json, update the six @ladybugdb/core entries in the
lockfile, and move coreVersion in the vendored FTS manifest. The FTS
extension stays at 0.21.0 because no v0.21.1 or v0.21.2 artifact is
published; the pairing assertion and its unit tests pass.
Add a regression to the delete-nodes-for-files suite: 2049 structs with
CONTAINS edges are loaded, every file is deleted, the graph is loaded
again, and each id and edge must appear exactly once.
Verification (Linux, Node 22.18):
- lbug-delete-nodes-for-files: 11/11 on 0.21.2. With the 0.21.1 native
packages swapped in, the new test fails with 4098 rows instead of 2049.
- Reporter recipe end to end on the built CLI: incremental analyze exits 0
with no duplicate ids on a Swift-heavy fixture and on a 70-file Python
fixture, with and without FTS. On 0.21.1 the Swift fixture exits 1.
- A 0.21.1-written database opens and reads under 0.21.2.
- prettier, eslint and typecheck:tests are clean on the changed files.
- The full lbug-db project had not finished when this was committed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* test(fts): say what the Windows warm-session branch covers
On Windows the repair step calls poolClose, which removes the pool entry
before the in-place writer opens. The step therefore checks the backend's
cached capability state, not a live pool connection. The comment now says so
instead of implying the pool stays warm. No behavior change.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* revert: take the @ladybugdb/core 0.21.2 bump out of this PR
Reverts 0cdc3dfe3. The bump fixes LadybugDB 0.21.0 and 0.21.1 returning every
row of a large table twice after a delete and re-copy, but on Windows it
crashes the vendored FTS extension, which is still the v0.21.0 build: four of
eight Windows shards failed with exit code 3221225477 (0xC0000005) in tests
that load libfts and build an FTS index. All eight passed on 9d79092ab with
core 0.21.1. A Windows extension is only published for v0.21.0, not for
v0.21.1 or v0.21.2.
The bump moves to its own draft PR (#3578) until a matching Windows
extension exists. This PR returns to its original query-integrity and
index-freshness scope.
Restores gitnexus/package.json, the six @ladybugdb/core entries in
package-lock.json, the coreVersion in vendor/lbug-fts/manifest.json, and
removes the regression test that belongs with the bump. All four files are
identical to origin/main (1b4e3f0a8) after this commit.
Verification:
- git diff origin/main for the four files is empty.
- fts-artifact-coverage and vendored-extension-path unit tests pass (31),
and assert-publish-fts-coverage reports core 0.21.1 paired with extension
0.21.0.
- The graph check on the staged revert was partial, because it only deletes
test code. The forward change was gated clean: 4 files, 6 symbols, all in
that test, no affected processes.
- Windows CI has not run on this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
resolveRImportInternal ran indexOf over the whole normalised file list for
every source() import (O(files) each). The resolver already receives
getWorkspaceFileIndex's per-pass normToRaw map (first raw spelling per
normalised path, built once), so use it for the exact-path lookups; the legacy
array-only call shape keeps the indexOf fallback.
Behaviour is identical (both pick the first file in list order). New unit
tests run one corpus (relative, ../, absolute, backslash and duplicate
spellings, missing, suffix fallback) through both paths and through the live
adapter, and assert the indexed path performs no indexOf scan (fails when the
change is reverted).
populateRNamespaceImports synthesised a named import for every
importFrom(localpkg, name). Names the provider does not export, does not
define at top level, spells with a dot, or defines beside a dotted S3
method of the same tail never resolve, yet the unresolved import counted as
an explicit name claim and suppressed the documented 0.5-confidence
global-name-fallback edge.
Skip the import when resolveRNamedImport returns null, so such names create
no phantom claim and their calls keep the fallback.
Upstream took SCHEMA_BUMP 133 to 137, so the R provider bump moves to 138.
The pin test and the taken-versions list are updated to match. The import-target
benchmark keeps both the R and Rust arms in the heap-budgeted and context
language lists, and the tree-sitter readiness tests keep the R grammar checks
against the reworked report (R is now a peer-patch grammar and the held-grammar
counts changed).
* test(accuracy): retire repaired Python accessor gaps
* fix(python): connect module accessor resolution
* fix(python): resolve calls through module-returning accessors
Bind `_pc().target()` and `m = _pc(); m.target()` to the module's
function when a local accessor returns an imported module, and count
capped-out dropped names in the impact lower bound.
Closes#3497
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor(python): share module-accessor position key (#3542)
- Reuse the capture-side positionKey in the receiver namespace resolver
instead of a second line:col helper plus an inline template.
- Note why the synthetic { moduleScope, scopes: [] } input is sufficient
for createNamespaceTargetCache (sealed disk-backed ParsedFile shape).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor(python): reuse subtype-dispatch positionKey (#3542)
- Export the existing line:col positionKey from subtype-dispatch instead of
defining an identical copy in module-accessors, so both Python capture
side channels key positions through one helper.
- Apply prettier to the Function-scope indexing line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(ci): unique test titles and rebaseline Python benches (#3542)
- impact-epistemic-lower-bound: `%j` rendered null, NaN and Infinity all as
`null`, so the test-completeness audit rejected the duplicate identities.
Use `%s` so every parameterized case has a distinct title.
- python-scope: capture fingerprint changes with the new module-accessor
capture facts (golden captures were already updated).
- receiver-resolution: +4 Python call drops (125 -> 129). The two-step
`x = f(); x.m()` form is now counted as an unresolved call instead of
vanishing: fastapi-depends `router.get/post` (unknown, x3) and
python-method-chain-binding `user.get_city()` (in-program; `User` has no
`get_city`, so the site is genuinely unresolvable).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(indexing): document native pool exhaustion recovery (U3)
Publish the reviewed recovery runbook and collapsed-write guardrails for #3526.
* docs(indexing): explain native buffer pool sizing (U3)
* fix(indexing): reject collapsed staged graph writes (U2)
* fix(indexing): abort native buffer exhaustion (U1)
* test(indexing): cover exhausted and collapsed graph writes
* ci(tests): split ubuntu coverage into 4 shards
Coverage shard 1/3 runs 20-25 minutes across branches and now hits the
25-minute job timeout (cancelled on this PR and on unrelated branches),
which skips coverage-merge and fails "every test executed" plus every
platform compatibility gate downstream. A fourth shard brings each slice
well under the cap. Shard job names are not required checks; the merge
job globs coverage-blob-*, so no other wiring changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(lbug): remove branching from buffer-exhaustion tests
Split the collapsed-staged-graph case into first-time and existing-index
tests sharing one helper, so every assertion runs unconditionally instead
of behind `if (hasExistingIndex)`. Parameterize the first-COPY exhaustion
case with its statement matcher (reusing isRelationshipCopy) rather than a
ternary on the phase name. Assertions are unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(deps): upgrade native tree-sitter to 0.25.1
* fix(tree-sitter): preserve sync loading and update verified capture baselines
Route C# scope queries through the synchronous native grammar loader so the compiled analyze module remains require-compatible on Node 22.18. Refresh only the audited empty-Python-module and renamed-PHP-fixture captures, and pin the runtime assertion to 0.25.1.
Validated 479 targeted tests, all 16 scope-capture benchmark cases, Python capture/import guards, compiled C# worker parsing, build, typecheck and formatting. Performance budgets are unchanged.
* fix(tree-sitter): refresh upstream vendors and preserve parser semantics
* fix(ci): refresh prebuilds and support Windows ARM
Refresh macOS, Linux ARM, and Windows x64 grammar binaries from the successful native CI builds. Pin node-gyp 12.4.0 so the Windows ARM jobs recognize Visual Studio 2026.
Use local Git filtering for E2E to avoid PR-files API timeouts on generated parser diffs.
Validation: 143 focused tests, Actionlint, and all 16 scope-capture benchmark checks passed. Full npm test run started; native database suites still running.
* fix(ci): refresh Windows ARM grammar prebuilds
Import all seven Windows ARM grammar artifacts from successful native build run 37965761760. Artifact filenames and SHA-256 hashes verified; all 42 native build jobs passed.
Validation: 31 prebuild coverage tests passed.
* fix(bench): stabilize scope capture timing guard
Warm each input size seven times and use 21 median samples while retaining isolated timing blocks. Yield between languages so native finalizers release allocations outside the measured sections. Keep all fixture sizes, fingerprints, scaling formulas, and budgets unchanged.
Validation: all 16 language guards passed with a 2 GB heap limit (1.38 deep-chain ratio; 1518 MB peak RSS). Linear and quadratic controls passed their expected guard outcomes. Formatting and graph change analysis passed. Full npm test is running without failures so far.
* fix(parser): reduce TypeScript capture traversal cost
* fix(test): await parser worker readiness before teardown
* fix(parser): load C# synchronously in workers
* fix(grpc): resolve native proto detections against their own file
With the native proto grammar active, sibling .proto files that declare
the same short service name under different packages tied in the
directory-based resolveProtoConflict heuristic and both providers were
dropped. A proto-origin detection already knows its defining file, so
use that file's entry before falling back to the heuristic.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci(packaging): assert native grammars load from the installed tarball
The packaged-install smoke only ran `gitnexus --version`, so a grammar
that failed to load from the published package (bundled runtime, patched
peer manifests, vendored prebuilds, C# sync loader, Protobuf wrapper)
would ship silently; the dev-tree ABI smoke also accepts vendored
grammars that report unavailable. Parse a sample for every registered
grammar plus the native Protobuf plugin from the installed package, on
the main thread and in a worker thread.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci(tree-sitter): track compatibility workarounds instead of 0.25 readiness
The daily readiness report still targeted tree-sitter@0.25.0, so after the
upgrade it would keep reporting "Blocked" and never close its tracking issue.
Repurpose it to track what we carry for the bundled runtime: derive the target
from the package.json pin, list each pack-time peer patch from
prepare-tree-sitter-bundle.cjs with its drop condition (bump, wait for an
upstream release, transitive, or intentionally pinned), and reword the
buckets. The workflow adopts the issue opened under the old title.
Also update the grammar-update-monitor header and auto-PR text, which still
described the 0.21.1 runtime and a tree-sitter-c hold.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(dart): assert both nested labels target the loop; fix Zig call-shape doc
The nested-label CFG test only jumped to `outer` and checked reachability,
which still passes when a jump is retargeted to the inner loop (its exit
reaches the outer header). Jump to both labels and assert the exact
`continue` edges; dropping the inner label now fails the test.
Update the Zig callable-flow comment: call_expression now wraps actuals in a
required `arguments:` field.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The linear-regex parser refused any pattern whose text contained "(?<", so
[(?<] and \(?<x were dropped as look-behind. Reject at the group the parser
reaches, which is never inside a class or after a backslash. A real look-behind
or named group is still refused, after any class, with the same linear parse.
S3method(generic, class, method) registers method, not generic.class, as the S3
method. parseRNamespaceExports ignored the third argument and so marked a
function called generic.class public while the registered method stayed
non-exported. Use the third argument when it is a plain name, else generic.class.
R matches named arguments first, so in setClass(a = "Q", "B", Class = "A") the
class is A. The naming-argument scan stopped at the first unnamed argument and
named both B and A. Keep scanning the list, once and cached, and drop the choice
when an argument spelled with the formal appears. Replaces the pinned known-limit
test and the known-limit wording in the file header and the provider guide.
getRTopLevelMethodOwnerName counted string-valued arguments, so a named where= or
valueClass= string became the setMethod class, and extractRefClassMethods took the
first string, so contains = "Base" ahead of Class = "Acc" named Base. Match the
signature argument with R's rule (named first, then positional into the remaining
formals) and the RefClass name with the Class formal.
declaredClassName and the new("Class") constructor scanner took the first
argument, so setClass(contains = "Base", Class = "Derived") anchored roxygen
types to Base and new(name = "x", "A") typed nothing. Select the class with
getRClassNameArgument (isRNamingArgument(arg, 'Class')), the choice the scope
query, definition hook and field extractor already make.
An unreadable DESCRIPTION left RPackageConfig.truncated false, so a
qualifier naming the package it declares was classed external and its
call site dropped. The sibling hidesUndiscoveredPackage case already
treats an unreadable DESCRIPTION as unknown; do the same here and cover
it with a test.
Tighten three weak test assertions: the escaped-bracket export pattern
now asserts its exact match results, the S4 anchors test proves each
generic is defined exactly once, and the worker owner-absence test
fails on any defined ownerId, including an empty string.