Commit Graph
1896 Commits
Author SHA1 Message Date
Ha1baraA11 3792ff1fda fix: preserve loose sibling import resolution
(cherry picked from commit 61e3b8fcbc)
2026-09-23 17:01:39 +01:00
Ha1baraA11 45e9af94db fix: guard ambiguous absolute Python imports
(cherry picked from commit 238c9df3d0)
2026-09-23 17:01:39 +01:00
Ha1baraA11 1c12006360 fix: resolve Python imports within scan root
(cherry picked from commit 962a0569b1)
2026-09-23 17:01:39 +01:00
Ha1baraA11 2944b2d8ec fix: resolve absolute Python package imports
(cherry picked from commit f1b880c726)
2026-09-23 17:01:39 +01:00
abhay-codes07 3454890057 fix(export): stop rewriting unchanged wiki/Obsidian pages on every run (#3060)
`to_wiki()` and `to_obsidian()` regenerate the full page set on every call and
write every page unconditionally — `to_wiki` even unlinks every `*.md` up front,
guaranteeing a full rewrite. An export re-runs on any graph.json change, so on
an always-on `watch`/export setup this rewrites tens of thousands of identical
pages per run (measured: 31,417 pages / 137 MB per export, ~7 GB/day), and each
rewrite is also a modification event that re-fires Obsidian re-indexing, sync
clients, and downstream inotify pipelines for content that did not change.

Compare before writing:

- New `paths.write_text_atomic_if_changed(path, text) -> bool` writes (and
  reports True) only when the content differs from what is on disk, comparing
  DECODED text — text-mode writes translate `\n`↔`\r\n` on Windows, so a byte
  compare would report every file changed.
- `export._owned_write` uses it and still records the note in `_written`
  (the ownership + prune-exclusion set, #1896), so an unchanged note stays owned
  and is neither pruned nor dropped from the manifest.
- `to_wiki` drops the up-front unlink-all — which forced the rewrite and opened
  a window where a reader saw an empty `wiki/` mid-export — for an end-of-run
  sweep that removes only the `*.md` this run did not produce, keeping the same
  orphan guarantee for relabelled/removed communities.

Tests: the helper skips an identical write (mtime untouched) and writes on a
diff or a missing file; an Obsidian re-run leaves an unchanged note's mtime
untouched yet still owned (not pruned) and still rewrites a note whose body
changed; a wiki re-run leaves unchanged articles untouched and still sweeps a
relabelled community's stale page. The wiki/export/obsidian suites stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit 91546de3b4)
2026-09-23 16:54:04 +01:00
abhay-codes07 7b2ac86e09 fix(terraform): redact secrets nested inside list values, not just maps (#3644 follow-up)
`_redact_value` (the #3644 guard that keeps hardcoded credentials in .tf files
out of graph.json and the MCP query/get_node surface) recursed into map values
but not list values. HCL routinely nests objects inside tuples —
`list(object(...))` variables, `dynamic` blocks, tuple defaults — and
`_parse_attr_value` turns those into Python lists of dicts, so:

    config  = { password = "x" }      -> redacted   (map recursion)
    configs = [{ password = "x" }]    -> LEAKED      (list not recursed)

The list form left the secret in the serialized node verbatim, defeating the
guard for a very common Terraform shape (a `connections`/`containers`/`secrets`
tuple of objects).

Recurse into lists and tuples as well, under the same key — the list branch is
only reached when the key is not itself sensitive (a sensitive key redacts the
whole value above), so a scalar element carries no key signal and is returned
as-is while a dict element is checked against its own inner keys. A non-secret
list of scalars is untouched.

Tests: a unit check that a secret inside a list-of-objects and a nested list is
redacted (while a sensitive key still redacts the whole list and a plain scalar
list is left intact), and an end-to-end check that a `password`/`token` inside a
tuple-of-objects attribute never reaches the serialized node. Both fail without
the fix. The terraform suites otherwise stay green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit 2998cb2121)
2026-09-23 16:54:04 +01:00
ayushcodes10 6a1a432de8 Update hash seed tests for the module based re exec and add a regression
The existing probe assertions now expect the module flag form. A new
test drives the probe with a launcher stub path that is not even a
real file in place of argv zero, confirming the fix no longer depends
on it being anything runnable, the exact shape of the Windows uv
launcher regression this closes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit e2fd1c4f30)
2026-09-23 16:54:04 +01:00
ayushcodes10 d9d4756506 Re exec the hash seed pin as a module, not by replaying argv[0]
The previous form replayed sys.argv[0] as the script path to run,
which happens to work for a POSIX console script wrapper or a python
dash m invocation, both of which are real, runnable Python content.
It does not work for a uv, pip, or pipx console script launcher on
Windows, which installs as a native executable stub with no such
content, so the interpreter has nothing to open and every affected
command failed outright the moment it tried to pin the seed.

Re execing through the module flag instead never depends on argv[0]
being anything runnable at all, so a launcher stub that is not even a
real file no longer matters, and this now matches the exact
invocation shape the end to end test for this function already
exercised and proved working.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 6509f609dc)
2026-09-23 16:54:04 +01:00
safishamsiandClaude Opus 4.8 a5957aa6ef release: 0.9.66 — five new language extractors (COBOL/VB.NET/R/Solidity/Erlang), deterministic PYTHONHASHSEED builds, Go interface + Rust generic resolution, Cargo workspace/subdir handling, method-ghost dedup, serve id-aliases, install CRLF fix, labeling retry, stale-root fallbacks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.66
2026-09-22 22:39:40 +01:00
ayushcodes10 f49ec5038c Add regression tests for the shared output graphify root marker fix
One test sets a shared, absolute GRAPHIFY_OUT and confirms the marker
written by an update from a relative path is resolved to an absolute
form. A companion test confirms the ordinary, relative GRAPHIFY_OUT
case still preserves the caller supplied relative path exactly as the
existing 777 test already expects, so this fix only changes the
shared output case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 181a46dca9)
2026-09-22 17:30:28 +01:00
ayushcodes10 5439e6d1f3 Resolve the graphify root marker when GRAPHIFY_OUT is shared and absolute
Update writes a raw, caller supplied watch path into the graphify_root
marker, which #777 deliberately chose over a resolved absolute so a
committed marker file stays portable across clones and CI runners.
That is fine as long as every reader of the marker runs with the same
CWD it was written from, which normally holds since the generated git
hooks and an unqualified graphify watch both assume it.

It stops holding once GRAPHIFY_OUT is set to an absolute, shared
location, the multi worktree setup from #686. The same marker file is
then reachable from any worktree's CWD, so a relative value like the
dot from graphify update dot resolves against whichever worktree
happens to read it later instead of the one that was actually
scanned, silently falling back to the wrong scan root.

Add a small helper that only resolves in that shared, absolute case
and otherwise keeps the existing relative behaviour untouched, so the
777 portability guarantee for the common, single clone case is
preserved rather than reverted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit e36edf8462)
2026-09-22 17:30:28 +01:00
Ha1baraA11 78b42dfcdc fix(build): fall back from stale graphify root
(cherry picked from commit 1717695609)
2026-09-22 17:30:28 +01:00
Ha1baraA11 1db41f0bef fix(labeling): retry and report partial community labels
(cherry picked from commit 315974e0a5)
2026-09-22 17:30:28 +01:00
ayushcodes10 d8dd477836 Add regression tests for the install CRLF and idempotency guard fix
Cover an unrelated mention of the word graphify no longer suppressing
the real registration block, a stale or hand edited block correctly
getting refreshed on re install instead of being left alone, and both
the CLAUDE.md and GEMINI.md writers now passing newline equals empty
string so an existing file's own line endings are never translated on
write.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit ddf2497f68)
2026-09-22 17:30:28 +01:00
ayushcodes10 f07423b8da Fix CRLF corruption and a bare substring idempotency guard in install
_register_always_on_block had three problems, all only visible on the
claude and codebuddy paths that call it. Path.write_text opens in
text mode, so on Windows appending a few lines to an existing
CLAUDE.md rewrote the WHOLE file to CRLF, turning a one line
registration into an eighty five line diff for anyone who keeps that
file in a dotfiles repo or syncs it across platforms. Separately, the
re install guard was a bare check for the word graphify anywhere in
the file, so a user whose file mentioned the project for any other
reason got silently treated as already registered and never received
the real block, and an edited or stale block could never be repaired
by re running install since the word was still present either way.

