* fix(lbug): self-heal read-only opens refused by an interrupted checkpoint
Homelab repro 2026-09-19 (image 1.6.10-20260917, @ladybugdb/core 0.19.x):
a wiki pod killed mid-CHECKPOINT left the engine's checkpoint artifacts on
disk (lbug.wal.checkpoint / lbug.shadow / checkpoint intent+apply locks),
and every later READ-ONLY open refused with 'Cannot open database in
read-only mode while checkpoint is in progress' — permanently, until a
writable open (any gitnexus analyze) happened to run.
Two defects, both fixed:
1. The refusal was unclassified. ensureReadOnlyConnectionUsable (direct
adapter) and openReadOnlyDatabase (pool) recovered missing-shadow and
shadow-replay errors but rethrew this one raw, so 'LadybugDB
unavailable for __wiki__' repeated forever. Add
isReadOnlyCheckpointInProgressError (LADYBUGDB-CONTRACT, live-verified
against 0.19.1) and route it through the same writable-open recovery —
including at OPEN time, where the refusal fires before any probe can
run (pool: init() moved inside the try; direct: doInitLbug catch).
2. The existing shadow-replay recovery was not durable. Reproduction
matrix on 0.19.1: the writable probe replays the WAL in MEMORY only —
without an explicit CHECKPOINT the engine drops the pages at close and
the follow-up read-only open silently serves the pre-checkpoint state.
Both recovery paths now CHECKPOINT after the probe, which applies the
replay, consumes the sidecars, and clears the checkpoint locks.
Verified end-to-end: real engine 0.19.1, killed-mid-CHECKPOINT state →
exact refusal → pool adapter self-heals → 80,800 rows intact, sidecars
consumed. The planted-signature integration test runs on any engine
version (refusal asserted only where the engine emits it, 0.19+).
* docs(architecture): list checkpoint-in-flight artifacts and the read-path self-heal
* fix(lbug): address review findings — shared cursor closer, tighten structural guard
Both findings from the gitnexus-check review on this PR:
1. pool-adapter.ts: the recovery CHECKPOINT closed its cursor with a bare
unawaited result.close?.(). Use the shared best-effort closer
(closeQueryResults) the repo already funnels both adapters through, so a
cursor-close failure stays cleanup and cannot escape as an unhandled
rejection.
2. sidecar-recovery.test.ts: the structural regex omitted the leading
negation, so as a substring match it also accepted the inverted
predicate (recover ONLY shadow-replay, exclude checkpoint) — the exact
regression the guard exists to prevent. Pin the full
'!isReadOnlyShadowReplayError(err) && !isReadOnlyCheckpointInProgressError(err)'
throw-through shape.
* test(lbug): wire the recovery plant into lbug-db/LBUG_NATIVE; force the refusal on any engine pin
Address the tri-review findings (all four):
P1 — the planted-signature integration test was collected by the parallel
'default' project and never by the serialized 'lbug-db' project, and
Windows/macOS CI never ran it: register it next to its sibling in
vitest.config.ts (lbug-db include + default exclude) and in
cross-platform-tests.ts LBUG_NATIVE, per TESTING.md's rule for native
@ladybugdb/core suites. Verified via 'vitest list --project lbug-db'.
P2 — on the committed 0.18.3 pin the plant passes as 'pool opens and
count(n)=300' without ever exercising the new classifier or recovery
CHECKPOINT. Add forced-refusal behavioral tests for BOTH adapters: a mocked
native layer whose first read-only Database refuses with the canonical
0.19 message, asserting the exact self-heal shape (ro-refused -> writable
open -> CHECKPOINT -> ro retry) and that a healthy db never triggers a
writable open. The direct adapter is lazy, so its refusal is scripted at
the first probe query rather than init().
P2 — the LADYBUGDB-CONTRACT header on isReadOnlyCheckpointInProgressError
claimed '^0.18.0' like its siblings; only 0.19.x emits this string (0.18.3
tolerates the state). State the first-observed version so a bump reviewer
validates the right binary.
P2 — the pool's writable replay recovery quarantined on missing-shadow
even when the error came from the post-probe CHECKPOINT, where the main
file has already changed: mirror the direct adapter's probeSucceeded guard
(replaySucceeded) and fail closed instead of parking a live sidecar.
Also assert walBuffer.byteLength > 0 in the plant so the fixture cannot
silently degrade into an empty shell on tolerant engines.
* test(lbug): fix hosted-CI failures — Windows handle release, version-gated plant, prettier
Address the CHANGES_REQUESTED review of the hosted run:
Windows blocker (Win32 Error 33, locked file region at the pooled reopen):
the fixture now makes handle release explicit — waitForFixtureRelease
probe-reads the db and its residual WAL with bounded retries after every
native close (plant, raw refusal probe, pool close), mirroring the
adapter's own Windows handle-release probing. The WAL is re-planted from
the captured bytes instead of renamed: a close-time auto-checkpoint can
consume the live .wal out from under the rename (ENOENT, second flake).
Version-gate the plant itself: on < 0.19 engines that tolerate the planted
signature, its synthetic sidecars are not a consistent staging state for
the old engine (double-apply replays surfaced as 'Person already exists in
catalog' — the third flake), and no refusal can be forced there anyway.
The plant now skips below 0.19 with that rationale in-file; the behavioral
contract on every pin stays with the forced-refusal units, and this native
suite remains registered in lbug-db + LBUG_NATIVE so Win/macOS exercise it
as soon as the pin moves off 0.18.3.
Also: prettier on the two flagged files (format gate).
* fix(lbug): keep failed checkpoint heals from re-entering CHECKPOINT
Wrap recovery failures without repeating native refusal text, skip already-wrapped errors in doInitLbug, and pin constructor-time heal plus the Windows reopen skip.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3340)
- Clean each forced-heal tmpDir in afterEach so earlier cases do not leak
- Compare major.minor when version-gating the interrupted-checkpoint plant
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3340)
Reset the pool forced-refusal mock Database sequence in native.reset() so later tests can still script the first construction as the checkpoint victim.
* Address PR review feedback (#3340)
Count every MATCH on the pool forced-refusal mock and assert the writable replay probe so CHECKPOINT-without-probe cannot stay green.
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(ingestion): tRPC pattern detection — procedures, curried calls, route extraction
Three changes to properly index tRPC router files:
1. HOC-in-pair patterns: detect procedures like
'create: procedure.mutation(async ({ input }) => {...})'
as named Function nodes (pair > call_expression > arguments > arrow_function)
2. Curried call detection: capture 'workflow(db)(input)' chained calls
where call_expression.function is itself a call_expression
3. tRPC route extraction: new route-extractors/trpc.ts detects
.query()/.mutation()/.subscription() procedures, maps to /trpc/* routes
with prefix inference from router variable names
4. Function/Const dedup: structural check skips Const nodes when
variable_declarator value is arrow_function/function_expression
5. Process-route linking: match by (filePath, methodName) instead of
filePath only, preventing shared flows across procedures in same router
(cherry picked from commit eced15e60a39a181a48bff8c5bbe278d26aa53b8)
* fix(route-extractors): line-by-line scanner for chained tRPC procedures
Original regex only matched direct patterns (list: proc.query()) but not
chained patterns (create: proc.input(z.object({...})).mutation()). Only
41/256 routes were detected on Jurialis.
Rewrote extractTrpcRoutes() as a line-by-line scanner that:
- Detects procedure keys starting with *Procedure builders
- Tracks currentProcedure forward until terminal .query()/.mutation()
- Handles .input() chaining naturally
- Deduplicates via seen Set on procedurePath
Result: 245 routes from 30 routers (was 41), 256 total after re-index.
(cherry picked from commit 49edf953e93e6b67b77932e866fcbfb8d089b1f3)
* feat(mcp): expose tRPC chains via context/query tools
Eight fixes to make the tRPC route->procedure->workflow->sub-workflow chain
visible through the standard MCP tools (context, query) that AI agents use,
instead of requiring raw Cypher queries.
- A context: order incoming/outgoing CALLS test-last, raise LIMIT 30->100
- B query: batched ENTRY_POINT_OF lookup, surface route URL on processes
- C query: mark process_symbols entry point with is_entry_point=true
- D entry-point-scoring: skip UTILITY_PATTERNS (get*/set*) penalty for
symbols in tRPC router files so framework boost (3.0x) is preserved
- E fts-schema: index Route nodes so /trpc/* URLs are keyword-searchable
- F tools: bump max_symbols default 10->25 to fit procedure->workflow chain
- G context: optional chain_depth (0-3) param walks CALLS edges in both
directions and returns layered chain field
- H LadybugDB bug workaround: WHERE r.type IN [...] silently drops edges
on relationship properties; replace with OR chains (7 occurrences in
local-backend.ts, pdg-impact.ts, graph-queries.ts)
Validated on Jurialis: setProviderCap procedure now appears as caller of
setProviderCapWorkflow, /trpc/cabinet.setProviderCap Route is searchable,
chain_depth=3 returns the layered call graph.
(cherry picked from commit c24df0adce91e80f191a5c5d2e8b14e9da677366)
* feat(mcp): surface is_entry_point + routes in context, raise query limit
context() is the mandatory pre-edit tool (AGENTS.md). Until now an agent
had to issue a separate query() call just to learn whether its symbol is
a process entry point or which HTTP route it handles. These two fields
make context() self-sufficient for the bmad-dev flow.
- context: query STEP_IN_PROCESS now returns p.entryPointId; new
ENTRY_POINT_OF lookup attributes routes only from processes where the
symbol is the entry point (middle steps do not own the route) plus any
direct Route->symbol handler edge (tRPC procedures outside any Process)
- context: new top-level is_entry_point (true only) and routes[] fields
({url, method?}), emitted only when non-empty to keep the diff additive
- query: raise default limit 5->10 so dense domains (tRPC action router
with 20 chains, data-export with 9 flows) surface more of their flow
set without an explicit param
(cherry picked from commit 004f5b0ba985fcef3dfac5e67a2f2f7262184215)
* docs(mcp): document chain_depth, routes, and entry-point flag; neutralize examples in comments
* fix(mcp): address code-review findings on tRPC entry points, queries, and dispatch
- entry-point scoring: optional-chain framework detection and normalize
path separators before router-pattern matching (P1 crash on .js routers)
- tRPC extractor: emit controllerName as null (callers resolve via
lookupClassByName; a router object stringified as name corrupted lookups)
- route dedup: key seenRoutes by method:url so GET/POST pairs survive
- legacy TS queries: drop curried-call patterns (arity-corrupting for
overload resolution) and require non-array callee on pair member calls
- registry-primary TS query: mirror the non-array-callee predicate on the
new pair member-expression patterns (fixes query compile error)
- group tool port: forward chain_depth into per-tool context args
* fix(mcp): nested tRPC router paths, controller-less route binding, query chain_depth
- trpc extractor: brace-depth nesting stack composes sibling router paths (bare router() import style included); merge-prefix dot normalization; strict publicProcedure allowlist gate; drop phantom router metadata
- call-processor: bind controller-less tRPC routes to same-file handlers via exact single-match symbol lookup; ambiguous or missing handlers skipped
- query/group query: optional chain_depth (0-3) enriches ranked processes with their context chain; fix _computeContextChain layer docstring
- tree-sitter TS/JS scope queries capture string-key function declarations; tRPC pattern scoring narrowed to server router files
- parse-cache schema bump to v103 for extractor and route-binding changes
- add trpc route extractor regression tests (9 cases incl. sibling nesting and merge prefix)
* Address PR review feedback (#3339)
Close remaining review threads: compact tRPC keys, comment-safe terminals,
quoted-key identifier HOC captures, group-query default alignment, and
LadybugDB label scalars on context chains.
* chore(autofix): apply prettier + eslint fixes via /autofix command
* test: pin PARSE_CACHE schema bump to 103 (PR #3339 review fixes)
* fix(ingestion): bind same-name tRPC handlers and surface HANDLES_ROUTE
Same-name procedures resolve by startLine, the extractor keeps nested paths
through multiline schemas, and context/query UNION HANDLES_ROUTE for leaf
procedures that never become Process entries.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Clamp group query bounds, drop HOC pair false positives, emit tRPC
terminal lines and hyphenated quoted keys, cap chain BFS concurrency,
and restrict the router utility exemption to accessor names.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Score JS/JSX tRPC routers like TypeScript, ignore inner db.query and
unrelated .merge calls, mask regex braces, and assert clamp tests invoke
the query mock.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix(ingestion,mcp): emit-side HOC callback guards + MCP schema prettier (PR #3339 round 2)
* Address PR review feedback (#3339)
Treat `/` after return-style keywords as a regex, drop the synthetic
appRouter path prefix, and bind the create mutation via an inline callback.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Qualify query() docs so is_entry_point is promised only when the entry
symbol is among the search hits.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Emit tRPC terminals only at procedure paren depth, drop the filename
prefix on bare appRouter = router(), mask regex after if (), and cap
groupQuery member fan-out.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
De-duplicate context-chain BFS nodes reached through multiple frontier
edges, and bind tRPC identifier callbacks (`.mutation(handler)`) to the
handler symbol instead of the procedure key.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Apply LIMIT 50 after DISTINCT neighbors in the context-chain BFS, and
treat the official lowercase `procedure` builder as a tRPC procedure key.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Keep the later duplicate tRPC object-literal key so route binding matches
the handler JavaScript actually ships.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Compose same-file identifier-mounted tRPC subrouters so admin: adminRouter
emits the live admin.list path instead of an unprefixed /trpc/list.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Memoize tRPC identifier-mount paths so a depth-N chain stays linear, and
gate it with the build-free measure.mjs / baselines.json harness.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Compose identifier mounts without phantom /trpc URLs, bind wrapped and
multiline handlers, fail-close missing bench budgets, and reject query
page bounds before search.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Reject advertised MCP query bounds in group mode and chain_depth
instead of clamping or accepting non-integers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Reject invalid groupContext chain_depth and ignore non-callable
same-file tRPC handler candidates.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Memoize live tRPC mount paths so the depth-N chain bench stays linear,
and render invalid group/MCP bounds without throwing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Keep t.merge('prefix.', namedRouter) procedures live by recording a
zero-hop mount so the unmounted-router drop does not hide them.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3339)
Recognize type-annotated `const adminRouter: AppRouter = t.router(`
bindings so identifier mounts still compose.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(storage): classify leftover per-branch index slots
Operators need a shared enumerator for deleted-branch leftovers before clean --stale or doctor can reclaim or report them.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(storage): reclaim a per-branch slot and empty branches/
Named clean --branch now shares one rm-then-registry helper so the last leftover slot can drop the empty branches directory, and a failed rm still keeps the retryable summary.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(cli): add clean --stale to reclaim leftover branch indexes
Operators can drop per-branch slots whose recorded branch is gone without remembering each name, while a git-list failure stays a no-op.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(cli): report leftover branch indexes in doctor
Operators can see cwd orphaned per-branch slots and their size, then reclaim them with clean --stale, without doctor deleting anything.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): keep unreadable branch slots out of stale reclaim
A stat error other than ENOENT/ENOTDIR must not look like a missing
directory, or clean --stale --force drops the registry row and leaves
the slot on disk.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(cli): keep leftover-slot display helpers in the CLI layer
Preview and doctor share one size formatter and an i18n path for
registry-only rows, so storage no longer owns display copy.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): keep leftover reclaim moving after a registry drop failure
Catch removeBranchIndex rejections so --stale continues, match doctor
registry rows through canonicalizePath, and size leftover slots sequentially.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): match leftover-slot registry rows with canonicalizePath
Use the repo-manager path contract so clean --stale and --branch still
see registry-only leftover rows when cwd and the stored path differ.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): contain leftover-slot deletes and re-check live heads
Refuse symlink and junction escapes under branches/, unlink slot links
instead of removing through them, and skip --stale --force when a name
is a local head again.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3338)
- Bound listLocalHeads spawnSync with GIT_PATH_LIST_MAX_BUFFER.
- Clarify that doctor leftover reporting is cwd-only, not registry-wide.
- Drop the MCP/serve assumption from clean --stale delete failures.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3338)
- Describe disk-only leftover slots in --stale help, not only recorded branches.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): stop doctor reclaim copy when heads cannot be listed
Doctor was naming clean --stale for leftover rows even when git cannot
list local heads, which is a no-op. Print the retry-git message instead (#3337).
Co-authored-by: Cursor <cursoragent@cursor.com>
* revert: drop Unreleased changelog notes from this branch
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): keep live branch pins when a tag shares the name
%(refname:short) disambiguates against tags, so clean --stale treated
still-local heads as leftover. Fail closed on obstructed slots and
unlistable branches/ directories.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3338)
Bound leftover-slot listing, revalidate paths immediately before delete, and keep registry rows when a stray disk-only directory claims a recorded branch.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(web): drop a folder onto the analyzer to upload it
The Local Folder panel of RepoAnalyzer is now a drop target. A dropped
folder is walked with the File and Directory Entries API
(DataTransferItem.webkitGetAsEntry), directories on the shared exclusion
list are pruned before they are read, and the resulting File objects are
handed to the existing filterRepoFiles -> uploadFolder -> trackJob path
with webkitRelativePath set to <folder>/<rest>, so the server receives the
same manifest shape the webkitdirectory picker produces.
Compared with the picker, the walk never enumerates node_modules or .git,
stops at the server's 20000 file cap and 64 path segments, skips
unreadable entries instead of failing, and reports progress while it runs.
Loose files and several folders at once are refused with a message (the
server accepts one top-level folder). The walk runs under the request
controller, so a mode switch or unmount aborts it; Analyze and the picker
are blocked while it runs. The panel-wide target also stops the browser
from navigating to a file dropped a few pixels off the button.
New strings in en and zh-CN; browsers without webkitGetAsEntry keep the
picker button and get an explanation.
* Address PR review feedback (#3315)
Clear readingCount only when this drop still owns the request controller, and skip oversized files before they count toward the 20k drop cap.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3315)
Count oversized files the drop walk skips in the summary droppedCount, and correct the 250-file batch-read comment.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(swift): match repeated SPM target prefixes
* test(swift): cover repeated SPM target prefixes
* test(swift): cover valid prefix before later partial match
* docs(swift): clarify target grouping parity scope
* docs(swift): clarify target grouping parity scope
* docs(swift): clarify target grouping parity scope
* fix(swift): resolve imports from Package.swift targets, not path segments
Stop fabricating IMPORTS from import Foundation onto a same-named folder.
Declare modules from Package.swift when the manifest is usable; keep
Sources/* for grouping and fail-open folder resolve minus SDK names.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): keep empty Package.swift declarations and nested .target() deps external
An inferred Sources/* folder is grouping-only. A dependency .target(name:) is not a module. Treat both as unresolved so import Foundation cannot bind to a decoy folder.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): keep implicit IMPORTS intra-group and gate linear Package.swift resolve
@_exported must not paint sibling files as implicit imports. A dedicated
bench pins declaration-only resolve and (t_4n/t_n)/4 linearity.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Honor member-only @_exported imports, skip comments while scanning
Package.swift factories, fail-open mixed helper-built target lists, and
block CoreData/CoreGraphics decoy folders on the inferred path.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Match path: "." as the package root, skip block-commented Package.swift
factories, read import kind from the clause only, and skip capture tests
when the optional Swift grammar is missing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): scan Swift import-kind without nested regex backtracking
CodeQL js/redos flagged the comment-skipping IMPORT_KIND_RE; a linear walk keeps the same kind tokens.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): linear Package.swift factory scan and gate Swift context
parseSwiftPackageManifest re-walked every prefix for comments (O(n²) in factory count). Resume the scan and cover nested factories in one pass. Wire Swift into the import-target context arm now that resolveImportTarget is 5-arg.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Tighten Package.swift and import-text scanners: skip comments/strings, reject escapes, treat ident + [ as incomplete, and drop the unused factory-comment wrapper.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): prettier the @_exported availability fixture
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Judge Package.swift completeness from Package(...)'s own targets: argument instead of raw-text regexes, nest block comments when reading an import kind, and strip leading ./ from declared target paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Collect Package.swift factories only from Package(targets: [...]), fail-open on computed array elements, and treat // after a label colon as a comment.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Ignore stray factories when Package() exists but omits targets:.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3105)
Require the Package-scan seen box so the always-true undefined guard goes away.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): resolve nested constructors in extensions
* fix(swift): preserve qualified extension owners
* Address PR review feedback (#3308)
Recover qualified Swift extension owners through public / attribute prefixes, and stop last-dot-guessing when source text is present.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): keep attribute text from stealing extension owners
Bound header recovery so @available messages cannot rekey a fragment, and still inject nested types when the Class scope has no bindings.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): nest comments and keep the public extension fixture valid
Review follow-up: skip nested /* */ in the header scan, put the Inner.Entry decoy in a parsed file, and mark Outer/Container/Entry public so the live fixture is valid Swift.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): recover Unicode identifiers as extension owners
The header regex was ASCII-only, so extension Café.Container keyed as Caf and dropped nested-type siblings. Match ID_Start/ID_Continue segments instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): skip raw strings and decode UTF-8 scope columns
Header recovery treated #"..."# as an ordinary quote and sliced Tree-sitter byte columns as JS offsets, so a same-line Café prefix or a raw attribute message could steal or drop the extension owner.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(analyze): expose process-detection budget overrides (#3313)
Operators can raise or lower process count, branching, trace depth, and the entry-point candidate pool via CLI, .gitnexusrc, or GITNEXUS_* without changing shipped defaults. A budget-only change re-detects flows on the next analyze without --force.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): say invalid budget flags still honor env
A rejected --max-processes value was described as falling back to the built-in default even when GITNEXUS_MAX_* still won the next precedence tier.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(analyze): share process-detection defaults and skip unused walks
Keep DEFAULT_CONFIG aligned with the budget resolver and count symbols only when maxProcesses is still dynamic.
Co-authored-by: Cursor <cursoragent@cursor.com>
* style(analyze): wrap process-detection budget files for prettier
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(analyze): name the real process-detection default formula
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(analyze): stop calling maxProcesses*2 a hard trace quota
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): say invalid env budget tokens fall back to defaults
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): recertify process-detection after in-place FTS abort (#3324)
Persist processDetection.uncertified on the in-place FTS dirty stamp when
the budget mismatched so a flagless retry cannot keep rewritten flows.
Qualify .gitnexusrc fail-fast copy and tighten related tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): skip live dirty stamp on atomic incremental (#3324)
POSIX atomic incremental mutates a staging copy, so stamping live incrementalInProgress before swap made a crash force-rebuild a healthy index. Align analyze --help with CLI > .gitnexusrc > env > default.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(changelog): drop the atomic-incremental dirty-stamp note
The code fix stays; Unreleased no longer lists that recovery change.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(cli): survive FTS SIGSEGV in --limit e2e
CREATE_FTS_INDEX can kill the setup analyze on some WSL hosts
(status null). Rebuild with --skip-fts and skip BM25-only
query --limit cases unless GITNEXUS_REQUIRE_FTS=1.
Refs #3324
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(cli): mark update-check child at import
Writing refresh-started from fetch() raced a 30s poll against
cold tsx boot on a loaded default-project worker.
Refs #3324
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3324)
Isolate default-budget FTS crash-marker tests from GITNEXUS_MAX_* env, assert uncertify-before-FTS order and deferred flow detection on park recovery, drop the dangling "then" from entry-point help, and correct stale streamGraphEmit docs without skipping the process-detection stamp.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(changelog): drop Unreleased process-detection notes
Keep the #3313 / #3322 code; Unreleased changelog matches main until release.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(analyze): load detected-branch sanitization from core git-ref
Keep the never-throw helper next to validateBranchName so run-analyze no longer imports CLI config parsing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): warn once when a checkout name cannot label the index
After the write lock settles, emit a single onLog warning and keep writing the workspace slot. Pin that run-analyze does not import CLI analyze-config.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): escape hidden checkout names in the detect-reject warning
Keep the rejected ref visible in onLog without replaying bidi or quote characters, and document that sanitizeDetectedBranch rethrows unexpected errors.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): keep detect-reject warnings on one line (#3325)
Git-legal U+2028/U+2029 checkout names were rejected as whitespace but left raw in the new onLog warning, so the message split across two lines. Escape those code points in the formatter without changing validateBranchName.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix(analyze): keep C1 and Unicode spaces in detect-reject warnings
Escape NEL and remaining whitespace as \uXXXX so stripControlCharacters cannot drop or disguise the rejected checkout name.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(web): drop TypeScript 7-incompatible tsconfig paths
Remove baseUrl and the dead ../shared include so web project references typecheck under TypeScript 7.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(cli): parse TypeScript with a TypeScript 6 API package
Keep AST guards working after the named typescript package becomes 7, which no longer ships the Compiler API.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(lint): pin root TypeScript to the 6 API package
Give typescript-eslint a TypeScript 6 peer so syntax-only lint still installs after CLI and web move to TypeScript 7.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(deps): compile first-party packages with TypeScript 7.0.2
Unify CLI and web on the same native compiler line as gitnexus-shared so typecheck and emit no longer split 5.x versus 7.x.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(ci): describe parent TypeScript 7 as the shared compiler
Stop saying web compiles shared with TypeScript 5 now that the parent lockfile is 7.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): compile shared from parent TypeScript on Vercel and skill-evolution
Stop isolated npm installs in gitnexus-shared so those paths do not pull a second TypeScript 7 optional-platform tree.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: record TypeScript 7 typecheck and Dependabot major-split policy
Keep contributor typecheck commands, and stop Dependabot from bumping shared onto a different TypeScript major than CLI and web.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(lint): pin root TypeScript to 5.9 so npm ci satisfies eslint peers
typescript-eslint 8 peers typescript below 6.0.0, so the typescript6 alias made quality lint npm ci fail with ERESOLVE.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(cli): drop the TypeScript 6 Compiler API package
TypeScript 7.0 has no classic createProgram surface, so parse-only
guards now use Babel and Mode 4 uses the TypeScript 7 Checker.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: align contributor setup with parent TypeScript 7 compile
Stop telling clones to npm-install gitnexus-shared; CI and Vercel already emit that package from a parent lib/tsc.js shim.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(web): typecheck React JSX on TypeScript 7 with explicit DOM libs
TypeScript 7 no longer implies DOM or auto-includes @types, so the web app must declare React/JSX settings while Vite keeps plugin-react.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test: pin Vercel --include=dev and share parse-only string helpers
Production npm ci omits the web TypeScript unless --include=dev is on that install. Move staticStringValue next to the other Babel walk helpers so CLI help and contract tests share one source.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(embeddings): spill cached vectors to a Float32 temp file
Keep restore metadata in RAM and write embeddings once the in-memory
row limit is exceeded so incremental analyze can survive large caches
without a full-table number[] heap (#3306).
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(lbug): stream CodeEmbedding cache under the connection lock
Spill vectors once the in-memory limit is crossed and fail the load
instead of adopting an empty snapshot, so incremental analyze cannot
OOM or quietly drop the restore cache (#3306).
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(analyze): restore cached embeddings from a streamed spill snapshot
Hold row metadata across wipe, materialize 200-row batches, and treat
cache-load failures as warn-and-continue so incremental analyze can
preserve vectors without a full-table heap (#3306).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3310)
Loop spill writes until the full vector lands, keep materialize failures out of the insert catch and the Phase 4 hash skip-set, and assert spilled restore subsets by node id instead of scan order.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3310)
Discard only this analyze run's embedding spills so a concurrent analyze on another index keeps its restore file, and isolate the default in-memory limit test from inherited env.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3310)
Mark a node stale when any restore batch fails so leftover chunks are deleted and rembedded, and exercise a full-length bad-magic spill header.
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(auto-sync): preserve PDG indexes across updates
* docs(auto-sync): document durable PDG synchronization
* Address PR review feedback (#3290)
- Correct requestedPdg state docs for threshold-skipped syncs
- Defer coalesced follow-up and skip failure-threshold counts for leftover-worker / retryable lock waits
- Document the pdg tri-state and caveat the 30m/5m example
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): raise Windows Ladybug #605 hang budget off the CI tail
Windows 3/3 typically finishes this native race in ~15s but has a 56s tail; 60s false-positives as deadlock. Keep the POSIX 60s detector and the completion/.shadow/row-count contract.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(embeddings): isolate local ONNX inference in a child_process sidecar
The analyze parent must not load onnxruntime-node. Fork a sidecar for
vectors only and reap it on worker exit; keep Ladybug writes in-process.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(embeddings): share the sidecar client across MCP, serve, and sync
Query hosts now use the core façade instead of a second in-process ONNX
embedder. Search skips an empty table, sync reaps beside closeLbug, and
ready means the stack is resolvable rather than a warm singleton.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(embeddings): refuse Intel Mac and unloadable prefix before npm heal
Analyze, sync, install, and the sidecar client now consult the platform
blocker before forking or downloading the optional stack. HTTP stays the
escape hatch; wasm is not treated as a rescue.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(embeddings): take the ONNX stack off default npm install
Pins live in gitnexusEmbeddingStack. embeddings install writes prefix
overrides before npm spawn. Leftover 1.6.12 package-first trees are residual.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(publish): drop grammar source from the published tarball
Every vendored grammar has 6/6 prebuilds, so files ships those plus
Leiden and FTS instead of parser.c. First ship stays above 80 MiB.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(embeddings): match MCP missing-stack warn to the R20 copy
Default install no longer calls the stack optional, so the once-per-backend
stderr assertion must look for the new lead line.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): bound sidecar death, cancel writes, and publish-file guards
Init-time native crashes no longer respawn a child on every query. Local
embedBatch honors AbortSignal after sidecar return, MCP query() surfaces
vector-lane degradation, disconnect always reaps, and the grammar prepack
guard checks files globs instead of on-disk prebuilds.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(embeddings): share runtime preflight and sidecar reap helpers
Analyze and embeddings-sync used the same blocker/prefix/install gate
with different error routing. One assessment keeps those paths aligned
without changing CLI vs thrown-error behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3287)
Keep a reaped sidecar from resetting its replacement, wait for dispose,
tighten the publish-files guard, and stop assuming a leftover ONNX tree in CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address remaining PR review feedback (#3287)
Clear the sidecar reap timeout, add init IPC slack, and isolate embeddings-sync tests from HTTP-mode env.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(embeddings): unstub globals after sidecar HTTP-mode tests
Keep a leaked fetch stub from failing assertions out of later tests in the same file.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(embeddings): pin sidecar success cases off darwin/x64
The runtime blocker reads the real process platform before the fork mock, so local-success tests must not inherit an Intel Mac host.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address remaining PR review feedback (#3287)
Keep vector degradation per query, treat leftover Intel-Mac stacks as not ready, and document that the CLI image no longer ships ONNX.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Simplify embedding sidecar shutdown and search hot paths
Drop redundant sidecar reaps and unused child helpers, and run FTS alongside semantic search.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address remaining PR review feedback (#3287)
Share HF attempt parsing with the sidecar init deadline, abort embed waits without killing the child, and restore last init options on recreate.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address remaining PR review feedback (#3287)
Treat sub-1 HF attempt env values as invalid, and drop leaked sidecar waiters when IPC send throws.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address remaining PR review feedback (#3287)
Keep sidecar init on a shared chain; each waiter can abort only its own wait.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): declare embedding-table existence probe as unordered LIMIT
The empty-table skip in semanticSearch is existence-only; declare it so the #2787 determinism guard stops failing coverage shard 3/3.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>