Skip to content

merge: 同步 257 个上游提交并兼容 Pinable 历史索引 - #6

Merged
PinableAgents merged 260 commits into
mainfrom
sync/upstream-2026-10-09
Oct 9, 2026
Merged

PinableAgents merged 260 commits into
mainfrom
sync/upstream-2026-10-09

Conversation

@PinableAgents

@PinableAgents PinableAgents commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

同步范围

  • Fork 起点:69f4e5d206c3f85aea2ae37142d77c43dfd725d0
  • Upstream main:b635dd467f0578926a9c01a37b9d28d2b26689f1
  • 共同祖先:7639c787e48be92ea4ca83c5c014d8dd480de470
  • 纳入 257 个上游提交,保留起点中 34 个 fork 独立提交;package/UI 版本同步到 1.6.2。
  • 最终 PR head:0c460b2f7638ca4529bf26bd2bf73ef24d09f66a
  • 最终文件树:f1c2b04a7b662bf37631380bffb378c327c5cd38
  • 真正的双父合并提交 5a72489f1d536e6cf76d786beb88754a43906f36 保留 fork/upstream 历史,其上正常追加两次小修复;没有 squash/rebase。
  • GitHub 测试合并 ref a628a68ece02846f1e32c3bb4c7be7025c4dcdb8 的父提交为 fork 起点与最终 PR head,文件树与最终 head 完全一致。

合并前验收已全部通过。以下统计均为最终 head/相同文件树的真实结果,不使用上一轮红灯或取消运行代替。仅通过 Create a merge commit 合入,禁止 squash/rebase。

冲突处理及减少重复冲突

1. 锁文件版本冲突

package-lock.json 仅将冲突的 workspace 版本合并为上游 1.6.2,保留 Pinable 的 @antv/g6 5.1.1、layout 依赖与双方非冲突更新。

2. 历史 schema 编号冲突

旧 Pinable v10/v12 与上游后续迁移编号不同义,不能整份覆盖或再次占用新的上游编号。保留全部上游数字迁移体和 v15 当前版本,独立 src/db/pinable-schema.ts 根据实际表/索引检测缺失能力,在事务内复用原上游 v10/v12 迁移体,补齐 fork 游标索引。保留原 schema_versions 记录、图数据与索引;只读连接不修复,较新未知 schema 不修改,健康重复打开不重复写入。

新增 10 项真实 SQLite 回归覆盖 legacy v12→v15、upstream v12/current v15、单个缺失索引、只读访问、失败原子回滚、幂等重复打开及未来版本保护。既有兼容测试只更新当前版本和模拟旧版本的历史范围,不降低断言。FORK.md 记录以后 fork 专属修复不再分配上游数字迁移编号,减少下一次同步的重复冲突。

3. 验证中拦截并修复的语义问题

  • 上游 StepsView 新增 Its callers and callees →,原中文模板覆盖测试失败。补齐词典,新增中文→英文→中文 DOM 本地化回归并断言 href 不变,保留原覆盖测试。
  • Windows 对普通文件下的子路径返回 ENOENT,原工作区错误路径测试失败。修复已存在的真实祖先必须为目录,不能把文件的后代当作离线项目。保留原断言,新增深层路径及文件符号链接两项真实文件系统回归;真正离线目录仍可使用。

本次共新增 13 项回归,同时进入完整套件及 Windows/macOS 定向验收。

最终验收结果

完整 CI:https://github.com/PinableAgents/codegraph/actions/runs/37891757909
桌面定向 CI:https://github.com/PinableAgents/codegraph/actions/runs/37891776905

检查 通过 失败 跳过
完整 engine/UI(545 个文件通过,16 个文件跳过) 6607 0 356
Rust kernel 构建 + 现有 4 个专项文件 134 0 0
Windows 2022 / Node 24.21.0,12 个定向文件 101 0 0
macOS 14 / Node 24.21.0,相同 12 个定向文件 101 0 0
既有同步工具回归 14 0 0
本地未修改的 runtime Python 工具 20 0 0
本地未修改的 watcher 探针 8 0 0

npm ci、npm run build、npm run build:lib 均通过;本地 Linux Go launcher 单元测试及 go vet 通过。未新增 skip、删除/降低断言或忽略退出码;356 个条件跳过没有计为通过,不宣称 100% 全部测试执行或覆盖率。

完整日志 artifact ID 11599210773,下载 ZIP SHA256 8b37634c6d9d79a423582aba967e445bd60e142f9b1c9a5831ddedf3e685864d;原生日志 artifact ID 11599065031。日志已实际下载、核对摘要和最终统计,新增 schema 10 项、原迁移 5 项、工作区路径 7 项、i18n 15 项和 UI 组件 53 项均通过。

桌面 CI 在隔离 maintenance 分支发起,但明确 checkout 最终 PR head 0c460b2,核对 SHA/文件树和祖先关系后才构建测试;不是使用 helper 源码冒充 PR 验收。前轮失败和取消运行保留为诊断,不作为最终成功凭据。

定制与操作边界

  • 使用既有 sync-upstream.mjs 正常三方合并与 --finish 更新 .pinable/upstream.json 为本次实际合并的上游 SHA;README 与该上游 blob 相同。
  • 保留浏览器 UI、G6、多项目工作区、中英文和主题定制,保留 CODEGRAPH_UI=1 显式开关与 Windows 隐窗修复。
  • Pinable runtime workflow、运行时配置和打包脚本、既有 sync-checks workflow 未修改;没有发布 Release/tag/npm。此验收不是六平台 runtime 重新打包或 Desktop 安装包端到端验收。
  • 不使用全局 ours/theirs、force push、reset、保护绕过或权限/发布凭据变更。
  • 临时历史导出/候选重现/跨平台检查工作流不进入本 PR 文件树或祖先。
  • Actions 令牌曾拒绝推送上游工作流变更;改用原本授权的 GitHub 连接以 expected-SHA 与非强推保护提交同一审核候选,未扩大令牌权限。

合并操作仍需最后重读 PR head、main、upstream main,防止并发改动;合并后核对上游祖先与落后提交数。