Switch the write to pass newline equals empty string so existing line
endings are never translated, and switch the guard to
_replace_or_append_section, the same marker anchored helper
claude_install and gemini_install already use, so a stale block is
correctly refreshed instead of silently skipped. That helper's
boundary detection was hardcoded to the next H2 heading, which does
not fit this block's own heading level, so it gains a boundary_prefix
parameter mirroring the one _remove_marker_section already has, kept
in sync with a new shared marker constant so the paired removal
function matches on exactly the same heading. Deliberately kept at H1
rather than switched to the H2 markers the issue itself suggested,
since the existing removal function anchors on H1 specifically to
never collide with a genuine user authored H2 heading elsewhere in
the same file, a design choice from a separate earlier issue that a
plain heading level change would have quietly broken.

gemini_install writes GEMINI.md through the exact same text mode
write, so it gets the same newline fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit c325aad3b0)
2026-09-22 17:30:28 +01:00
ahm3dwasim a1d2318e50 fix(serve): get_node/get_neighbors accept node_id/id, not just label
The two node-lookup MCP tools read arguments["label"] directly, so a client
that passes the node identifier under "node_id" (or "id") gets a
KeyError('label') instead of a result. Several of the agent frameworks graphify
advertises support for send the argument that way, so the tools are unusable
from those clients while query_graph/god_nodes work fine against the same graph.

Extract a small module-level _node_arg() that accepts label/node_id/id, return
a short guidance string when none is given (instead of raising), and declare
node_id in both inputSchemas so the parameter is discoverable. Add a unit test.

(cherry picked from commit cf84c59735)
2026-09-22 17:30:28 +01:00
oleksii-tumanov 7a508a71bd fix(cargo): resolve inherited workspace dependency identity
(cherry picked from commit 4d8fa2e93c)
2026-09-22 15:00:07 +01:00
ayushcodes10 9b42507014 Add regression tests for cargo subdirectory manifest discovery
Cover a Tauri style layout where the manifest lives one level down and
must still be found, a discovered workspace manifest whose own
members resolve relative to its own directory rather than the outer
scan root, a decoy manifest inside a dependency install folder that
must never be searched into, and a manifest nested past the discovery
depth cap that is correctly left unfound.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 6d2ef5455f)
2026-09-22 14:58:22 +01:00
ayushcodes10 165a7da4a1 Discover a Cargo.toml in a subdirectory when none sits at the scan root
introspect_cargo only ever looked at root divided by Cargo dot toml,
with no search, so any repository whose crate lives in a subdirectory
got no crate nodes or dependency edges at all, even though the AST
pass had already parsed every rs file there. The common case is a
Tauri app, whose default scaffold keeps its manifest under its own
frontend adjacent crate folder rather than the project root.

