* fix(fastapi): carry package router mount prefixes to child routes
* fix(fastapi): address review feedback on nested router prefixes (#3408)
- Skip unprefixed includes in the parse-impl legacy loop so a bare
include_router in another file no longer shadows the real prefix.
- Union exact-file prefixes with legacy long/short prefixes via a shared
mergeMountPrefixes helper in both ingestion and the group extractor.
- Join the parent APIRouter(prefix=...) between the mount prefix and the
child include prefix.
- Resolve the group layer over every repo path (empty files included) so
absolute-import ambiguity matches ingestion.
- Memoize (file, prefix) frames so diamond-shaped include graphs stay
linear; drop the stack.pop() non-null assertion.
- Accept extra keyword arguments and a trailing comma in unprefixed
include_router calls without double-firing on prefix= calls.
- Document that pass-through is limited to a host named `router`.
- Bump parse-cache SCHEMA_BUMP to 123 for the new capture fields.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(fastapi): seed prefix propagation from bare router mounts (#3408)
- A router mounted without a prefix now seeds traversal with an empty
prefix (only when no prefixed mount targets the same file), so its own
APIRouter(prefix=...) reaches unprefixed children on both surfaces.
- An all-empty chain records nothing and leaves the child on its legacy
fallback.
- The bare-mount integration test no longer asserts that the test app's
unprefixed mount is absent; it pins only that the real prefix survives.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(fastapi): capture include prefixes after nested-call arguments (#3408)
- Let the Shape A/B and unprefixed include_router patterns step over one
level of nested calls such as dependencies=[Depends(auth)], so a
prefix= written after them is captured by the worker (the group
layer's tree-sitter patterns already handled this shape).
- Replace the unit test that pinned the dropped prefix with one that
pins the captured prefixes and the unprefixed Depends-only edge; add a
group-layer parity test.
- Correct the diamond test comment to 2^39 root-to-leaf paths.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): model restoring helper calls in decorator identity (#3414)
A bare module-level call to a same-file helper whose every global
binding of a descriptor name is an unconditional del or builtins import
now restores the builtin at the call site, matching CPython. A nonlocal
rebind nested in the enclosing function now shadows an owned builtins
import, closing a false builtin. Unprovable call orders stay fail-closed
and are pinned against CPython 3.11.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(review): apply review findings
Close three false-builtin paths the review found: a match-pattern
capture now counts as a binding (at any scope, including inside a
restoring helper), a call before the helper's def no longer counts as a
restore, and the helper-name uniqueness check sees match captures.
Pin the helper rejections (conditional restore, async, early and nested
return, wildcard import) against CPython 3.11.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cache): bump parse-cache schema to v122 for #3414
Python decorator identity verdicts changed, so warm v121 ParsedFiles
would replay stale receiver bindings.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): require an argument-free call to a helper with no required parameters
A call that fails to bind the helper's parameters raises TypeError
before the body runs, so it proves no restore. Keep such calls
fail-closed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): count only real captures and module bindings for decorator identity
Match value patterns, class names and keyword keys read a name rather
than capture it, so they no longer shadow a builtin descriptor. A local
of the same name as a restoring helper no longer disqualifies the
module-level helper; only a module binding or a global rebind does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(python): state the fail-closed contract of the descriptor identity table
`false` means the resolver does not prove the builtin, not that CPython
shadows it. Cases where CPython keeps the builtin but the resolver fails
closed carry a comment saying so.
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(python): resolve decorator identity like CPython
Decorator identity was decided by two different predicates. One read
the raw decorator text, so a trailing comment such as
`@staticmethod # type: ignore` hid the builtin and turned an explicit
first parameter into a fabricated receiver. The other trusted any
`staticmethod` spelling, including a module-level rebinding and a
`staticmethod(classmethod(f))` stack, which CPython cannot call.
Read the decorator expression node only, and recognize a bare builtin
descriptor only when the file does not rebind that name. Publish subtype
capacity only for a plain function or a single builtin staticmethod or
classmethod wrapper. Drop a no-op coverage guard, move the implicit
classmethod comment next to the code it describes, and bump the parse
cache to v121.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): shadow builtin descriptors only by visible bindings
The whole-file identifier scan counted plain reads (`staticmethod(f)`),
`from builtins import staticmethod`, and bindings that run after the
decorator as rebindings. CPython evaluates a class-body decorator with
LOAD_NAME when the `def` runs, so none of those change which object the
decorator names. Methods decorated with the real builtin lost their
subtype call shape, and static methods lost their first parameter in
arity metadata.
Move decorator identity into builtin-descriptors.ts and count only
binding occurrences (assignment and loop targets, walrus, def/class,
parameters, import aliases, except/with/match captures, del, type
parameters, and wildcard imports) that are visible where the decorator
runs: the class body or module before the definition, a repeating
enclosing loop, any binding in an enclosing function, and any
global/nonlocal rebind.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): model global and del like CPython's symbol table
`global x` and `nonlocal x` bind nothing; they redirect the declaring
function's own bindings of `x` to an outer scope. A bare declaration
was treated as an unconditional rebinding, so `@staticmethod` anywhere
in the file lost builtin recognition.
A module- or class-level `del` restores the outer lookup rather than
binding the name. Treat an unconditional `del` that runs after a
binding and before the decorator as undoing that binding. A `del`
inside control flow may not run, and a `del` inside a function still
makes the name local there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): order global rebinds and honor builtins re-exports
A function that declares `global staticmethod` and assigns it rebinds
the module name only when called, and it cannot be called before the
top-level statement that defines it runs. Treat such a rebind as
visible only when that statement precedes the decorator, or when the
decorator sits in a deferred class body. `nonlocal` rebinds stay
visible anywhere in the enclosing function.
`from builtins import staticmethod as staticmethod` binds the builtin
to its own name, so it no longer counts as shadowing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(python): scope the descriptor-identity CPython claim
The wildcard-import case expects the fail-closed resolver verdict, not a
CPython outcome, because the imported module's exports are unknown.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): resolve descriptor names as LOAD_NAME does
Model each binding by its effect on the namespace (Language Reference
4.2.1): an import that binds the `builtins` object itself (plain, aliased
to the same name, or `from builtins import *`) restores the builtin, a
module- or class-level `del` unbinds so lookup falls through, and any
other binding shadows. Resolve the decorator like LOAD_NAME (4.2.2):
the class namespace, then module globals, then builtins, each as it
stands when the `def` runs.
A restoring effect counts only as an unconditional simple statement that
runs before the decorator, so an import or `del` under `if`/`try` or a
loop stays fail-closed. A helper's `global` delete depends on whether
the helper is called, which the resolver does not model, so it keeps the
override.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor(python): simplify decorator descriptor lookup
Derive the descriptor type and name set from one `as const` list,
replace indexed non-null assertions with destructuring, build the scope
chain without an assertion, and skip the enclosing-function owner lookup
when the decorator has no enclosing function. Key the stacked-decorator
verdicts by case name so a failure names its case.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(python): resolve enclosing-function and deferred descriptor lookups
A class body reads a free name from the innermost enclosing function that
binds it (Language Reference 4.2.2). Evaluate that function's namespace
the same way as the class and module ones, so an unconditional
`from builtins import staticmethod` there resolves to the builtin. Any
other binding still shadows, including a local assigned only after the
class, which raises NameError rather than falling back to the builtin.
A class body inside a function runs whenever that function is called,
which can be any time after its top-level statement starts. Read module
state at that statement instead of after the whole module, so an earlier
`del` restores the builtin, and treat any later module override as
possibly visible.
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(python): suppress uncertain decorated receivers
* fix(ci): keep uncertain Python receivers out of method arity
Unrecognized decorators now leave the receiver kind unproven, but the
first parameter is still the implicit receiver slot for ordinary bound
calls. Method extraction stopped stripping it, so decorated methods
reported one extra parameter and shifted capture arity metadata.
Share the uncertain-receiver classification between type-binding
synthesis and parameter extraction, then refresh the Python capture
golden and benchmark fingerprint for the intended capture change.
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(python): resolve mixin calls with CPython C3 order
Breadth-first MRO bound a diamond mixin call to the wrong base, and dropping the site left the real method out of the graph. Use C3 and take the first compatible method in that order.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix(python): correct mixin receiver baseline counts
* fix(python): guard incomplete mixin inheritance
* fix(python): record unresolved MRO tail coverage (#3393)
Track a missing subtype target when the last indexed MRO owner has an unindexed parent, and cover the case with an integration test. Correct the C3 fixture description.
Note: local full npm test timed out amid parse-worker startup failures; focused tests and benchmark baseline passed.
---------
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>
* feat(mcp): add read_file + grep tools (REST parity for /api/file slice + /api/grep)
* chore(autofix): apply prettier + eslint fixes via /autofix command
* Address PR review feedback (#3377)
- Fail read_file and grep when full source is unavailable, matching the HTTP 410 contract instead of an empty grep or a not-found on a missing checkout.
- Reject branch on those tools so a pinned index is not labeled onto checkout bytes, and stop advertising branch in their schemas.
- Point the grep hint at a 0-based read_file window, pass caseSensitive and literal through, and test the handlers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3377)
- Keep read_file and grep in the multi-repo schema requirement without advertising branch.
- Reject negative maxLines and return integer slice bounds for fractional line positions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3377)
- Reject a negative read_file endLine before slicing so JavaScript does not treat it as an offset from the end of the file.
- Drop the fractional startLine/endLine claim so the integer schema is the advertised contract.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3377)
- Skip indexed grep paths whose realpath leaves the checkout so a symlink cannot return lines from outside the repo.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(bench): record the 19-tool MCP roster
read_file and grep are real tools, so tools/list and GITNEXUS_TOOLS both
moved from 17 to 19. The timing ratios were already inside budget.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(mcp): share read_file and grep contracts with existing helpers
Boolean grep flags go through isFlagTrue, the whole-file cap is one constant, and checkout tools stay on the per-repo schema without advertising branch.
---------
Co-authored-by: svjack <svjack@example.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(#2965): system headers must not resolve to in-repo files
C and C++ use two syntactically distinct include forms:
#include <x.h> -- angle-bracket: search system include paths only
#include "x.h" -- quoted: search relative to the including file first
The old suffix-match fallback in resolveCImportTarget had no awareness
of this distinction, so a repo containing its own stdio.h would capture
every #include <stdio.h> and resolve it to the local file.
Fix:
- Add isSystem?: boolean to the wildcard variant of ParsedImportSyntax
- interpretCImport / interpretCppImport now set isSystem from the
@import.system tree-sitter capture (present for angle-bracket form)
- resolveImportTarget in both cScopeResolver and cppScopeResolver
short-circuits to null when context.parsedImport.isSystem is true,
refusing to suffix-match system headers against workspace files
- Remove C and C++ from KNOWN_GAPS in the conformance test; add proper
test cases with a parsedImport factory that distinguishes angle-bracket
(isSystem:true) from quoted (isSystem:false) includes
Test: all 36 external-import-conformance cases pass, including the two
new c/cpp arms that were previously in KNOWN_GAPS.
* fix(#2965): update cpp-imports unit tests for isSystem field
Two test assertions were broken by the interpreter change:
1. Local include: the wildcard ParsedImport now always includes
isSystem (false for quoted includes). Updated expected object
to include isSystem:false.
2. System header: the old test asserted interpretCppImport returned
null for system headers. The refactored design moves the null
decision to the resolver layer (cppScopeResolver.resolveImportTarget)
so the call graph and resolution stay separate concerns. The
interpreter now returns { kind:'wildcard', isSystem:true } and
the test name/assertion are updated to reflect this.
* fix(#2965): resolve C and C++ includes on search paths
Angle includes follow each translation unit's include roots, so a local
stdio.h no longer captures system headers or another file's -I list.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3341)
- Accept in-repo include roots whose names start with `..` while still rejecting parent escapes.
- Correct the import-target bench comments so CONTEXT_LANGS and newPass match C/C++ header passes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(c,cpp): read include config per directory for monorepos (#2965)
Angle includes now resolve only against declared search roots, so config
read only at the repo root left monorepo sub-projects with nothing declared.
- compile_commands.json, compile_flags.txt, .ccls, .clangd and
c_cpp_properties.json are read in every directory; a file takes the
nearest one, and a nearer database's entry beats a shallower one (clangd).
- CMake include_directories / target_include_directories are read:
directory-scoped and PRIVATE roots reach their subtree, PUBLIC and
INTERFACE roots reach every file. ${CMAKE_CURRENT_SOURCE_DIR} and friends
expand; unresolvable variables and generator expressions are dropped.
- Declared roots win: implicit include/Headers/inc roots apply only when no
config speaks for the file, and never to a database-listed file.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(c,cpp): give CMake PUBLIC includes only to linked targets (#3341)
target_link_libraries now decides who sees PUBLIC and INTERFACE roots, so an unrelated target no longer resolves another package's headers.
---------
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: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(auto-sync): allow self-hosted remotes via allowed_hosts
Auto-sync skipped any remote whose host was not github.com, gitlab.com, or gitee.com. Operators can now name exact extra DNS hosts in watch_config.yml without opening the default set.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#3391)
- Dial auto-sync DNS names as absolute hosts and store that URL so a later fetch cannot follow a search domain.
- Reject ambiguous numeric host spellings; an exact dotted IPv4 the operator listed stays opt-in.
- Document allowed_hosts on the root auto-sync contract.
Note: pre-existing failure in unit tests that require dist/cli/index.js and parse-worker.js (this worktree has no build); not addressed by this PR.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(swift): discover nested Package.swift manifests for module grouping
A Swift monorepo laid out as Core/<pkg>/Package.swift has no root manifest
and no root Sources/, so every Swift file fell into one __default__ module.
The Swift resolver now walks the repo (bounded, skipping dot, ignored, and
Xcode bundle directories) and adds each nested package's targets, keyed by
repo-relative directory and ordered deepest-first so first-match grouping
picks the most specific target. Import resolution keeps the root view.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(swift): bound implicit IMPORTS edges by a total budget
Implicit same-module IMPORTS are n*(n-1) edges per module, and the graph
keeps every relationship in one Map capped by V8 at 2^24 entries. Emission
now fills a 4M-edge budget smallest module first and skips, with a warning,
any module that does not fit, so no module layout can crash analyze.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(swift): skip pairwise sibling passes for oversized modules
The target-siblings and sibling-type-bindings passes copy every file's
declarations into every other file of a module, so heap grows as n^2:
about 3.8 GB at 1,000 files with 15 defs each, about 15 GB at 2,000.
Modules over 1,000 files now skip both passes with a warning and resolve
through the global name fallback. GITNEXUS_SWIFT_MAX_MODULE_FILES changes
the ceiling.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(swift): cover nested SwiftPM packages end to end
A fixture with three nested Package.swift manifests (two declaring a target
named Net) and no root manifest. Each target gets its own implicit IMPORTS,
none cross packages, and Config() resolves to the caller's own package.
The Swift capture golden and scope-capture fingerprint grow with the new
fixture corpus only.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(review): apply review findings
- Rebase nested target paths with the existing Zig path helpers, which also
reject Windows drive paths and paths that resolve to the repo root (whose
empty prefix would group every file into one target).
- Document GITNEXUS_SWIFT_MAX_MODULE_FILES in the README env table.
- State that skipped modules resolve through lower-confidence fallback edges,
why nested-type fragments still run for oversized modules, and fix a stale
loader name in the target-grouping header.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(swift): cover every skipped Xcode bundle suffix in the package walk
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(swift): model modules the way the compiler does
Replace the caps from the first round with representations that stay
linear, and derive module identity from the same sources the compiler uses.
- Same-module visibility is one File -> Module IMPORTS edge per file
(reason `module-membership`) instead of an edge per ordered file pair.
Incremental importer expansion treats files sharing a Module hub as
importers of each other. The 4M edge budget is gone.
- Sibling declarations and type bindings live in one shared table per
module in the namespace channel C# uses since #1871, instead of being
copied into every file. The 1,000-file ceiling and
GITNEXUS_SWIFT_MAX_MODULE_FILES are gone.
- Modules come from root and nested SwiftPM manifests (Sources, Source,
src, srcs; plugins under Plugins; the newest Package@swift-X.Y.swift)
and from Xcode native targets in project.pbxproj, including Xcode 16
synchronized folders. Target paths are matched from the repo root, so
a vendored copy of the same layout is no longer grouped into a root
target (this reverses #2931's floating match).
- `import X` resolves to the modules named X instead of any folder named
X. A name no module carries is external when every manifest and
project was read completely.
- The global-name-fallback veto uses the same membership, so test
targets, custom-path targets and Xcode targets have module identity.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(bench): move the Swift package bench to module grouping
The bench still imported the removed groupSwiftFilesBySpmTarget. Its
first-wins probe keeps its meaning under root-anchored grouping: the
clash file sits under Sources/Mod0, and the Sources/Mod1 further down its
path is a vendored copy.
Plugins are now non-importable modules under Plugins/, so parse_targets
counts importable source targets (still 3) and parse_binary_skipped also
checks that the plugin is recorded that way. Baseline values unchanged;
the notes say why the definitions moved.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(swift): match compiler module names, manifests, and target filters
- Module names follow the compiler's c99 mangling (`my-lib` imports as
`my_lib`); Xcode targets use a literal PRODUCT_MODULE_NAME or
PRODUCT_NAME when the project sets one.
- Package manifests are one-file modules, as SwiftPM compiles them. Files
outside every target are one-file modules once every manifest and
project was read; otherwise they keep the shared __default__ module.
- Xcode 16 synchronized-folder exceptions add a file to a target that
does not list the folder, and remove it from one that does.
- SwiftPM `sources:` / `exclude:` narrow a target; a computed list marks
the manifest unreadable.
- The default target folder is chosen once per package, as SwiftPM does,
instead of per target.
Refs #3355
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Address PR review feedback (#3387)
- Inferred Swift folders keep SwiftPM's first predefined parent when a
target name repeats (Sources before srcs).
- The pbxproj parser rejects a \U escape without four hex digits instead
of decoding garbage, so the project reads as incomplete.
- Extension owners are stamped in every Xcode membership of a shared file.
- A plugin name never reaches a plugin: neither the fallback veto nor the
folder-index fallback treats a known non-importable module as imported.
- A file an Xcode target compiles keeps that membership even when it also
lies under a SwiftPM target directory.
- The workspace scan bounds the queue, not only the directories read.
- Integration tests require each module hub to exist before comparing.
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>