Skip to content

feat(graph): resolve Python imports and add a cycles command - #359

Open
danzrou wants to merge 1 commit into
trailhq:mainfrom
danzrou:feat/import-cycles
Open

danzrou wants to merge 1 commit into
trailhq:mainfrom
danzrou:feat/import-cycles

Conversation

@danzrou

@danzrou danzrou commented Sep 11, 2026

Copy link
Copy Markdown

Add Python import-edge resolution to the graph and a new graft cycles command (plus a graft_find_import_cycles MCP tool) that reports every resolved file-to-file import cycle.

Python imports:

  • Resolve absolute and relative (dotted) module specifiers to in-repo file nodes, honoring package __init__ modules and disambiguating ambiguous suffixes by source root.
  • Flag lazy edges: imports inside functions/classes or under if TYPE_CHECKING: are marked lazy and carry their import line, so cycle reports can distinguish breakable edges from eager ones.
  • Edges gain optional lazy/line metadata; eager occurrences win over lazy ones on dedup.

Cycles:

  • findImportCycles / formatImportCycles detect and render file-level import cycles across all languages, marking lazy edges and import lines.
  • Exposed as the graft cycles CLI command (workspace-aware) and the graft_find_import_cycles MCP tool, added to the MCP instructions within the existing <1000-char budget by trimming the prose.
  • cycles added to the tracked telemetry commands.

Tests cover Python import extraction and resolution, cycle detection, the CLI command, the MCP tool/advertising, and the telemetry contract.

Add Python import-edge resolution to the graph and a new `graft cycles`
command (plus a `graft_find_import_cycles` MCP tool) that reports every
resolved file-to-file import cycle.

Python imports:
- Resolve absolute and relative (dotted) module specifiers to in-repo
  file nodes, honoring package `__init__` modules and disambiguating
  ambiguous suffixes by source root.
- Flag lazy edges: imports inside functions/classes or under
  `if TYPE_CHECKING:` are marked `lazy` and carry their import line, so
  cycle reports can distinguish breakable edges from eager ones.
- Edges gain optional `lazy`/`line` metadata; eager occurrences win over
  lazy ones on dedup.

Cycles:
- `findImportCycles` / `formatImportCycles` detect and render file-level
  import cycles across all languages, marking lazy edges and import lines.
- Exposed as the `graft cycles` CLI command (workspace-aware) and the
  `graft_find_import_cycles` MCP tool, added to the MCP instructions
  within the existing <1000-char budget by trimming the prose.
- `cycles` added to the tracked telemetry commands.

Tests cover Python import extraction and resolution, cycle detection,
the CLI command, the MCP tool/advertising, and the telemetry contract.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

🌱 graft blast radius

4 areas changed → 5 areas can be affected. 17 dependent symbols, depth 2.
Tests: no test reaches MCP Instructions; 1 area updated its tests.
Tag: @Frankie-Xu — 7 of 9 areas · @afeddersen — 3 of 9 areas · @tpoignonec — Graph Cycle Management, Graph Building

