Skip to main content

Find the holes your code already drew.

Project description

absentia

Find what you forgot to write. (The holes your code already drew.)

Most code analyzers find what's wrong. absentia finds what's missing.

Take 48 event handlers in your codebase. 47 call bus.unsubscribe() in their cleanup paths. One doesn't. That one is a memory leak waiting for the user who triggers the right interaction — and no linter, type-checker, or AI reviewer will catch it, because nothing told them to expect that pattern. Absentia learns the pattern from the 47 and flags the outlier with a 0.94-confidence score.

Pattern mining over your AST. No LLM, no rule database, deterministic — same input, same gaps. The rules come from your code itself: if 9 of your 10 API endpoints use @audit, absentia tells you about the 10th; if 8 of your 10 panels have a corresponding test file, absentia tells you about the 2 that don't. Every gap traces back to the rule that produced it, and every rule traces back to the members of your codebase that exhibit it.

GAPS                                              confidence ≥ 0.80   3

▶ src/api/users.py:42       fn `delete_user`     missing @audit       0.90
  src/api/orders.py:15      fn `refund`          missing test pair    0.80
  panels/code_editor.py:842 fn `_render_gutter`  missing @lru_cache   0.83

DETAIL: g-7c91
  rule       r-a3f2  ·  fns in src/api/ have @audit
  support    9/10  (confidence 0.90)
  exhibits   create_user  update_user  list_users  get_user  …
  violator   ✗ delete_user

Real gap and rule IDs are seven characters after the prefix (g-7c91234, r-a3f2bc7); the 4-char forms above are shortened for readability.

Why this exists

Most code-hygiene tools answer one of two questions:

  • "Does this code violate a rule someone wrote?" Linters and style checkers (ruff, ESLint, etc.). The rules come from a human or a config file.

  • "Is this code likely buggy?" Static analyzers (mypy, pyright, sonarqube). The rules come from compiler theory or hand-coded heuristics.

Neither answers the question that actually keeps codebases consistent over years: "Does this code follow the patterns the rest of this codebase follows?"

Most code drift isn't bugs and isn't style violations — it's a piece that diverged from a convention nobody wrote down. absentia mines the conventions and finds the divergences. It's the difference between "your if should have a space after it" (a global rule) and "every other endpoint in this folder logs the user_id, this one doesn't" (a local pattern your team established without writing it down).

Install

The engine isn't on PyPI yet (still pre-1.0). The absentia name is claimed on PyPI as a metadata-only placeholder — pip install absentia today resolves to v0.0.1 with no CLI, just the pitch — so install from the repo until the v1.0 cut:

git clone https://github.com/skbays03/absentia.git
cd absentia
pip install .                   # or `pip install -e .` for an editable install

Or with pipx (recommended for CLI tools — installs into an isolated environment, puts absentia on your PATH):

git clone https://github.com/skbays03/absentia.git
pipx install ./absentia

Requires Python 3.13+. Cross-platform (macOS, Linux, Windows). On Windows, the same pip install . / pipx install ./absentia commands work in PowerShell or cmd; if you want a venv first, activate it with .venv\Scripts\activate instead of the POSIX source .venv/bin/activate.

Quickstart

From any project directory:

absentia init      # create absentia.toml + .absentia/
absentia           # open the TUI

That's it. absentia scans your code, mines patterns, and shows you a navigable list of gaps. Use j/k to move, to open in your editor, s to suppress with a reason, e to see why a gap was flagged.

For CI and scripting:

absentia check               # human-readable list; exit 1 if any gaps
absentia check --json        # machine-readable
absentia check --max-gaps 5  # tolerate up to 5 gaps before failing the build
absentia check --cold        # dev-time: ignore parse cache and re-parse the
                           # whole tree (or just `--cold src/foo.py` for one
                           # file). Useful when you suspect cache weirdness
                           # or are benchmarking the parse stage.
absentia check --language python,rust          # restrict to specific languages
absentia check --exclude '**/vendor/**'        # skip a glob pattern
absentia check --exclude tests --exclude docs  # multiple --exclude allowed
absentia --debug check                          # diagnostic prints to stderr
absentia --no-color check                       # force-disable ANSI color

Symmetric flags: absentia est accepts the same --config, --jobs, --json, --quiet, --language, --exclude, --cold as check, so muscle memory transfers between the two.

The full flag list (including --config, --min-confidence, est's --recalibrate / --use-synthetic / --history, top-level --purge / --jobs-default, and others) lives in the CLI reference.

What absentia finds

Examples of typical gaps:

Decorator inconsistency

src/api/users.py::delete_user
  missing  @audit decorator
  why      9/10 fns in src/api/ have @audit

Missing sibling files

src/api/orders.py::refund
  missing  sibling test (tests/test_orders.py::test_refund)
  why      8/10 fns in src/api/ have a sibling test

Inconsistent inheritance

panels/quirky_panel.py::QuirkyPanel
  missing  inherits from BasePanel
  why      12/14 classes in panels/ inherit from BasePanel

Import omissions

tools/bug_cli.py
  missing  imports `from .common import setup_logging`
  why      9/11 files in tools/ import setup_logging

Naming pattern breaks

tests/test_users.py::should_validate_email
  missing  starts with `test_`
  why      142/150 fns in tests/ follow `test_*`

How it works

Four deterministic conceptual stages:

  1. Parse — tree-sitter walks your code and extracts entities (functions, classes, files, imports, decorators).
  2. Group — selectors organize entities into groups by directory, decorator, parent class, name pattern, or user-defined criteria.
  3. Mine — within each group, frequency analysis finds features appearing in most members. Features above your confidence threshold become rules.
  4. Compare — entities in a rule's group that don't satisfy its predicate become gaps.

Run absentia twice on the same code and you get the same output. The runtime progress UI shows five stages — walk → parse → store → mine → finalize — adding I/O bookends around the conceptual core; see architecture and performance for the full pipeline view, and how mining works for the algorithm-deep walkthrough.

Performance

absentia scans the entire Linux kernel — 65,004 files / 686,923 entities across ~30 million lines of C — in ~24 seconds warm / ~48 seconds cold at default jobs on a 10-core M-series MacBook. Warm breakdown: parse ~8 s (cache hits) + mine ~12 s + store ~2 s. Cold breakdown: parse ~31 s + mine ~12 s + store ~3 s. Single-process baseline ~95 s cold. Most projects on this scale rarely apply — typical real-world codebases scan in seconds (and warm-rescan a small edited subset in fractions).

The mining stage is the headline optimization story: a name-indexed find_symmetry_gaps (no more O(P×N) per-pair-per-entity scan) plus mypyc-compiled mining.py and symmetry.py together cut mining-stage wall-clock on the kernel from ~5 minutes to ~12 seconds — a ~25× speedup, gap counts byte-identical to the pre- optimization baseline.

If you're running on a free-threaded Python (3.13t / 3.14t), the ThreadPool worker cap rises automatically from 4 to 7 (one per mining strategy) — more headroom for mining-stage parallelism once the C-extension ecosystem catches up to the no-GIL ABI. No-op on regular CPython. (Speedup percentage isn't pinned: most tree-sitter wheels still ship GIL-only ABIs, so the path can't be benchmarked end-to-end as of early 2026.)

Full benchmark table covering all 17 built-in extractors (16 languages; TypeScript and TSX share a tree-sitter grammar but emit distinct extractors) — small smoke-test corpora plus the Linux kernel as the big-corpus case study (687 k entities, ~30 M LOC of C) — in architecture and performance.

Curious what your machine looks like? Run absentia est from any project directory for a hardware-calibrated cold-scan estimate — see the estimator methodology for the math.

What absentia is not

  • Not a linter. Linters enforce rules someone else wrote. absentia enforces rules your codebase already follows.
  • Not a code reviewer. absentia doesn't critique correctness, security, or design. It surfaces consistency gaps.
  • Not a fixer. absentia finds; humans fix. Auto-patching is a different product with very different tradeoffs.
  • Not a resource-leak detector. Patterns like open()/close(), lock()/release() — anything where the language or runtime defines the pair — are the linter's job. Use pylint, flake8-resource-leak, or Python's with statement for those. absentia catches project-specific paired calls (your event-bus subscribe/unsubscribe, your custom audit begin/commit) — conventions no off-the-shelf linter knows.
  • Not a control-flow analyzer. absentia's read is coarse: "this function calls A but not B." It won't verify that B is called along every code path. Type systems and resource-leak linters own that layer.
  • Not AI. No LLM, no embeddings, no model. Rules are statistical facts about your code, computed by counting. See why no LLM.

TUI vs CLI

Bare absentia opens the TUI — the primary interface, built for exploration. Drill from a gap to its rule, from a rule to its other members, from there to their gaps. Filter live with /. Suppress with s. Watch mode (w) auto- rescans every 2 seconds — incremental, so unchanged files hit the parse cache.

absentia check is the batch mode for CI, scripting, and editor integrations. It honors --json, --max-gaps, --quiet, and exits with a meaningful status code.

Configuration

Per-project config in absentia.toml:

[scan]
include   = ["src/", "tests/"]
exclude   = ["src/vendor/"]
languages = ["python"]

[mining]
min_confidence     = 0.8
min_group_size     = 5
max_predicate_size = 2

[selectors.directory]
enabled = true

[selectors.decorator]
enabled = true
exclude = ["@property", "@staticmethod"]

See the configuration reference for every option.

Status

Stable. v1.0 ships the public API + config format covered by SemVer; breaking changes go in major-version bumps. New gap detectors, extractor improvements, and TUI features land in minor versions.

Documentation

License

Licensed under the Apache License, Version 2.0. See NOTICE for attribution.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

absentia-1.0.0.tar.gz (331.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

absentia-1.0.0-cp313-cp313-win_amd64.whl (283.8 kB view details)

Uploaded CPython 3.13Windows x86-64

absentia-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (412.8 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

absentia-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (403.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

absentia-1.0.0-cp313-cp313-macosx_11_0_arm64.whl (309.1 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

File details

Details for the file absentia-1.0.0.tar.gz.

File metadata

  • Download URL: absentia-1.0.0.tar.gz
  • Upload date:
  • Size: 331.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for absentia-1.0.0.tar.gz
Algorithm Hash digest
SHA256 762632e3fd6d6d1810a1163e27453567c985e6c4ecbfb121d0c6588971275d13
MD5 6b533d8cd3a8f6982c465e8e9326a27d
BLAKE2b-256 09e23007029e32230d65bf0838fc58f73d1dc32310d0d9e020ae055a87ae3094

See more details on using hashes here.

Provenance

The following attestation bundles were made for absentia-1.0.0.tar.gz:

Publisher: publish.yml on skbays03/absentia

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file absentia-1.0.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: absentia-1.0.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 283.8 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for absentia-1.0.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 da134a4778cf1593af0c126efb412a52455e36627917bd411c79d434475f0e6c
MD5 73eb704460f492028a9787386d4884a3
BLAKE2b-256 9c8663bddcd457e77ce96b12fd9642700161b9f94eb0c5d70902e59684f80726

See more details on using hashes here.

Provenance

The following attestation bundles were made for absentia-1.0.0-cp313-cp313-win_amd64.whl:

Publisher: publish.yml on skbays03/absentia

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file absentia-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for absentia-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1400a437d1b6cd9b801242a9a9cca8cefbdd80bc2366ffa50a95d0ee0b9184d4
MD5 8a65acc3e273374a4f09edf8789b1142
BLAKE2b-256 b2808446bee4082a3dc73c74e0035276753a5f62564d28f519f29affa3d595d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for absentia-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on skbays03/absentia

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file absentia-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for absentia-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 8ce20eb48885edec97037c271a33ff6af91207cbb8a45b4b7b10642bdf2c71f8
MD5 51c5e9b6eb73e98dda0449a8e113cb40
BLAKE2b-256 92ac5b0fb6e8c5364b54a33d9e261ef34077871872c5535c6bb724dcc3557a1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for absentia-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on skbays03/absentia

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file absentia-1.0.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for absentia-1.0.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dc355182ccf4fcd085b11e6c5b8bae9ce5367a9b153a46d618ff4f42e06e3b18
MD5 168da363f428473ba35145a23f175052
BLAKE2b-256 07a13b3c37731598de0bef5d181d70e46a2631a57f7da44fca594dd6bbebe3c4

See more details on using hashes here.

Provenance

The following attestation bundles were made for absentia-1.0.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: publish.yml on skbays03/absentia

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page