grammar-inference-engine/docs/adr/0005-import-extraction-per-cluster.md
tobjend ca7ccb36ff
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
docs: add 8 architecture decision records
ADR 0001: nvim-treesitter highlights.scm as capture source
ADR 0002: language-agnostic method extraction via child_by_field_name
ADR 0003: method-level n-gram clustering before inference
ADR 0004: frequency filter with min_coverage threshold
ADR 0005: import extraction per cluster
ADR 0006: argument pattern extraction via AST node classification
ADR 0007: JSON output for LLM prompt injection
ADR 0008: BEX ensemble for grammar inference
2026-07-03 22:01:35 +02:00

2.1 KiB

5. Import extraction per cluster

Date: 2026-07-03

Status: Accepted

Context

An LLM prompted with a behavioral convention like every → assertEquals → verify still needs to know which imports to use. Without imports, it will guess the wrong library — writing from unittest.mock import patch instead of import io.mockk.every, or importing from jest instead of vitest.

Imports are the bridge between abstract conventions and actionable code.

Decision

For each cluster, scan the source files whose methods belong to that cluster and extract all unique import lines.

Language-agnostic approach: match lines against common import patterns:

  • import ... (Java, Kotlin, Python, Go, JS/TS)
  • from ... import ... (Python)
  • require ... / require_relative ... (Ruby, JS)
  • #include ... (C/C++)
  • use ... (Rust)
  • include ... (Ruby)

Scan the first 200 lines of each file (imports are always at the top), deduplicate across files, and sort the result.

File-to-cluster mapping is preserved by tracking (file_path, sequence) pairs through the pipeline. After frequency_filter (which preserves order and count), we use object identity to map each clustered sequence back to its source file.

Consequences

Positive:

  • Each cluster shows exact import lines used by its methods.
  • An LLM can copy these directly — no guessing.
  • Reveals library choice conventions: kotlin.test.* vs org.junit.jupiter.api.*, io.mockk.coEvery vs io.mockk.every.

Negative:

  • Import scanning re-reads files (second pass). Negligible cost since files are small and OS-cached.
  • 200-line scan limit might miss imports in files with very long license headers.
  • Lines containing import in prose (comments, strings) may produce false positives — rare in practice.

Alternatives Considered

  • Single global import list: Simpler but useless — conflates imports from unrelated clusters.
  • No imports: LLM must guess. Leads to wrong imports and broken code.
  • Per-file imports (not per-cluster): Too granular — mixes test imports with production imports in the same file.