Add a bounded depth search that only runs when the root manifest is
missing, reusing the same noise directory pruning the AST scanner
already applies so a vendored or example manifest nested inside a
dependency folder never wins over the project's own. A workspace
manifest found this way now resolves its own members relative to its
own directory rather than the outer scan root, since those two can
now differ.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 577e51749a)
2026-09-22 14:58:22 +01:00
ayushcodes10 137ead6d56 Add regression tests for cargo extraction with a missing manifest
One test runs extract with the cargo flag against a repo that has no
Cargo.toml at all and confirms it still succeeds and still writes the
graph built from the code already indexed, with a note explaining the
crate edges were skipped. A companion test confirms a repo that does
have a valid manifest still gets its crate node, so the fix only
changes the missing manifest path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit a9b14fef06)
2026-09-22 14:58:22 +01:00
ayushcodes10 b11b035722 Degrade instead of aborting when cargo introspection finds no manifest
FileNotFoundError is a subclass of OSError, so a missing Cargo.toml at
the scan root was caught by the same handler meant for ImportError
and connection failures and exited the whole process. That fires
after the AST pass has already completed, discarding every file of
completed structural extraction work just because an optional
enrichment source found nothing to read, an ordinary condition for
any repo whose manifest lives in a subdirectory, such as a Tauri app
that keeps it under its own separate frontend adjacent crate folder.

cargo_result already starts as the empty, handled shape the merge
step downstream expects, so catch FileNotFoundError on its own, print
a note, and let extraction continue with an empty cargo result instead
of exiting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 94cc22be3b)
2026-09-22 14:58:22 +01:00
ayushcodes10 890856f873 Add regression tests for the hash seed pinning fix
Since a real re exec replaces the process it runs in, every test that
needs to observe whether it fired runs a small probe in its own
subprocess with a deliberately constructed environment, rather than
mocking inside the pytest process itself, which pytest's own handling
of its current test marker would defeat anyway. Cover each of the
four affected commands re executing with the seed pinned when unset,
an explicit seed never being overridden, unrelated commands never re
executing, the guard against firing while pytest itself is mid test,
a failed re exec being survived rather than raised, and a full real
invocation of update still completing successfully end to end through
the whole re exec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 2f5f2d705e)
2026-09-22 14:58:13 +01:00
ayushcodes10 3d1a03ef2a Pin PYTHONHASHSEED for update, extract, cluster only and label
The generated git hooks already export PYTHONHASHSEED=0 because
networkx's Louvain implementation iterates string keyed sets whose
order is randomized per process, so community assignments otherwise
churn between runs with no code change. A bare graphify update, which
the CLAUDE.md template tells agents to run after every code change,
skipped this, clustering differently every time and producing a
large, spurious diff in the output directory.

PYTHONHASHSEED is read once at interpreter startup, so setting it on
os.environ from inside an already running process has no effect on
that process's own hash randomization. The only way to pin it for a
command already in flight is to restart the interpreter with it set
from the start, so main() now re execs itself with the seed set
before doing anything else, for exactly the commands whose output
depends on clustering, only when the caller has not already chosen a
seed themselves, degrading instead of raising if the re exec itself
fails so an unusual host that disallows it can still run graphify.

Skips this entirely while running under pytest, detected via the
PYTEST_CURRENT_TEST variable pytest itself sets for the duration of
a running test. Dozens of existing tests call main() directly with a
monkeypatched argv to simulate a full CLI run in process, which only
worked because main() was previously free of this kind of side
effect; without the guard, every one of those calls replaced the
test process running them the moment PYTHONHASHSEED happened to be
unset, which it normally is in a dev or CI environment, with no
traceback or error, just an empty, silently green test run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit b70382ceda)
2026-09-22 14:58:12 +01:00
carterko23 081004f60b fix(go): repoint intra-module imports_from edges from go_pkg_ sinks to the package's files
extractors/go.py mints every import target as `go_pkg_<raw import string>` with
no module lookup, so an intra-module import never reaches a file: the sink has
no source_file and nothing links it onward. `affected` and any reachability
walk miss every consumer that arrives through an import, while qualified calls
(`b.F()`) resolve, because the call pass already filters callees by
_go_import_path_for_file. This inverts that mapping once per run and repoints
the intra-module `imports_from` edges onto the imported package's file nodes,
the way the JS/TS resolver repoints module specifiers. Stdlib and external
imports have no file in the corpus and stay sinks by design.

Measured on a 744-file single-go.mod module, same commit, with and without
this change: a 111-file handler package goes from 0/21 direct intra-module
imports resolved to 21/21 (24/24 transitive); the 57 intra-module go_pkg_
nodes disappear; +16,616 imports_from edges; no other relation changes.

