Commit Graph
2426 Commits
Author SHA1 Message Date
Raphael Malikian 3ab2cbabb4 fix(mcp): preserve parallel edges through graph.json round-trip and MCP output
Fixes #4500

Signed-off-by: Raphael Malikian <rtmalikian@gmail.com>
(cherry picked from commit b30705dd3f)
2026-10-16 18:25:28 +01:00
berce e2d2fddffb fix(skill): dispatch Pi's semantic subagents as pi-subagents' delegate
The Pi skill rendered the shared agent-tool-disk dispatch, which tells the
host to use subagent_type="general-purpose". pi-subagents has no agent by
that name and rejects it as unknown, so semantic extraction never started
(#4494). Step B3's re-run hints named the same agent.

Pi now gets its own dispatch fragment that sends one builtin `delegate`
agent per chunk; delegate has read, bash, edit and write. The two B3 hints
are core slots filled per dispatch variant, so every other host renders
byte-identical output.

(cherry picked from commit a0b3a053a5)
2026-10-16 18:21:34 +01:00
abhay-codes07 97922b9c66 fix(extractors): share the in-flight scan root via base, not graphify.extract (#3666)
`extractors/markdown.py` read the scan root of the extraction in flight with a
function-local `import graphify.extract` + `getattr(_extract,
"_XAML_ACTIVE_EXTRACT_ROOT", None)`. That violates MIGRATION.md invariant #4
("never import from graphify.extract inside this package; direction is
extract.py -> extractors/"), and the `getattr(..., None)` meant a rename of the
module global would silently disable Markdown wikilink (Obsidian vault)
resolution rather than fail loudly.

Move the ambient scan root onto `extractors/base.py` — where the extractors
legitimately live — as `set_active_scan_root()` / `active_scan_root()`.
`_safe_extract_with_xaml_root` now publishes the root there (still saving and
restoring for nested extraction), the two XAML readers and the Markdown vault
lookup read it through the accessor, and `markdown.py` imports it in the correct
direction. The `_XAML_ACTIVE_EXTRACT_ROOT` module global in extract.py is gone;
nothing else referenced it.

Behavior is unchanged — the same process-global root is set around each
extraction and read by the same sites. Tests assert the extractor no longer
imports graphify.extract (fails on the old code), that the set/get/restore
contract nests correctly, and that markdown's accessor IS the base accessor.
The markdown, XAML/dotnet, and language suites stay green; the only failures on
this machine are the pre-existing v8 baseline set (c-include determinism,
wikilink-unicode, two watch cwd-deleted cases), which fail identically on clean
v8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit 86721f765d)
2026-10-16 18:21:28 +01:00
ayushcodes10 5fec9d32e8 Add regression tests for the drifted confidence fold
Covers every shape the issue reports: the non numeric drifted words,
a numeric looking string that still backfills confidence_score, a
case only variant that gets recased rather than treated as an
unknown word, the three valid spellings staying untouched, and the
new hyperedge fold. A build_from_json test reproduces the issue's
own warning text end to end and checks it no longer prints.

(cherry picked from commit da369c5ea8)
2026-10-16 18:21:28 +01:00
ayushcodes10 5a1cf6adb6 Fold model drifted string confidences, not just numeric ones
The confidence fold only ever normalized a NUMERIC legacy confidence
(a pre enum graph's bare float) to INFERRED. A free form string the
semantic pass sometimes drifts into instead, high, confirmed,
medium high, medium low, or a lowercase inferred, passed through
untouched, so a graph carrying one re warned on every command
forever, exactly the gap the numeric fold's own docstring already
described but never closed for strings.

Pulls the shared logic into its own function so edges and hyperedges
can both call it, since a hyperedge carries the same confidence and
confidence_score pair and had no fold at all. A case variant of a
real enum value is corrected to its exact spelling, since it already
is the enum value and only needs recasing. Anything else folds like
the numeric case, backfilling confidence_score when the word happens
to parse as a float, and becomes INFERRED either way, since every
confidence reaching this fold came from the LLM semantic pass by
definition. Checked against validate.py's own VALID_CONFIDENCES
rather than a second copy of the enum.

(cherry picked from commit a158b6aaa9)
2026-10-16 18:21:28 +01:00
ayushcodes10 eadf123fa9 Add a regression test for the source less collision survivor
Covers the exact repro from issue 4492: a node with a source_file and
one without, sharing an id that neither file encodes. Checks both
arrival orders keep the sourced node, matching the sibling order
independent tests already covering the lifecycle and path form
tiebreaks right next to it.

(cherry picked from commit 151becb880)
2026-10-16 18:21:28 +01:00
ayushcodes10 6ed8fb1de6 Let a sourced node beat a source less namesake in collision rank
When neither colliding node's own file encodes the id, both read as
mere references and tie on the first key in collision_rank too. The
remaining keys then fell straight to the reversed path segments,
where an empty path (no source_file at all) sorts before any real
one, so the node with no source_file always won. That node is the
one telling us nothing about where the entity lives; the sourced one
was the real record, and it was the one getting dropped.

Adds a source_file presence check right after the first key, the
same rule pick_winner already applies one level up for this exact
reason (issue 3775/3786). It changes no collision between two
sourced nodes, since both sides of that key already agree there.

(cherry picked from commit 8359922c33)
2026-10-16 18:21:28 +01:00
safishamsi e37a75a6b9 fix(deps): cap tree-sitter-dm <0.26
The 0.26.0 DreamMaker grammar changed the type-path node shape, so
extract_dm() no longer finds type definitions (a `.dm` file yields only
its own file node). uv.lock pins 0.25.1, so dev/CI are unaffected, but a
fresh `uvx --from graphifyy[all]` / `[dm]` resolves tree-sitter-dm from
PyPI with no upper bound and pulls 0.26.0, silently breaking DM
extraction for end users. Cap both the `dm` and `all` extras to
>=0.25,<0.26 (matching the tree-sitter-cpp pin style).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit c36fbc117b)
2026-10-16 18:21:27 +01:00
Deepjyoti-Sarmah 6801164019 test(skill): keep graphify off PATH and satisfy pyright in the update test
bash runs the backticked text in Part C's comment when graphify is on PATH, which broke the test in CI. The blocks do not need it. Also narrow three Optional values for pyright. The test checks the same things.

(cherry picked from commit e487672683)
2026-10-16 17:14:07 +01:00
Deepjyoti-Sarmah b4406d7cd7 fix(skill): aider and devin --update populate .graphify_detect.json
Part A reads .graphify_detect.json and Step 9 deletes it, but the aider
and devin --update sections never wrote it again, so Part A raised
FileNotFoundError after any full build.

The sections now write it from .graphify_incremental.json, as the
generated references/update.md does, with each file's own path
convention.

The test no longer seeds the file; it asserts the update section wrote
it.

(cherry picked from commit ca090b6da5)
2026-10-16 17:14:07 +01:00
Deepjyoti-Sarmah ea2bc68ba8 fix(skill): aider and devin code-only runs reach the merge (#4447)
On aider and devin nothing wrote .graphify_semantic.json for a code-only
run, so Part C raised FileNotFoundError, and the --update paragraph sent
the agent from Part A to a merge that reads .graphify_extract.json.

Part B now has the fast-path block, the paragraph names Step 3A, the
fast path, Part C and the merge, and aider's only-deletions guard writes
the file its merge reads.

Adds tests/test_code_only_update_all_hosts.py for the two monoliths and
a skillgen audit entry for the new lines; the 14 generated hosts are
left to #4313.

(cherry picked from commit bbe1a7ca9b)
2026-10-16 17:14:07 +01:00
Deepjyoti-Sarmah f2f1b66c56 fix(skill): drop backticks bash ran inside Part C and the update merge (#4461)
Bash expands backticks inside a double-quoted -c block, so two comments
ran graphify update and a command named changed before Python started.
Drop the backticks and keep the words; regenerate with --bless. The
gen.py monolith audit accepts the new Part C comment text.

(cherry picked from commit b05a09a1d7)
2026-10-16 17:13:58 +01:00
Deepjyoti-Sarmah 0a2732122e test(skill): no command runs from backticks in -c "..." blocks
Checks every generated skill file statically, and runs the Part C and
update merge statements under bash with a stub interpreter. This commit
alone fails 58 cases (29 per check); the next commit fixes them.

(cherry picked from commit 0bbdd9d36c)
2026-10-16 17:13:54 +01:00
Vibhanshu Mishra 0051c3e6bc fix: materialize extraction for code-only incremental updates
Ensure code-only incremental updates generate a fresh AST-only extraction before merging.

Prevent FileNotFoundError when semantic extraction is skipped.

Preserve existing semantic graph data and cache entries.

Add regression coverage for added, modified, and deleted code files.

Refs #3232

Co-authored-by: OpenAI Codex <codex@openai.com>
(cherry picked from commit 8868862892)
2026-10-16 17:13:49 +01:00
Vibhanshu Mishra 3f75d42a67 fix(exports): preserve custom Neo4j and FalkorDB push URIs
Preserve user-supplied database URIs in generated export instructions.

Prevent Neo4j and FalkorDB push workflows from replacing custom destinations with localhost.

Document safe URI quoting and credential handling.

Add regression coverage for URI forwarding and export-only behavior.

Co-authored-by: OpenAI Codex <codex@openai.com>
(cherry picked from commit 813b94ff51)
2026-10-16 17:13:49 +01:00
samie a. 5fcdb2d791 test: pin in-file calls to salted Rust functions and Go ids outside a collision group
Two guards for the review questions on #4468: an in-file call to
`ignore()`, `e()` or `E()` binds by the name as written and reaches the
salted node (on v8 the calls to `ignore()` and `E()` bind nowhere, since
those functions had no node), and the other types in a Go file that holds
a case-only pair keep the ids they have without the pair.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5c49344807)
2026-10-16 17:12:53 +01:00
samie a. 918483e6a3 fix(rust): resolve a forward reference to a type that keeps its id beside a case-only sibling
#3782 left a reference placed above `struct Handle` on a sourceless stub
whenever another file-level item folded to the same id (`fn handle`,
`const HANDLE`, an `impl` for `HANDLE`), because walk() gave that id to
whichever item came first. Those items now get ids of their own, so the
struct owns its id and the reference can resolve to it, as a reference
below the declaration already did. Without this, the stub only folds back
when the type's name is unique across the corpus.

A declared type is now pre-registered unless a non-type item has its
exact name (`fn Foo()` beside `struct Foo {}` still share one node), and a
`type HANDLE` alias still keeps its stub. The #3782 test that pinned the
stub for the case-only shapes now asserts the resolution; the shapes that
still share a node keep the stub assertion.

local_type_names becomes a frozenset, which is what
_rust_collect_type_refs is annotated to take.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f710c8a16e)
2026-10-16 17:12:53 +01:00
samie a 102fff9f15 fix(go,rust): keep same-file declarations whose names differ only by case apart
make_id casefolds, so in one file `type ResponseWriter interface` and
`type responseWriter struct` (gin), or `struct Ignore` and `fn ignore`
(ripgrep), mint one node id. The walk keeps whichever
declaration it meets first and drops the other: the dropped struct's
methods hang off the interface, and a dropped function's calls are
credited to the struct.

Go: the #2779 census now also counts `type` declarations, using its
existing rule. The exported type keeps the plain id and the other is
salted; its methods, receiver anchors and in-file type references use the
salted id. Types are counted first so methods are keyed under their
owner's final id.

Rust: the #3782 pre-scan already lists every file-level item behind an
id, so it doubles as the census. The one declared struct/enum/trait in a
group keeps the plain id (type positions and `impl` blocks name it, so it
and its methods do not move) and every other member is salted with a
hash of its own name; with no single declared type, every member is
salted. `#[cfg]` twins are one name and still share one node.

A name that does not collide keeps its id. The census is per file: a
case-only pair split across two files of one Go package is not covered.

Related to #3302 and #2810.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bef78ffdbd)
2026-10-16 17:12:53 +01:00
rohit-jsfreaky 425a402bd7 feat(solidity): resolve calls, super calls and modifiers inherited from base contracts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit cdcec4510a)
2026-10-16 17:12:42 +01:00
rohit-jsfreaky e9d06faab3 test(python): pin that forwarded import edges only gate fixture calls on a rebuild
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit 5b0ca535c1)
2026-10-16 17:11:42 +01:00
rohit-jsfreaky 9b667f6edf feat(python): type member calls on pytest fixture parameters from the fixture's returned class
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit 3093df2250)
2026-10-16 17:11:42 +01:00
FolatheDuckofDuckingburg 4c65a7f09a fix(ts): preserve ambiguous constructor assignment guard
(cherry picked from commit 32816cc55a)
2026-10-16 17:11:42 +01:00
FolatheDuckofDuckingburg 6948da8e8a test(ts): cover method-assigned field receivers
(cherry picked from commit 4b8c5d68f1)
2026-10-16 17:11:41 +01:00
FolatheDuckofDuckingburg d19401ebe5 fix(ts): infer field types from method assignments
(cherry picked from commit 61d4b3b9c2)
2026-10-16 17:11:41 +01:00
Mpasha17 c4589a0c1b fix(ts): don't guess between members the normalized key can't tell apart
Review on #4479: after the exact-name lookup, the `_key` fallback in
_resolve_typescript_member_calls still matched loosely. With #4475 splitting
close/#close, updateHash/_updateHash and set/$set into separate nodes, one
normalized key can now map to two members, and method_index kept whichever
was registered last. So c._close() bound to #close, c.UpdateHash() to
_updateHash, and c.a() to $a, none of which is the called name. Track keys
that more than one member normalizes to and skip the fallback for them (no
edge rather than a guess). Exact hits, and loose hits with a single
candidate, are unchanged.

Co-Authored-By: Grok Bot
(cherry picked from commit a1ce057757)
2026-10-16 17:11:41 +01:00
Mpasha17 70a600196f fix(js): keep class members that differ only by _, $ or # as separate nodes
make_id drops _, $ and #, so updateHash/_updateHash, close/#close and
set/$set in one JS/TS class body minted one id and the second member
disappeared, with its calls attributed to the survivor. Group each class
body's member names by id and reuse the Python/C# collision rule
(_underscore_salted_nid, now with configurable private prefixes): a unique
public name keeps its id, colliding private ones get a salted id.
Non-colliding members keep their ids. The TS cross-file member resolver now
matches the exact member name before falling back to the normalized key, so
c.set() still binds to set and not $set.

Fixes #4475

Co-Authored-By: Grok Bot
(cherry picked from commit 03beac9d09)
2026-10-16 17:11:41 +01:00
Hemanth 627c49a770 fix(python): salt module-level class ids that collide with a function or class (#2810)
The Python collision pre-scan (#3302) recorded module-level functions and
class methods but not module-level class names, so a class sharing an id
with a function or another class after normalization still lost one
declaration to first-wins add_node:

- `class Session` / `def session()` (requests/sessions.py): session() is
  absent from the graph, and which one survives depends on declaration order
- `class Foo` / `class FOO`, `class Pair` / `class _Pair`

Record module-level classes in the same scope as functions and salt the
class id with the existing _underscore_salted_nid rule. Methods of a salted
class are minted under the salted id, so the pre-scan now keys their groups
under the class's resolved id; keying them under the plain one would drop
the existing `_get`/`get` protection inside that class. Same-file base-class
references go through the same salt (as C# already does), so
`class Sub(Session)` binds to the real class instead of minting a sourceless
`Session` stub. The pre-scan also looks through decorated_definition, so
`@dataclass class Session` and the `_x` / `@property x` method idiom are
covered too.

Ids change only for files that contain such a collision; cached AST results
for them keep the collapsed ids until the AST cache schema is bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 03b420df97)
2026-10-16 17:11:41 +01:00
Deepjyoti-Sarmah 74ed051be0 fix(hooks): rebuild after cherry-pick, rebase and pull --rebase (#4490)
post-commit skipped on CHERRY_PICK_HEAD, which git leaves on disk while
the hook runs, so a clean pick never rebuilt. A rebase has no hook that
fires once at the end, because post-commit and post-checkout skip while
rebase-merge/ exists.

Drop the CHERRY_PICK_HEAD check. For a range pick or revert, skip every
step but the last and diff from sequencer/head, so one rebuild covers
the range; ignore a stale sequencer/ left by git reset --hard. Add a
post-rewrite hook that rebuilds on rebase.

(cherry picked from commit 47085dd742)
2026-10-16 17:10:31 +01:00
Deepjyoti-Sarmah b3af356532 fix(hooks): drop MERGE_HEAD guard from post-merge so real merges rebuild (#4463)
git leaves .git/MERGE_HEAD on disk while post-merge runs and deletes it
only after the hook returns, so the guard exited on every merge that
makes a merge commit. Only fast-forwards rebuilt. The post-commit and
post-checkout guards are unchanged.

(cherry picked from commit c37d35bd50)
2026-10-16 17:10:31 +01:00
Mpasha17 6c81b838b5 fix(detect): only treat a valid .git as a VCS root (#4444)
_find_vcs_root() accepted any .git, so an empty or partial .git folder in a
home directory became the ignore-walk ceiling and its .gitignore leaked into
unrelated scans. A .git directory now needs HEAD, objects/ and refs/ (what
git checks), and a .git file must start with gitdir: (worktrees,
submodules). No git subprocess; OSError means not a root. Other VCS
markers are unchanged.

The three VCS-boundary tests that faked a repo with a bare mkdir .git now
use a minimal valid .git.

Co-Authored-By: Grok Bot
(cherry picked from commit f0b6493163)
2026-10-16 17:10:31 +01:00
berce 7b76ce3a97 fix(js): drop the barrel-named import edge once the import resolves past it (#4486)
A named import through a barrel gets an `imports` edge from the structural
extractor to `<barrel>_<symbol>`, and the symbol facts add a second one to
the defining symbol. The barrel defines nothing, so the first edge dangles
and build mints it as an external stub. Through a named re-export barrel
the first edge was repointed instead, so the same import came out as two
identical edges.

After the facts loop, drop the structural edge when its import resolves
to exactly one owned symbol behind the barrel. An ambiguous barrel (one
name from two modules) and a chain that ends in a third-party package
keep the edge, as before.

(cherry picked from commit 0aaac55a86)
2026-10-16 17:10:31 +01:00
berce f97677d5d1 fix(js): fall back to src/ when a workspace package's exports name only dist/ (#4485)
#3834 let a workspace import whose `main`/`types` point into dist/ fall
through to `src/index`, but an `exports` map returned its own candidates
and never reached that fallback. A tsc-built package with
`"exports": {".": {"import": "./dist/index.js"}}` therefore resolved to
dist/ (outside the corpus, so an external stub) when it was built, and to
nothing on a clean clone.

Append each build-output exports target's source twin (dist/sub.js ->
src/sub) and, for the root entry, src/index and index. They come after the
exports targets, and _resolve_workspace_import already prefers a file
outside build output, so source wins when it exists and dist/ stays the
fallback.

(cherry picked from commit 705abe6c4c)
2026-10-16 17:10:31 +01:00
N. Hariharan 13294c0cf0 feat(llm): opt-in backend selection, disable switch, and honest auto-detect (#4361)
detect_backend() picks a cloud provider purely from whichever API key is set
in the environment, and community labeling additionally falls back to claude -p
when the CLI is on PATH. Keys in a developer environment are often there for
other tools, so a bare `graphify extract .` could send uncached documents to a
provider the user did not intend.

Add explicit control without breaking existing auto-detect flows:

- GRAPHIFY_BACKEND=<name> selects a backend explicitly and wins over ambient
  keys; GRAPHIFY_BACKEND=none disables every LLM backend, including the label
  claude -p fallback. `graphify extract` then keeps the local AST pass and
  reports what it held back instead of failing or auto-selecting a provider.
- Auto-detection now reports the exact environment variable that selected the
  backend, so sending a corpus to a cloud provider is never a surprise.
- The claude -p labeling fallback is announced, and GRAPHIFY_BACKEND=none opts
  out of it (it is otherwise kept: silently downgrading good labels to
  "Community N" was the reason it exists).
- extract_files_direct raises a clear error naming GRAPHIFY_BACKEND when the
  library caller would otherwise auto-detect a disabled backend.

README data-residency note updated. detect_backend_with_source() now carries the
detection logic and names its source; detect_backend() delegates to it.

Note: this deliberately keeps auto-detection ON by default (the issue's
alternative #2) rather than flipping cloud use to opt-in, which would break
every existing key-less `graphify extract` flow. GRAPHIFY_BACKEND makes the
opt-in switch a one-liner for a follow-up if maintainers prefer it.

(cherry picked from commit 7e2467ea43)
2026-10-16 17:10:31 +01:00
Dennis Cornwell 08f5304ac7 fix(llm): pin source_file to the dispatched file in the extraction prompt
The native-backend extraction prompt never says what source_file means; its
only guidance is the schema placeholder "relative/path". When a document
cites, links, or describes another file (a "Source:" line, a "Related:" list,
a wiki link, a class a design doc specifies), the model often attributes the
document's own entities to that file. On an incremental run the out-of-scope
filter (#1895) then drops them, so the document silently loses nodes (#2297,
#3377), or a doc-described API is attributed to the code file (#2270).

The skill path already states a source_file RULE in extraction-spec.md. Add
the native-backend equivalent: source_file is the `path` of the enclosing
<untrusted_source> block (or the listed source_file for an image), a file
named inside a source is a reference rather than the source, and the
reference is modelled as a node attributed to the enclosing file plus a
`references`/`cites` edge. The prompt change re-keys the semantic cache via
the existing prompt fingerprint (#1939), so affected files re-extract on
their next change.

Closes #3377
Refs #2270, #2297, #2217, #3982, #2276

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ff0b4e0dcd)
2026-10-16 17:10:31 +01:00
safishamsiandClaude Opus 4.8 10130e79ed chore(release): 0.9.91
Eight community PRs: Windows MCP-stdio hang (#4441), Rust use->declaration
(#4443), JS/TS file-scope callback param shadowing (#4457), sibling-file-id
symbol retention (#4452), TS class-field receiver scoping (#4297), Ollama
/api/chat num_ctx (#4414), cluster label retention on majority overlap (#4451),
and an ingest symlink test fix (#4442). AST cache schema bumped to 16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.9.91
2026-10-14 23:09:54 +01:00
SrijanSriv a0587559ca fix(cluster): keep a community label when membership overlap stays a majority (#2298)
A one-node membership change failed the exact signature check and replaced the saved name with the hub label. Keep the name when the shared nodes are a strict majority of both the saved set and the current set.

Related to #2298

(cherry picked from commit 62e83eabff)
2026-10-14 22:53:26 +01:00
smngvlkz f48425e6e0 send ollama requests to /api/chat so num_ctx actually applies
(cherry picked from commit 5f7b2b4dc8)
2026-10-14 22:53:26 +01:00
rohit-jsfreaky 990ab1b75c fix(ts): read this.x from the caller's class and its bases only, with constructor, accessor and static fields
Two review findings on the per-class field table:

- A `this.x` the class table did not type fell back to the file-wide name table,
  where constructor parameter properties of every class in the file (and locals
  named like a field) still lived, so the cross-class leak came back through them.
  `this.x` in a method of a known class now reads only that class and its bases.
- Fields of an unnamed class (`export default class { .. }`) were dropped; they go
  to the file table again, which their class-less methods still read.

To make the class-only lookup complete, a class's fields now come from declared
fields, parameter properties, `this.x = param` / `this.x = new T()` in the
constructor, and `get x(): T` / `set x(v: T)`; a field declared on a base class is
found through `inherits` and its type is judged by the declaring file's imports;
static fields are kept apart and read only from static methods. Fields live on the
class node (`_ts_fields`, `_ts_static_fields`), forwarded on changed-files rebuilds
together with the unchanged files' import edges, so an inherited field resolves the
same as in a full build.

vs 0.9.84: TypeORM `this.x.m()` 79.6% -> 96.1% (+95 edges confirmed by the compiler,
0 wrong), NestJS 78.5% -> 81.6% (+16, 0 lost), excalidraw +10 / -1 wrong; hono, mermaid,
typescript-eslint unchanged. Changed-files rebuild == full build on NestJS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit b1c79fc726)
2026-10-14 22:53:26 +01:00
rohit-jsfreaky 5fd413a057 fix(ts): scope class-field receiver types to the field's own class
Class fields went into the per-file receiver table, so two classes in one file
with a same-named field of different types typed each other's `this.repo`
(`A.run` bound to `RepoB.save`), and a local named like a field took the field's
type. Only the first commit of #4231 shipped in 0.9.81; this is its second one.

Fields now go to a per-class table (`ts_type_table["fields"][Class][field]`), and
only a `this.x.m()` call, marked `ts_this_field` on its raw call, reads it through
the caller's own class. `const { x } = this` / `{ x: y } = this` in the enclosing
function mark `x.m()` / `y.m()` the same way, and classes are matched casefolded
like node ids (`class UsecaseVisitor` beside `const usecaseVisitor`). Every other
receiver uses the file table as before.

vs 0.9.84: excalidraw -1 wrong / +2 correct edges, TypeORM -2 (a local sharing a
field's name), NestJS, hono, mermaid, typescript-eslint unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit 1d2e2bc47e)
2026-10-14 22:53:26 +01:00
Deepjyoti-Sarmah c29950f822 fix(extract): keep a symbol that spells a sibling's old file id on its own node (#4449)
extract() renames each file's old file-level id (its path with the extension, esquema_x_sql for x.sql) to the canonical one, on every node, edge endpoint and raw call that spells it. A symbol of another file can spell it too: const sql in x.mjs, so it took the file node's id and was dropped by dedup.
The rename now follows only the file that minted the key, wherever another file's own symbol carries the same id; a file that only refers to the owner renames as before, so every key nobody contests behaves exactly as today.
Swift extension nids and Objective-C field-table keys follow the same rule; the Objective-C path has no test that reaches it.

(cherry picked from commit 2876a1ebbf)
2026-10-14 22:53:26 +01:00
berce 5faa05c852 fix(rust): only land a use rooted in this crate
The review bot is right that `use serde::Serialize;` should not land on a
local `Serialize`. Leaving out std/core/alloc was not enough: any other
crate has the same problem. A `use` is now resolved only when its path
starts at `crate`, `self` or `super`.

(cherry picked from commit ee27336a4f)
2026-10-14 22:34:17 +01:00
berce ef1d256ec8 fix(rust): land a use on the declaration it imports (#4439)
A Rust `use` targets the bare, case-folded name it imports. That reached the
declaration only when a type annotation in the corpus had left a sourceless
node under the same id for the stub rewire to remap. Otherwise the edge
dangled, and since 0.9.63 build mints the target as an external stub that
ties with the real declaration in `affected` and `explain`.

The `use` edge now carries the name as written, and the rewire is lent a
sourceless node for each imported name that has none. The lent nodes are not
kept, so an import with no unique declaration dangles exactly as before.
Standard-library paths are left out.

An incremental build re-extracts only the changed files, so the rewire also
takes the unchanged ones as candidates; without that it resolved less than a
full build.

Bumps the AST cache schema: cached edges lack the name.

(cherry picked from commit 672a2b69b1)
2026-10-14 22:34:17 +01:00
berce 1af5104b68 fix(js): shadow a file-scope callback's own params (#4448)
A closure passed in a module-level call statement is walked under the
file node (#3124), but its params and locals were never registered for
that walk. A parameter passed on as a call argument then resolved against
the corpus-wide label index and produced an indirect_call to any function
with the same name, including module-private ones in other packages.

Record the closure's bindings in closure_locals_by_body, the same way the
#2568 branch already does for const-assigned closures.

(cherry picked from commit 60cfd51b7d)
2026-10-14 22:31:57 +01:00
berce e7772b2d18 test(ingest): compare the symlink target against what readlink returned before (#4435)
On Windows readlink() returns the target as \\?\C:\..., so comparing it to
the plain sentinel path failed three parameters of
test_binary_captures_skip_existing_entries even though the capture left the
link alone. Read the target right after creating the link and compare against
that, which is what "untouched" means on every platform.

(cherry picked from commit eb027be627)
2026-10-14 22:31:57 +01:00
berce 9dfe13254f fix(mcp): don't hand git the server's stdin in _git_head (#4440)
graph_stats asks git for HEAD to report staleness. The child inherited the
MCP stdio pipe, and on Windows git blocks on it: the call sat on the 3s
timeout, or hung under Claude Code. prs.py already isolates stdin (#3318);
these two _git_head copies were missed.

(cherry picked from commit 7092f980ed)
2026-10-14 22:31:57 +01:00
safishamsiandClaude Opus 4.8 fe085ceddd docs(translations): regenerate all 31 READMEs from the current English
Re-translate every localized README to match the reorganized English
(demo and benchmarks above the docs hub, number-bearing headings,
tightened install, trimmed per-platform/project-config sections, the
one-install line). Headers and the language switcher are preserved
verbatim; code blocks, URLs, badges, brand names, and figures are kept
byte-identical; repo-relative links use the docs/translations/ relative
convention; no emojis, no em dashes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-14 13:06:45 +01:00
safishamsiandClaude Opus 4.8 041771be72 docs(translations): drop em dashes and flag emojis from all 31 READMEs
Apply the same house-style cleanup as the English README: collapse
every em-dash run to a colon and replace the flag-emoji language
switcher with plain text language names, across all translated READMEs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-13 23:45:50 +01:00
safishamsiandClaude Opus 4.8 3d65480648 docs(readme): surface demo and benchmarks above the docs hub, number the headings
- move the Documentation link hub below See-it-in-action, What-it-does,
  and Benchmarks, so a skimmer sees real output and numbers before a
  wall of external links
- let the outcome-bearing headings carry a true number already in the
  body: 'Benchmarks: 0.497 recall@10, 0 LLM credits' and
  'What files it handles: ~40 languages'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-13 23:37:37 +01:00
safishamsiandClaude Opus 4.8 dc4457a2a8 docs(readme): tighten install and trim over-inflated sections
- one 'one install, any assistant, any OS' line (fixes the 15+/20+/22
  assistant-count mismatch to a consistent 20+)
- de-dupe the install steps: the quickstart lives in the hero; the
  Install section now leads with install methods and the auto-detect
- state the Python/uv prerequisite inline at the hero
- sell 'graphify install' auto-detect (no per-assistant setup)
- trim the four per-platform deep-dives and the Project-configuration
  cache/import prose out to docs.graphify.com

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-13 23:19:52 +01:00
safishamsiandClaude Opus 4.8 be10de610e docs(readme): drop em dashes and flag emojis
Replace every ' — ' descriptor dash with a colon and swap the
flag-emoji language switcher for plain text language names, to match
the house style (no emojis, no em dashes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-13 23:16:40 +01:00