atif-sql · Module map
Section titled “atif-sql · Module map”Seven uv workspace members live under packages/*, declared at pyproject.toml:100; the root carries
no [project] table because it is a virtual workspace holding only the member list, the shared dev
dependency-group, and the shared tool config (pyproject.toml:1). The internal import graph is a
star: atif-cli declares five siblings as ==-pinned dependencies
(packages/atif-cli/pyproject.toml:32), plus one further edge from atif-analytics to atif-models that
a forbidden import-linter contract leaves open (pyproject.toml:520), with every other pair closed
by an independence contract (pyproject.toml:515). Every cross-package import sits inside a
function body or a TYPE_CHECKING block so the CLI’s fast path pulls in no duckdb, harbor, lancedb,
boto3, or polars, which is why PLC0415 is ignored workspace-wide (pyproject.toml:164). Modules
below are ordered by total source LOC, descending; LOC figures are wc -l over the file.
atif-analytics
Section titled “atif-analytics”run_analyze composes eight pipelines in a fixed stage order — cluster, terms, community, classify,
trajectory, conflicts, friction, perceived
(packages/atif-analytics/src/atif_analytics/application/analyze.py:36). The first three are
structural math at zero LLM cost (:8); the remaining five call a model and honor dry_run, which
defaults to True as a cost guard so those stages return plan dicts instead of spending (:19). Every
classifier system prompt lives in one module, four of them assembled by concatenating a shared
appendix (packages/atif-analytics/src/atif_analytics/application/prompts.py:1006), and the pydantic
v2 response schemas they bind against are pure domain models whose field descriptions are themselves
part of the prompt surface (packages/atif-analytics/src/atif_analytics/domain/models.py:3). This is
the one member permitted to import a sibling — atif-models and nothing else
(pyproject.toml:520).
packages/atif-analytics/src/atif_analytics/application/prompts.py(1021 LOC) — the task-framing system prompts, public constants assembled atpackages/atif-analytics/src/atif_analytics/application/prompts.py:1006.packages/atif-analytics/src/atif_analytics/domain/structure/community.py(643 LOC) — pure Leiden+CPM and mutual-kNN graph math, no I/O, withgraspologic-nativebehind one solver seam (packages/atif-analytics/src/atif_analytics/domain/structure/community.py:261).packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py(638 LOC) — three friction tiers behind a message-length pre-filter: regex fast path, deterministic stamp rules, then the LLM (packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py:5).packages/atif-analytics/src/atif_analytics/application/use_cases/trajectory.py(523 LOC) — one window per text step, sent in chunks of at most 16 windows with a shared anchor turn (packages/atif-analytics/src/atif_analytics/application/use_cases/trajectory.py:13).packages/atif-analytics/src/atif_analytics/domain/models.py(516 LOC) — the response schemas, fromSessionClassification(packages/atif-analytics/src/atif_analytics/domain/models.py:22) toPerceivedErrorsResult(:484).packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py(451 LOC) — LangSmith’s Perceived Error definition on the conflicts chassis; clean sessions produce zero rows (packages/atif-analytics/src/atif_analytics/application/use_cases/perceived.py:16).packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py(426 LOC) — one row per detected stance-conflict pair, keyed on two turn uuids, with refusals routed to a sidecar (packages/atif-analytics/src/atif_analytics/application/use_cases/conflicts.py:11).packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py(404 LOC) — one row per session, anti-joined against the parquet cache and written in crash-resilient chunks (packages/atif-analytics/src/atif_analytics/application/use_cases/classify.py:8).
atif-duck
Section titled “atif-duck”register(con, corpus_root) binds a DuckDB connection to a contract-shaped corpus tree and exposes 16
views plus 9 macros (packages/atif-duck/src/atif_duck/__init__.py:9,
packages/atif-duck/src/atif_duck/infrastructure/registry.py:1212). Those names are not introspected
at runtime: a static catalog answers atif-sql schema in under 50 ms with no DuckDB bind, and two
drift tests assert it column-for-column against DESCRIBE and signature-for-signature against the
DDL (packages/atif-duck/src/atif_duck/domain/catalog.py:3). The 12 analytics views and 13 analytics
macros register separately, each only when its backing parquet is populated, because a corpus with no
atif-sql analyze run is the default state
(packages/atif-duck/src/atif_duck/infrastructure/analytics.py:124). It is the one member with
domain/ and infrastructure/ but no application/, a deliberate shape that is why it carries no
layers contract among the seven (pyproject.toml:462).
packages/atif-duck/src/atif_duck/infrastructure/registry.py(1294 LOC) — the raw readers, the 16 core views, the 9 macros, and the Lance attach path (packages/atif-duck/src/atif_duck/infrastructure/registry.py:830).packages/atif-duck/src/atif_duck/infrastructure/analytics.py(581 LOC) — the v2 views and macros over the analytics parquet outputs, gated on file presence (packages/atif-duck/src/atif_duck/infrastructure/analytics.py:124).packages/atif-duck/src/atif_duck/domain/catalog.py(417 LOC) —VIEW_NAMES(packages/atif-duck/src/atif_duck/domain/catalog.py:28),MACRO_SIGNATURES(:247), the analytics catalogs (:269), and oneDESCRIPTIONSentry per object (:326).packages/atif-duck/src/atif_duck/domain/examples.py(251 LOC) —build_examplesderives one runnable query per catalog object fromARG_EXEMPLARSrather than hardcoding strings (packages/atif-duck/src/atif_duck/domain/examples.py:178).packages/atif-duck/src/atif_duck/domain/embedding_guard.py(83 LOC) — the read-side provider and dimension guard, a deliberate twin of atif-embed’s copy because the independence contract forbids sharing it (packages/atif-duck/src/atif_duck/domain/embedding_guard.py:5).packages/atif-duck/src/atif_duck/__init__.py(40 LOC) — re-exports the catalog constants and the fourregister*entry points (packages/atif-duck/src/atif_duck/__init__.py:30).packages/atif-duck/src/atif_duck/domain/__init__.py(32 LOC) — the pure catalog layer, no duckdb import (packages/atif-duck/src/atif_duck/domain/__init__.py:5).packages/atif-duck/src/atif_duck/infrastructure/__init__.py(17 LOC) — the registry layer’s re-export surface (packages/atif-duck/src/atif_duck/infrastructure/__init__.py:5).
atif-cli
Section titled “atif-cli”The composition root: it declares five siblings as ==-pinned dependencies
(packages/atif-cli/pyproject.toml:32) and wires every cross-package seam — the ConverterPort
adapter, the clock, version pins, the DuckDB connection (packages/atif-cli/src/atif_cli/app.py:5).
Ten commands hang off one cyclopts App: nine @app.command functions from convert
(packages/atif-cli/src/atif_cli/app.py:226) to schema (:1055), plus the cron sub-app
registered at :65, with main (:1093) exposed as the single console script named atif-sql
(packages/atif-cli/pyproject.toml:42). Heavy imports — duckdb, harbor through atif-converter,
pydantic through atif-corpus — are deferred into the command bodies that use them so schema,
--help, and --version stay on a lean import graph that a fresh-interpreter test pins
(packages/atif-cli/src/atif_cli/app.py:22). Failures resolve to stable exit codes, 64 for parse, 65
for catalog, 70 for runtime, split between a pure taxonomy module
(packages/atif-cli/src/atif_cli/errors.py:25) and a driver-dependent classifier
(packages/atif-cli/src/atif_cli/duck_errors.py:34) so the lean path never imports duckdb.
packages/atif-cli/src/atif_cli/app.py(1120 LOC) — the ten commands and every wiring decision between them (packages/atif-cli/src/atif_cli/app.py:52).packages/atif-cli/src/atif_cli/cron.py(302 LOC) —cron installprints a crontab block and never writes one;cron statusreports per-lane lock and last-run state from injected probes (packages/atif-cli/src/atif_cli/cron.py:7).packages/atif-cli/src/atif_cli/output.py(293 LOC) —--format autoresolves to a table on a TTY and JSON on a pipe (packages/atif-cli/src/atif_cli/output.py:74);emit_cursorstreamsfetchmanybatches so the client holds one batch (:154).packages/atif-cli/src/atif_cli/duck_errors.py(96 LOC) — the duckdb exception classifier, plus a widened caught set for the registration path sinceEmbeddingProviderMismatchis not aduckdb.Error(packages/atif-cli/src/atif_cli/duck_errors.py:31).packages/atif-cli/src/atif_cli/converter_adapter.py(72 LOC) —RealConverteradapts atif-converter’s use case to atif-corpus’s port, the one place allowed to import both (packages/atif-cli/src/atif_cli/converter_adapter.py:44).packages/atif-cli/src/atif_cli/errors.py(62 LOC) —EXIT_CODESand theClassifiedErrorshape, pure and free of anyatif_*import (packages/atif-cli/src/atif_cli/errors.py:52).packages/atif-cli/src/atif_cli/__main__.py(8 LOC) —python -m atif_clireaching the samemain(packages/atif-cli/src/atif_cli/__main__.py:5).packages/atif-cli/src/atif_cli/__init__.py(3 LOC) — package docstring only (packages/atif-cli/src/atif_cli/__init__.py:3).
atif-embed
Section titled “atif-embed”The vector-search write path: Cohere Embed v4 on Bedrock behind EmbeddingProvider, a local LanceDB
store behind VectorStorePort, and a corpus reader behind TextRowsPort
(packages/atif-embed/src/atif_embed/domain/ports.py:31). run_backfill anti-joins step text against
the store’s uuid-to-text-hash map, embeds the misses, and bounds loss three ways — chunked discovery,
mid-run checkpoints, and per-batch isolation inside a chunk
(packages/atif-embed/src/atif_embed/application/embed.py:61). Two stamps keep the vector space
honest: model_id and dimension on every row, checked on both the write and the read path so a
provider switch fails loud instead of corrupting kNN
(packages/atif-embed/src/atif_embed/domain/embedding_guard.py:3), and text_hash of the exact text
a row was built from, so a re-conversion under a stable uuid reads as stale
(packages/atif-embed/src/atif_embed/domain/text_stamp.py:3). Additive schema columns migrate online
through a metadata-only add_columns, keyed on a SCHEMA_VERSION sidecar file
(packages/atif-embed/src/atif_embed/infrastructure/lance_store.py:61).
packages/atif-embed/src/atif_embed/infrastructure/lance_store.py(444 LOC) — connect, open or create, online-migrate, delete by predicate, append, index, and compact (packages/atif-embed/src/atif_embed/infrastructure/lance_store.py:195).packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py(432 LOC) — theinvoke_modeladapter under a tenacity retry with botocore retries off, and the document-int8/ query-floatasymmetry (packages/atif-embed/src/atif_embed/infrastructure/cohere_bedrock.py:284).packages/atif-embed/src/atif_embed/application/embed.py(277 LOC) —discover_unembedded(packages/atif-embed/src/atif_embed/application/embed.py:43),run_backfill(:61), and the syncembed_querythe search command calls (:265).packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py(250 LOC) — reads the contract corpus layout with its own DuckDB connection, since importing atif-duck is forbidden (packages/atif-embed/src/atif_embed/infrastructure/corpus_text_rows.py:155).packages/atif-embed/src/atif_embed/domain/ports.py(119 LOC) — the three Protocols:EmbeddingProvider(packages/atif-embed/src/atif_embed/domain/ports.py:31),VectorStorePort(:59),TextRowsPort(:87).packages/atif-embed/src/atif_embed/domain/embedding_guard.py(83 LOC) —ensure_store_matches, the fail-loud provider and dimension rule (packages/atif-embed/src/atif_embed/domain/embedding_guard.py:46).packages/atif-embed/src/atif_embed/domain/errors.py(81 LOC) — theDomainErrortaxonomy the CLI catches, withterminalseparating operator-needed states from retryable ones (packages/atif-embed/src/atif_embed/domain/errors.py:14).packages/atif-embed/src/atif_embed/domain/text_stamp.py(71 LOC) —text_hash(packages/atif-embed/src/atif_embed/domain/text_stamp.py:38) and theMAX_EMBEDDABLE_CHARShead-only cap (:35).
atif-corpus
Section titled “atif-corpus”One materialization pass is sweep, scan, plan, convert, write, advance watermark, and materialize is
that pass (packages/atif-corpus/src/atif_corpus/application/materialize.py:476). The decision half
is pure — the domain never stats a file and never reads a clock, so build_plan partitions scanned
sessions into to-materialize, up-to-date, and skipped-live deterministically from mtimes in epoch
nanoseconds plus an injected now (packages/atif-corpus/src/atif_corpus/domain/sessions.py:159). The
write half is atomic by construction: artifacts land in a temp session directory that is renamed into
place, and every writer fsyncs the tmp file before the rename and the directory after, because
atif-duck reads the corpus with no locks and no journal
(packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py:5). Conversion arrives through the
ConverterPort Protocol, typed to the contract’s artifact shapes rather than converter internals
since this member may never import atif-converter
(packages/atif-corpus/src/atif_corpus/domain/ports.py:41).
packages/atif-corpus/src/atif_corpus/application/materialize.py(634 LOC) — the pass itself (packages/atif-corpus/src/atif_corpus/application/materialize.py:476), the report value object (:118), and the publicread_watermarkits consumers call (:160).packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py(222 LOC) — the only place this member stats the source corpus, keeping a genuinely absent transcript separate from a failedstatso a transient error never deletes live artifacts (packages/atif-corpus/src/atif_corpus/infrastructure/scanner.py:15).packages/atif-corpus/src/atif_corpus/domain/sessions.py(211 LOC) —SessionSource(packages/atif-corpus/src/atif_corpus/domain/sessions.py:42),QuiescencePolicy(:71),MaterializationPlan(:108), andbuild_plan(:159).packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py(158 LOC) — tmp-sibling write, fsync, rename, for files (packages/atif-corpus/src/atif_corpus/infrastructure/atomic.py:64) and whole directories (:98).packages/atif-corpus/src/atif_corpus/domain/watermark.py(78 LOC) —diff_source_mtimespartitions two mtime maps into added, modified, and removed (packages/atif-corpus/src/atif_corpus/domain/watermark.py:58).packages/atif-corpus/src/atif_corpus/domain/layout.py(75 LOC) —CorpusLayoutis the single writer-side computation of every contract path (packages/atif-corpus/src/atif_corpus/domain/layout.py:26).packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py(69 LOC) — a scriptableConverterPortfake shipped ininfrastructureso the type checker holds it to the port on every run (packages/atif-corpus/src/atif_corpus/infrastructure/fake_converter.py:3).packages/atif-corpus/src/atif_corpus/infrastructure/settings.py(58 LOC) —ATIF_SQL_-prefixed settings whose default factories read env at call time, not import time (packages/atif-corpus/src/atif_corpus/infrastructure/settings.py:44).
atif-converter
Section titled “atif-converter”convert_and_audit returns a conversion result paired with a loss report — the trajectory plus an
accounting of what upstream dropped
(packages/atif-converter/src/atif_converter/application/convert_and_audit.py:105). The conversion is
one private harbor call, ClaudeCode._convert_events_to_trajectory, confined to a single adapter
module behind a startup assertion
(packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py:112), which is why the
dependency is ceilinged at harbor>=0.22.0,<0.23 (packages/atif-converter/pyproject.toml:23). The
seven known conversion gaps are types rather than prose — FidelityGap enumerates them
(packages/atif-converter/src/atif_converter/domain/fidelity.py:44) and a pure enrichment pass repairs
three by re-running harbor’s deterministic normalization order over the raw records
(packages/atif-converter/src/atif_converter/domain/enrichment.py:198). A fingerprint snapshot is
taken before harbor reads and re-checked afterwards, so a session that resumes writing mid-conversion
fails instead of yielding mutually inconsistent artifacts
(packages/atif-converter/src/atif_converter/infrastructure/raw_records.py:110).
packages/atif-converter/src/atif_converter/domain/enrichment.py(412 LOC) —enrich_trajectoryrestores source uuids, the compact-summary flag, and the cache-creation total (packages/atif-converter/src/atif_converter/domain/enrichment.py:198).packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py(195 LOC) — the only module importing harbor, which ships nopy.typed, so the untyped surface stays contained (packages/atif-converter/src/atif_converter/infrastructure/harbor_adapter.py:11).packages/atif-converter/src/atif_converter/infrastructure/raw_records.py(164 LOC) — the one raw reader the census, edges emitter, and enrichment share, withmutated_filesas the race check (packages/atif-converter/src/atif_converter/infrastructure/raw_records.py:146).packages/atif-converter/src/atif_converter/application/convert_and_audit.py(155 LOC) — the use case (packages/atif-converter/src/atif_converter/application/convert_and_audit.py:105) and its refusal on any mid-window mutation (:93).packages/atif-converter/src/atif_converter/domain/fidelity.py(137 LOC) —RecordTypewith 12 members (packages/atif-converter/src/atif_converter/domain/fidelity.py:18),FidelityGapwith 7 (:44), and theLossReportvalue object (:83).packages/atif-converter/src/atif_converter/domain/edges.py(101 LOC) — oneedges.jsonlline per raw record, key order fixed by the contract (packages/atif-converter/src/atif_converter/domain/edges.py:22).packages/atif-converter/src/atif_converter/infrastructure/census.py(89 LOC) — the raw-side counts that form the input half of a loss report, derived from an already-parsed snapshot (packages/atif-converter/src/atif_converter/infrastructure/census.py:57).packages/atif-converter/src/atif_converter/domain/errors.py(70 LOC) — six typed exceptions under oneDomainErrorbase, with exit-code mapping left to the CLI (packages/atif-converter/src/atif_converter/domain/errors.py:14).
atif-models
Section titled “atif-models”A deliberately narrow seam: a system prompt, a user prompt, and a pydantic schema in; a validated
instance of that schema out (packages/atif-models/src/atif_models/domain/ports.py:116). Its only
in-repo consumer is atif-analytics — the single edge the forbidden contract leaves open
(pyproject.toml:520) — and the registry is the only place in the workspace where a Bedrock model id
is written down, so a pipeline names a family and a size alias and lets resolve pick the id
(packages/atif-models/src/atif_models/domain/registry.py:6, :109). The default adapter posts an
OpenAI chat-completions body to invoke_model in strict json_schema mode, dispatching the blocking
call through anyio.to_thread under a capacity limiter with tenacity owning the retry loop and
botocore’s own retries disabled
(packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py:142). Strict mode demands
additionalProperties: false at every object level and every property listed in required, which one
pure transform enforces before the schema reaches the wire
(packages/atif-models/src/atif_models/domain/schema.py:40).
packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py(314 LOC) —OpenAiBedrockProvider(packages/atif-models/src/atif_models/infrastructure/openai_bedrock.py:142), the retryable-code set (:63), and usage extraction (:124).packages/atif-models/src/atif_models/domain/registry.py(151 LOC) —ModelSpec(packages/atif-models/src/atif_models/domain/registry.py:39),resolve(:109), andestimate_costover per-1M-token rates (:125).packages/atif-models/src/atif_models/domain/ports.py(143 LOC) — theLlmStructuredProviderProtocol (packages/atif-models/src/atif_models/domain/ports.py:116), the terminal-versus-retryable error split (:43), and lock-guarded usage accumulation (:78).packages/atif-models/src/atif_models/domain/schema.py(109 LOC) —to_openai_strictkeeps$defsand$ref, dropsdefaultandtitle(packages/atif-models/src/atif_models/domain/schema.py:40).packages/atif-models/src/atif_models/infrastructure/settings.py(84 LOC) —LlmSettingscarries the family, region, concurrency, and per-pipeline size overrides (packages/atif-models/src/atif_models/infrastructure/settings.py:28).packages/atif-models/src/atif_models/__init__.py(14 LOC) — states the ownership rule: no other member hardcodes a model id (packages/atif-models/src/atif_models/__init__.py:9).packages/atif-models/src/atif_models/domain/__init__.py(9 LOC) — the pure layer: no boto3, no env, no clock (packages/atif-models/src/atif_models/domain/__init__.py:7).packages/atif-models/src/atif_models/infrastructure/__init__.py(8 LOC) — adapters and env settings, importing downward intodomainonly (packages/atif-models/src/atif_models/infrastructure/__init__.py:7).
See also
Section titled “See also”- processes — 42 shared source citations
- business logic — 39 shared source citations
- impact analysis — 38 shared source citations
- contract map — 37 shared source citations
- debugging guide — 24 shared source citations