Skip to content

Releases: raykuonz/graphify-sf

v0.4.1

Choose a tag to compare

@raykuonz raykuonz released this 14 Jul 07:25

A correctness- and honesty-focused release: closes several MultiDiGraph parallel-edge-drop gaps left
over from the 0.4.0 migration (including two introduced by this release's own new code, caught and
fixed during a multi-round adversarial review before shipping), hardens the Apex extractor against
comment/string false positives, normalizes LLM backend config drift, and fixes ambiguous doc-mention
resolution. Every fix below shipped with a regression test proven to fail on the pre-fix code.

Fixed — MultiDiGraph parallel-edge drops (serve.py, merge-graphs, and the new bfs_impact tool)

  • MCP tools and the CLI's path command now read every parallel edge, not just the first.
    serve.py's get_node, get_neighbors, and shortest_path, plus graphify-sf path, previously
    called the single-edge edge_data() helper on the default MultiDiGraph, so a node pair with two
    relations (e.g. queries + dml to the same object) silently reported only one of them — the same
    class of bug the 0.4.0 MultiDiGraph migration fixed elsewhere, unfixed in the MCP/CLI layer until now.
    All four now use edge_datas() and surface every relation. get_node/get_neighbors may now return
    one entry per relation instead of one per neighbor (each entry still shaped
    {id, label, sf_type, relation, confidence}); shortest_path's per-hop relation/confidence
    becomes a list when a hop has more than one parallel edge (a scalar for the common single-edge case).
    If you consume these MCP tools, re-check any caller that assumed exactly one entry per neighbor.
  • merge-graphs/merge-driver no longer drop parallel edges, on either axis. The union previously
    went through a plain nx.Graph(), collapsing same-direction parallel edges (e.g. queries + dml on
    one pair) before the multigraph-aware rebuild ever ran; then, after being rewritten to preserve those,
    it dropped edges that shared a relation but differed only in operation (e.g. two dml edges, one
    create one update, on the same pair). The union now hashes the full edge identity (minus the
    per-graph key/source_file fields that legitimately vary across inputs), so every distinct parallel
    edge survives a 2- or 3-way merge while genuinely-identical duplicates (the base/ours/theirs case)
    still coalesce to one.
  • The new bfs_impact MCP tool (see Added) now reports every parallel relation to a neighbor. Its
    first implementation stored one {relation, confidence} pair per neighbor id in its BFS accumulator,
    re-introducing — in code this same release added — the exact parallel-edge collapse fixed above for
    the other MCP tools. relation/confidence are now lists when a neighbor has more than one parallel
    edge (a scalar for the common single-edge case), mirroring shortest_path.

Fixed — Apex extraction honesty (comments, strings, method scoping, generics)

  • Apex class extraction no longer treats commented-out or string-literal code as real. insert/
    update/etc. inside //, /* */, or a string literal previously produced real edges to a nonexistent
    target; comments and string contents are now scrubbed before extract_apex_class's DML, method-call,
    SOQL, EventBus.publish, and Custom Metadata/Setting-access extraction.
  • Known gap (disclosed, not fixed this release): Apex HTTP-callout detection
    (extract_apex_class's endpoint scan) and extract_apex_trigger's call-scanning still read raw,
    unscrubbed source, so commented-out or string-embedded code in those two paths can still produce a
    phantom edge. Pre-existing, not introduced by the fix above; tracked for a future release.
  • Apex calls edges no longer leak across method boundaries. A method's call-edge scan previously
    ran to end-of-file instead of to its own closing brace, so every method accumulated calls that
    actually belonged to methods below it in the same class.
  • Apex implements clauses with a generic argument list no longer split incorrectly.
    implements Comparator<Account, String> previously split on the comma inside the generic brackets and
    emitted a bogus edge treating String as an interface.
  • Apex DML operand→type resolution is now scoped per method, not per class. Two methods reusing the
    same local variable name with different declared types (e.g. Account a in one method, Contact a in
    another) previously collided via dict.setdefault, silently misattributing the second method's DML.

Fixed — LLM backends, doc mentions, MCP robustness, --verbose accounting

  • LLM backend config drift across the 6 supported backends resolved. OpenAI previously had no
    max_tokens entry and fell back to a hardcoded 8192 (half of every sibling backend's 16384);
    Gemini silently ignored the documented GRAPHIFY_SF_MAX_OUTPUT_TOKENS env override; temperature was
    never passed to Claude's SDK call and was hardcoded (ignoring config) for Bedrock. All 6 backends now
    resolve max_tokens/temperature through the same codepath. Kimi's thinking-disable and Ollama's
    context-sizing quirks moved from inline branches to a data-driven per-backend hook with no behavior
    change (proven by regression test). The openai backend's effective max_tokens changes from 8192
    to 16384
    — this changes output size/cost for existing --backend openai users; not a silent change.
  • Doc/PDF cross-reference mentions no longer resolve to an arbitrary match when ambiguous. When a
    mention's label matched multiple graph nodes, the resolver previously picked the first one arbitrarily
    (e.g. a DocumentSection heading could win over the real CustomObject it was named after). It now
    prefers non-document candidates, and falls back to emitting all remaining candidates at a reduced
    INFERRED confidence score when the ambiguity is real. A small denylist now filters common English
    words that happen to be PascalCase-shaped (suffixed names like Account__c always bypass it), and a
    mention inside a fenced or inline code span now gets a higher confidence score than one in bare prose.
  • MCP tool int arguments (bfs_impact's max_depth/limit and others) no longer raise on
    non-numeric input.
    A caller-supplied non-numeric value previously raised an uncaught ValueError;
    a new _get_int helper now degrades to the tool's default and clamps out-of-range values.
  • --verbose node accounting no longer mislabels retained nodes as stubs on --update. Incremental
    mode merges with the entire prior graph.json, so a node carried over unchanged from a file no longer
    on disk was counted as a build-injected stub; the stub term now reports n/a (incremental merge) on
    the incremental path, matching the existing n_pruned_edges guard.

Added

  • bfs_impact MCP tool — a depth-bounded, direction-aware (forward/reverse/both) blast-radius
    traversal, EXTRACTED-edges-only by default (include_inferred opt-in), with relation_filter,
    limit/truncated reporting, and a by_type breakdown. Closes the gap between graphify-sf's own
    1-hop-only get_neighbors/get_node tools and the multi-hop impact analysis downstream consumers had
    to reimplement on top of raw graph.json.
  • --verbose/-v node-drop accounting on build/update — prints
    N extracted → M deduped-by-label (K merged) → +S stub → F final nodes (P dangling edge(s) pruned) so
    the extract→dedup→build node-count pipeline is no longer opaque.

Verification

  • 503 tests (up from 451 at 0.4.0), ruff check/ruff format --check clean, zero new runtime
    dependencies. Every fix above was independently reproduced against a pre-fix checkout before and after
    the change, not just asserted by a passing test suite.

v0.4.0

Choose a tag to compare

@raykuonz raykuonz released this 16 Jun 14:26
7f8b299

graphify-sf v0.4.0

Full local-extractable coverage expansion + MultiDiGraph fidelity fix. Every extraction Epic was verified by parsing the actual graph.json with negative controls.

Coverage breadth (pure-additive)

  • Integration: Apex/Flow HTTP callouts → NamedCredential/ExternalEndpoint; RemoteSiteSetting fix + endpoint URL; External Data Source/Object; Platform Events; Apex→CMDT/CustomSetting; AuthProvider/CSP/CORS; Workflow Outbound Messages.
  • Security/sharing: OWD sharingModel; Sharing Rules/Sets; PermSet Group membership; userPermissions + tab/app visibility; Restriction/Duplicate/Matching Rules.
  • Automation: Workflow Field Updates/Tasks; Process Builder typed distinct; platform-event/scheduled flow triggers; Validation Rule standard-field refs.
  • UI linkage: LWC schema/label/resource imports; Aura/VF controller wiring; VF standardController; FlexiPage embeds; StaticResource/QuickAction/CustomTab/CustomApplication.
  • Data model: Field Sets + Global Value Sets; CompactLayout/ListView/RecordType field refs; polymorphic lookups.
  • Reporting/packaging: Report/Dashboard/ReportType dependency chain; InstalledPackage + managed-namespace tagging.

⚠️ Breaking contract change — graph.json is now a MultiDiGraph

graph.json declares "multigraph": true; links entries carry a key; a node-pair may appear multiple times. Fixes the same-direction relation collapse (an Apex class that both queries and dml-writes one object previously kept only one relation). Downstream consumers must read ALL links for a pair (filter, not find).

Honesty layer

EXTRACTED = source fact; INFERRED = heuristic. Runtime-only facts excluded by design.

Full test suite: 451 passed.

v0.3.9

Choose a tag to compare

@raykuonz raykuonz released this 16 Jun 03:19
da52729

Distribution reliability: the prebuilt CLI binary now ships inside npm via platform-specific scoped subpackages (@graphify-sf/cli-<plat>-<arch>) pulled in through optionalDependencies + os/cpu, so npm install makes no network request on the happy path — even on networks that block GitHub Releases. The GitHub download remains a non-fatal last-resort fallback (0.3.8 behaviour preserved).

Python package version bumped to 0.3.9 for lockstep with npm + the tag (no functional Python changes).

See CHANGELOG for details.

v0.3.8

Choose a tag to compare

@raykuonz raykuonz released this 16 Jun 01:58
9e9b27b

Non-fatal npm postinstall: a blocked/failed binary download no longer aborts npm install.

  • install.js always exits 0; binary is fetched lazily on first run
  • Remediation printed: pipx install graphify-sf or export GRAPHIFY_BIN=/path
  • New GRAPHIFY_BIN env escape hatch for air-gapped / proxy-restricted networks
  • npm package version corrected 0.3.1 -> 0.3.8 (stale version produced a 404 download URL)
  • npm regression suite (node:test) + CI job

See CHANGELOG for details.

v0.3.7

Choose a tag to compare

@raykuonz raykuonz released this 14 Jun 13:07

Fixed

  • macOS: parallel extraction no longer crashes the worker pool and silently truncates the graph. The PyInstaller-frozen binary had no multiprocessing.freeze_support(), so on macOS/Windows (spawn start method) each pool worker re-executed the binary with Python's bootstrap args (-B --multiprocessing-fork). The CLI parser intercepted them, rejected them (error: unknown command '-B'), and every worker died — leaving only lightweight sequentially-extracted bundles and producing a severely truncated graph (e.g. 572 nodes vs ~9700, with zero CustomObject/CustomField/Flow) while still exiting 0. Linux (fork) was unaffected. freeze_support() now runs first so spawn workers bootstrap correctly. Verified by running the frozen binary under a forced spawn start method: full graph restored (9383 nodes, 658 objects, 3239 fields, 166 flows).
  • A failed worker pool no longer yields a silently-incomplete graph that exits 0. A BrokenProcessPool was previously swallowed; any pool-level failure now aborts parallel, re-runs the full extraction sequentially, and emits a loud WARNING: parallel worker pool failed — falling back to sequential.

Added

  • GRAPHIFY_SF_MP_START env var to override the multiprocessing start method (spawn/fork).

v0.3.6

Choose a tag to compare

@raykuonz raykuonz released this 14 Jun 11:02

Fixed

  • Cross-type node merges eliminated. deduplicate_by_label no longer conflates distinct components that normalise to the same label (e.g. an ApexClass and a similarly-named LWCComponent, or a CustomObject and its CustomTab/Settings/PermissionSet). It wrongly merged 83 node pairs on a real 9k-node org and rewrote their edges onto the wrong survivor. Merging is now gated on sf_type; the intended chunked-duplicate case still works.
  • Self-referencing lookup fields keep their object→field contains edge. Previously the contains and the field→object references edge shared an undirected node-pair and one overwrote the other, orphaning the field. The build now keeps the higher-priority relation on collision (contains outranks references).
  • Together, object→field attachment on the test org went from 99.4% to 100% (18 orphaned fields → 0).
  • The post-scan log printed the raw skipped list object instead of a count.

Added

  • .gitignore / .forceignore are honored by default to cut graph noise, using full gitignore semantics (negation, anchored, **) via pathspec. Ignore files are discovered by walking up from the scanned directory, so graphify-sf force-app still picks up the project-root files.
  • --include-ignored flag to scan everything regardless, plus an honest per-source skip summary after each scan.

Full multi-relation edge preservation (read+write on the same object, both edges on self-lookups) is the planned 0.4.0 MultiGraph work.

v0.3.5

Choose a tag to compare

@raykuonz raykuonz released this 13 Jun 23:36
f3bd92e

Fixes the 0.3.4 Apex DML operation feature, which produced zero dml edges on real-world Apex (DML operands are almost always lowercase local variables like insert c;, and the edge guard only accepted capitalized operands). The extractor now resolves the operand variable to its declared SObject type (local declarations, generics, method params) and builds the dml edge to the resolved object, with operation + INFERRED confidence. Unresolvable operands are skipped (no misleading edge); primitives are never DML targets. Known limitation: multiple DML ops against the same object from one component are still merged into one edge in the built graph (per-operation locatable edges planned as a follow-up). See CHANGELOG.md.

v0.3.4

Choose a tag to compare

@raykuonz raykuonz released this 13 Jun 15:23
f20a4cc

Apex DML edges now preserve the write operation: apex -> object dml edges carry an operation field derived from the DML verb (insert->create, update->update, delete->delete, plus SF-native upsert/merge/undelete preserved). Relation stays dml and confidence stays INFERRED (backward-compatible). Dedup is now by (object, operation). Mirrors the 0.3.3 flow change, so both major write paths (Flow and Apex) now expose read/write operation semantics. See CHANGELOG.md for details.

v0.3.3

Choose a tag to compare

@raykuonz raykuonz released this 13 Jun 14:54
6ca8308

Flow record operations now preserve read/write semantics: flow -> object edges carry an operation field (read/create/update/delete) while the relation stays references (backward-compatible). Dedup is now by (object, operation), so a flow that both reads and writes the same object yields distinct edges. See CHANGELOG.md for details.

v0.3.2

Choose a tag to compare

@raykuonz raykuonz released this 12 Jun 12:06
3bc4448

See CHANGELOG.md for details.