Closes #3746

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit fb40961f4a)
2026-09-22 14:58:12 +01:00
oleksii-tumanov f7bc904bda Preserve case-only interface signature owners
(cherry picked from commit 72d5914a10)
2026-09-22 14:58:12 +01:00
oleksii-tumanov bda82f9ded fix(go): preserve interface method signature references
(cherry picked from commit 00fb309983)
2026-09-22 14:58:12 +01:00
safishamsiandClaude Opus 4.8 6fdc26db03 build: lock the new grammar deps for the R/Erlang/Solidity/VB.NET extractors
Regenerated uv.lock to add tree-sitter-language-pack (R + Erlang grammars),
tree-sitter-solidity, and tree-sitter-vb-dotnet (+ transitive embedded-template,
yaml) that the 0.9.66 extractor batch introduced. uv lock --check passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-22 14:53:59 +01:00
safishamsiandClaude Opus 4.8 0e855f3b40 fix: complete the Solidity/method-ghost/Erlang review items (#3716, #3719, #3714 follow-ups)
- #3716 (Solidity): non-callable members (struct/enum/event/error/state-var) had
  no signature and fell back to the LINE NUMBER for their node id, churning ids on
  incremental rebuilds. Use the member name (unique per kind in a contract scope)
  as the discriminator instead.
- #3719 (method-ghost dedup): gate the candidate index on a method-shaped label
  (leading '.' AND '()' suffix) so a dotfile label like '.env' is never treated as
  a method; normalize the index/lookup key via make_id for parity with the alias
  path. Adds a negative test proving two same-named methods in one file are left
  unmerged (the len==1 ambiguity guard).
- #3714 (Erlang): the remote-call resolver reads module/name/arity off node
  metadata, which the incremental context builders stripped for non-Ruby langs,
  so foo:bar() silently failed to resolve on 'graphify update'. Forward the Erlang
  resolution metadata keys through the watch.py/cli.py allow-lists (same fix shape
  as the elixir _elixir_module marker).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-21 19:44:17 +01:00
shobhitagnihotri69 3097e9d2fa fix(build): deduplicate spec-conformant method nodes and aliases (#3705)
(cherry picked from commit fefdb5f074)
2026-09-21 19:19:56 +01:00
Abdullah 89c27c7b80 feat: add Erlang language extractor
(cherry picked from commit 3a92853329)
2026-09-21 19:19:26 +01:00
Abdullah 9f069eafea feat: add Solidity language extractor
(cherry picked from commit 41a1575e5e)
2026-09-21 19:13:40 +01:00
Abdullah d68d6012bd feat: add R language extractor
(cherry picked from commit 250395de31)
2026-09-21 19:10:13 +01:00
Abdullah ebe1f32c81 feat: add VB.NET language extractor
(cherry picked from commit 304c82fe10)
2026-09-21 19:06:50 +01:00
Abdullah ab568c8f42 feat: add COBOL language extractor
(cherry picked from commit 6573ea6306)
2026-09-21 18:04:14 +01:00
shobhitagnihotri69 5da2da3978 fix(build): preserve higher confidence on same-relation edge collision (#3703)
(cherry picked from commit 0b5d0e96c7)
2026-09-21 18:04:04 +01:00
safishamsiandClaude Opus 4.8 20a20d30d8 test(ts): de-flake the normalizer scaling test that reddened v8 CI
The #3359 scaling guard flaked on CI 3.13 (3.2x vs a 3.0x ceiling): at n=1000 the
measurement (~0.03s) is small enough that a GC pause landing in process_time
inflates the ratio. Not a regression — the normalizer path is unchanged; it was
green on 0.9.64 by luck. Harden it: disable GC around the measured section,
measure at 4k/8k where O(n) work dominates and linear converges to a clean ~2x,
and relax the ceiling to 3.5x (still separates linear from quadratic 4x).
Observed locally: 2.10x, stable across repeated runs on 3.10 and 3.13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 18:53:35 +01:00
safishamsiandClaude Opus 4.8 7ca736cf94 release: 0.9.65 — Pillow CVE floor, Go/Swift/Java/Verilog extractor fixes, JS block scoping, fail-closed eviction deadlock fix, vis-network crash + tooltip encoding, repo-link/docs cleanup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.65
2026-09-20 18:46:53 +01:00
safishamsiandClaude Opus 4.8 86d8aadd3b fix(export): complete the vis-network crash fix and pin the tooltip XSS boundary (#3699, #3686 follow-ups)
#3699: the Fermat-spiral seed positions referenced `i` inside RAW_NODES.map(n => ...)
which has no index param, so the emitted JS threw 'ReferenceError: i is not defined'
and graph.html failed to render at every graph size. Bind the index: map((n, i) => ...).

#3686: the tooltip `title` sinks were correctly un-escaped (vis-network renders a
string title via Popup.setText -> innerText, verified in the pinned 9.1.6 bundle:
its only non-Element branch is innerText and it has zero innerHTML=<var> sinks).
Documented that invariant at the sink as the #1838 stored-XSS boundary.

Adds two guards to test_export.py: a vis-network version pin assertion (a bump must
force re-verifying the innerText tooltip path) and a spiral-seed indexed-map guard
(catches the free-`i` regression, which the substring-only export tests missed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 18:07:23 +01:00
Sanjaiyan Parthipan 124c263488 fix(export): prevent vis-network stack overflow crash on HTML render
Resolves the `BarnesHutSolver.js` maximum call stack size exceeded
error. By explicitly initializing node X/Y coordinates using a
sunflower spiral (Fermat's spiral) formula, nodes no longer spawn
stacked on top of each other. This prevents `avoidOverlap` physics
forces from dividing by zero and exploding into infinite values
on the very first frame.

(cherry picked from commit a20b3593bd)
2026-09-20 18:01:00 +01:00
himanshupatro-334 785ee5a426 Fix HTML entity encoding in graph tooltips
(cherry picked from commit 88025d0a93)
2026-09-20 18:01:00 +01:00
Abdullah 9a06e95408 docs: add GitHub issue and pull request templates
(cherry picked from commit 9ff3e92d10)
2026-09-20 18:00:48 +01:00
Abdullah aa5b334e69 Update link for graphify Enterprise
(cherry picked from commit ac219fa9e4)
2026-09-20 18:00:48 +01:00
viral-antuit 5f6b2a2e83 fix: security pillow updated to 12.3.0 for cve-2026-54058
(cherry picked from commit adaedf5c5e)
2026-09-20 18:00:48 +01:00
safishamsiandClaude Opus 4.8 2ff5db3c37 test(watch): drop two broken #3695 symlink tests that never trigger fail-closed (#3697 follow-up)
The two test_issue_3695_symlink_worker_* tests monkeypatch _reconcile_existing_graph
to inject a deleted_source_identities collision, then assert the 'fail-closed: kept'
message prints — but on v8 that setup never reaches the corpus-sweep excluded-alive
branch, so the message never fires and the tests fail regardless of the fix. The
production fix stays genuinely guarded by the honest tests (fail_closed_preserved /
full_rebuild_preserves_alive / reextracted / shrink_involving), verified to fail
without the watch.py change and pass with it, plus the real-OS-symlink
test_requires_symlinks_rebuild_symlink_worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 15:07:29 +01:00
himanshupatro-334 c99c3a67bb Strengthen fail-closed regression assertions
(cherry picked from commit e7c15f0512)
2026-09-20 13:59:37 +01:00
himanshupatro-334 87a82770eb Fix fail-closed AST ownership eviction
(cherry picked from commit ae5654a8c2)
2026-09-20 13:59:37 +01:00
ayushcodes10 873b5ebde8 Add regression tests for block scoped let/const shadowing
Covers the issue's three reported shapes (const inside an if block,
a for of binding referenced after the loop, and a closure that
outlives the loop), the control case that must not regress (a
reference still inside its own loop), var's continued function wide
reach in the same shapes, and a genuine reference elsewhere still
resolving. The C style for loop case asserts on source_location since
both calls read as identical source text and a plain edge presence
check cannot tell "only the outside call resolved" from "both wrongly
resolved."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 9801c0eac1)
2026-09-20 13:59:26 +01:00
ayushcodes10 4a4fd974ce Scope JS let/const bindings to their own block, not the function
_js_local_bound_names collected let/const declarator targets, and a
for of/for in loop's own binding, into one function wide shadow set,
but both are block scoped in JS, unlike var, which is function scoped
through hoisting regardless of nesting depth. A reference to a
same named module callable made outside the block that actually binds
the name had its genuine indirect_call edge suppressed, whether the
binding sat inside an if, try, or bare brace block, came from a for
of/for in loop referenced after the loop ends, or was read from a
closure that outlives the loop.

The function wide set now only collects var, distinguished by
checking whether a declarator's parent is a variable_declaration
rather than a lexical_declaration, and a for in loop's own binding
only when it uses var. Every let/const and for/for of/for in binding
is instead folded into extra_locals for exactly its own block or loop
subtree, the same mechanism already used for catch bindings, so a
reference genuinely inside that block or loop still correctly shadows
while one outside it resolves. A C style for loop's own let/const
initializer gets the same treatment, since it is scoped to the whole
loop the same way a for of binding is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 875c2a4761)
2026-09-20 13:59:26 +01:00
rajatnagda45 199d741f67 fix(verilog): link a module instantiation to the local definition, not a phantom
A module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses _make_id(stem, name). The
two never matched, so instantiating a module defined in the same file:

    module counter(...); endmodule
    module top;
      counter u1(...);
    endmodule

split `counter` into a real definition node and a second, sourced phantom
that carried the `instantiates` edge. `top`'s dependency on the module you
actually defined was invisible, and because the phantom was sourced it also
baked this file's path into the id and blocked the corpus-level rewire that
would otherwise collapse it (the #1402 duplicate shape the other
extractors guard against).

Pre-scan module names so a same-file instantiation (Verilog allows
forward/any-order instantiation) resolves to the definition id. A module
defined in another file becomes a SOURCELESS stub instead, so the
corpus-level rewire collapses it onto the real definition rather than a
per-file duplicate — mirroring the Go extractor's ensure_named_node.

Adds regression coverage for both the local-definition link and the
cross-file sourceless stub.

(cherry picked from commit bca8bbac72)
2026-09-20 13:59:25 +01:00
rajatnagda45 c049ea0a69 fix(java): attach enum body members to the enum, not the file
A Java enum wraps its fields, constructors and methods in an
enum_body_declarations node, nested under enum_body after the constant
list. The generic walker recurses into a class body keeping the enclosing
type as the parent scope, but an unknown wrapper node resets that scope to
None (an unknown wrapper usually IS a scope boundary). That reset orphaned
every enum method, field and constructor onto the file:

    public enum Planet {
        EARTH(5.976e+24), MARS(6.421e+23);
        private final double mass;
        Planet(double mass) { this.mass = mass; }
        public double surfaceGravity() { return 6.673e-11 * mass; }
    }

Planet became a bare list of constants; surfaceGravity was emitted as a
file-level function and the constructor was dropped entirely.

enum_body_declarations is not a scope of its own - its members belong to
the enum - so recurse through it transparently, preserving the enum as the
parent scope (mirrors the companion_object and ERROR handling already in
the walker). Constructors and methods now hang off the enum via method
edges and their bodies are walked as call-graph scopes.

Adds regression coverage: members attach to the enum and don't leak onto
the file, an inter-method call is captured, and the constant case_of edges
are untouched.

(cherry picked from commit 820018ee3c)
2026-09-20 13:59:25 +01:00