colbymchenry and others added 30 commits September 30, 2026 05:52
… never by name alone (colbymchenry#2125)

Found by the README-wide sweep — Django/Flask/FastAPI apps' most-depended-on
lists were test and serializer methods: netbox `UserConfig.all` (3,610 from
`X.objects.all()`), `PortSerializer.create` (2,093 from `.objects.create`),
a test-local `FakeQuerySet.count` (1,910); healthchecks `AuthTestCase.get`
(880 from `Channel.objects.get()`), `ProfileAdmin.login` (702 from
`self.client.login()`); mealie `_FakeHTTPResponse.json`.

Python keeps a call through a receiver it cannot name (`User.objects.get`,
`self.client.login`, `request.POST.get`) as the bare `get` / `login`, and
exact-name matching then took any class's method by proximity. The call's
shape is now read at its column (the call's start):
- `bare` (`get(1)`): not a method — Python has no implicit self — and not a
  project symbol when the file takes that name `from` a module the project
  doesn't contain (`from django.shortcuts import render`);
- `chained`: only a member of what the chain names last — a method of a
  class of that name (case/underscores ignored: `self.store` → `Store`,
  `self.user_service` → `UserService`) or a function/class in a module of
  that name (`app.helpers.slugify()` → helpers.py);
- `self.x()` / `cls.x()`, and chains split across lines: unchanged.
Fuzzy matching is case-exact for Python (`dir(…)` ≠ class `Dir`).
Lexical reachability now walks every enclosing scope, so a method of a
class declared inside a function is only reachable in there (memoized per
candidate).

A/B vs main (edge diffs, every change classified): netbox −18,091/+73
(indexes 8s → 5s), healthchecks −3,354/+361, pytest −2,950/+105,
mealie −1,802/+6, allauth −1,086/+57, DRF −943/+50, flask −167/+17,
httpx −147/+24. Gains are precise member links (`self.channel.notify` →
Channel.notify, `item.stash.get` → Stash.get, `config.cache.get` →
Cache.get, `self.provider.verify_token`); the one deliberate loss class is
calls through an instance whose type needs inference (`flask.g.setdefault`
in flask's own repo). excalidraw, typeorm, gson, okio, AutoMapper, gin and
hono byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…atic imports (colbymchenry#2126)

Found by the README-wide sweep: halo's most-depended-on symbols were an
`EmailVerificationService.verify` (1,038 — Mockito's `verify(mock)`) and a
builder's `eq` (845 — ArgumentMatchers `eq`); retrofit's a test helper's
`assertThat` (1,357 — Truth); mall's a DTO's `hashCode` (Object's).

A bare Java call (`verify(x)`, `helper()`, `this.x()`, `super.x()`) now has
only method candidates declared on a class around it, on one of that class's
supertypes (read transitively from the declarations' `extends` /
`implements` — the resolved edges don't exist yet on the first pass), or
imported statically (`import static a.B.m;` / `a.B.*`). Memoized per type
and per file.

A/B vs main (edge diffs, every change classified): halo −2,103/+145,
retrofit −1,380/+18, commons-lang −202/+87, jsoup −92/+45, mall −76,
gson −7/+4. Gains are the right target where one exists: halo's `and` /
`equal` / `isNull` through `import static …Queries.*`, jsoup's `attr(…)`
through `LeafNode`, commons-lang's `addExact` on its own class instead of a
nested MathBridge, retrofit's `getRawType` through `CallAdapter.Factory`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2128)

Calls in `.vue`, `.svelte` and `.astro` files carry their own language, so
the JS/TS rules from colbymchenry#2120 never applied to them: halo's `t('…')` from
`useI18n()` went to an interface property `t` (562 callers) and `ref(…)`
from `vue` to a local `ref` (523).

"Outside the repository" is now a package the importing file's package.json
chain (or a Deno import map) declares, a Node built-in, `@std/…`, or a
framework's virtual module. An alias the resolver can't follow — SvelteKit's
`$lib/…`, a nested Nuxt app's `~/…`, a nested app's own `@/…` — stays the
project's, and a `workspace:`/`file:`/`link:` dependency is in the repo.

SFC files also get the JS built-ins (`fetch`, `Error`, `Map`), and a file's
own import of a built-in's name (`import Map from './Map.svelte'`) is the
import. Untyped three-segment chains (`api.groupReports.getAll()`) keep
resolving in SFCs, where they reach their API client class.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…repo (colbymchenry#2129)

create-t3-turbo, tamagui's starter and react-native-reusables keep an Expo
app beside a Next.js app. Expo Router read the Next app's `app/` folder as
its own, so `layout.tsx` became a `/layout` screen, `page.tsx` a `/page`
one, `_components/posts.tsx` and `api/auth/[...all]/route.ts` screens too,
and a shared `packages/app/` library's files became screens as well.

A resolver can now name `appDependencies`; its extractor runs on a file
only when that file's package.json or an enclosing one declares one of
them. When no manifest in the project declares any, detection found the
framework by other evidence and it runs everywhere, as before. Expo Router
names `expo-router`, Next.js `next`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…uxt routes only in Nuxt (colbymchenry#2130)

vue-element-admin, vue-admin-template and vben had no routes: their tables
are named arrays (`export const constantRoutes = [...]`, `const routes:
RouteRecordRaw[] = [...]`) or per-module objects (`const tableRouter = {…}`)
handed to `new Router(...)`, and most of their screens are `children`.

The Vue Router reader now reads those tables in any file that builds a
router, imports vue-router or lives in a router/ directory; joins children
onto their parent's path (a parent is a screen only when no child claims
its address and it doesn't redirect); binds a lazy view by the FILE it
imports (all of vue-element-admin's are `…/index.vue`), through an alias the
resolver can't follow by the one file in the app with that path; links each
screen to its parents' components as layouts; and takes `this.$router.push`.

Nuxt's file routes move into their own `nuxt` resolver, detected only in a
Nuxt app: halo's plain-Vue console got 30 made-up screens from its `pages/`
folders. A Nuxt app at the repository root now gets its routes, and a
top-level `pages/index.vue` is `/`, not `/index`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rs (colbymchenry#2131)

A `(group)` directory shares a layout and is never part of the URL, and a
parameter's `=matcher` checks its value. Kept in the name, shadcn-svelte's
`src/routes/(app)/(layout)/blocks/+page.svelte` was `/(app)/(layout)/blocks`,
so no `goto('/blocks')` or `<a href="/blocks">` ever reached it and its
Screens showed no navigation at all.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry#2132)

react-boilerplate's header links are `export default styled(Link)`…``,
used as `<HeaderLink to="/features">`; takenote's guards render v5's
`<Redirect to="/" />`. Neither tag was read, so both apps had routes and
no links between their screens.

The link synthesizer now reads `<Redirect to>` (navMethod `redirect`) and
any styled-components / emotion wrapper of `Link` / `NavLink` — declared in
the file, or imported from a file that declares or default-exports one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…elative navigate (colbymchenry#2133)

From the router sweep:
- angular-spotify had 12 routes all named `/` and no navigation. Its lazy
  routes are `async () => (await import('@ws/home')).HomeModule` through an
  Nx library's `src/index.ts` barrel, which the mount pass never looked
  through; and every lib is its own `src/`, so each routes file got its own
  route table and no template's `routerLink` found the screen it names.
- jira-clone had 3 routes and no navigation: its issue route is
  `issue/:${ProjectConst.IssueId}` (a class `static readonly`), its root
  redirect lives in `app.routes.ts`, which only mounts, and
  `navigate(['project', 'issue', id])` has no leading `/`.

Now: lazy mounts follow `export … from` barrels (and an NgModule's routing
imports); `(await import(x)).M` is read; route constants may be class
statics (strings or objects), enum members, or live behind a barrel, and a
template-literal path takes constant holes; a routes file that only mounts
contributes its redirects at its mount prefix; the route table is keyed by
the `angular.json` / `nx.json` workspace; and `router.navigate` /
`createUrlTree` without `relativeTo` resolve a bare first command from the
root, as Angular does (template `routerLink` stays relative).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ly imports (colbymchenry#2134)

- Expo Modules: a JS call on a `requireNativeModule('N')` binding — typed
  (`<CameraNativeModule>`) or not, local or imported (expo-camera's
  `import CameraManager from './ExpoCameraManager'`) — resolves to module
  N's declared function, Swift first and Kotlin beside it, else to the
  member on the binding's declared type. It went to the same-named static
  method making the call.
- A JS/TS member call on an import binding never picks a method declared in
  the calling file, and ruling those out never manufactures a unique guess.
- `import type { X }` was parsed as a default import named `type` (every
  type-only import bound `type`), and `{ a, type B }` bound `type B`.
- A name the file binds from an out-of-repo package names nothing in the
  project, for every reference kind, and JS/TS fuzzy matching is exact-case:
  trpc's 402 `Record<…>` references went to a `record` property, element-plus's
  121 `mount` imports from @vue/test-utils to a test helper.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…st decides (colbymchenry#2135)

- `app/blog/og-image/[post]+api.ts` was a screen named
  `/blog/og-image/[post]+api` (evanbacon.dev). An Expo API route is now one
  endpoint per exported HTTP method (`GET /blog/og-image/:post`), bound to
  its handler, the way Next.js `route.ts` files are.
- The per-app framework gate (colbymchenry#2129) took ANY enclosing manifest's
  declaration: react-native-true-sheet's root declares expo-router for its
  example app, so its `docs/` Next.js app got 16 Expo screens (`/layout`,
  `/page`, `/api/search/route`, …). Walking up from a file, the first
  manifest that names any gated framework now decides.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ymchenry#2136)

Extraction keeps `super.didMoveToWindow()` under the bare method name, so
every strategy resolved it to the enclosing override itself. Across the
bridge sweep that was 139 self-edges on Charts, 142 on commons-lang, 101
on BookStack, 92 on realm-swift, 82 on react-native-svg — lifecycle
overrides and delegates drawn as recursion.

`gateSuperSelfCall` declines a `calls` result that targets the calling
node when the call site is written through `super` / `base` (C#) /
`[super …]` (Objective-C) / `parent::` (PHP) / `super().` / `super(C, self).`
(Python). Plain recursion keeps its edge.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…SX via default imports (colbymchenry#2137)

- `requireNativeComponent('X')` (Paper) now emits the same JS `component`
  node a Codegen spec does, so the Fabric pass links it to the native view /
  manager classes; the extractor also runs on `.js` / `.jsx` spec modules
  (segmented-control's spec is Flow `.js`). react-native-maps' `AIRMap`
  gains its iOS implementation.
- A JSX tag that names no node is the file's DEFAULT import of a module's
  one component: `<RNCSegmentedControlNativeComponent>`, and element-plus's
  test `<Autocomplete>` for `autocomplete.vue` (whose component node is
  `autocomplete`). A named import never takes a barrel's one component.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…hannel (colbymchenry#2138)

The rn-event-channel pairs a native emit with a JS listener on the event's
LITERAL name. Most libraries name events through constants — NetInfo
listens with `addListener(PrivateTypes.DEVICE_CONNECTIVITY_EVENT, …)`, many
emit with `.emit(EVENT_NAME, …)` or `sendEventWithName:kEvent` — so their
channels were empty.

Identifier / member-chain event names are now read to the literal their
declaration holds (JS/TS const & enum, Java `static final`, Kotlin
`const val`, ObjC `NSString *const` / `#define`, Swift `let` / enum case),
scoped as the language scopes them: a bare name in the same file or behind
an import, `Owner.NAME` inside Owner or a namespace import's file. The
handler of a constant-named listener is a function in the same file, else
the enclosing one. A first cut that read constants by name alone turned a
`.emit(eventName, …)` parameter into some test's `eventName = "pong"`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e languages (colbymchenry#2139)

The last-resort fuzzy match looks names up in a lowercase index — right for
PHP, Pascal/Delphi, CFML, COBOL and VB.NET, whose identifiers ignore case,
and wrong for every other language. jsoup's 1,844 `@Test` annotations
decorated a `CharPredicate.test` method; commons-lang's `new BitSet()`
instantiated a `bitSet` field accessor 64 times; mall's `new Info()` an
`info()` endpoint; gson's `Method` type a test's `method()`; fmt's
`#include <optional>` gtest's `Optional`.

This generalizes the per-language exact-case rules (Rust, Python, JS) into
one: outside CASE_INSENSITIVE_LANGUAGES a fuzzy candidate must match the
reference's exact name. The cross-language fuzzy-gate test now builds its
winner from an exact-case name.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ls (colbymchenry#2140)

A `<script>` block goes through the TS/JS extractor, which extracts no
symbol for a method in an object literal — so every Vue 2 component, and
every Vue 3 one not on `<script setup>`, had no `handleLogin`, no
`mounted`: their calls, their `this.$router.push`, and the template's
`@click="handleLogin"` all belonged to the file.

`vue-options-api.ts` reads the options object the default export is
(directly, or via defineComponent / Vue.extend / Vue.component) and names
each function in it — `methods`, `computed` (incl. get/set objects),
`watch` (incl. `{ handler }`), `data`, `setup`, lifecycle hooks, Nuxt 2's
asyncData/fetch — as `method` nodes owned by the component; the Vue
extractor re-attributes the references and edges written inside each.

A component's own method is only reached as `this.m()` in that component:
the member-call and exact-name strategies no longer hand it
`e.preventDefault()` (ImageCropper's `preventDefault` method took every
event's call), `this.editor.setValue()`, or `this.$refs['input'].click()`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…n()` is a bare call (colbymchenry#2141)

- The name prefilter checks a case-SENSITIVE set of known names, so in PHP,
  Pascal, CFML, COBOL and VB.NET — whose identifiers ignore case — a call
  written in another case (`formatprice()` for `FormatPrice`, TestBox's
  `ToMatchWithCase()`) was dropped before any strategy ran. Those languages
  now also check a lowercase set (built lazily, only when asked).
- `isBarePhpCall` took any `>` before a name for `->`: every call after `=>`
  in a PHP array (`'total' => count($items)`, `'by' => user()->id`,
  `'label' => trans('…')`) was a member call, and found a same-named method
  — 166 wrong edges on BookStack (`trans` → LocaleDefinition::trans,
  `new Response` → ImageStorageDisk::response, `url()`, `config()`, `count()`).
  It now requires `->` / `?->` / `::`.
- A fuzzy `instantiates` never names a method.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…archy (colbymchenry#2142)

Extraction records `$this->m()` under the bare name, so exact and fuzzy
matching took ANY same-named method: Drupal core's tests' `$this->assertEquals()`
(PHPUnit's) went to a comparator class 8,832 times, `$this->assertSession()`
to WebDriverTestBase 9,556 times, `$this->t()` to `Views::t` 6,811 times;
Laravel's `$this->assertCount()` to FilesystemAdapter 1,722 times.

A call written through `$this->` / `self::` / `static::` (or `parent::`) now
takes only a METHOD of the enclosing class's ancestry — `extends` and trait
`use`, read from source and resolved through the file's `namespace` / `use`
imports (aliases included) to fully qualified classes — or a subclass's
(a base calling what a subclass defines). Past an ancestor outside the
repository (Orchestra's TestCase) a repository trait's method still counts,
an unrelated class's never does. Inside a trait, `$this` is the using class,
so trait bodies are not scoped. Multi-line `use A,\n B;` trait lists parse.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A Lua `local` (variable or function) belongs to the file that declares it,
but cross-file name matching treated it like a global. kong's spec helpers
re-bind busted's globals (`local it = it`, `local assert = require
"luassert"`), and every other spec file's `it(…)` / `assert(…)` /
`describe(…)` linked to them — 15,776 edges; koreader's `local ipairs =
ipairs` in one module took the project's `ipairs` / `pairs` / `type` /
`tonumber` (2,329).

isVisibleAcrossFiles now reads a Lua/Luau variable or function's
declaration line and rejects a `local` one from another file (the reference
stays unresolved rather than taking a runner-up). Globals stay visible.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…enry#2144)

testthat runs each test file in an environment of its own, so a variable a
test assigns is invisible to the package and to other tests. ggplot2's
`c <- ggplot(…)` in test-facet-grid-.R was the one node named `c`, and took
2,455 of the package's `c(…)` calls (the sweep's top-depended symbol).

isVisibleAcrossFiles rejects an R variable/constant under `tests/` from
another file — except `helper-*.R` / `setup-*.R`, which testthat sources for
every test. (A first cut that limited every R call to function nodes lost
ggplot2's factory-defined `geom_point <- make_constructor(…)`, dplyr's
`summarize <- summarise` and shiny's aliases.)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A receiver-less Dart call now reaches only a method of the enclosing class,
something it extends / mixes in / implements, or an extension on one of those
types, and the nearest one wins. The hierarchy is read from each declaration's
head (comments and type arguments dropped), since supertype edges don't exist
on the first pass. Body-less abstract members (extracted as owned functions)
are scoped too.

Mixin-application classes (`class A = B with C;`) were extracted as
`<anonymous>` with no supertypes; both the wasm extractor and the Rust kernel
now name them and emit their extends/implements refs (kernel parity verified
on bloc, riverpod, shelf, dio, getx).

A/B (edges removed / added): riverpod -1945/+517, shelf -785, getx -855/+18,
bloc -178/+95, dio -78.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2146)

A Kotlin call's top-level candidate (a function, an extension function, a
property) must be in the caller's package, imported by name, or in a
star-imported package. Class members aren't judged: a lambda's receiver can
put any type's members in scope.

A function type with a qualified receiver (`(A.Builder.() -> Unit)?`) broke
the tree-sitter-kotlin parse badly enough to drop the enclosing class;
an offset-preserving preParse blanks the qualifier (hoisted, so the kernel
parses the same bytes — parity 0 diffs on Exposed, okhttp, nowinandroid).

A/B (edges removed / added): Exposed -5370/+4256 (1,318 R2DBC assertEquals
misroutes; DatabaseTestsBase's members and 250 extends/imports edges back),
koin -965/+960, okhttp -690/+675 (567 MockResponse to the imported
mockwebserver3 class), okio -98/+44, nowinandroid -14.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nry#2147)

Ruby types nothing, so two guesses produced most of a Ruby repo's wrong
call edges:
- `receiver.m()` through an untyped receiver took the project's one method
  named `m`. It now also needs a word shared between the receiver and the
  method's owner (`web_push_request.legacy_encrypt` → WebPushRequest);
  `self` / `self.class` are exempt.
- a bare `m()` inside a class took any class's `m`. It now reaches only the
  class's ancestry — superclasses and include/extend/prepend modules, read
  from source and resolved against the lexical nesting. Calls in module
  bodies and top-level blocks (specs, DSLs) are not judged.

A/B (edges removed / added): rubocop -4406/+262, mastodon -2642/+540,
jekyll -343/+10, lobsters -243/+4, sinatra -207/+2, devise -112/+3.
Deliberate losses: instance_eval'd DSL blocks inside a class method
(sinatra's own tests' `mock_app { get '/' }`), and helpers on a framework
class the repo reopens past an external superclass (devise setup_mailer).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2148)

- A bare call in a .cfc reaches only the component's own methods and those
  of the components it extends (extends="a.b.C" read from source, matched
  to indexed .cfc files by longest path suffix). Calls in .cfm templates and
  multi-line chain links (name not on the ref's line) are not judged.
- The untyped-receiver rule from the Ruby fix now covers CFML too: the one
  project method named `m` needs a receiver named after its owner. The word
  match also accepts the receiver's whole name (`cbsecurity` → CBSecurity)
  and ignores trailing digits (`executor1` → Executor); Ruby is
  byte-identical to main.

A/B (edges removed / added): coldbox -2429/+295, TestBox -255/+31,
fw1 -103/+4, cbsecurity -8/+5, cfdocs 0. Deliberate losses: framework-repo
calls through conventionally named, untyped receivers (ColdBox's `event.*`
→ RequestContext, TestBox's `variables.assert.*` → Assertion).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…bymchenry#2149)

VB.NET's extractor keeps a call's last name only, so `Me.Panel.Controls.Add(x)`
and `New System.Drawing.Size(1, 2)` arrived as bare `Add` / `Size` and exact /
fuzzy matching took any class's member (even an enum case). The receiver is
now read from the line: a member candidate needs `Me` / `MyBase` / `MyClass`,
its own type or module as the last receiver segment, or a `(Of T)` type
argument (`GetService(Of Notifier).Notify()`). Genuinely bare names are
unchanged.

A/B (edges removed / added): SCrawler -3452/+6, remoteapptool -143/+0,
CompactGUI -31/+5, ViVeTool-GUI -1, TaskbarX 0.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…enry#2150)

* fix(objc): a message reached by name alone must be in reach

- `[self m]` / `[super m]` / `[[self class] m]` (receiver dropped by the
  extractor) reach only methods of the sender's hierarchy, read from
  `@interface Name : Super` plus a small UIKit/AppKit/MapKit superclass table
  for categories on system classes. When tree-sitter-objc loses an
  @implementation and its methods become functions, a same-file function,
  one in the file named after a hierarchy class, or any when the sender's
  class itself was lost, still counts; `[super init]` never lands on another
  file's function.
- C call syntax (`completionBlock()`) never means a method or property.
- Local receivers are typed from ObjC declarations (`FMResultSet *rs`,
  `(nullable FMResultSet *)rs`, block params), and objc joins the untyped-
  receiver languages for the unique-name guess (`weakSelf` / `strongSelf`
  count as self). Shared receiver words must be 3+ letters so class prefixes
  (`SD`, `NS`) don't match; Ruby/CFML unchanged but one wrong lobsters edge.

A/B (edges removed / added): SDWebImage -321/+47, fmdb -28/+85,
CocoaLumberjack -19/+10, AFNetworking -13/+1, MBProgressHUD 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(objc): a class or typed-property receiver keeps its inherited method

`[AllTypesObject objectsInRealm:…]` (a class method inherited from RLMObject)
and `[managed.anyDataObj containsObject:]` (a property declared
`RLMSet<…> *anyDataObj`) now keep the unique-name match when the receiver's
class — or one of the property's declared types — has the method's owner in
its hierarchy. realm-swift goes from -2562/+588 to -504/+529 vs main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…2151)

A bare C# name (a `calls` or `references` ref with no dot, where the line
shows no receiver) can mean a member only of the types around it (outer
classes included), their base types (read from `class X : Base, IFoo`
heads, every partial declaration), or a static using: the file's
`using static`, any `global using static`, or `<Using Include="…"
Static="true"/>` in the .csproj / Directory.Build.props files above it.
Names outside any recovered type are not judged.

A/B (edges removed / added): Newtonsoft.Json -1394/+910 (enum-valued
properties now reach the enum type), AutoMapper -135/+58, eShop -117/+5,
serilog -20/+17, CleanArchitecture 0.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…or it (colbymchenry#2152)

PHP joins the untyped-receiver languages for the unique-name method guess:
`$x->m()` takes the one project method named `m` only when the receiver
shares a word with its owner, or names a class (the whole name, or its last
camel word: `$newRole` → Role) whose ancestry — extends + trait `use`,
namespace-aware — has the method.

A/B (edges removed / added): guzzle -359, BookStack -302, console -124,
Slim -124, monica -53. Removals are PSR-7 / framework / exception calls
bound to a test double or unrelated helper; the deliberate loss is calls
through receivers named after neither (`$loaner->loansAsLoaner()`).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2153)

The unique-name method guess no longer applies to a Lua/Luau call through a
standard or host library table (string, table, math, io, os, coroutine,
debug, utf8, package, bit, jit, ffi, vim, ngx, busted's assert/spy/stub/mock,
love) unless the project defines that function on the table itself (kong's
globalpatches define ngx.sleep), nor to a string method called on a value
(`s:find`, `s:match`, `s:gsub` …). Other untyped receivers keep the guess:
a word rule like Ruby's was tried and lost koreader's OO calls
(`self.ui:reloadDocument()` → ReaderUI).

A/B (edges removed / added): kong -2742, koreader -101, lazy.nvim -84,
telescope.nvim -47, Fusion -9, lua-resty-core -1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ymchenry#2154)

`bp.add_url_rule('/path', 'name', view_func | positional view, methods=[…])`
and a project's own wrapper that hands a list of paths and a `view_func=`
over (flaskbb's `register_view(auth, routes=[…], view_func=X.as_view(…))`)
now produce route nodes linked to the view. A class-based view is ANY unless
`methods=` says; a function defaults to GET. A call whose path isn't a
string literal (the wrapper's own `add_url_rule(route, …)`) adds nothing.

flaskbb 0 → 103 routes (+104 edges); flask's own tests +27; CTFd, microblog,
bluelog byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2155)

- Route files (`routes.draw`, or under config/routes/) are read as a block
  structure: `namespace` / `scope` add path + controller module, nested
  `resources … do` sit under `/:parent_id`, `member` / `collection` add
  `/:id/x` / `/x` actions, `root` and `resource` / `resources` options
  (`only:` / `except:` across lines, `path:`, `controller:`) are honored;
  `do` and `if` / `unless` / `case` / `begin` open frames an `end` closes.
- Detection accepts `gem "rails"` (double quotes), railties, and a Rails
  engine's `config/routes.rb` anywhere (solidus).
- A namespaced `controller#action` only reaches controllers of its own
  module path when any exist — an admin controller that inherits `index` is
  not the API's `ZonesController#index`.

Route→action edges main → this branch: mastodon 221→578, solidus 0→319,
rubygems.org 108→192, maybe 127→189, lobsters 141→196; no non-route edge
lost except solidus's CamelCase refs now resolved by the Rails model
resolver (`Order` → Spree::Order model, was a state-machine module).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry and others added 25 commits October 7, 2026 09:16
…ater (colbymchenry#2425)

A router's navigation call (`history.push('/login')`, `navigate('/login')`,
`router.push('/x')`, `goto('/x')`) names its route by path and its reference
by the router's method, so sync never noticed a route that changed in another
file. A synced index then kept answers a fresh index of the same files does
not have (CG-33):

- A call parked as failed while its route was missing never matched the colbymchenry#1240
  retry, which keys on the names the synced files define.
- A call bound to a catch-all, a parameter route or the other arm of a
  conditional kept that binding.
- A route renamed in place by runPostExtract kept the calls bound to it.
- Template links from a routes file that matches no synthesis trigger were
  never redrawn.

When a navigation router is detected, sync now reads the route nodes at its
first file change and again after runPostExtract. For each route on one side
only, it puts back the calls the routers' new `navigation` hook (method tails
and app scope) says that route can answer: the failed ones, and the
`navigates` edges a resolver made. The orchestrator's orphan sweep resolves
them, under the same 500-per-name ceiling, and the sync refreshes synthesis.
This covers re-extracted and removed files, postExtract renames and
cross-file table routes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2419)

goImplementsEdges (colbymchenry#584) offered only structs as implementers. A Go defined
type over a slice, map, function or basic type declares methods the same
way (gin's `formSource map[string][]string` has TrySet, prometheus's
`staticDiscoverer []*targetgroup.Group` has Run, an adapter
`HandlerFunc func(...)` has ServeHTTP) and is extracted as a `type_alias`
that owns them through go-method-contains edges. So it never satisfied an
interface, and interfaceOverrideEdges, which walks only class, struct and
union, could not link a call through the interface to its methods.

Structs and defined types are now candidates together, in one canonical
(file, line) order. A defined type declares every method it has, so under
the per-interface cap it ranks with the structs that declare theirs, ahead
of promoted-only ones. interfaceOverrideEdges also walks Go `type_alias`
nodes. Every Go type's embeddings come from the batched prefetch (the
per-type lazy lookup for defined types is gone), and a type without a
method is dropped before the per-interface loop. A true alias
(`type A = B`) is not extracted, so it never implements anything itself.

iterateNodesByKindIn takes several kinds, interleaved in its one order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…owercase name calls that type's own member (colbymchenry#2413)

A C++ call on a variable, parameter or member the calling function or its
class declares as a namespace-qualified type the project does not declare,
with a lowercase name (std::string, std::vector, absl::flat_hash_set, ...),
no longer falls through to Strategy 3's guess by the method's name. leveldb's
`std::string saved_key_; saved_key_.clear()` reached Slice::clear, protobuf's
`std::string proto; proto.append(...)` LeftoverBuffer::append.

The gate applies to `.` on a value and `->` through a raw pointer; `->` on a
smart pointer, iterator or optional still reaches the element type. It skips
declarations it cannot trust: read from outside the caller's function and
class (including a class nested in the caller's), contradicted by the call
(`.` on a pointer), shadowed by an unreadable re-declaration (`auto`,
range-for, structured binding), unqualified, with unbalanced `<>` (the tail
of a multi-line declaration), or a `_t` name (scalars, tags, traits like
std::conditional_t). A `std::` name counts as the project's only when the
project declares it in `std` (vendored googletest's testing::internal::string).

Receiver inference now blanks comments before matching declarations, so
`// ... non-null imm_` no longer types leveldb's imm_ as `null`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t names (colbymchenry#2417)

tree-sitter-go parses `type Event = mvccpb.Event` as a `type_alias`, beside
the `type_spec` of a defined type, and both extractors read `type_spec`
only. etcd's client/v3 `Event` had no node; a use through its package
(`clientv3.Event`, prometheus's `storage.AOptions{…}`) linked to nothing,
and a use inside it linked by name to any type so named, another package's
included.

- Extraction (TS and kernel): an alias is a `type_alias` node referencing
  each type its right-hand side names, at the name so resolution reads the
  package back; an alias of a struct or interface literal is a struct or
  interface. The alias's own type parameters and Go's predeclared types are
  skipped. A generic alias, which tree-sitter-go 0.23 parses as a
  `type_spec` around an error, reads as an alias on wasm; the kernel defers
  its file.
- A method called on an alias-typed value is the aliased type's: an alias
  of a project type is followed in its own package, through pointers and
  further aliases; one of an outside type resolves by name as before.
- An alias's target written through a package that is none of the file's
  imports as indexed stays unresolved, as an embedded type's does, so the
  alias never links to itself or a namesake.
- With Go defined types as implementers (colbymchenry#2419), an alias that owns
  methods written with it as the receiver implements what they satisfy,
  in place of the type it names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers no JSX child (colbymchenry#2442)

The jsx-render pass reads tag names off a parent's source with a pattern,
and two kinds of name it read that way linked a same-named class or
component elsewhere:

- A name written only in a type argument or type parameter list:
  outline's `<PaginatedList<Document> …>` and `useState<User>()`, or
  bulletproof-react's generic `<Entry extends BaseEntity>(…) =>`. A `<Name`
  right after an identifier opens a type argument list (the rule colbymchenry#2420
  applies to the values a file declares, here for every name), and a
  `<Name extends …` opens a type parameter list.
- A name the parent binds itself by its first tag, a `const`/`let`/`var` or
  a parameter (name-matcher's `jsCodeBindsName`, now exported): outline's
  `const Content = variant === "dropdown" ? DropdownMenu.SubContent :
  ContextMenu.SubContent`, `(Widget, index) => <Widget />`. Such a tag
  renders only a component the parent declares inside itself, else
  nothing. The check runs only for names the parent also writes outside a
  tag. Destructured names still link by name, as for a destructured call.

Fresh before/after indexes on 13 repos (main ed199e6): 66 jsx-render
edges removed and none added; no other edge, node, ref or file changes.
Every removal checks out against the TypeScript compiler (38 names with
no JSX tag in the parent, 28 tags bound to a local); 64 of the 66 targets
were wrong, 2 right by luck. mantis, proshop_mern, next-saas-starter and
create-t3-turbo are byte-identical, and Screens is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edder's method of that name (colbymchenry#2430)

Go has no inheritance. A struct that embeds another (`type Engine struct
{ RouterGroup }`) has an `extends` edge to it since colbymchenry#2397, and the
interface-dispatch bridge read every `extends` edge as an override, so it
linked RouterGroup.Use to Engine.Use. A call on a *RouterGroup only ever
runs RouterGroup's own Use, so a flow or impact walk through it went on
into Engine.Use and the code only the engine runs.

The bridge now skips a Go type's supertype edge unless it points at an
interface: only a call through an interface dispatches. Structs that embed
an interface keep their links, and so do the go-implements edges, so a call
through IRoutes still reaches both RouterGroup.Use and Engine.Use. Other
languages are unchanged.

Removed on gin / prometheus / etcd: 1 / 112 / 60 interface-impl edges, all
from a struct's method to a struct embedding it; nothing else in the graph
changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…can't read (colbymchenry#2449)

tree-sitter-cpp has no preprocessor, so an attribute macro where it
expects a name or a type misparses the declaration around it. The C++
preParse already blanked one export macro before a class name, an inline
macro before a return type, and a lone macro line before a declaration.
It missed:

- several macros, or one with arguments, between `class` and the name
  (protobuf's generated `class PROTOBUF_EXPORT
  PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED Any final : public Message`,
  rocksdb's `struct ALIGN_AS(64U) HandleImpl`), and a macro before a
  partial specialization's name;
- a macro between a pointer and the declared name (`const Descriptor*
  PROTOBUF_NONNULL descriptor()`), after a parameter list
  (`unknown_fields() const ABSL_ATTRIBUTE_LIFETIME_BOUND {`, leveldb's
  `LOCKS_EXCLUDED(mu_) {`), after a declared name (`int count_
  GUARDED_BY(mu_);`), or opening a declaration
  (`PROTOBUF_FUTURE_ADD_EARLY_NODISCARD absl::string_view name() const`);
- a lone macro line with a comment under it ({fmt}'s
  `FMT_BEGIN_EXPORT` above `// A generic formatting context ...`).

Every generated protobuf message class misparsed, so its members were
indexed as namespace-level functions; leveldb's annotated methods became
phantoms named after the annotation.

The new passes match on the code alone (comments and directives as
spaces, literals as placeholders), blank only the macro tokens, and keep
every offset. They run in the hoisted preParse, so files that now parse
clean go through the native kernel, at parity. The passes share one mask
per file: a pass that blanks nothing hands the next the same string, and
one that blanks hands it the mask blanked the same way.

colbymchenry#2426's brace-scope fixtures misparsed only through
PROTOBUF_FUTURE_ADD_EARLY_NODISCARD and ALIGN_AS(64U), which the preParse
now blanks. They now use macros it doesn't know (NODISCARD,
cacheline_aligned(64U)), with the same expected scopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xport (colbymchenry#2433)

`const X = require('./x').default` and `const { default: X } = require('./x')`
map to a named import of `default`. That is right for a CommonJS module that
sets the property itself (`exports.default = fn`, or the dual
`module.exports = X; module.exports.default = X`), but a module written as an
ES module and compiled to CommonJS sets it to its default export, which is no
named export. References through the binding stayed unresolved (bitwarden's
`new OsBiometricsServiceMac(...)`) or fell to name matching: the local
variable holding the module, or a namesake method in another package.

findExportedSymbolWalk now falls back to the module's ESM default export when
the named lookup finds no `default`. Only for JS-family refs, only in the
module the require names (`export * from` forwards no default), and only when
that module has an ESM default export at all: a single-file component, or an
`export default` statement. A CommonJS module's `exports.x = function`
declarations are flagged exported, so without that gate the
first-exported-function guess would invent a default for it.

bitwarden +2 edges (the two failed `instantiates` refs). react-native +872
-617: all 617 re-resolved at the same site (541 from the local require
binding to the real function or class, 47 from a namesake method in another
package to the right module, 28 metadata only, 1 from a type signature to the
implementation), 870 of 872 added edges on the declared default or what it
holds. express, eslint, mocha, koa, body-parser, fastify, topcoder,
proshop_mern, next-saas-starter and create-t3-turbo byte-identical. Dead-code
claims unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…method (colbymchenry#2444)

Both extractors record `srv.(KVServer).Range(ctx, in)` as a bare `Range`
call at the column where its receiver expression starts, as they record
any call through an expression. Resolution matched the name alone, so
every handler protoc-gen-go-grpc emits went to the `UnimplementedKVServer`
stub beside the `KVServer` interface (72 edges in etcd), and calls like
`c.Reader.(*pipe).Close()`, `p.(Pausable).Pause()` or
`v.(featuregate.MutableFeatureGate).Set(…)` linked to a namesake or to
nothing.

The call's chain is now read back from its column (operand, selectors,
assertions, calls and indexes, past strings, runes and comments, and on to
the next line after a trailing `.`). When the link of the call's name
follows an assertion, the call is a method of the asserted type, found
where Go finds it:
- a bare `T` or `*T` in the call's own package, or one it dot-imports;
- a `pkg.T` in the imported project package;
- its own method, the one its interface declares, or one promoted from a
  type it embeds, through the package-scoped lookup colbymchenry#2361 added; an alias
  is followed to the type it names (colbymchenry#2417).

A type from outside the project (`http.Flusher`), a predeclared one
(`error`), an alias of an outside type (`type Ctx = context.Context`, for
which the package-scoped lookup would fall back to matching by name) or a
type literal (`interface{ Flush() }`) links nothing. The check runs ahead
of the framework, import and name strategies, and ahead of the built-in
filter, which dropped a method named `close` or `copy` called through an
assertion. Two calls of one name in a chain share the column
(`b.(*Builder).Add(1).Add(2)`); both are taken for the one made through the
assertion, whose edge is there either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a test (colbymchenry#2434)

Google names C++ tests `foo_unittest.cc` (protobuf, Breakpad, glog,
Chromium), not `foo_test.cc`. The test-file check wanted a separator right
before `test`, so each one read as production code: search and explore
ranked it alongside the code it tests, `codegraph affected` never listed
it, and the resolver's test rules ran backwards for it. Production calls
landed on a unittest's own declarations (`stack_frame_entries_.size()` on
a unittest's `StackHelper`, a template's `AddressType()` on a unittest's
typedef), and a unittest's calls into test suites outside what it
includes (a Python `*_unittest.py` using a `tests/` fake) were treated as
production calls. colbymchenry#2421 already links a C/C++ unittest to the test
helpers it includes.

`unittest` and `unittests` join the separator-delimited test suffixes
(`foo_unittest.cc`, `foo-unittest.cpp`, `foo.unittest.js`), and a file
named so is a test suite to the resolver, as `foo_test.cc` is. A bare
`unittest.go` stays production code: promtool's runs rule tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…al named like an import is that variable (colbymchenry#2448)

goRefQualification read a dotted Go call's package qualifier from its line,
as the line's only `X.Name` spelling. A second spelling of the name left it
with none (`Digest:` beside `digest.Digest(dig)`, `"Log using Infof"`), and
another selector before it was taken instead (`klog` for `err.Error` in
`klog.Error(err.Error())`). It now takes the reference name's first segment,
the only one that can be a package.

A call through a parameter or local that takes an import's name (etcd's
`jwt, err := newTokenProviderJWT(…)`, testify suites' `suite` receivers) is
a call on that variable. A per-file scope reader finds the parameters,
receivers, results, `:=`, `var` and `const` names in scope at the call, so
such a call is no longer one into the package.

The variable's declared type, when its declaration writes one, decides what
the call reaches:
- An outside package's type, or a value an outside package's function hands
  out (`clock := clocktesting.NewFakePassiveClock(…)`), has none of the
  project's methods.
- A project package's type is resolved as the receiver's type. This also
  covers parameters declared through a package (`logger logger.Interface`).
- A field read through the local (`metadata.Type.String()`) keeps its own
  type.

A sync drops a file's scope reading with the other per-file memos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ckage imports look local (colbymchenry#2437)

The out-of-repo import guard (`isOutOfRepoImport`) never fired in two
setups:

- A catch-all `paths` key. The guard started with `isExternalImport`,
  which treats an import as local when it starts with a root alias's
  prefix, and a `"*"` key's prefix is empty. On cord-field
  (`"*": ["./typings/*"]`), 169 imports of `Typography` from
  `@mui/material` bound to the project's own `Typography`.
- An alias that lands on disk but outside the index. `fileExists` falls
  back to `fs.existsSync`, which also answers for a directory or a file
  under `node_modules`. home-assistant's
  `"lit/decorators": ["./node_modules/lit/decorators.js"]` bound 4,916
  `@property()` decorators to an unrelated `property` field, and
  topcoder's `config` package landed on its `config/` folder.

The guard now skips the alias-prefix test (it already asks
`resolveImportPath` whether an alias maps the import to a file) and
counts that resolution only when it lands on an indexed file. Other
callers of `isExternalImport` are unchanged: resolving an import still
needs the prefix test so `"*": ["src/*"]` aliases resolve at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e first — graph byte-identical (colbymchenry#2431)

bareCallReceiver joined the ref's line with the seven after it for every
bare-recorded call, then cut the text at the ref's column. On a minified
bundle that line is the whole file, so every call copied it: about 1 MB
per call on go-ethereum's graphiql.min.js, 9 GB over that repo's call
refs.

It now searches the ref's own line from the column first. Every match of
the call pattern starts with the name, so a match there with no earlier
occurrence of the name on the line is where the joined lines match first
too. With no match on the line (a call that continues on the next one),
or with the name earlier on it (an earlier `name[…` or `name<…` can run
past the line break and enclose the match), the joined lines decide as
before. The result is the same for every input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nry#2441)

A Go defined type (`type WatchChan <-chan WatchResponse`, gin's
`type HandlerFunc func(*Context)` and `type HandlersChain []HandlerFunc`)
is a `type_alias` node, and it referenced nothing: only an `=` alias
(colbymchenry#2417) named the types on its right-hand side. Impact on
`WatchResponse` or gin's `Context` missed the declarations built from
them, and everything that uses those.

Fix (TS extractor and the Rust kernel, mirrored): goAliasTypeNames /
alias_type_names walk the `type` field of every type_alias node, a
defined type's as well as an alias's, so each type it names becomes a
`references` ref on that name, where resolution reads the package
qualifier back. Still skipped: the declaration's own type parameters,
Go's predeclared types, and now its own name written bare, which in a
recursive type (prometheus' `type stateFn func(*Lexer) stateFn`) is the
declaration itself: no self-edge, and no failed row that would make
dead code treat every namesake as referenced. A qualified name is never
skipped (`type PutResponse pb.PutResponse` names pb's).

Resolution is unchanged. A defined type is a new type: goAliasTarget
still follows `=` aliases only, so a method called on a defined type
is not looked up on its underlying type. The gateTargetKind rule that
leaves a type_alias's reference written through a package the index
doesn't know unresolved now covers defined types too; without it, every
such name bound to the same-file declaration of its name (`type Op
clientv3.Op` linked itself).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ds (colbymchenry#2436)

A data router's `lazy: () => import('@/pages/invocation_page')` loads the
folder's `index.ts`. When that file only forwards the page, with
`export * from './invocation_page'`, `export { default } from './page'` or
`export { Page as Component } from './page'`, the route linked nothing: the
module was read for an export of its own and none was found. The lookup now
follows the module's re-exports a few hops to the file that declares the
page, the way JavaScript resolves them (`export *` never forwards the
default, a name two `export *` modules both forward is exported by
neither), and reads a module's own `export { Page as Component }` clause.
`React.lazy` values in a route file, which go through the same lookup,
follow such barrels too.

On luci-go's milo/ui, 51 of 91 failed lazy route refs now link; the rest
load a file under `src/build/` (not indexed), export only a `loader` or a
`handle`, or pick a named export in an async loader. Sync links a page
added or edited behind its barrel the way a fresh index does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t counts (colbymchenry#2447)

goImplementsEdges matched method names only, so a struct whose same-named
method takes or returns a different number of values, which Go can never
accept, was linked as an implementation (etcd's watch cache as a
peerGetter), and enough such look-alikes filled the per-interface cap
before the real implementer (grpc-go's xDS TransportBuilder).

Each wanted method now also needs a declaration with its parameter and
result counts, read off the stored signature text; a signature that
doesn't read rules nothing out. A gRPC client interface (every method
takes `opts ...grpc.CallOption` last) also accepts its RPCs' server
arities, so `KVClient.Range -> kvServer.Range`, the bridge explore
follows from a client call to its handler, stays. Defined types (colbymchenry#2419)
are held to the same counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s own class's method (colbymchenry#2443)

normalizeCppTypeName cut each `<…>` at its first `>`, so a declared type
with template arguments nested in its template arguments kept debris:
`autovector<std::pair<int, FileMetaData*>>` read as `autovector >` and
`Striped<CacheAlignedWrapper<port::Mutex>>` as `Striped >`. No class has
such a name, so the call was never resolved on the receiver's class. A
capitalized debris name made isUndeclaredTypeName take the type for an
outside one and silence the call (rocksdb's `mutex_.Get(key)`), and a
lowercase one fell to a guess by the receiver's words (rocksdb's
`files_marked_for_compaction_.clear()` reached `CompactionInputFiles::clear`).

Template arguments are now stripped with their nesting by
cpp-type-aliases' depth-tracking helper, exported as
stripCppTemplateArguments; cpp-supertypes' identical private copy now
imports it. A `>` that closes no `<` is the end of a declaration begun on
an earlier line (`std::unique_ptr<BlobContents>>>& blob_reqs` under
`autovector<std::pair<…,`): a depth strip alone would read the argument's
type there, so such text names no type. The scan back to the receiver's
declaration then marks it shadowed (colbymchenry#2413), as it does for a declaration
whose type it can't read, so a member or earlier variable of the same
name found further up doesn't decide the call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion the query names (colbymchenry#2453)

Explore's named walk is breadth-first, and the first callee listed claimed
each node it reached. An interface method calls every implementation at the
same depth, so when several of them call the next named symbol, the Flow went
through whichever implementation the index listed first, even when the query
named another one. On prometheus, `Engine.execEvalStmt Queryable.Querier
fanout.Querier NewMergeQuerier` went through the TSDB's `DB.Querier`; on gin,
once defined types count as implementers, `setter.TrySet` went through
`headerSource.TrySet` when the query named `formSource.TrySet`.

A node reached again from the same depth now takes the route that ends on
fewer unnamed hops, then the one through more named symbols, and keeps the
first otherwise. Of two equally deep named ends, the seed keeps the one whose
route passes more named symbols. The expansion order, the visited set and the
NAMED_VISIT_CAP bound are unchanged, so the walk makes the same callee lookups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aration is indexed (colbymchenry#2446)

`struct Foo { … } foo;`, `class Bar { … } bar;` and `static struct { … } SPT;`
outside any function or class body went to the variable extractor, which
skipped the declaration's children, so the type, its methods and its
enumerators were never indexed in either extraction engine.

Both engines now walk such a type before the variables: in C always, and in
C++ when the tree has no errors or the file is walked in brace scopes. This
converges with colbymchenry#2426's walk for erroring C++ files; a C++ file whose braces
don't balance is still left alone, since error recovery can run a class past
its own `}` there. The declaration's function-as-value scan skips the walked
type, which captured its own. An unnamed struct, union or enum takes the name
of the first variable its declaration declares (also inside function bodies,
where it was `<anonymous>`), as `typedef struct { … } Name;` already takes the
typedef name, and the comment above the declaration is the type's docstring.
cDeclaratorIdentifier moves to languages/c-cpp.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…of exactly that name (colbymchenry#2445)

The resolver's name-existence pre-filter read no leading `::`: its `::`
branch wanted the separator past the first character, and the single-`:`
loop skips names holding `::`. So `::leveldb::RepairDB(…)`,
`::ROCKSDB_NAMESPACE::RepairDB(…)` or protobuf's `::_pbi::…` calls were
dropped before any strategy ran, unless a detected framework claimed them.
protobuf's got through only because it ships three Swift files: the
Swift ↔ Objective-C bridge claims any name with a `:` in it, which is why
colbymchenry#2415 left claimsReference() universal.

- The pre-filter checks a C or C++ name without its leading `::`.
- claimsReference() is asked only of the frameworks that resolve the
  reference's language (frameworksFor), like resolve() since colbymchenry#2415.
- matchByQualifiedName takes a global-qualified C/C++ name as exactly
  that qualified name. Its suffix match took a namesake nested in another
  namespace or class: with the pre-filter alone, 23 of the 24 edges it
  added on leveldb, fmt and rocksdb were wrong, mostly wrappers like fmt's
  mock `test::open` linked to the `::open(…)` they wrap, and protobuf's
  `::operator delete` went to `DynamicMessage::operator delete`. A
  declaration inside a namespace a macro opens (fmt's `struct pipe`) only
  looks global in the index, so it is skipped; step 5 still reads those.
- `::std::…` and `::memset` count as `std::…` and `memset` for the
  built-in check, and step 5 returns early for a single-segment name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2429)

`const Wrapper = styled.div<WrapperProps>` + a template literal came out as a
`constant`, so the JSX-render synthesizer skipped it: `<Wrapper>` got no
render edge, or bound to a same-named component in another file.
tree-sitter's tagged-template call takes no type arguments, so when the type
argument also reads as an expression the initializer parses as comparisons,
`(styled.div < WrapperProps) > template`, not as a call. (A type argument
that can't be an expression, `<{ open: boolean }>`, error-recovers into a
call on the tag, which colbymchenry#841 already took.)

reactComponentHoc now also takes that comparison chain: the leftmost operand
is the `styled` tag against a `<`, the rightmost is the template against a
`>` (`>>` when the type argument ends in its own `<...>`), and operators in
between belong to the type argument (`<A & B>`). Mirrored in the kernel's
TS/JS walker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n the module appears later (colbymchenry#2450)

A default, namespace or aliased import binds a name its module never
declares: `import tagsController from './tag/tag.controller'`,
`import { Component as GroupsPageWrapper } from './groups_page'`. Every
reference through that name (the import, a call, `new`, a base class, a
JSX or template tag, an Express mount) was parked under the name, which
no file a sync adds or changes carries. So a module added after its
importers were indexed, restored after a delete, or given its export in a
later edit never relinked them, while a fresh index did.

Such a reference is now parked under its project module's key
(`module:tag`), which sync already looks up for every file it adds or
changes (colbymchenry#2422), and it is still found by its own name: a binding its
module never resolves is linked by that name alone, as vben's
`{ VbenFormSchema as FormSchema }` from a workspace package is. Schema
v15 adds the partial index that by-name lookup needs; its leading
`status` keeps the planner off idx_unresolved_status, which read 830K
rows on vscode's index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ry#2438)

tree-sitter-go puts the comment above `type Foo struct{}` before the
`type_declaration`, but the node is made from the spec inside it, so both
extractors found no docstring for an ungrouped struct, interface, defined
type or alias. A type inside a `type ( ... )` group kept its own comment,
which sits beside it in the parentheses.

Go's docstring is now read from the declaration when the declaration
holds one spec and nothing precedes that spec inside it. The leading
comment of a group with several members stays the group's.

Reading from the declaration exposed comments that trail the line above:
`const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto
package` would have become `type MetricType int32`'s doc. For every Go
declaration, a comment that begins after code on its line, and one that
begins on the line such a comment ends, is now left out, as go/parser
reads them. Seven function docstrings on etcd and prometheus lose one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve both parents, G6, workspace, locale/theme customizations and runtime packaging. Resolve lock version and legacy v12 schema collision by feature-detected transactional repairs without claiming upstream migration numbers. No release or main update.
Retain the exhaustive visible-copy test and add a Chinese-English-Chinese DOM localization round trip that also verifies the navigation target stays unchanged.

Copy link
Copy Markdown
Owner Author

验证发现并修复一个非文本冲突:上游 StepsView.svelte 新增 Its callers and callees →,既有 Pinable 全模板中文覆盖检查准确拦截了缺失翻译。没有放宽或移除原断言;提交 b0b5e6447f38fda1675ac091a9e3c9f7868df0e3 增加对应词典项和中文→英文→中文 DOM 本地化回归,检查链接 href 不变。

当前 PR 文件树为 a40a62ac823bec6552da366a3a77751502659efd,在原真实合并提交之上仅新增这 21 行词典/测试内容;上游祖先关系不变。

原提交的 Rust kernel 专项 134/134 通过;macOS 定向验证数据库新回归 10/10、既有迁移兼容 5/5 及工作区/G6 通过,但中文覆盖检查有 1 项失败(97 通过 / 1 失败 / 0 跳过)。这些是旧提交诊断,不作为新提交的成功凭据。新 head 的完整 engine/UI、native kernel 与 Windows/macOS 同源定向检查正在重新执行。main 保持不变,只有全部检查成功才合入。

Preserve the existing non-missing-path error contract when Windows reports ENOENT for a child of a regular file. Verify resolved existing parents are directories before treating descendants as offline. Keep the original regression and add two real-filesystem deep-path cases, including a file symlink.
@PinableAgents
PinableAgents marked this pull request as ready for review October 9, 2026 06:18
@PinableAgents
PinableAgents merged commit 5ab8447 into main Oct 9, 2026
2 checks passed

Copy link
Copy Markdown
Owner Author

已完成合并后核验(2026-10-09):

  • 本 PR 已通过 merge commit 合入 main,实际提交 5ab8447fc0b8bd3890c8da5aa347b05a1389aca6;父提交为原 main 69f4e5d... 和已验证 head 0c460b2...。
  • 合并前重新读取 fork main、PR head、upstream main 均未变;merge API 携带 expected head SHA。合并后重新读取上游仍为 b635dd467f0578926a9c01a37b9d28d2b26689f1。
  • GitHub compare:status=ahead, ahead_by=38, behind_by=0,merge base 正是本次最新上游;257 个上游提交已全部包含,原 fork 34 个独立提交及本次 4 个合并/修复提交保留。
  • 最终 main 文件树 f1c2b04a7b662bf37631380bffb378c327c5cd38 与通过完整 PR CI 和 Windows/macOS 定向 CI 的文件树相同。
  • .pinable/upstream.json 已实际记录上游完整 SHA。
  • 合并前验收:完整 engine/UI 6607 通过 / 0 失败 / 356 跳过;Rust kernel 专项 134/134;Windows 和 macOS 定向检查各 101/101,0 跳过。完整 CI https://github.com/PinableAgents/codegraph/actions/runs/37891757909 ,桌面 CI https://github.com/PinableAgents/codegraph/actions/runs/37891776905 。

没有重新发布 runtime Release、改动发布凭据或保护规则。临时 maintenance 工作流不在最终 main 文件树/祖先中;本次 source 同步已完成。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants