Skip to content

Dependency graph

The internal modules and their highest-frequency external dependencies. Internal nodes are the uv workspace members declared by [tool.uv.workspace] members = ["packages/*"] (pyproject.toml:109-110); external nodes are first-order distributions taken from a member’s own [project.dependencies], never from the installed transitive closure.

The internal direction is enforced rather than conventional. pyproject.toml:553 declares [tool.importlinter] over every root package (:554), and mise run lint:imports (mise.toml:172) is one of the gates mise run check depends on (mise.toml:245-265). Some of those contracts fix the shape drawn here: the independence contract at pyproject.toml:605-608 forbids any import edge among atif_converter, atif_corpus, atif_duck, atif_models, and atif_embed, and the forbidden contract at :610-614 allows atif_analytics to import atif_models and nothing else among the members.

all members atif-cli atif-analytics atif-converter atif-corpus atif-duck atif-embed atif-models loguru polars pydantic duckdb pydantic-settings tenacity pyarrow anyio cyclopts boto3 anthropic

Some declared external distributions are left off the diagram. Importing modules are found by grepping every *.py under packages/*/src for a from X or import X line at any indentation.

elided node importing module declared at import site
pytz none packages/atif-duck/pyproject.toml:33 no import site; DuckDB’s own client imports it to materialize TIMESTAMPTZ, per the comment at :30-32
zstandard atif-corpus most often; atif-analytics, atif-converter, atif-duck, and atif-embed too packages/atif-corpus/pyproject.toml:22 packages/atif-corpus/src/atif_corpus/infrastructure/source_archive.py:38
imagesize atif-converter packages/atif-converter/pyproject.toml:30 packages/atif-converter/src/atif_converter/domain/blobs.py:69

These ordered pairs of members import each other, and no other pair does. Each edge is an import from a file under the source member’s src/.

edge contract that permits it
atif-cli to atif-analytics atif-cli is the composition root; it is in neither restrictive contract’s restricted set (pyproject.toml:608, :613)
atif-cli to atif-converter as above; declared packages/atif-cli/pyproject.toml:33
atif-cli to atif-corpus as above; declared packages/atif-cli/pyproject.toml:34
atif-cli to atif-duck as above; declared packages/atif-cli/pyproject.toml:35
atif-cli to atif-embed as above; declared packages/atif-cli/pyproject.toml:36
atif-analytics to atif-models the forbidden contract’s single permitted edge (pyproject.toml:610-614); declared packages/atif-analytics/pyproject.toml:23

Absences carry meaning too. atif-cli imports atif_models in no source file even though it composes everything else; it reaches the model registry through atif-analytics, and its [project.dependencies] list omits atif-models accordingly (packages/atif-cli/pyproject.toml:32-43). And no edge exists in either direction between atif-converter, atif-corpus, atif-duck, atif-embed, or atif-models: they communicate only by writing and reading the corpus on disk. Adding any such edge fails the lint:imports gate of mise run check.

The inter-member dependencies are ==0.3.0-pinned rather than bare (packages/atif-cli/pyproject.toml:32-36) because [tool.uv.sources] at :60-65 is a local source an external installer never sees; a bare name would ship as a Requires-Dist resolved from the public PyPI namespace.

One edge per external distribution, drawn from the member whose files import it most often. Ties break on import-site count, then on the member’s own src line count, descending.

external node edge drawn from declared at import site
loguru atif-converter packages/atif-converter/pyproject.toml:24 packages/atif-converter/src/atif_converter/domain/codex_conversion.py:70
polars atif-analytics packages/atif-analytics/pyproject.toml:25 packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py:41
pydantic atif-converter packages/atif-converter/pyproject.toml:23 packages/atif-converter/src/atif_converter/domain/atif/trajectory.py:10
duckdb atif-duck packages/atif-duck/pyproject.toml:20 packages/atif-duck/src/atif_duck/infrastructure/registry.py:132
pydantic-settings atif-duck packages/atif-duck/pyproject.toml:25 packages/atif-duck/src/atif_duck/infrastructure/lake_settings.py:15
tenacity atif-models packages/atif-models/pyproject.toml:32 packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py:34
pyarrow atif-duck packages/atif-duck/pyproject.toml:29 packages/atif-duck/src/atif_duck/infrastructure/columnar.py:287
anyio atif-models packages/atif-models/pyproject.toml:23 packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py:30
cyclopts atif-cli packages/atif-cli/pyproject.toml:37 packages/atif-cli/src/atif_cli/app.py:69
boto3 atif-embed packages/atif-embed/pyproject.toml:20 packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py:136
anthropic atif-models packages/atif-models/pyproject.toml:22 packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py:51

What the drawn edge deliberately compresses:

Declared dependencies are not the installed closure

Section titled “Declared dependencies are not the installed closure”

The diagram’s external nodes are what a member asks for, and that is a strictly smaller set than what uv sync installs. The gap includes a web stack this system never runs: harbor, a dev-group dependency (pyproject.toml:158), lists fastapi, supabase, and uvicorn in its own dependency block, and uv sync installs the dev group by default. No member declares any of them, and grep -rnE "^[[:space:]]*(from|import) +(fastapi|uvicorn|starlette|supabase)(\.| |$)" --include='*.py' packages/ returns no matches across every source and test file. atif-sql exposes no HTTP surface; it is one console script, atif-sql = "atif_cli.app:main" (pyproject.toml:74), over a set of libraries.

Every storage engine in the graph is embedded, so no server node belongs here either: duckdb runs in-process (packages/atif-duck/pyproject.toml:20) and the vector store is local Lance files that atif-embed writes through DuckDB’s lance extension (packages/atif-embed/pyproject.toml:21). The only edge that leaves the machine is boto3 to Amazon Bedrock, from packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py:136 and packages/atif-models/src/atif_models/infrastructure/bedrock_common.py:14, where both atif-models adapters build their client. The anthropic distribution adds no edge of its own: atif-models imports only its transform_schema, a local schema mapping (packages/atif-models/src/atif_models/infrastructure/anthropic_bedrock.py:51), and sends the Claude calls through the same boto3 client.