flowchart TB
  A0(("Session Metrics<br/>7 symbols"))
  A1(("Graph Building<br/>5 symbols"))
  A2(("Graph Operations<br/>2 symbols"))
  A3(("Telemetry Tracking<br/>2 symbols"))
  A4(("Pull Request Review<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3,A4 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Session Metrics 7 src/claude/session-metrics.ts:L1-L212 session-metrics.ts — imports, depth 1 Contract Telemetry
Graph Building 5 src/graph/build.ts:L151-L410 buildGraph — calls, depth 1 Graph Cycle Management
Graph Operations 2 src/engine.ts:L91-L101 graph — calls, depth 2 Graph Cycle Management
Telemetry Tracking 2 src/telemetry/sessions.ts:L1-L113 sessions.ts — imports, depth 1 Contract Telemetry
Pull Request Review 1 src/app/review.ts:L45-L99 reviewPullRequest — calls, depth 2 Graph Cycle Management
Who knows this code — 6 people across 9 areas
Area Who knows it
Graph Cycle Management · changed @Frankie-Xu — 2 commits, last 15d ago · @tpoignonec — 1 commit, last 15d ago
Contract Telemetry · changed @Frankie-Xu — 1 commit, last 15d ago
MCP Instructions · changed @afeddersen — 1 commit, last 11d ago · @Frankie-Xu — 1 commit, last 15d ago
Command Line Interface · changed @Frankie-Xu — 4 commits, last 11d ago
Session Metrics · affected @afeddersen — 1 commit, last 11d ago · Marcelo Caporale — 1 commit, last 11d ago
Graph Building · affected Buseong Kim — 1 commit, last 11d ago · @tpoignonec — 1 commit, last 15d ago
Graph Operations · affected @Frankie-Xu — 2 commits, last 11d ago · 贾旭卿 — 1 commit, last 15d ago
Telemetry Tracking · affected @afeddersen — 1 commit, last 11d ago · @Frankie-Xu — 1 commit, last 15d ago
…1 further area

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 17 dependent symbols, grouped by area

Session Metrics — 7 symbols in 7 files

  • src/claude/session-metrics.ts:L1-L212 — session-metrics.ts (imports, depth 1)
  • src/claude/state.ts:L1-L104 — state.ts (imports, depth 1)
  • src/claude/format.ts:L1-L238 — format.ts (imports, depth 2)
  • src/claude/hooks.ts:L1-L463 — hooks.ts (imports, depth 2)
  • src/claude/stats.ts:L1-L27 — stats.ts (imports, depth 2)
  • src/claude/statusline.ts:L1-L36 — statusline.ts (imports, depth 2)
  • src/claude/sync-run.ts:L1-L42 — sync-run.ts (imports, depth 2)

Graph Building — 5 symbols in 5 files

  • src/graph/build.ts:L151-L410 — buildGraph (calls, depth 1)
  • src/graph/check.ts:L58-L164 — checkGraph (calls, depth 1)
  • src/graph/container.ts:L151-L208 — extractContainer (calls, depth 1)
  • src/graph/refresh.ts:L150-L227 — ensureFreshGraph (calls, depth 2)
  • src/graph/workspace.ts:L630-L655 — federateCheck (calls, depth 2)

Graph Operations — 2 symbols in 1 file

  • src/engine.ts:L91-L101 — graph (calls, depth 2)
  • src/engine.ts:L82-L84 — checkGraph (calls, depth 2)

Telemetry Tracking — 2 symbols in 2 files

  • src/telemetry/sessions.ts:L1-L113 — sessions.ts (imports, depth 1)
  • src/telemetry/track.ts:L1-L156 — track.ts (imports, depth 1)

Pull Request Review — 1 symbol in 1 file

  • src/app/review.ts:L45-L99 — reviewPullRequest (calls, depth 2)
Test signal per changed area — 1 ✓ · 1 ✗ · 2 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • Graph Cycle Management — 3 of 10 reached · 3 test files changed here: test/graph-cycles.test.ts, test/graph-python-imports.test.ts, test/graph-python-resolve.test.ts
    • not reached: compareMembers, cycleEdges, enter, walk, add, hit, runWorkspaceCycles
  • Contract Telemetry — 1 test file changed here: test/telemetry-contract.test.ts
  • MCP Instructions — 0 of 2 reached · no test file reaches it
    • not reached: mcpInstructions, callSingleTool
  • Command Line Interface — no function, method or class changed here
47 test suites also reference this code

56 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/ask-index.test.ts
  • test/ask.test.ts
  • test/claude-format.test.ts
  • test/claude-hooks.test.ts
  • test/claude-state.test.ts
  • test/claude-statusline.test.ts
  • test/claude-tally.test.ts
  • test/container-extract.test.ts
  • test/context-only-dir.test.ts
  • test/context.test.ts
  • test/covers.test.ts
  • test/generic-extract.test.ts
  • test/graph-bindings.test.ts
  • test/graph-cross-language.test.ts
  • test/graph-extract-dedup.test.ts
  • test/graph-follow-submodules.test.ts
  • test/graph-go.test.ts
  • test/graph-incremental.test.ts
  • test/graph-invariants.test.ts
  • test/graph-java.test.ts
  • …27 more

⚠️ 1 changed file not in the graph (TELEMETRY.md) — no parser claims the extension, or the index predates the file.

graft blast · refs/graft/base...HEAD · depth 2 · 18 changed files

Open the interactive graph → — click an area to see the code that changed, and the line that reaches it.

github-actions Bot added a commit that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant