perf(skill): build the graph once across Steps 4-5#1717
Open
Echolord wants to merge 678 commits into
Open
Conversation
…ion (PR Graphify-Labs#864, Jstottlemyer) Co-Authored-By: Jstottlemyer <Jstottlemyer@users.noreply.github.com>
…s#852, spindle79) Co-Authored-By: spindle79 <spindle79@users.noreply.github.com>
…(PR Graphify-Labs#859, voidborne-d) Co-Authored-By: voidborne-d <voidborne-d@users.noreply.github.com>
…ows.md path prefix, sidecar graphifyignore bypass, manifest ast/semantic split - skill-windows.md: add graphify-out/ prefix to 5 paths in Step B3 that were being read/written without prefix (lines 244,245,373,387,388,407,413) causing semantic cache to always appear empty on Windows (Graphify-Labs#862) - detect.py: gate converted office/Google Workspace sidecar paths through _is_ignored() before appending to files list so .graphifyignore rules apply to graphify-out/converted/ sidecars (Graphify-Labs#861) - detect.py: split manifest entries from {mtime,hash} to {mtime,ast_hash,semantic_hash}; save_manifest() accepts kind="ast"| "semantic"|"both"; detect_incremental() accepts kind= and checks the right hash field so graphify update (kind=ast) no longer prevents graphify extract from re-running semantic extraction (Graphify-Labs#857) - watch.py: all three save_manifest() calls use kind="ast" (AST-only rebuild) - __main__.py: both extract save_manifest() calls use kind="both" - backwards compatible: legacy {mtime,hash} entries treated as ast_hash only 775 tests passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tall works in linked worktrees In a linked worktree .git is a file not a directory, so root/.git/hooks fails with NotADirectoryError. Fall back to git rev-parse --path-format=absolute --git-path hooks which resolves the correct hooks dir from any worktree. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ect (Graphify-Labs#869, Graphify-Labs#870) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tors - Add .sh, .bash, .json to CODE_EXTENSIONS in detect.py so files reach extractor - Fix test_detect_incremental manifest path collision with new .json extension - Update test_watch to reflect .json/.sh are now watched extensions - B-1: only emit source imports for paths that exist on disk - J-1: replace stat()+read() with bounded read to eliminate TOCTOU - J-3: move pair_count cap inside loop so it is honoured exactly - J-4: namespace $ref/extends refs with "ref_" prefix to prevent ID collision Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eload on graph change Graphify-Labs#873: Remove blanket dot-prefix exclusion from detect.py and extract.py collect_files(). Add framework caches (.next, .nuxt, .turbo, .angular, .idea, .cache, .parcel-cache, .svelte-kit, .terraform, .serverless, .graphify) to _SKIP_DIRS so they stay blocked. Meaningful dot dirs (.github, .claude, etc.) are now indexed. Graphify-Labs#874: Add _maybe_reload() with mtime+size stat key and threading.Lock to serve.py. call_tool and read_resource call _maybe_reload() on every request; the graph reloads automatically when graph.json changes without restarting the MCP server. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s#831) graphify.exe and python.exe are co-located in the same Scripts\ directory for both uv tool and pipx installs. Use Get-Command graphify to find that directory and pick up the correct Python instead of falling through to bare python (which is a different interpreter that doesn't have graphify). Also replace all bare `python .graphify_*` invocations throughout the skill with `& (Get-Content graphify-out\.graphify_python)` so every step uses the saved interpreter, and fix the bash $(cat ...) syntax to PowerShell & (Get-Content ...). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ify-Labs#831) antigravity install on Windows now installs skill-windows.md instead of skill.md by redirecting to a new antigravity-windows platform config at install time via sys.platform check. Python detection in Find-GraphifyPython now uses uv tool dir (respects UV_TOOL_DIR) and pipx environment --value PIPX_LOCAL_VENVS (respects PIPX_HOME) instead of guessing from the shim location. The graphify.exe shim and python.exe live in different directories for both uv and pipx on Windows so the previous Get-Command approach was wrong. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move all intermediate data files and temp step scripts from the project root into graphify-out/ to match the bash skill behaviour. Also fix the > redirect on detect.json which wrote UTF-16LE in PowerShell 5.1 - now uses Out-File -Encoding utf8 consistently. Files moved: .graphify_detect.json, .graphify_ast.json, .graphify_extract.json, .graphify_semantic.json, .graphify_analysis.json, .graphify_labels.json, .graphify_incremental.json, .graphify_old.json, all .graphify_step_*.py temp scripts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…label normalization Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…se merges on short labels (Graphify-Labs#878) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cherry-picked from PR Graphify-Labs#881 (merged into v7) — adds case study artifacts matching the worked/karpathy-repos/ convention.
…raphify-Labs#882) Alembic/Flask-Migrate revisions, Django migrations, and protobuf/OpenAPI generated files produce hundreds of degree-1 rationale nodes labeled as 'possible documentation gaps'. Their module docstrings are revision annotations or boilerplate, not architectural rationale. - Add _is_autogenerated_python() in extract.py detecting Alembic, Django migrations, and generic DO-NOT-EDIT markers; skip module docstring only - Function/class docstrings inside those files still extracted as normal - report.py: exclude file_type=rationale nodes from isolated-node gaps section — rationale nodes are degree-1 by construction; flagging them as missing edges was always wrong - 5 new tests covering Alembic, Django, protobuf, false-positive guard, and function-docstring passthrough
…ctions In Python+TypeScript monorepos, the call and import resolvers match by label across language boundaries (AuthError -> Member), producing false positives that dominate 'Surprising Connections' due to cross-dir (+2) and cross-community (+1) bonuses. Expand the existing calls guard to also cover uses edges, and zero the cross-dir and cross-community bonuses for these pairs — not just conf_bonus. Leaves semantically_similar_to, EXTRACTED, and AMBIGUOUS edges unaffected. 5 new tests: calls suppressed, uses suppressed, semantically_similar_to preserved, same-language INFERRED preserved, cross-language EXTRACTED preserved.
Removes uv.lock from .gitignore and tracks the lockfile so contributors and CI get the same resolved dependency tree. graphify is shipped as a CLI (uv tool install graphifyy / uvx graphifyy), so the application convention of committing the lockfile fits better than the library convention of ignoring it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…bs#1143) python -m graphify.serve graph.json --transport http --port 8080 serves the same MCP tools over the Streamable HTTP transport (spec 2025-03-26) so a single shared process can serve the graph for a whole team. - _build_server() refactors server registration into a shared factory (stdio behavior is byte-for-byte unchanged — all 52 existing tests pass) - _ApiKeyMiddleware: raw ASGI (not BaseHTTPMiddleware) preserves SSE streaming; constant-time compare; RFC-6750 case-insensitive Bearer; blank-key normalized to no-auth - DNS-rebinding protection via TransportSecuritySettings; wildcard binds disable it and print an exposure warning when no api-key is set - session_idle_timeout reaps idle stateful sessions (default 3600s) so a long-running shared server does not leak memory on client disconnect - Dockerfile + .dockerignore for containerized team deployment - 16 new tests via in-process ASGI test client (importorskip-guarded) - stdio remains the default; no change for existing setups Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…1155) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s#1159 Graphify-Labs#1107 Graphify-Labs#1103 (graph quality + new features) Graphify-Labs#1118 — prune stale AST nodes on full re-extraction (Graphify-Labs#1116) Stamps every AST-extracted node with _origin="ast" in extract(). On a full rebuild _rebuild_code drops any AST-marked node absent from the fresh output even when its source file survives, fixing stale symbols. Backward-compat: marker-less nodes from pre-1118 graphs survive one cycle then self-heal. Graphify-Labs#1110 — stop reading images and PDFs as garbage in headless extract Images route through per-backend vision payloads (base64/data-URI/bytes for claude/openai/bedrock); non-vision backends get _strip_pixels for graceful degradation. PDFs reuse pypdf. 5MB cap, 20-image chunk limit. Graphify-Labs#1159 — Salesforce Apex extractor (.cls, .trigger) Regex-based extractor: classes, interfaces, enums, methods, triggers, SOQL/DML edges. No new dependency. Dispatched as .cls and .trigger. Graphify-Labs#1107 — Azure OpenAI Service backend (--backend azure) Uses AzureOpenAI SDK client (from existing openai package). Auto-detects when AZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT both set. Uses max_completion_tokens (not deprecated max_tokens). Graphify-Labs#1103 — live PostgreSQL introspection (--postgres DSN) graphify extract --postgres "postgresql://..." introspects tables, views, routines, and FK relations via information_schema (SERIALIZABLE READ ONLY). Credentials sanitized on error. New graphify[postgres] extra (psycopg3). Union-resolved llm.py conflict: Azure functions + bedrock images= param. Fixed test_image_vision.py mock to accept timeout= kwarg (our Graphify-Labs#1112). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hify-Labs#1160) Graphify-Labs#1154: scope numpy>=2.0 constraint to python_version>='3.13' only. numpy 1.26.4 ships no cp313 wheel so uv sync falls back to a source build requiring a C compiler. The marker avoids forcing numpy 2.x on 3.10-3.12 users who have working 1.x environments. Graphify-Labs#1160: codex platform skill now installs to .codex/skills/graphify/ instead of .agents/skills/graphify/. The hook already wrote to .codex/ so the skill destination was inconsistent. Propagates automatically through install/uninstall (both read _PLATFORM_CONFIG dynamically). Updated all codex-specific test assertions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(hooks, sensitive filter, score_nodes) Graphify-Labs#1170 — replace nohup with cross-platform Python detach in git hooks. Git for Windows MSYS has no nohup so post-commit/post-checkout hooks silently failed. Now uses subprocess.Popen with DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP on Windows, start_new_session=True on POSIX. Quoting-safe (argv list). Fixes Graphify-Labs#1161. Graphify-Labs#1169 — fix _is_sensitive false positives on topic-mentioning filenames. token-economics-of-recall.md and password-policy-discussion.md were silently dropped as secrets. Generic keywords (token/secret/password) now only fire when the keyword ends the filename stem or the stem is ≤2 words. Specific patterns (.env/.pem/id_rsa etc.) remain unconditional. Graphify-Labs#1165 — fix multi-word endpoint resolution in _score_nodes. graphify path "AuthService" "UserRepo" never fired the exact-match bonus because per-token comparison never equalled the full label. Now joins normalized tokens and compares against the full label and its tokenized form. O(1) per node, affects query_graph and shortest_path uniformly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…st drift (Graphify-Labs#1174 Graphify-Labs#1173 Graphify-Labs#1172 Graphify-Labs#1163) Graphify-Labs#1174: affected.py load_graph now forces directed=True before node_link_graph, matching the identical fix in serve.py and __main__.py. Undirected graphs (directed:false in graph.json) were causing in_edges to fall back to a direction-blind scan, missing true callers and reporting false positives. Regression test added. Graphify-Labs#1173: post-commit and post-checkout hook bodies now read graphify-out/.graphify_root before calling _rebuild_code, falling back to Path('.') if absent. A scoped build (graphify src/) no longer gets silently expanded to the full repo on the next commit. Tests added. Graphify-Labs#1172: Step 9 cleanup split into rm -f for fixed files and find -maxdepth 1 -delete for the chunk glob. Under fish/zsh an unmatched glob aborts the entire rm -f line, leaving temp files on disk. Fixed in the three skillgen source fragments and regenerated. Graphify-Labs#1163: detect_incremental type guard on stored mtime — if the manifest contains a dict-valued mtime (schema drift from older versions), coerce to None rather than propagating a non-numeric into comparisons. Regression test added. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on --update (Graphify-Labs#1178) Three-part fix: dedup.py: Pass 1 exact-merge now skips nodes with an empty source_file. Previously all no-source_file nodes with the same label landed in one bucket and were merged, destroying distinct symbols (third-party deps, standalone functions) that happened to share a short name. update.md (skillgen + all 13 host variants): the --update merge now passes both deleted AND changed files to prune_sources, mirroring what watch._rebuild_code already does correctly. Old nodes for re-extracted files are pruned before fresh AST is inserted — no fuzzy reconciliation needed, no cross-file collapse possible. export.py: anti-shrink guard message now names fuzzy dedup as a possible cause (not only "missing chunk files"), and advises a full rebuild as the safe recovery path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds graphify skill installation for CodeBuddy (https://www.codebuddy.ai/). CodeBuddy uses the same agent+hook mechanism as Claude Code. - graphify codebuddy install — writes ~/.codebuddy/skills/graphify/SKILL.md and a CODEBUDDY.md always-on section - graphify codebuddy uninstall — removes both cleanly - graphify install --platform codebuddy — same as above - Registers Bash + Read|Glob PreToolUse hooks in .codebuddy/settings.json - Full install/uninstall roundtrip tests (35 tests) Co-authored-by: studyzy <studyzy@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x README hook description
graphify codebuddy install was writing CODEBUDDY.md and settings.json
but not copying the SKILL.md. Added _copy_skill_file("codebuddy") call
to match the --platform codebuddy path. README hook description updated
from "Glob and Grep" to "Bash search and file reads" to match actual
hook matchers (Bash + Read|Glob).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Labs#1180) The Agent Skills spec only defines name, description, license, compatibility, metadata, and allowed-tools as valid frontmatter fields. The trigger: /graphify line was non-spec, silently ignored by spec- following hosts, and flagged by agentskills validate CI checks. - gen.py: removed trigger emission from _render_frontmatter; added _is_trigger_line() helper for roundtrip allow-list - fragments/core/aider.md: removed hardcoded trigger: /graphify - platforms.toml: removed trigger doc comment and trigger="" entries - test_skillgen.py: replaced trigger-assertion tests with a single test asserting no host has trigger: in frontmatter - Regenerated all 125 skill artifacts Routing intent is preserved: the description field already contains "treated as a graphify query first" and "graphify-out/ exists". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `graphify-mcp` as a named console script pointing to `graphify.serve:_main`, making the MCP stdio server directly invocable as a first-class CLI command from uv tool / pipx installs. MCP client configs can now use `"command": "graphify-mcp"` instead of `python -m graphify.serve`. Co-authored-by: jr2804 <jr2804@users.noreply.github.com>
Adds support for the XML-based `.slnx` solution format (VS 2022 17.13+ replacement for `.sln`). Extracts project references as `contains` edges and build dependencies as `imports` edges. XXE-protected XML parsing with size cap. Wired into `_DISPATCH` and `CODE_EXTENSIONS`. 6 new tests passing. Co-authored-by: bakgaard <bakgaard@users.noreply.github.com>
…Graphify-Labs#1197) Adds extra_body parameter support for custom/OpenAI-compat providers so users can pass provider-specific params (e.g. thinking budget for Claude via Bedrock compat). Adds multi-batch label_communities for 16k-context models — batches multiple community descriptions into a single LLM call instead of one per community. Partial batch failures are handled gracefully. Co-authored-by: EirikWolf <EirikWolf@users.noreply.github.com>
…Labs#1195) Guards _norm, _norm_label, and _strip_diacritics against None node labels that cause TypeError in unicodedata.normalize(). Fixes Graphify-Labs#1194. Consistent with existing security.py:270 precedent. Co-authored-by: freiit <freiit@users.noreply.github.com>
…edup prefix merge - analyze.py: pass length_bound=max_cycle_length to nx.simple_cycles() so networkx prunes during enumeration instead of post-filtering; drops report generation from never-returns to ~0.1s on dense graphs (Graphify-Labs#1196) - llm.py: replace hardcoded min(40+16*n,4096) label_communities token budget with _resolve_max_tokens(min(64+24*n,8192)) — 24 tok/community covers 5-word JSON entries; 8192 cap fits 16k-context models; env var now honoured (Graphify-Labs#1200) - dedup.py: add prefix-extension guard in Pass 2 and _llm_tiebreak — skip merge when one normalised label is a strict prefix of the other (getActiveSession / getActiveSessions, parseConfig / parseConfigFile). Option (a) rejected: dropping the >=12 early-out from _short_label_blocked breaks test_typo_merged (Graphify-Labs#1201) - tests/test_dedup.py: two new regression tests verifying prefix guard fires for extension pairs and does not fire for same-length typo pairs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- security.py: replace global socket.getaddrinfo monkey-patch with per-connection _SSRFGuardedHTTPConnection/HTTPSConnection subclasses (thread-safe, closes TOCTOU) - security.py: add GRAPHIFY_MAX_GRAPH_BYTES env var override for 512MB cap (MB/GB suffix supported); improve cap error message to cite the env var - llm.py: wrap untrusted source files in XML delimiters with sha256 fingerprint; neutralise jailbreak sentinel tokens to mitigate prompt injection - dedup.py: skip code nodes in label-based dedup passes; code symbols now deduplicated by ID only, preventing distinct same-named symbols from merging - extract.py: cross-file calls resolution now consults import evidence before bailing on ambiguous callee names; emits EXTRACTED edges when named import is unambiguous - analyze.py: extend _BUILTIN_NOISE_LABELS with stdlib types and modules - __main__.py: CLAUDE.md template uses MANDATORY language for graphify-first rule; PreToolUse hook message hardened to imperative; graphify export html auto-falls back to community-aggregation view when graph.json exceeds size cap - tests/test_pg_introspect.py: add importorskip guard for tree_sitter_sql Closes Graphify-Labs#1211, Graphify-Labs#1210, Graphify-Labs#1205, Graphify-Labs#1219, Graphify-Labs#1227; resolves discussion Graphify-Labs#1019 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ignore checks - graphify/_minhash.py: self-contained MinHash/MinHashLSH using pure numpy, byte-identical hash math to datasketch (sha1_hash32, Mersenne-prime permutation). Drops datasketch + scipy transitive dep — eliminates EDR hang on Windows where numpy.testing platform.machine() subprocess spawn was intercepted at import time - dedup.py: import from graphify._minhash instead of datasketch - pyproject.toml: replace datasketch>=1.6 with numpy>=1.21 - detect.py: memoize _is_ignored/_eval results in a dict[Path,bool] cache per detect() call; each unique ancestor dir evaluated once across all sibling files, eliminating ~42M redundant fnmatch calls on large repos (~34% whole-run speedup) - tests/test_minhash.py: 11 tests including import-isolation guard asserting scipy and numpy.testing are not loaded after import graphify.dedup - tests/test_detect.py: 2 cache tests — correctness (cached==uncached with negation patterns) and hit-count (each dir evaluated exactly once across siblings) Closes Graphify-Labs#1234, closes Graphify-Labs#1235 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…re, JSON config detection - export.py: guard to_obsidian/to_canvas against dangling community member IDs (KeyError crash when a node in communities dict is absent from graph, Graphify-Labs#1236) - detect.py: NFC-normalize path before hashing Office sidecar filename to fix macOS NFC/NFD mismatch causing --update to re-extract all Office files (Graphify-Labs#1226) - extract.py: add _is_config_json() to skip data JSON files (only extract package.json, tsconfig.json, eslint, deno, JSON Schema etc.) eliminating 561 orphan key-nodes on large repos (Graphify-Labs#1224) - llm.py: add GRAPHIFY_LLM_TEMPERATURE env var + _resolve_temperature() helper; auto-omit temperature for o1/o3/o4/gpt-5 reasoning models that reject temp=0; mirrors GRAPHIFY_MAX_OUTPUT_TOKENS precedence pattern (Graphify-Labs#1191) - tests: 20 new regression tests across obsidian, detect, extract, llm_backends Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runs graphify against its own source on every GitHub release (AST-only, no API cost) and attaches graph.json + graph.html + GRAPH_REPORT.md as graphify-self-graph.tar.gz to the release. Also supports manual runs via workflow_dispatch, uploading the bundle as a 7-day workflow artifact. Closes Graphify-Labs#1238 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- extract graphify/ (code only) instead of . to avoid LLM API key requirement; the repo root contains docs/skills/.md files that trigger semantic extraction - use --out . so output writes to ./graphify-out/ not ./graphify/graphify-out/ - remove --out from export html (flag does not exist; HTML auto-written next to graph) - drop nonexistent --code-only flag from extract command - add comments explaining each flag's behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…quirement graphify/skills/ contains 126 markdown files that trigger semantic extraction. Add a temporary .graphifyignore entry during CI to keep the build pure AST. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-graph extract intentionally stops at graph.json; GRAPH_REPORT.md requires cluster-only. Use --no-label to skip LLM community naming (no API key needed in CI). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
llm.py: add explicit edge direction rule to extraction system prompt — source = actor (caller/importer/subclass), target = acted-upon (callee/ imported/base). LLM was systematically emitting callee->caller for calls edges because the schema never stated direction semantics. build.py: extend ghost-node merge to catch LLM nodes that populate source_location (bypassing the old None check). Now uses _origin=="ast" as the canonical signal — AST nodes always win; any non-AST node sharing (basename, label) with an AST node is collapsed into the AST canonical. Fixes LLM bare-stem IDs (bpe_get_pairs) surviving alongside AST parent-qualified IDs (mingpt_bpe_get_pairs) and carrying reversed edges. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 4 pickles the built graph and defers report generation; Step 5 loads the pickle (falling back to graph.json, which preserves the directed flag the old Step 5 rebuild silently dropped) and generates the report once with real community labels. The update-flow graph diff reuses the same pickle. Also corrects the stale --cluster-only note: the CLI has been self-contained (labels + report + html) since Graphify-Labs#1097, and the intermediate files Steps 4-5 need no longer exist at that point. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
In the Claude skill (
graphify/skill.md+graphify/skills/claude/references/update.md), Step 4 built the graph, clustered, and wrote GRAPH_REPORT.md with placeholder "Community N" labels; Step 5 then re-read the full.graphify_extract.jsonand rebuilt the entire graph a second time just to regenerate the report with real labels. The two-block structure is necessary (the agent names communities between them), but the rebuild is not.This PR makes Step 4 pickle the built graph (and stash token counts in the analysis JSON), and Step 5 load the pickle and generate the report exactly once with real labels. The placeholder report and placeholder question generation are gone. Step 9 cleanup removes the pickle. The update flow's graph-diff block reuses the same pickle instead of a third rebuild.
Also fixed along the way
--directedbug: the old Step 5 rebuild calledbuild_from_json(extraction)with nodirectedflag, so directed graphs silently lost edge direction in the final report pass. The pickle preserves the graph as built; the fallback path loadsgraph.jsonand honors itsdirectedflag.--cluster-onlydoc:references/update.mdtold the agent to "run Steps 5-9 as normal" aftergraphify cluster-only, but the intermediate files those steps read (.graphify_extract.json,.graphify_analysis.json) are deleted by Step 9 cleanup, and the CLI has been self-contained (re-labels, regenerates report + html) since Community labels remain default (Community 0, Community 1, etc.) even when Gemini semantic extraction succeeds #1097. The doc now says not to run them.Verification
Executed the new Step 4 and Step 5 blocks verbatim against a small code-only corpus (detect, AST extract, merge, then both blocks): the pickle path and the graph.json fallback path both produce a correct GRAPH_REPORT.md with real community labels, plus graph.json and
.graphify_labels.json.Scope note
Only the Claude progressive bundle is changed. The other per-platform variants (
skill-amp.md,skill-codex.md, etc.) carry the same Step 4/5 pattern; happy to extend the fix to them in this PR or a follow-up if you prefer.🤖 Generated with Claude Code