Skip to content

Risk hotspots

Risk here is a score over signals a command can produce, each countable and each tied to a line of source: 2 × T + 2 × E + 0.5 × W + 1 × C + 1 × H + U/10. T counts defects a type checker located in the file; E and W count error- and warn-severity scanner findings from the scanner report tier (mise.toml:675); C counts complexity ratchets the ruff config sets at a function living in this file (pyproject.toml:325-348); H counts distinct unguarded concurrency or IO windows, where a window whose own docstring names its recovery does not count; and U is uncovered units (missed lines plus missed branches) under the branch-coverage config at pyproject.toml:626. Combined line-and-branch coverage is measured against the fail_under = 89 floor at pyproject.toml:651, so U/10 puts a file’s share of the remaining uncovered units on the same scale as one located defect.

Churn is rejected as a signal. One author wrote 88 of the 93 commits (the other five are three dependabot[bot] and two github-actions[bot] commits, none touching packages/*/src), and the history opens with one commit on 2026-08-28 that added 276 files, 89 of the 154 Python files under packages/*/src among them, so commit frequency measures authoring order rather than defect density and ranking by it would produce confident noise. The Trend column below still runs the mechanical 30-day slope rule over those 154 files (↑ rising is a count of commits in the 30 days ending 2026-10-12 more than one standard deviation above the median: the median is 1 and the population standard deviation 2.27, so four commits or more), but it contributes zero weight to the score, and ↓ falling is unreachable because one standard deviation below the median is less than zero. Top owner is likewise the sole author on every row and carries no bus-factor information. Further limits: both type checkers pass at zero (ty check reports no diagnostic and pyright strict reports 0 errors and 0 warnings), so T is 0 on every file; bandit’s one open finding workspace-wide is LOW severity, so E and W never discriminate either, and the order rests on C, H, and U. U comes from a branch-coverage run of the suite rather than from the tree, so it’s the one input here that a reading of the code, git, and the linters can’t re-derive. packages/atif-embed/src/atif_embed/domain/ports.py is excluded from the ranking despite reading as uncovered, because exclude_also drops a Protocol’s ... body while still measuring its def line (pyproject.toml:652-659); that is a measurement artifact, not a gap.

File Trend Open findings Top owner Citation
atif_duck.infrastructure.registry ↑ rising none sole author packages/atif-duck/src/atif_duck/infrastructure/registry.py
atif_analytics.infrastructure.parquet_cache → flat none sole author packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py
atif_cli.app ↑ rising none sole author packages/atif-cli/src/atif_cli/app.py
atif_converter.domain.enrichment → flat none sole author packages/atif-converter/src/atif_converter/domain/enrichment.py
atif_analytics.application.use_cases.friction → flat none sole author packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py
atif_analytics.infrastructure.corpus_reader ↑ rising none sole author packages/atif-analytics/src/atif_analytics/infrastructure/corpus_reader.py
atif_duck.domain.examples → flat none sole author packages/atif-duck/src/atif_duck/domain/examples.py
atif_analytics.application.analyze → flat none sole author packages/atif-analytics/src/atif_analytics/application/analyze.py
atif_cli.output → flat none sole author packages/atif-cli/src/atif_cli/output.py

Every B608 hardcoded-SQL site carries a # nosec B608 that names its reason, so bandit skips all 69 of them (each MEDIUM severity at LOW or MEDIUM confidence when the markers are ignored). Its one open finding is a LOW-severity B105 on a record-type constant at packages/atif-converter/src/atif_converter/domain/codex_fidelity.py:37, a file outside the table, which is why Open findings reads none on every row. The build-configuration rule that asks [tool.uv] for a dependency cooldown (uv-missing-dependency-cooldown) is met: pyproject.toml:124 sets a seven-day exclude-newer window, and the comment at :115-123 names the rule. The SQL sites are the ones that ruff suppresses per line (ruff check --select S608 packages/ exits clean while the same run with --ignore-noqa over packages/*/src reports 70 sites against bandit’s 69, the two sets differing by three statements the tools parse differently), and each # noqa: S608 names what it interpolates (pyproject.toml:210-213). The scanner tier therefore carries no unaudited exposure, which is why coverage, complexity, and concurrency carry the ranking instead.

What’s there. The DuckDB view and macro registry that binds a connection to the materialized <corpus_root>/sessions/<id>/ tree and exposes it as the stable SQL surface, with raw readers built as CREATE TEMP TABLE over read_json under an explicit strict-projection columns filter (packages/atif-duck/src/atif_duck/infrastructure/registry.py:3-20). Every registration function follows a register-or-fail-loud contract: log through logger.exception and re-raise on any DDL failure (:62-63, :994-997, :1566-1569, :2161-2164).

Recent activity. 12 commits in the 30-day window against a median of 1, so ↑ rising under the mechanical rule, which here measures how often the file was edited, not whether it is destabilizing.

Owners. The sole author at 100% (all 14 commits touching the path; the history has no merge commits).

Findings. More of the workspace’s B608 sites live here than in any other file (28 of 69), each carrying a per-line # nosec B608 that names its interpolation: a module-constant reader or table with catalog columns (:347, :400), a glob or file list the statement binds as a parameter (:767, :869), the corpus name through sql_literal (:1014), local regex literals (:1384), an int()-coerced width (:1792), an int-derived alias (:1881), and the pricing rates (:2044). The file holds no type-checker defect; _pricing_values_clause escapes the model name through sql_literal and coerces both rates with float() (:1915-1942, specifically :1939), because the pricing parameter is reachable by an in-process embedding caller even though the CLI never passes one, and a str rate there would get past the type checkers (:1932-1937). Its uncovered units sit in register_views (:1023-1569), register_macros (:1945-2164), register_vss (:1642-1798), _gate_session_dirs (:521-572), and _pricing_values_clause itself, although tests drive each of those paths: 14 test files call register, which runs register_views and register_macros, seven call register_vss, and packages/atif-duck/tests/test_duck_views.py:663 passes a hostile model name through the pricing clause. The register_vss gap is the one that matters most: the ATTACH-failure arm that degrades a Lance directory to an empty-store fallback and logs rather than raising (packages/atif-duck/src/atif_duck/infrastructure/registry.py:1723-1727) is untested silent degradation: no test calls register_vss on a directory that fails to ATTACH.

atif_analytics.infrastructure.parquet_cache

Section titled “atif_analytics.infrastructure.parquet_cache”

What’s there. The sharded parquet cache behind the analytics pipelines: write_part appends a new shard and read_all unions every whole shard (packages/atif-analytics/src/atif_analytics/infrastructure/parquet_cache.py:90-111, :114-129). iter_part_files is the one choke point both readers share, and it drops any shard too small to carry parquet framing (:71-87).

Recent activity. One commit in the window, at the median, so → flat. Churn adds nothing to its score.

Owners. The sole author at 100% (both commits touching the path).

Findings. No scanner findings, no complexity ratchet, and one unguarded IO window. write_part‘s sharded branch drops part-<time_ns>.parquet straight into the live directory (:102-103) that iter_part_files globs (:86), with no tmp-then-rename; the code anticipates concurrency explicitly, since the nanosecond suffix exists to avoid collisions “when two part-writes land in the same millisecond under concurrency” (:99-101). The read path does apply the size guard: MIN_PARQUET_BYTES’ own docstring states the size check “is what keeps a torn artifact from failing a pipeline that could skip it” (:27-32), iter_part_files filters every globbed part through it (:87), and the legacy single-file branch applies it too (:107). The guard catches a stub of 16 bytes or fewer, not a half-written file, so a reader that globs while polars is still writing a larger shard hands it to pl.read_parquet (:129) and fails there.

What’s there. The cyclopts composition root and the only module importing atif-converter, atif-corpus, and atif-duck together, wiring every cross-package seam: the ConverterPort adapter, the clock, version pins, the DuckDB connection (packages/atif-cli/src/atif_cli/app.py:3-9). Heavy imports are deferred into the command bodies so the schema / --help fast path stays lean, a property pinned by a fresh-interpreter test rather than by a lint (:50-53).

Recent activity. The most commits of any source file (19, 16 of them in the window), so ↑ rising, which is expected of a composition root that gains a wiring line whenever any member changes.

Owners. The sole author at 100% (all 19 commits touching the path).

Findings. No B608 site: the search kNN statement in search_rows binds the vector, the session id, and k as parameters and formats only the width len(query_vector) and constant fragments into _SEARCH_SQL (:2330, :2347-2354). It sets no complexity ratchet: search takes seven parameters and the widest command, analyze, takes 13, while max-args = 19 sits at atif_corpus.application.materialize.materialize (packages/atif-corpus/src/atif_corpus/application/materialize.py:1497). Its uncovered units concentrate in the commands that reach outward: analyze (packages/atif-cli/src/atif_cli/app.py:1725-1826), convert (:690-772), and status (:1211-1380), with a few in search (:2123-2303) and main (:2538-2571). analyze always calls Bedrock, and embed and search do with the default Cohere provider, so the least-covered command body in the tree is also the one that spends money; the uncovered region inside analyze is the settings-override chain that applies the budget ceilings a crontab line depends on (:1791-1805).

What’s there. A pure function over a trajectory dict plus raw records (no harbor import, no IO) that repairs some of the named fidelity gaps by re-running harbor’s deterministic normalization order (packages/atif-converter/src/atif_converter/domain/enrichment.py:3-30). It exists because harbor 0.22.0 puts no record identity in step.extra: it reads requestId off the message dict and id off the event dict, while real transcripts carry both on the event, so nothing lands in extra and there is nothing to join on (:15-20).

Recent activity. One commit in its history and none in the window, below the median, so → flat.

Owners. The sole author at 100% (the one commit touching the path).

Findings. No scanner findings, no IO windows (this is a pure function), and two of the workspace’s complexity ratchets set by one function: max-branches = 39 and max-complexity = 38 are both pinned at enrich_trajectory (pyproject.toml:339, :334, :343-348; the function at packages/atif-converter/src/atif_converter/domain/enrichment.py:198). Each branch is one named fidelity gap, which is why the count is a ratchet rather than a target. Its uncovered units sit mostly inside enrich_trajectory (:198-412) and _visible_user_text (:113-146), the helper that replicates harbor’s rules for which user records produce a visible text message. Both untested regions are alignment-failure paths: the user-step mismatch arm that logs a warning and truncates attribution from that step onward (:350-360) is the behavior a reader most needs pinned, since it silently narrows enrichment coverage rather than failing.

atif_analytics.application.use_cases.friction

Section titled “atif_analytics.application.use_cases.friction”

What’s there. A tiered friction detector over short user-role messages: a pre-filter, a regex fast path at confidence 0.9, deterministic stamp rules, and an LLM tier for everything else (packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py:3-28). The source column is the row’s provenance tier rather than the engine that computed it, and downstream atif-duck views bind to those literal values, so the vocabulary is a fixed contract (:34-38).

Recent activity. Two commits in the window, under the four the rule needs, so → flat.

Owners. The sole author at 100% (all three commits touching the path).

Findings. No scanner findings, and the max-statements = 119 ratchet, set at _friction_async (pyproject.toml:341, :336; the function at packages/atif-analytics/src/atif_analytics/application/use_cases/friction.py:210). The coverage gap and the complexity outlier are the same region: most of the file’s uncovered units sit inside _friction_async (:210-498), with the rest in detect_user_friction (:501-606) and deterministic_stamps (:146-202). This is the file where the two signals coincide most tightly: the function with the most statements in the workspace is also the least-exercised, and it is the function that decides which candidate messages cross into the billable LLM tier. The budget-guard posture that governs that decision assumes every candidate reaches the LLM even though roughly half survive the fast tiers (:554-556), and the third-party disclosure is explicit: short user message bodies leave the machine on tier 4 (:40-41).