1933 Commits
Author SHA1 Message Date
safishamsiandClaude Opus 4.8 4000de1546 release: consolidate unreleased 0.9.69 into 0.9.68
0.9.68 was bumped locally but never published to PyPI (latest published
is 0.9.67), so fold the second triage round's five PRs into a single
contiguous 0.9.68 release rather than skipping the number. No PyPI gap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.68
2026-09-25 16:33:01 +01:00
safishamsiandClaude Opus 4.8 636f2fe08a release: 0.9.69
Complete the skillgen --watch INPUT_PATH injection fix in the add-watch
reference (#3742), preserve cross-file edges on incremental update
(#3812), stop C# type references resolving to same-named non-type nodes
(#3815), prefer a real source location over richness in dedup (#3786),
and resolve Maven inherited groupId + ${...} properties in pom.xml
(#3823).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 16:22:25 +01:00
himanshupatro-334 89280317ed fix: preserve cross-file edges during incremental updates
(cherry picked from commit b6f3adffc1)
2026-09-25 16:15:15 +01:00
safishamsiandClaude Opus 4.8 8595749021 security(skillgen): neutralize the --watch INPUT_PATH sink in add-watch reference
Completes #3742: the Step 1 fix closed the .graphify_root shell line, but
the progressive-disclosure add-watch reference still passed the raw,
agent-substituted INPUT_PATH placeholder unquoted into a bash command
($(cat .graphify_python) -m graphify.watch INPUT_PATH), the same
injection class - a scan root like $(...) / ; rm ... would execute.

The watcher targets the folder graphify already extracted, so read the
trusted graphify-out/.graphify_root that Step 1 resolves (the same
sidecar pattern this reference already uses for .graphify_python)
instead of re-substituting the untrusted placeholder. Regenerated all
14 host add-watch artifacts + expected/ snapshots; monolith-roundtrip
and always-on-roundtrip stay green (cores untouched).

The identical sink in the aider/devin monolith cores is tracked
separately (editing a core requires a gen.py sanctioned-diff).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 16:07:51 +01:00
ayushcodes10 43026db66f Add regression tests executing Step 1 with a hostile substituted path
Extracts the real Step 1 bash block from the committed skill.md
artifact, substitutes INPUT_PATH the way an agent following the
instructions would, and actually runs it, so the fix is checked
against what users receive rather than the fragment source alone. One
test confirms a command substitution embedded in the substituted path
never runs, another confirms a semicolon separated command never
runs, a third confirms a legitimate path with a space in it still
resolves correctly, and a fourth confirms a nonexistent path still
fails the whole step loudly. All four were checked against the
pre fix block first, three failed as expected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit ad98afa9f5)
2026-09-25 16:01:53 +01:00
ayushcodes10 0dfcd7fdd9 Regenerate skill artifacts for the Step 1 injection fix
Ran the generator, then the bless command, with no manual edits.
Confirmed clean with the check, audit coverage, monolith roundtrip,
and schema singleton verification commands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 2b41299b89)
2026-09-25 16:01:53 +01:00
ayushcodes10 c8e5bfe2bb Stop substituting INPUT_PATH into an interpreted shell command in Step 1
Step 1's own bash and PowerShell fragments both built a command whose
literal text was the resolved scan path, substituted verbatim by the
agent following the instructions before that line ever ran. The bash
form, echo dollar paren cd INPUT_PATH and pwd close paren, still
expands a command substitution even inside the outer double quotes,
and the cd itself was entirely unquoted; the PowerShell form passed
INPUT_PATH as a bare unquoted argument to Resolve Path, exposed to the
same class of problem through a stray semicolon or pipe. A path drawn
from an untrusted source, a malicious readme's suggested command, a
copied value from an untrusted page, executed as code the moment this
line ran, before any Python was ever reached.

Both now capture INPUT_PATH through a construct their own interpreter
guarantees performs no expansion at all: a quoted bash heredoc for the
posix form, a single quoted PowerShell here string for the windows
form. The raw text reaches Python (respectively Resolve Path) only as
inert data on standard input, never as source the shell itself
parses. Verified for the posix form by actually executing the
extracted Step 1 block with a malicious substituted value and
confirming no injected command runs; the PowerShell form could not be
executed in this environment (no interpreter available to install
without an interactive credential prompt), so it was written using
well established, unambiguous here string syntax and reviewed
carefully rather than run.

As a side effect of no longer building the path through an unquoted
cd, a legitimate path containing a space is now handled correctly
too, and a nonexistent path now fails the whole step loudly instead
of silently continuing past a swallowed cd error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 9a7e5d0a51)
2026-09-25 16:01:53 +01:00
ayushcodes10 d7e3c6f7f2 Add a regression test for the stray source_location gate
Covers the review finding directly: a candidate with a
source_location but no source_file must not out rank a fully bare,
richer candidate. Confirmed against pre fix code via git stash, the
test fails there and passes only with the fix applied.

(cherry picked from commit f6afc87140)
2026-09-25 16:01:47 +01:00
ayushcodes10 9bc077b907 Gate the source_location tiebreak on a real source_file
A stray source_location with no source_file, a location pointing at
an unstated file, which _merge_missing_attributes can produce by
backfilling one field but not the other when a survivors own
source_file is an empty string rather than None, was still granted
an edge over a fully bare candidate by the location tiebreak, even
though neither side actually has real provenance. The tiebreak now
only counts source_location when source_file is present too, so a
location with no file falls through to richness like any other bare
field would.

Fixes review finding on PR 3786.

(cherry picked from commit 3b3cc81541)
2026-09-25 16:01:47 +01:00
ayushcodes10 a71ee67d7c Add regression tests for the source_location provenance tiebreak
Covers the review finding directly: a record with a source_file but
no source_location must not lose to a richer, unlocated stub that
also carries a source_file, and the tiebreak must stay inert when
both sides already have a real location, preserving the existing
richness ordering there.

Confirmed against pre fix code via git stash: the direct repro test
fails there and passes only with the fix applied.

(cherry picked from commit 4915270490)
2026-09-25 16:01:47 +01:00
ayushcodes10 f7092b45d5 Extend the provenance tiebreak to source_location too
source_location is richness ignored the same way source_file already
is, so two candidates that both carry a source_file could still be
ordered by field count alone, letting a record with no real location
out score one that also knows exactly where in the file it lives. The
codebase genuinely emits source_location as None on some records, so
this is a real distinction, not a hypothetical one. The tiebreak now
checks source_location as a second provenance step, after source_file
and before richness, and only changes the outcome when the two sides
disagree on having one.

Fixes review finding on PR 3786.

(cherry picked from commit 1d33a488e0)
2026-09-25 16:01:47 +01:00
ayushcodes10 699d18f760 Add regression tests for the pick winner provenance tiebreak
One test is the issue's own exact reproduction, a source less stub
that genuinely out scores a real located declaration on field count
alone, checked in both argument orders. A companion test confirms the
provenance tiebreak stays inert when both candidates carry a source
file, so the existing richness then length ordering this file already
established is untouched. The first test was checked against the pre
fix code and failed there as expected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 0e155019e3)
2026-09-25 16:01:47 +01:00
ayushcodes10 02b32f4cfe Give a real source location priority over richness in pick winner
source_file and source_location are both in the set of keys the
richness score deliberately ignores, since #3372 added that score to
compare actual content, not placement. That leaves richness with no
way to tell a real, located declaration apart from a source less
stub that happens to carry a couple of incidental bookkeeping keys,
letting the stub out score and replace the genuine record purely on
field count.

Provenance is now checked ahead of richness in the survivor scoring
tuple, so a node with a real source location always wins over one
without. This only changes the outcome when the two sides actually
disagree on provenance; every case where both, or neither, side
carries a source file keeps the exact ordering the richness fix
already established.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit a9180c0da7)
2026-09-25 16:01:47 +01:00
himanshupatro-334 bb373632d6 fix: prevent C# member nodes from being resolved as types
(cherry picked from commit 25de4ace89)
2026-09-25 16:01:47 +01:00
himanshupatro-334 769851dfae fix: exclude non-types from C# type resolution
(cherry picked from commit dfe5a220b2)
2026-09-25 16:01:47 +01:00
Vladimir Babin cac084cf3d fix(manifest_ingest): resolve inherited groupId and ${...} properties in pom.xml (#3806)
(cherry picked from commit 292b9d8465)
2026-09-25 16:01:47 +01:00
safishamsiandClaude Opus 4.8 d197ae36fa release: 0.9.68
Windows hash-seed re-exec wait (#3816/#3799), Terraform secret-named
variable-default/output redaction (#3817), COBOL fixed-format
sequence-number detection (#3813) and PERFORM...THRU both-endpoint
linking (#3814), and gate the GRAPH_REPORT undocumented-components
reason on a real semantic layer (#3828/#3801).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 15:54:48 +01:00
ayushcodes10 88776942ef Add regression tests for the undocumented components gate
Covers issue 3801 directly: a code plus concept only graph (no
document, paper or image nodes anywhere) must not offer undocumented
components as a reason for an isolated node, only possible missing
edges. Adding a single document node elsewhere in the same graph
shape brings the fuller reason back, confirming the gate tracks the
graph's actual content rather than being tied to a particular test
fixture shape.

Confirmed against pre fix code via git stash: the first test fails
there and passes only with the fix applied.

(cherry picked from commit 02e1b4d6c2)
2026-09-25 15:44:21 +01:00
ayushcodes10 6b6778aede Gate the undocumented components reason on a real semantic layer
GRAPH_REPORT.md's Knowledge Gaps section always offered undocumented
components as a possible explanation for an isolated node, regardless
of whether the graph could ever actually have one. That phrase only
means anything when a semantic layer exists, document, paper or image
nodes an LLM extracted meaning from, where an isolated node genuinely
could be mentioned but never explained further. A code only graph has
no such nodes at all, so offering it as a live possibility on every
report is misleading.

The reason now checks whether any node in the graph carries a
document, paper or image file_type before including that clause,
using the same VALID_FILE_TYPES distinction validate.py already
draws between the semantic layer and code/rationale/concept nodes, so
no new build time flag is needed.

Fixes issue 3801.

(cherry picked from commit 8a49e1f38a)
2026-09-25 15:44:21 +01:00
breken-ai 0dd9f8ba5c fix(terraform): redact secret-named variable defaults and output values
A variable or output names its secret in the block label, so the literal
sits under the generic key default/value and the key-name check never
flags it: variable "db_password" { default = "..." } reached graph.json
verbatim. Redact default/value when the label matches the sensitive-key
pattern or the block sets Terraform's own sensitive = true.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 01e417166b)
2026-09-25 15:44:21 +01:00
Sinan Gümüşkabak f3483171b9 fix(cli): wait for the hash-seed re-exec on Windows instead of os.execvpe (#3799)
On Windows os.exec* does not replace the process: CPython spawns a new one
and the caller exits at once, so update/extract/cluster-only/label returned
before graph.json was written, and the exiting parent intermittently died
with an access violation (exit 139). Run the child with subprocess, wait for
it and propagate its exit status there; POSIX keeps the real os.execvpe.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 94a9657e93)
2026-09-25 15:44:21 +01:00
abhay-codes07 45ceb2273a fix(cobol): link both endpoints of a PERFORM ... THRU range
`PERFORM A THRU Z` (and `THROUGH`) runs the whole paragraph range A..Z, but the
PERFORM pattern captured only the first name, so a `calls` edge was emitted to A
and the range-end Z got none. "What performs Z?" then answered nothing even
though this paragraph does, and any reachability walk stopped short of the
range's tail.

Capture the optional `THRU`/`THROUGH` target and enqueue both endpoints for
resolution against the paragraph table. Intermediate paragraphs in the range are
not individually linked (that would need paragraph source ordering); the two
endpoints are the reliable, common signal and both now resolve. A plain
`PERFORM A` is unchanged.

Regression test: `PERFORM A-PARA THRU Z-PARA` now links MAIN to both A-PARA and
Z-PARA (fails on the old single-capture pattern).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit abcdde1a65)
2026-09-25 15:44:08 +01:00
abhay-codes07 81afa16bd9 fix(cobol): detect fixed-format source that carries sequence numbers
The fixed/free format detector counted a line as fixed-format only when its
first six columns were blank (`^ {6}[ *\-/ ]`). Legacy mainframe COBOL — the
main reason to have a COBOL extractor — puts a sequence NUMBER in columns 1-6,
so every such file scored zero fixed markers and was classified free-format.

In free mode the code is taken verbatim (no column-8 slice), so the sequence
number stays glued to the statement: `000400 MAIN-PARA.` no longer matches the
paragraph anchor `^NAME .? .$`, and the whole procedure layer disappears. A
seven-line program with two paragraphs and a PERFORM collapsed to just the file
and program nodes — zero paragraphs, zero `calls` edges — silently.

Match a sequence area that is six spaces OR six digits, followed by a valid
indicator column (space = code, `*`/`/` = comment, `-` = continuation, `D` =
debug). Once the file is correctly read as fixed-format the existing
column-8..72 slice strips the sequence number and paragraphs/PERFORMs resolve
as they should. Blank-sequence fixed-format and genuinely free-format source are
unaffected (a 6-digit prefix is not something free-format code begins a line
with).

Regression test: a sequence-numbered program now yields its program, both
paragraphs, and the PERFORM `calls` edge — and fails on the old detector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit 96b9522bd5)
2026-09-25 15:43:14 +01:00
safishamsiandClaude Opus 4.8 8e09034743 docs: standardize the Discord invite to discord.gg/XDnKVpzdXB
Point the README and he-IL translation at the single current invite (the old 2DDrEgvZb4/598Ad9zQZ links are retired). All Discord references now resolve to one canonical invite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 13:52:21 +01:00
safishamsiandClaude Opus 4.8 fe566710cc docs: add community banner for the Discussions welcome post
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-25 13:29:51 +01:00
safishamsiandClaude Opus 4.8 4c735618f3 release: 0.9.67 — Windows hash-seed re-exec fix, PHP closures, absolute Python import resolution, package/module phantom-cycle fix, terraform list-secret redaction, idempotent wiki/Obsidian writes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.67
2026-09-23 20:23:51 +01:00
safishamsiandClaude Opus 4.8 793aca6714 test(php): cover file-scope, no-duplicate, and method-scope closures (#3409/#3461 follow-up)
#3461 fixes #3409 (PHP closures produce no nodes / inner calls lost). Adds the
coverage its tests omitted:
- the reporter's exact repro — a FILE-SCOPE argument-position closure — asserting
  the closure node exists AND its inner call resolves to the closure (not the file);
- a no-duplicate-node assertion guarding the walk partition against double-walk;
- a method-scope closure asserting its inner call attributes to the closure, not
  the enclosing method (pins the function_boundary_types behavior).

Verified end-to-end: closure->handler() edges attribute to {closure#N} nodes with
no duplicates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-23 19:45:40 +01:00
nikhil2004 95dd8cf9a0 fix(php): allow relative prefixes in route groups and correctly fail tests on extractor error
(cherry picked from commit 29ed19f1e4)
2026-09-23 19:42:04 +01:00
nikhil2004 4c74bd68fe fix(php): prevent closure fallback leak and support fluent routing prefixes
(cherry picked from commit b8e2033e3c)
2026-09-23 19:42:04 +01:00
nikhil2004 904deaecab fix(php): prevent non-route closures from adopting parent route names
(cherry picked from commit 9d592e6996)
2026-09-23 19:42:04 +01:00
nikhil2004 10f144de06 fix(php): match correct AST node names and scan nested closures
(cherry picked from commit ceb961a6b3)
2026-09-23 19:42:04 +01:00
nikhil2004 435f5a88e9 refactor(extract): hoist PHP route helper to module level (#3409)
Address maintainer nits by moving _php_get_route_name and the
_PHP_ROUTING_VERBS frozenset out of the _extract_generic body
to the module level, preventing them from being repeatedly
re-initialized per file on large codebases.

(cherry picked from commit e845f15b68)
2026-09-23 19:42:04 +01:00
nikhil2004 8227c468c2 fix(extract): robust PHP nested routing closure extraction (#3409)
Address edge cases identified in review:
- Nested route closures (e.g., inside group() or prefix()) now correctly compose their full path by walking up the AST across closure boundaries.
- Non-routing closures passed to methods that share verbs (like ->get('user:42')) are no longer misidentified as routes. A route path must begin with '/' to qualify, otherwise it correctly falls back to an ordinal name.

(cherry picked from commit 38861aa1c4)
2026-09-23 19:34:18 +01:00
nikhil2004 2c172e4969 fix(extract): stable closure naming for PHP (#3409)
Address reviewer feedback:
- Replace line-based {closure@N} names with stable ordinal {closure#N}
  names scoped per enclosing class or file. Ordinals are unaffected by
  line insertions/deletions above a closure; only reordering closures
  or inserting one earlier in the same scope shifts later ordinals,
  which is a much rarer edit.
- Route closures passed to routing verbs (get/post/put/patch/delete/
  options/any/match/map) receive a semantic 'VERB /path' label that
  is both stable and directly meaningful to graph queries (the primary
  use-case from the issue).

Note: adding anonymous_function_creation_expression and arrow_function
to function_boundary_types (introduced in the first commit) means calls
inside a closure now attribute to the closure rather than the enclosing
named function. This is the correct behaviour; existing codebases with
closures inside named functions will see those edges move on next
incremental update.

(cherry picked from commit 5400b9cbd7)
2026-09-23 19:34:18 +01:00
nikhil2004 cd3fd9436e feat(extract): support PHP closures (#3409)
The bug:
PHP closures (`anonymous_function_creation_expression` and `arrow_function`)
were invisible to the graph, disconnecting API routes and callbacks from
the backend code.

The fix:
- Added closure syntax node types to _PHP_CONFIG's `function_types`.
- Updated engine.py to synthesize line-bound names (e.g., `{closure@42}`)
  for unnamed closures matching these AST node types, enabling them to be
  extracted as linkable nodes in the graph without ID collisions.

(cherry picked from commit c8c3af1960)
2026-09-23 19:34:18 +01:00
safishamsiandClaude Opus 4.8 f2b65b78ec fix(python): reconcile #3784 phantom-edge retraction with #3729 package imports
#3729 (absolute package import resolution) and #3784 (retract phantom
package/module-collision import edges) landed in the same cut and interacted: a
legitimate `from pkg import sub` (pure package, no colliding pkg.py) now resolves
its provisional edge to the real `pkg/__init__.py` node, but #3784 retracted it —
via the __init__.py candidate id and the len==1 blind fallback — even though there
is no name collision (no phantom cycle to fix). This dropped the correct
package-dependency edge (caught by test_absolute_package_import_targets_package_init).

Tighten #3784 to retract ONLY genuine module-collision phantoms: drop the
__init__.py candidate id forms (keep the module-file `pkg`/`pkg.py` collision ids
that the #3777 fix actually targets) and remove the len==1 blind fallback (a real
phantom always matches by its module-file id, so no fallback is needed). Both the
#3729 package-import tests and all 26 #3784 phantom-cycle tests now pass together.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-23 17:38:41 +01:00
himanshupatro-334 310b317355 fix: retract provisional package import edges
(cherry picked from commit 9a54565cb4)
2026-09-23 17:02:17 +01:00
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