backend/pyproject.toml
Rama main · réplica de lectura del repositorio original.
[project]
name = "agenticos"
version = "1.65.1"
description = "OS for your agents."
requires-python = ">=3.15"
license = { text = "Apache-2.0" }
authors = [
{ name = "DEENUU1", email = "kacper.wlodarczyk@vstorm.co" },
]
dependencies = [
# Uncapped on purpose. FastAPI, Pydantic AI, Logfire and genai-prices track
# the newest release: this platform's whole subject matter moves at their
# pace, and genai-prices in particular *is* the model price snapshot, so an
# old one means budgets computed from stale prices. `make deps-upgrade`
# bumps them, the `dependency-freshness` workflow tries the newest of
# everything on a schedule, and Dependabot opens the PR. A cap here (it was
# `<0.137`) turns all three into no-ops.
# The floor is a correctness floor, not a taste one: `Depends(..., scope=)`
# is what makes the request's transaction commit before the response is sent
# (#353), and an older FastAPI answers `TypeError` at import rather than
# quietly reverting - but only 0.141.1 has been run against this suite.
"fastapi>=0.143.2",
"uvicorn[standard]>=0.54.0",
"pydantic[email]>=2.14.3",
"pydantic-settings>=2.16.2",
"python-multipart>=0.0.33",
"logfire[fastapi,asyncpg,redis,httpx]>=5.4.0",
"sqlalchemy[asyncio]>=2.1.8",
"asyncpg>=0.32.0",
# Alembic's, not the application's. Every application URL is
# `postgresql+asyncpg`, so this reads as dead to a grep - but `alembic/env.py`
# builds a *sync* engine from `DATABASE_URL_SYNC`, which is
# `postgresql+psycopg2://`. Removing it breaks every migration (#155). Moving
# Alembic to asyncpg was tried for 9 MB of image (#2219) and dropped: asyncpg
# is strict about bind types where psycopg2 is not, and the backfills that
# would meet that run only against data no local check has.
"psycopg2-binary>=2.9.13",
"alembic>=1.19.2",
"greenlet>=3.5.5",
"pyjwt>=2.15.1",
"bcrypt>=4.0.0",
"authlib>=1.9.5",
# Imported at module scope in `app/services/rag_document.py`, and until #155
# declared only in the `dev` group - so `uv sync --no-dev`, which is what the
# image builds with, was relying on Starlette to pull it in.
"anyio>=4.16.0",
"redis>=8.1.0",
"aiosmtplib>=5.1.3",
# Starlette's requirement for `SessionMiddleware`, which `main.py` adds for
# the OAuth handshake. Declared here rather than left transitive because the
# middleware is ours: Starlette itself installs without it (#155).
"itsdangerous>=2.2.0",
# The floor is ours as well as `pydantic-ai-harness`'s: the sandbox runs on
# `pydantic_ai.workspaces` and passes `workspace=` to a run, both from 2.52,
# and stating it keeps a resolver from picking a slim the harness cannot run
# on. Dependabot raises it to
# whatever is current, which is fine as long as the lock resolves to at least
# as new: this group's bump once locked 2.30.0 while `main` already held
# 2.31.0, and the image installs the lock verbatim (`uv sync --frozen`).
"pydantic-ai-slim[openrouter,anthropic,google,duckduckgo,web-fetch,groq,mistral,cohere,huggingface,xai,openai-realtime,google-realtime,xai-realtime]>=2.66.0",
"genai-prices>=0.2.1",
"pydantic-ai-slim[mcp]>=2.66.0",
# The MCP SDK. Uncapped since #1820 moved the one path where our code and
# the SDK exchange HTTP objects onto `httpx2`, which 2.0 migrated the whole
# SDK to: `PinnedAsyncClient` (`app/core/pinned_http.py`) and the MCP OAuth
# flow are `httpx2` now, so an SDK-built request reaches a client that can
# send it and a malformed endpoint raises an exception our refusals catch.
#
# **Floored at 2, not merely uncapped.** `app/agents/mcp.py` imports
# `create_mcp_http_client` and `streamable_http_client` unconditionally, and
# both are 2.x names - 1.x spelled the transport `streamablehttp_client` and
# had no client factory at all. A non-frozen install that resolved back to
# 1.x would fail every streamable probe at import time; the image installs
# the lock verbatim, but nothing else does.
#
# The rest of this codebase stays on `httpx` deliberately. The boundary is
# wherever an object crosses into or out of a vendor SDK, and nothing else
# here does: the portals, the channels, `model_catalog` and the web-research
# search all build and send their own requests.
"mcp>=2.3.0",
# The platform's own MCP server and its OAuth endpoints (#2058, #2059).
"fastmcp>=4.1.6",
# Wrapped by three capabilities: `guardrails` (the ready-made secret/PII
# detectors and the input/output/tool guard edges), `planning` (the plan
# store, tools and cache-safe tail reminder) and `compaction` (the
# context-window handling and its strategies). Uncapped for the same reason
# Pydantic AI is: this is where those are maintained, and pinning would
# freeze the detectors and the model registry they read. Every agent also
# wraps RepairToolArguments, whose json-repair dependency is selected by
# its capability extra in the 0.64 release.
"pydantic-ai-harness[e2b,repair-tool-arguments]>=0.66.0",
"websockets>=17.4",
# Required, not optional: hybrid retrieval fuses BM25 with the vector search
# and a deployment cannot choose otherwise. It is also the only reason
# `numpy` (24 MB) is installed - a real cost, taken deliberately (#155).
"rank-bm25>=0.2.2",
"pymupdf>=1.28.2",
# `app/services/channels/chart_png.py` renders a chart tool's output for a
# channel that cannot show HTML. A transitive of the parsers as well, but the
# direct use is ours.
"pillow>=10.0.0",
"python-docx>=1.1.0",
"openpyxl>=3.1.0",
"llama-cloud>=2.18.0",
"liteparse>=2.18.0",
# `html.parse` reads markup with bs4's standard-library parser and picks
# elements with soupsieve. Both already ship through `pydantic-ai-slim`'s
# web-fetch extra; declared here so a release of it cannot drop them.
"beautifulsoup4>=4.15.0",
# Soup Sieve 4 hides template contents from document selectors; preserve
# html.parse links and matches until that major change is supported.
"soupsieve>=2.9.2,<4",
# Google Drive speaks plain REST through `app/services/rag/drive_api.py`;
# `google-auth` only signs the service account's token, over `requests`.
# `google-api-python-client` was 108 MB of discovery documents for four URLs.
"google-auth[requests]>=2.62.1",
"boto3>=1.43.126",
# Two HTTP libraries, deliberately. `httpx` is what fourteen modules build
# and send their own requests with - the portals, the channels, the model
# catalog, the web-research search - and `httpx2` is what the MCP SDK is
# written against since 2.0. Declared rather than inherited through `mcp`
# because `app/core/pinned_http.py` imports it directly: the boundary is
# wherever an object crosses into or out of a vendor SDK, and a library our
# own code imports is one this manifest names (#1820).
"httpx[http2]>=0.28.1",
"httpx2>=2.13.1",
"click>=8.5.1",
# `cli/commands.py` renders every list the CLI prints. It has no import site
# under `app/`, which is how it was briefly deleted as dead (#155) - the
# `deptry` step reads `cli/` as well for exactly that reason.
"tabulate>=0.9.0",
# Renders a Markdown artifact into the page it is served as. Already here
# through `rich`; declared because `app/services/artifact.py` imports it.
"markdown-it-py>=4.0.0",
"tqdm>=4.70.1",
"tavily-python>=0.8.5",
"pydantic-monty>=1.5.0",
# Floors at 2.0, where each skill became its own deferred capability. The
# platform's three skills tools collapsed to one: the catalog is the model's
# own capability list, `load_capability` opens a skill, and only
# `read_skill_resource` survives as a tool (#1658).
"pydantic-ai-skills>=2.1.1",
# `console` for the tools, `workspaces` for the workspace capabilities,
# `remote` for the Files panel's archive client and `daytona` for Daytona
# connections (the `daytona` package; the old `daytona-sdk` never imported).
# Deliberately not `docker`: this container never touches the socket, so the
# Docker SDK has no business in the image. The service runs beside us as
# `sandboxd`. Floors at 0.2.32 for named sandboxd sessions and Daytona
# sandboxes, which a workspace keyed on its scope opens or attaches by name
# (#2003).
"pydantic-ai-backend[console,remote,workspaces,daytona]>=0.2.39",
# Delegation. Floors at 0.2.16 for three fixes this integration depends on:
# a suspended child no longer reported to the parent as completed (#64),
# `event_stream_handler_factory` on the toolset so a fan-out's events can be
# labelled per delegation (#65), and a bounded wait in `cancel_all`'s
# `finally` so a child that swallows cancellation cannot hang our teardown
# (#66).
# 0.2.17 first carried the caller-supplied-subagent ask_parent fix a sync
# delegate needs to reach the person waiting on its parent
# (subagents-pydantic-ai#76); 0.2.18 also fixes the general-purpose delegate
# (#78). 0.2.19 adds `TaskHandle.exception`, which is what keeps a provider's
# own text off a delegated run's row (#699). 0.2.20 reads that field in
# `check_task` and `wait_tasks` themselves, so a status answer names the
# exception's class rather than embedding the provider's message - the one
# surface `tool_retry_notice` cannot reach, because a `ToolReturnPart` is
# stored whole (#819). 0.2.22 puts the subagent's name on the delegation's
# bound state, which is the only way a persisted `ask_user` part can say
# which delegate asked - `ask_parent` hands the surface the question and
# nothing else (#1042). 0.2.25 runs a delegate in its parent's workspace,
# which is how a delegate sharing the parent's `sandbox` reaches the same
# files now that the files live in `ctx.workspace` (#2003). The floor is
# 0.2.25.
"subagents-pydantic-ai>=0.2.25",
"cryptography>=50.0.2",
"aiogram>=3.31.0,<4.0",
"slack-sdk>=3.46.0",
# What Slack Socket Mode actually needs. `app/services/channels/slack.py`
# imports `slack_sdk.socket_mode.aiohttp` - the transport a bot uses when the
# deployment exposes nothing - and slack-sdk keeps aiohttp behind an extra, so
# a plain `slack-sdk` does not pull it. It resolved anyway, because something
# else in the tree happened to bring aiohttp; the day that goes, the import
# raises inside a `try/except ImportError` that turns it into a bot which
# quietly never starts rather than a build that fails.
# Not `slack-sdk[optional]`, which is a grab bag: aiodns, boto3, SQLAlchemy,
# websockets and websocket-client for one transport's HTTP client. And not
# `slack-sdk[socket-mode]`, which does not exist - the only extra is
# `optional`, so the advice in that ImportError's message was unfollowable.
"aiohttp>=3.14.5",
# Cron schedules: the next fire after a given instant for a crontab
# expression. Interval schedules need no library, but "daily/weekly at HH:MM"
# and any raw crontab do - and getting DST and month lengths right by hand is
# the kind of arithmetic a dependency exists to own.
"croniter>=3.0.0",
"xlrd>=2.0.2",
"odfpy>=1.4.1",
"python-pptx>=1.0.2",
"olefile>=0.47",
# Signing in with a directory account (#1773): the two-step bind against
# Active Directory, OpenLDAP or FreeIPA. Pure Python, so the image needs no
# libldap and no compiler for it, which is why it and not `python-ldap` or
# `bonsai`. LGPL-3.0, used unmodified and replaceable by reinstalling - the
# decision is recorded in `licenses/policy.toml`.
"ldap3>=2.9.1",
# The expression language workflow `data.map` and `logic.if` nodes evaluate:
# declarative JSON queries with no assignment, loops or user functions, so a
# graph never executes code (#1789). MIT; already installed through boto3.
"jmespath>=1.1.0",
# Checks an `agent.run` step's structured answer against the JSON Schema the
# step declares, before anything downstream reads it (#1789). MIT; already
# installed through the MCP SDK.
"jsonschema>=4.26.0",
# An explicit registry disables remote reference fetching in MCP schemas.
"referencing>=0.37.0",
# The `guardrails` phone detector, which the harness's PII patterns lack. The
# lite build: the same `phonenumbers` module and numbering-plan metadata,
# without the 20 MB of carrier, geocoding and time zone data nothing here reads.
"phonenumberslite>=9.0.43",
"regex>=2026.9.10",
# The ingest pool's memory watchdog (#2219): the resident size of a job's child
# process and of whatever it starts - LibreOffice for a conversion - so a parse
# that grows past its limit is stopped and the parent says why. `/proc` alone
# would do on Linux and nowhere else.
"psutil>=7.0",
# Web Push to browsers and installed PWAs (#2097): the message encryption
# (RFC 8291) and the VAPID signature (RFC 8292) every push service demands.
# MPL-2.0, reviewed in licenses/policy.toml.
"pywebpush>=2",
"pydantic-ai-otari>=0.1.0,<0.2",
]
[project.optional-dependencies]
# The web browser's one engine dependency (`browser_use`, #2127). The loop is this
# repository's and the browsing model is the run's own, so all a library does is
# speak CDP.
#
# `cdp-use` is a typed, async client for the whole Chrome DevTools Protocol and
# nothing else: `client.send.Accessibility.getFullAXTree(...)`,
# `Page.captureScreenshot`, `Input.dispatchMouseEvent`. It ships `py.typed`, and
# it carries `httpx`, `typing-extensions` and `websockets>=15.0.1` - floors, not
# pins - so the extra costs this lock no downgrade at all.
#
# **Not `browser-use` itself.** 0.13.x hard-pins `openai`, `google-genai` and
# `requests` below what Pydantic AI and this project need, and uv refuses to lock
# even an optional extra whose pins conflict with the base ones. Its approach -
# one structured step per model call over a numbered element table - is what
# `_decision.py` does, on the run's model.
#
# **Not `browser-harness`**: a CLI and a per-user daemon with synchronous helpers,
# built to attach a coding agent to a developer's own Chrome, not a library for a
# multi-tenant service.
browser = [
"cdp-use==1.4.5",
]
# Integrated Windows sign-in (SPNEGO), the `KERBEROS_ENABLED` half of #1773.
# `gssapi` publishes no Linux wheel: it compiles against the system Kerberos
# headers, which is why it is an extra rather than a dependency every install and
# every CI job would have to build. The image takes it with
# `--build-arg EXTRAS="--extra kerberos"`, and the Dockerfile installs
# `libkrb5-dev` to build and `libgssapi-krb5-2` to run only for that build.
kerberos = [
"gssapi>=1.9.0",
]
# Mirage is optional; its exact pin is shared with the test environment.
# The S3 family remains unavailable because aioboto3 pins an incompatible boto3.
mirage = [
"mirage-ai[pydantic-ai,ssh,mongodb,gridfs,postgres,redis,email,nextcloud,hf-buckets,chroma,mem0,langfuse,qdrant,lancedb]==0.0.8",
]
# PEP 735 dependency group — installed by `uv sync` / `uv sync --dev` and
# excluded by `--no-dev`. (A `dev` extra under [project.optional-dependencies]
# only installed via uv's deprecated dev-extra special-casing, so `uv sync --dev`
# could skip it on some uv versions and leave pre-commit/ruff/ty unspawnable.)
[dependency-groups]
dev = [
# The `mirage` extra's pin, for the suite - see the extra.
"mirage-ai[pydantic-ai,ssh,mongodb,gridfs,postgres,redis,email,nextcloud,hf-buckets,chroma,mem0,langfuse,qdrant,lancedb]==0.0.8",
"pytest>=9.2.0",
"anyio[trio]>=4.16.0",
"pytest-cov>=6.1.0",
"httpx>=0.28.0",
"ruff>=0.17.4",
"ty>=0.0.99",
"pre-commit>=4.7.0",
"pytest-xdist>=3.8.0",
# Shuffles the suite every run and prints the seed, so an order-dependent
# test fails here rather than months later in an unrelated pull request.
# It carries its own xdist support (the controller's seed is handed to each
# worker through `workerinput`), without which the workers would collect in
# four different orders and `-n auto` would abort on the mismatch.
"pytest-randomly>=5.0.0",
"vulture>=2.14",
# vulture reads the code and finds what is written but unused; deptry reads
# the manifest and finds what is declared but unimported. Nothing noticed
# `fastapi-cache2` or `jinja2` going dead, which is the half of #155 worth
# more than the megabytes it recovered.
"deptry>=0.23.0",
# `ldap3` ships no annotations; typeshed's stubs are what lets the adapter
# over it be held to the platform layer's type gate.
"types-ldap3>=2.9.13",
# The public API's contract test validates real responses against the
# schemas its OpenAPI document publishes (#1796). It already arrives
# through the MCP SDK; declared so the test does not lean on that.
"jsonschema>=4.26.0",
]
# The documentation site. Its own group rather than part of `dev`, because the
# reference pages import the application to read its docstrings - so this group
# needs the runtime installed, and CI's lint job does not need mkdocs.
docs = [
"mkdocs>=1.6.0",
"mkdocs-glightbox>=0.5.2",
"mkdocs-material>=9.5.0",
"mkdocstrings[python]>=1.0.8",
# One build per locale from `<page>.<locale>.md` files sitting beside the
# English source, so `/install/` keeps its URL and `/pl/install/` is added
# rather than everything moving under `/en/`.
"mkdocs-static-i18n>=1.2.3",
]
[project.scripts]
agenticos = "cli.commands:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["app", "cli"]
# Dead-code detection. Two modes share this config:
# `make lint` runs `vulture` at the default confidence below - a blocking gate,
# but only on what vulture is sure of (unused variables, parameters, imports).
# Unused *functions and methods* sit at 60% confidence here because the
# codebase is registry-driven: FastAPI route handlers, Pydantic validators and
# capability-protocol methods are all called by name from a framework vulture
# cannot see, so a blocking function-level gate would be false positives all
# the way down. `make dead-code` lowers the confidence to 60 for a periodic,
# human-read scan instead - the same role `dependency-freshness` plays.
# `ignore_decorators` removes the framework false positives from both modes;
# `ignore_names` is the allowlist for anything left that is reachable in a way
# vulture cannot follow - add `name # why` when a real false positive appears.
[tool.vulture]
paths = ["app"]
min_confidence = 80
ignore_decorators = [
"@field_validator",
"@model_validator",
"@field_serializer",
"@model_serializer",
"@computed_field",
"@router.*",
"@*.get",
"@*.post",
"@*.put",
"@*.patch",
"@*.delete",
"@*.websocket",
]
ignore_names = []
# The other half of vulture: vulture finds code nothing calls, deptry finds a
# distribution nothing imports. DEP002 is the gate this was added for - that is
# the failure this repository has actually had, with `fastapi-cache2` and
# `jinja2` sitting declared and unimported until somebody read all 46 lines by
# hand (#155).
#
# It reads `app`, `cli` and `alembic`, which is every tree that ships, and the
# scope is the whole point rather than a detail: reading `app` alone called
# `tabulate` dead when `cli/commands.py` imports it, which took the e2e seed
# down on this very branch. A tree that ships and is not scanned is a tree whose
# imports do not count.
#
# Two rules are off, each for its own reason, and turning either on is its own
# change rather than a flag flip:
# DEP001 (imported, undeclared) fires on `faker` and `resend`, neither declared
# anywhere and neither in `uv.lock`. `faker` is guarded; `resend` is not, so
# a deployment that configures it raises `ModuleNotFoundError` at first send.
# That is #829, and the decision there is whether they become extras - not
# whether deptry shouts.
# DEP003 (imported, only transitive) fires 27 times on `starlette`, `mcp`,
# `yaml`, `openai`, `botocore`, `ddgs` and `opentelemetry` - every one of
# them arriving through a declaration we do control (`fastapi`, the
# `pydantic-ai-slim` extras, `boto3`, `logfire`). Declaring each by hand
# would double the manifest to restate what those extras already promise.
#
# DEP004 (imported, declared dev-only) is the second gate: it found `anyio`,
# imported at module scope in application code and declared only in the `dev`
# group, and there is nothing left for it to be noisy about.
[tool.deptry]
known_first_party = ["app", "cli"]
ignore = ["DEP001", "DEP003"]
[tool.deptry.per_rule_ignores]
# Required, and imported nowhere in the scanned trees on purpose. Each is either
# a runtime somebody else imports or a driver a URL scheme selects.
DEP002 = [
"python-multipart", # Starlette parses uploads with it; FastAPI refuses `File(...)` without it
"asyncpg", # selected by the `postgresql+asyncpg://` URL
"psycopg2-binary", # selected by Alembic's `postgresql+psycopg2://` URL
"greenlet", # SQLAlchemy's async bridge
"itsdangerous", # Starlette signs `SessionMiddleware` cookies with it
"aiohttp", # `slack_sdk.socket_mode.aiohttp` imports it; nothing here does
]
[tool.ruff]
target-version = "py315"
line-length = 100
# These run on the runner's own `python3` (3.12), before any toolchain is
# installed - CI's first job, and the dependency-graph submission - so they keep
# to syntax that interpreter parses.
per-file-target-version = { "../deploy/updater/*.py" = "py312", "../scripts/ci_changed_scope.py" = "py312", "../scripts/dependency_snapshot.py" = "py312" }
[tool.ruff.lint]
# Dotless i is a distinct Turkish letter used in localized messages.
allowed-confusables = ["ı"]
# The rule set is deliberately wide. A linter that only catches formatting is a
# linter people stop reading; these are the categories that catch real defects
# in an async, multi-tenant service — swallowed exceptions, blocking calls in a
# coroutine, mutable defaults, SQL built by string concatenation.
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear — mutable defaults, unused loop vars, except sequencing
"C4", # flake8-comprehensions
"UP", # pyupgrade
"SIM", # flake8-simplify
"RUF", # ruff-specific
"ASYNC",# flake8-async — blocking calls inside coroutines
"S", # flake8-bandit — the security rules that are not noise
"A", # flake8-builtins — shadowing `id`, `type`, `list`
"DTZ", # flake8-datetimez — naive datetimes, which corrupt spend windows
"T20", # flake8-print — stray print() in a service
"LOG", # flake8-logging — logger misuse
"G", # flake8-logging-format — f-strings in log calls
"PIE", # flake8-pie
"RET", # flake8-return
"TID", # flake8-tidy-imports
"PTH", # flake8-use-pathlib
"ERA", # eradicate — commented-out code
"TRY", # tryceratops — swallowed and over-broad exception handling
]
ignore = [
"E501", # line length is the formatter's job
"E402", # module-level import not at top — the template's conditional imports
"B008", # call in a default argument — this is how FastAPI Depends works
"B905", # zip() without strict=
"RUF012", # mutable class attribute without ClassVar
"RUF059", # unpacked variable never used
"TRY003", # long message outside the exception class — our errors carry context
"TRY400", # logger.error vs logger.exception — both are used deliberately
"S101", # assert — used in tests, and allowed there by the per-file rules
# S105/S106 fire on any identifier containing "token", "password" or
# "secret". In an auth service that is `token_type="bearer"`, an enum member
# named TOKEN, and a help string called token_hint. The signal-to-noise
# ratio makes the rule unreadable rather than useful; real secret handling
# is covered by the vault tests, which assert no key reaches a response.
"S105",
"S106",
# Our own async surface is fully typed; ASYNC109/240 fire on the template's
# RAG pipeline, where a `timeout` parameter and pathlib are deliberate and
# rewriting them is a separate piece of work.
"ASYNC109",
"ASYNC240",
]
[tool.ruff.lint.per-file-ignores]
# Tests legitimately do things production code must not: assert, hardcoded
# credentials in fixtures, reaching into private helpers to pin a branch.
# Alembic and the repository-root guard scripts came under ruff in #229 (before
# it, both were outside the paths ruff was invoked on and these ignores were dead
# config). Autogenerated migrations carry a commented-out downgrade stub (ERA001)
# and raw SQL passed to `op.execute` (S608); the usual structural rules make a
# migration harder to read, not easier.
"alembic/**" = ["ERA001", "S608"]
# The repository-root `scripts/` are console guards run by `make lint` and a Stop
# hook: printing to stdout is their whole purpose (T201), and `docs_drift.py`
# shells out to `git` by name with a literal argv it builds itself (S603, S607) -
# the same partial-path invocation already accepted for `tests/test_migrations.py`.
"../scripts/**" = ["T201", "S603", "S607"]
# The load harness is a console tool with the same shape: it prints its progress
# and its report to stdout (T201), because a load run is watched while it runs and
# its output is redirected into a results file afterwards. It is not imported by
# the application - nothing under `app/` may import it - and it carries no gate.
"../loadtest/**" = ["T201"]
# Never run in the API: a sandbox runs its source with `python3 -c` and the
# computerd image copies it in, as the desktop's own account. Its answer is its
# output (T201), and it runs the desktop's `xdotool` and `exo-open` by name with
# an argv it builds itself, the entry path an argument (S603, S607).
"app/agents/capabilities/computer_use/_desktop_apps.py" = ["T201", "S603", "S607"]
# Postgres cannot parameterise an identifier, so a table name must be
# interpolated. `_validate_collection_name` rejects anything but
# [a-zA-Z0-9_] before it reaches a query, and every *value* is bound. This is
# the correct pattern, not an exception to it.
"app/services/rag/vectorstore.py" = ["S608"]
# The memory facts recall is the same shape: the interpolated pieces are the
# halfvec cast (built from an int dimension) and a fixed `IS NULL` / `= :scope`
# choice, and every value is bound. The vector column has no SQLAlchemy type, so
# this is the only way to run the KNN, not an exception to the bound-value rule.
"app/repositories/memory.py" = ["S608"]
# FastAPI exception handlers run while sys.exc_info() is still set, so
# logger.exception() captures the traceback correctly here despite not being
# lexically inside an `except` block.
"app/api/exception_handlers.py" = ["LOG004"]
# "id" shadows the builtin, but it is the registry's vocabulary and what agent
# specs store. Any other name would read worse at every call site.
"app/agents/capabilities/_registry.py" = ["A002"]
# Fixture helpers name their parameter `id` to mirror the model they stand in
# for; renaming it would make the fixtures read less like the thing they mock.
# Fixture and stub classes name their parameter `id` to mirror the model they
# stand in for; renaming it would make them read less like the thing they mock.
# Also relaxed here: the style rules whose whole value is readability in long
# production functions, which short test bodies do not have.
"tests/**" = [
"S105",
"S106",
# A default like `password="secret"` in a fixture helper is the fixture, not
# a leak. Real secret handling is asserted in tests/test_vault.py.
"S107",
"S311",
# tests/test_migrations.py shells out to alembic on purpose; the "untrusted
# input" is a literal argv this file wrote.
"S603",
# tests/test_ssrf.py names 0.0.0.0 because refusing to reach it is the
# behaviour under test.
"S104",
"A002",
"TRY300",
"RET504",
"PLR2004",
"SLF001",
]
# The RAG pipeline came with the generator. `filter` as a parameter name, md5
# as a cache key (not a security primitive) and two blocking reads inside
# third-party SDK calls are its debt, tracked rather than silently accepted:
# they move out of this list when that subsystem is rewritten.
"app/services/rag/retrieval.py" = ["S324"]
"app/services/file_upload.py" = ["TRY300", "PTH123", "PTH108"]
# Template-inherited subsystems. These are readability rules whose value is
# real but whose cost right now is churn in code we did not design and are not
# yet responsible for. They lift file by file as each subsystem is rewritten —
# the list shrinking is the progress marker.
"app/core/sanitize.py" = ["PTH119", "S107"]
"app/services/agent.py" = ["TRY300", "TRY301"]
"app/services/channels/telegram.py" = ["TRY300", "S107"]
"app/services/rag/ingestion.py" = ["TRY300", "PTH123"]
"app/services/rag/documents.py" = ["PTH123"]
"app/worker/tasks/rag_tasks.py" = ["TRY300"]
"app/clients/redis.py" = ["TRY300"]
"app/core/security.py" = ["TRY300"]
"app/commands/seed.py" = ["S311"]
# A polling loop that exits on a flag rather than an Event. Same tracked debt as
# the rest of the generator's channel code.
"app/services/channels/slack.py" = ["ASYNC110"]
# The CLI binds 0.0.0.0 because that is what a container needs, and its
# command bodies read fine as try/except/return.
"cli/**" = ["S104", "TRY300"]
[tool.ruff.lint.flake8-tidy-imports.banned-api]
# Annotations are evaluated lazily since Python 3.14 (PEP 649), so this import
# changes nothing but turns every annotation into a string - which a library
# reading annotations at runtime then has to resolve itself (#2229).
"__future__.annotations".msg = "annotations are lazy on Python 3.14+; drop the import"
[tool.ruff.lint.isort]
known-first-party = ["app", "cli"]
[tool.ty.terminal]
# The "warn" rules below are downgraded because the template's own, untyped-by-
# design dependencies trip them - not because a finding there is safe to ignore.
# `error-on-warning = true` means a warning gates the run exactly like an error
# does; the platform-layer override further down still promotes the same rules
# to `error` for our own code, so a violation there is the more severe
# annotation (red, not yellow) even though both now fail the run the same way.
# `false` here is what let 61 warning-level diagnostics exit 0 and look
# identical to a clean run - green check, no annotation, nothing distinguishing
# the two.
error-on-warning = true
[tool.ty.environment]
python-version = "3.15"
[tool.ty.src]
# `app/commands/` used to be here, which quietly took `app/commands/bootstrap.py`
# — a gated platform module — out of the checker altogether. An exclusion that
# hides one of the files the gate is supposed to cover is worth 23 warnings from
# the generator's other commands.
exclude = [
"alembic/",
"tests/",
"cli/",
]
[tool.ty.rules]
# FastAPI Depends() injects at runtime — default=None on typed params is intentional
invalid-parameter-default = "warn"
# Starlette/FastAPI handler signatures use Union types that ty flags
invalid-argument-type = "warn"
# ORM models with Optional fields trigger on attribute assignment
invalid-assignment = "warn"
# Callable[..., Any] doesn't expose __name__ but function objects do
unresolved-attribute = "warn"
# datetime.utcnow deprecation warnings
deprecated = "warn"
# Optional deps imported inside try/except (pymupdf, docx, faker) and
# feature-gated models referenced best-effort by the admin dashboard. These are
# expected to be absent in some configs, so don't surface them at all.
import-not-found = "ignore"
# dict() overload matching on dynamic types from validators
no-matching-overload = "warn"
# Template uses mypy-style `# type: ignore` comments which ty doesn't recognize
unused-type-ignore-comment = "warn"
# A `# ty: ignore` needed in one database/feature config can be unused in
# another generated from the same template — don't fail on that.
unused-ignore-comment = "ignore"
# Operations on Unknown types from untyped libraries (pymupdf, etc.)
unsupported-operator = "warn"
# redis-py / asyncio overloads return Awaitable[T] | T which ty rejects as awaitable
invalid-await = "warn"
# sessionmaker(class_=AsyncSession) returns sync sessionmaker per overloads
# but the resulting callable still produces an async context manager.
invalid-context-manager = "warn"
# Helpers like get_agent accept **kwargs but their stubs only list specific params
missing-argument = "warn"
# Every rule above is downgraded because of a library we did not choose and do
# not own. None of those reasons apply to the code we wrote, so the platform
# layer — everything AgenticOS adds on top of the generated template — gets them
# back as errors, and `ty check` can fail a build again.
#
# The list below is `[tool.coverage.run] include`, verbatim and in the same
# order. It is the same definition of "ours" for both gates on purpose: a module
# held to 100% coverage is held to the type checker too, and a module added to
# one list belongs in the other. `tests/test_coverage_gate.py` keeps the two
# from drifting.
[[tool.ty.overrides]]
include = [
"app/agents/**",
"app/commands/bootstrap.py",
"app/commands/refresh_assistant.py",
"app/commands/audit_skill_bindings.py",
"app/commands/audit_verify.py",
"app/commands/vault_rotate.py",
"app/commands/sandbox_runtimes.py",
"app/commands/invite_members.py",
"app/commands/data_protection.py",
"app/commands/mcp_audit.py",
"app/core/permissions.py",
"app/core/catalog/**",
"app/core/secret_kinds.py",
"app/core/tool_identity.py",
"app/core/vault.py",
# The audit trail, fail-closed and part of the caller's transaction. Every
# service that records a privileged action is already in this list; the trail
# they write to was not (#20).
"app/core/audit.py",
"app/core/background.py",
# The dedicated bounded pool for blocking file work, kept off the executor
# bcrypt and DNS share; its write is cancellation-safe (#1108).
"app/core/blocking.py",
# The LibreOffice conversion the RAG parser (and, later, chat) drives, owned
# here so a hung `soffice` is killed on timeout rather than orphaned (#1685).
"app/core/office_convert.py",
# The SSRF allow/deny every outbound URL a tenant chooses is checked against -
# a webhook, an MCP server address. A hole here is a request to an internal
# address the operator cannot see, which is the pair to pinned_http below (#28).
"app/core/sanitize.py",
# The bounded opening of a skill or context body that a listing card peeks at.
"app/core/excerpt.py",
# The client that dials the address it validated, for a URL the party being
# validated chose. A miss here is an SSRF the operator cannot see.
"app/core/pinned_http.py",
# Its first sync connector caller: a crawl whose every link is chosen by the
# site it reads, and the page text that decides what a sync re-embeds (#984).
"app/services/rag/connectors/web.py",
"app/services/rag/web_page.py",
# Which host is sent the Entra app's secret, its Graph token or neither.
"app/services/rag/connectors/sharepoint.py",
# The query strings a knowledge search runs, and the one place an expansion
# failure is allowed to fall back to the plain query (#1649).
"app/services/rag/query_analysis.py",
# The one size limit that runs before a body is read. Every other one in this
# codebase measures bytes that have already arrived.
"app/core/body_limit.py",
# The worker's judgement of its own event loop. It decides to `SIGKILL` the
# process it runs in, on the two stacks that have no supervisor reading a
# beat from outside - so a wrong verdict is a restart loop in production.
"app/core/watchdog.py",
# What a refusal is allowed to say about the field it refuses. One shape on
# the wire and one place that builds it - the module reads `loc` and `msg`
# and nothing else, which is what keeps a caller's own submission out of a
# response body a fifth call site would otherwise hand it (#882).
"app/core/field_errors.py",
"app/core/phone.py",
# Whose memory a row is - the three owner-key shapes, and the one place they
# are built and read. A leaf module because everything from the models to the
# capability needs it and every other home is an import cycle (#788).
"app/core/memory_keys.py",
# Which tables alembic owns and which the vector store makes at runtime. It is
# two statements, and one of them decides whether `db-check` reports real drift.
"app/db/vector_tables.py",
# Which explicit `null` on an update may reach a row. It is one function and
# it stands between a legal request and a 500 naming a database constraint
# (#637), which is the kind of thing a branch that never runs is exactly how
# it comes back.
"app/db/updates.py",
"app/services/access.py",
# How often a stranger may reach a public surface. It was configured and
# never applied for the whole life of the product (#39), which is the
# argument for holding the replacement to the bar every other refusal is
# held to rather than reporting it.
"app/services/rate_limit.py",
"app/services/agent_chat.py",
"app/services/follow_ups.py",
"app/services/mentions.py",
"app/services/agent_environment.py",
"app/services/agent_exposure.py",
# What the model is actually told about each tool, read off the built
# toolset rather than the catalog's hand-typed one-liner - the Toolbox
# panel and the exposure form's channel-lookups list both read it (#1473).
"app/services/capability_contracts.py",
"app/services/agent_trigger.py",
"app/services/trigger_events.py",
"app/services/trigger_dedupe.py",
"app/services/trigger_templates.py",
"app/schemas/trigger_template.py",
"app/services/agent_registry.py",
"app/services/agent_runner.py",
"app/services/agent_templates.py",
"app/services/agent_defaults.py",
"app/services/shipped_agents.py",
"app/services/agent_session.py",
# The turn loop itself, shared by the dashboard chat and the embedded
# widget since #634. One frame vocabulary for both sockets, and the sink a
# public surface filters at - so a toggle that fails open sends an agent's
# reasoning to a stranger.
"app/services/run_stream.py",
"app/services/run_reaper.py",
"app/services/run_heartbeat.py",
"app/repositories/agent_workspace.py",
"app/repositories/local_service.py",
"app/repositories/sandbox_connection.py",
"app/repositories/skill_proposal.py",
"app/services/attachments.py",
# The one subprocess in the chat attachment feature (#1591): a managed,
# killable `soffice` for DOC. Every branch is a refusal or a teardown - a
# timeout, a cancellation, a nonzero exit, an oversized output - so it is held
# to the same bar as the other security-sensitive services here.
"app/services/office_convert.py",
"app/services/chat_timeline.py",
"app/services/local_service.py",
"app/services/sandbox_connection.py",
"app/services/sandbox_runtimes.py",
"app/services/sandbox_workspace.py",
"app/services/sandboxd_compat.py",
# A `.zip` or `.tar.gz` unpacked into a workspace, and a folder zipped out
# (#2479). Every refusal here - a `../` member, a link, a device, a bomb - is
# what stops an upload from writing outside its folder.
"app/services/workspace_archive.py",
"app/services/usage_report.py",
"app/services/skill_proposal.py",
"app/services/skill_workspace.py",
"app/services/channels/attachments.py",
"app/services/channels/sms_media.py",
"app/services/channels/dedupe.py",
"app/services/channels/stream_lease.py",
"app/services/channels/feedback.py",
"app/services/channels/membership.py",
"app/services/channels/mentions.py",
# Approvals and questions offered as buttons in a chat, and the Slack app
# around the bot (#2064, #2067, #2068).
"app/services/channels/prompts.py",
# Next steps offered as buttons, a press of which is the next message (#2301).
"app/services/channels/next_steps.py",
"app/services/channels/notification_send.py",
"app/services/channels/handback.py",
"app/services/channels/continuation.py",
"app/services/parked_runs.py",
"app/services/channels/slack_answer.py",
"app/services/channels/slack_app.py",
"app/services/channels/slack_manifest.py",
"app/services/channels/telegram_format.py",
"app/services/channels/telegram_press.py",
# What the Discord adapter says and reads, without the I/O (#2079).
"app/services/channels/discord_protocol.py",
"app/repositories/channel_prompt.py",
"app/services/collection_access.py",
"app/repositories/collection_teardown.py",
"app/repositories/pending_sandbox_destroy.py",
"app/services/embed_session.py",
"app/services/embed_voice_js.py",
"app/services/embedding_resolution.py",
"app/services/approvals.py",
"app/services/audit.py",
"app/services/impersonation.py",
"app/services/ws_auth.py",
"app/services/health.py",
# The job queue that replaced Prefect (#2219). The claim spends an attempt
# before the job runs and the worker never hot-loops, so a job that kills
# its process cannot loop and a database that is down cannot spin it.
"app/worker/*.py",
"app/worker/tasks/background_tasks.py",
"app/repositories/background_job.py",
"app/repositories/background_schedule.py",
"app/repositories/background_task_kind.py",
"app/repositories/background_worker.py",
"app/services/background_jobs.py",
"app/services/api_runs.py",
"app/api/routes/v1/admin_background_jobs.py",
"app/api/routes/v1/run_requests.py",
"app/services/email/templates.py",
"app/services/ingestion_config.py",
"app/services/rag/failures.py",
"app/services/rag_scratch.py",
# Who hears that a run stopped on its budget or parked on an approval. The
# emails are the only signal for a run nobody was watching — a Slack
# mention, a schedule — so a silent failure here is indistinguishable from
# an agent that simply had nothing to say.
"app/services/notifications.py",
"app/services/notification_catalog.py",
"app/services/notification_center.py",
"app/services/notification_delivery.py",
"app/services/notification_routes.py",
"app/repositories/notification_route.py",
"app/schemas/notification_route.py",
"app/repositories/notification.py",
"app/api/routes/v1/notifications.py",
# The one action in this feature no organization-scoped role can reach.
"app/services/announcement.py",
"app/repositories/announcement.py",
"app/api/routes/v1/admin_announcements.py",
"app/services/mcp_catalog.py",
"app/services/portal_catalog.py",
"app/services/portals/__init__.py",
"app/services/portals/base.py",
"app/services/portals/exceptions.py",
"app/services/portals/github.py",
"app/services/portals/github_app.py",
"app/services/portals/_api.py",
"app/services/portals/_calendar.py",
"app/services/portals/_files.py",
"app/services/portals/github_oauth.py",
"app/services/portals/google_calendar.py",
"app/services/portals/google_drive.py",
"app/services/portals/mail_auth.py",
"app/services/portals/reply_metadata.py",
"app/services/reply_expectation.py",
"app/services/portals/mail_thread.py",
"app/services/portals/microsoft_files.py",
"app/services/portals/outlook_calendar.py",
"app/services/portals/registry.py",
"app/services/mcp_connection.py",
"app/services/mirage_mounts.py",
# Which providers can draw an image, derived from the SDK. What it decides is
# what the Builder offers, and a probe that quietly starts answering "no"
# would empty that picker without a single test failing.
"app/services/decision_models.py",
"app/services/image_models.py",
"app/services/model_profile.py",
# Whether an agent can run on a model. A classification that quietly widens
# hides chat models from every picker; one that narrows lets a speech model
# through publish (#2487).
"app/services/model_outputs.py",
"app/services/organization_secret.py",
"app/services/openai_codex.py",
# Same shape and the same reason as `image_models`: what a picker offers is
# decided by filtering a data file against what the platform can actually
# reach, and an entry that quietly stops being offered - or starts being
# offered and fails on the first voice note - is invisible without a test.
"app/services/mcp_listing.py",
"app/services/mcp_registry.py",
"app/services/mcp_audit.py",
"app/services/speech_to_text.py",
"app/services/speech_to_text_settings.py",
"app/services/realtime_models.py",
"app/services/provider_keys.py",
"app/repositories/voice_call.py",
"app/services/voice/**",
"app/api/routes/v1/voice.py",
"app/worker/tasks/voice_tasks.py",
"app/db/models/voice_call.py",
"app/schemas/voice.py",
"app/services/transcription.py",
"app/services/text_to_speech_models.py",
"app/services/text_to_speech/__init__.py",
"app/services/text_to_speech/clients.py",
"app/services/text_to_speech/exceptions.py",
"app/services/text_to_speech/synthesis.py",
"app/services/video_models.py",
"app/services/video_generation/__init__.py",
"app/services/video_generation/clients.py",
"app/services/video_generation/exceptions.py",
"app/services/video_generation/generation.py",
"app/services/provider_error_code.py",
"app/services/transcription_usage.py",
# The rest of `app/services/rag/` came with the template and is reported
# rather than gated. This module is ours and is only refusals - where a
# remote file may be written, and what may reach a Drive query - so the
# branch that is never taken in normal operation is the whole point of it.
"app/services/rag/remote_names.py",
# The Drive REST client that replaced Google's discovery SDK: written here,
# so held to the bar.
"app/services/rag/drive_api.py",
# What a chunk is, and therefore what a search can find. Written here rather
# than imported (#158), and the rest of the pipeline can only retrieve what
# this module decided to store - so it is held to the bar the code it
# replaced never was.
"app/services/rag/_splitters.py",
# The retrieval filter contract (FA-039): the narrowing-only business
# filters, the server-trusted tenant/authorization scope, and their
# composition. Trust-boundary validation, gated where the rest of the RAG
# pipeline is only reported.
"app/services/rag/filters.py",
"app/services/rag/overlay.py",
"app/services/rag/parent_context.py",
# Whether a citation may name a page: a Markdown file was cited as "page 1" (#2489).
"app/services/rag/citation.py",
"app/services/sharing.py",
"app/services/skills.py",
"app/services/exporting.py",
"app/services/run_export.py",
"app/services/spend.py",
"app/services/stats.py",
"app/services/transcript.py",
"app/db/models/agent_environment.py",
"app/schemas/agent_environment.py",
"app/repositories/agent_environment.py",
"app/repositories/agent_exposure.py",
"app/repositories/agent_trigger.py",
"app/repositories/ingestion_spend.py",
"app/repositories/mcp_connection.py",
"app/repositories/mcp_server_check.py",
"app/repositories/organization_secret.py",
"app/repositories/_visibility.py",
"app/repositories/resource_grant.py",
"app/api/routes/v1/_sharing_routes.py",
"app/api/routes/v1/_sharing_loaders.py",
"app/api/routes/v1/secrets.py",
"app/api/routes/v1/agent_environments.py",
"app/api/routes/v1/agent_exposures.py",
"app/api/routes/v1/agent_triggers.py",
"app/api/routes/v1/trigger_webhooks.py",
"app/api/routes/v1/local_services.py",
"app/api/routes/v1/sandbox_connections.py",
"app/api/routes/v1/skill_changes.py",
"app/api/routes/v1/workspaces.py",
"app/api/routes/v1/_workspace_bytes.py",
"app/api/routes/v1/sharing.py",
"app/api/routes/v1/stats.py",
"app/repositories/run_manifest.py",
"app/db/models/intercepted_tool_call.py",
"app/repositories/intercepted_tool_call.py",
"app/repositories/dashboard_layout.py",
"app/services/dashboard_layout.py",
"app/schemas/onboarding.py",
"app/repositories/onboarding.py",
"app/services/onboarding.py",
"app/api/routes/v1/me_onboarding.py",
"app/api/routes/v1/me_dashboard_layout.py",
"app/repositories/dashboard_preset.py",
"app/services/dashboard_preset.py",
"app/repositories/context.py",
"app/services/context.py",
"app/repositories/project.py",
"app/services/project.py",
"app/services/project_workspace_guard.py",
"app/services/project_workspace_transfer.py",
"app/worker/tasks/project_tasks.py",
"app/api/routes/v1/projects.py",
"app/api/routes/v1/context.py",
"app/db/models/dataset.py",
"app/schemas/dataset.py",
"app/schemas/dataset_helpers.py",
"app/repositories/dataset.py",
"app/repositories/_visibility.py",
"app/services/dataset.py",
"app/services/dataset_files.py",
"app/api/routes/v1/datasets.py",
"app/db/models/knowledge_gap.py",
"app/schemas/knowledge_gap.py",
"app/repositories/knowledge_gap.py",
"app/services/knowledge_gaps.py",
"app/api/routes/v1/knowledge_gaps.py",
"app/services/system_agents.py",
"app/services/system_agent_runner.py",
"app/db/models/case_score.py",
"app/schemas/scoring.py",
"app/repositories/case_score.py",
"app/services/scorers.py",
"app/services/scoring.py",
"app/db/models/case_generation.py",
"app/schemas/case_generation.py",
"app/repositories/case_generation.py",
"app/services/case_generation/__init__.py",
"app/services/case_generation/facade.py",
"app/services/case_generation/sources.py",
"app/services/case_generation/drafts.py",
"app/services/case_generation/prompt.py",
"app/services/case_generation/estimate.py",
"app/services/case_generation/generation.py",
"app/services/case_generation/worker.py",
"app/api/routes/v1/dataset_generations.py",
"app/schemas/agent_test_set.py",
"app/services/agent_test_set.py",
"app/api/routes/v1/agent_test_sets.py",
"app/db/models/artifact.py",
"app/db/models/artifact_share_request.py",
"app/schemas/artifact.py",
"app/schemas/artifact_share_request.py",
"app/repositories/artifact.py",
"app/repositories/artifact_share_request.py",
"app/services/artifact.py",
"app/services/artifact_actions.py",
"app/services/artifact_bindings.py",
"app/services/artifact_log_bindings.py",
"app/services/artifact_consent.py",
"app/services/artifact_diff.py",
"app/services/artifact_lint.py",
"app/services/artifact_ownership.py",
"app/services/artifact_public_data.py",
"app/services/artifact_request_service.py",
"app/services/artifact_requests.py",
"app/services/artifact_render.py",
"app/api/routes/v1/_artifact_bytes.py",
"app/api/routes/v1/artifacts.py",
"app/db/models/visual.py",
"app/db/models/pending_sandbox_destroy.py",
"app/schemas/visual.py",
"app/repositories/visual.py",
"app/services/visual.py",
"app/services/visual_render.py",
"app/core/map_tiles.py",
"app/services/tile_proxy.py",
"app/api/routes/v1/visuals.py",
"app/db/models/api_key.py",
"app/schemas/api_key.py",
"app/repositories/api_key.py",
"app/services/api_key.py",
"app/api/routes/v1/api_keys.py",
"app/db/models/oauth.py",
"app/schemas/oauth_server.py",
"app/repositories/oauth.py",
"app/services/oauth_server.py",
# A phone signed in from a QR the console shows: single-use, a minute, and the
# session opened only when the phone redeems it (#2097).
"app/services/device_pairing.py",
# Web Push to the browsers a person subscribed: the deployment's own signing
# key, made on first use, and a push channel through the delivery outbox.
"app/services/web_push.py",
"app/repositories/push_subscription.py",
"app/schemas/push.py",
"app/api/routes/v1/push.py",
"app/services/apns.py",
"app/services/push.py",
"app/repositories/apns_device.py",
"app/api/routes/v1/mcp_oauth.py",
"app/api/public_api.py",
"app/api/routes/v1/public_openapi.py",
"app/services/platform_mcp/__init__.py",
"app/services/platform_mcp/_api.py",
"app/services/platform_mcp/_oauth.py",
"app/services/platform_mcp/_tools.py",
"app/schemas/change_event.py",
"app/services/change_feed.py",
"app/api/routes/v1/change_events.py",
"app/services/device_hub/__init__.py",
"app/services/device_hub/_hub.py",
"app/services/device_hub/_protocol.py",
"app/services/device_hub/workspace.py",
"app/services/device_hub/services.py",
"app/services/device_hub/local_context.py",
"app/services/device_hub/exceptions.py",
"app/api/routes/v1/device_hub.py",
"app/db/models/organization_assistant.py",
"app/repositories/organization_assistant.py",
"app/schemas/assistant.py",
"app/services/assistant.py",
"app/api/routes/v1/assistant.py",
"app/schemas/resource_usage.py",
"app/services/resource_usage.py",
"app/services/knowledge_reach.py",
"app/repositories/memory.py",
"app/repositories/memory_claim.py",
"app/repositories/system_agent.py",
"app/repositories/outcome.py",
"app/services/memory/_native.py",
"app/services/memory/_claims.py",
"app/services/memory/_runtime.py",
"app/services/memory/extraction.py",
"app/services/memory/facade.py",
"app/services/primary_conversation.py",
"app/services/outcome.py",
"app/services/system_agent.py",
"app/services/routine_authority.py",
"app/services/routine_devices.py",
"app/services/routine_health.py",
"app/repositories/routine_health.py",
"app/db/models/routine_health.py",
"app/services/imessage_webhook.py",
"app/api/routes/v1/imessage_webhook.py",
"app/repositories/routine_device_wait.py",
"app/db/models/routine_device_wait.py",
"app/schemas/local_target.py",
"app/agents/routine_authority.py",
"app/agents/routine_actions.py",
"app/db/models/task_board.py",
"app/repositories/task.py",
"app/schemas/task.py",
"app/services/tasks.py",
"app/services/task_access.py",
"app/services/task_columns.py",
"app/services/task_execution.py",
"app/api/routes/v1/tasks.py",
"app/worker/tasks/task_tasks.py",
"app/services/project_tracking.py",
"app/schemas/project_tracking.py",
"app/schemas/decision_episode.py",
"app/schemas/telephony.py",
"app/db/models/telephony.py",
"app/repositories/telephony.py",
"app/services/telephony/*.py",
"app/agents/capabilities/phone_calls/*.py",
"app/repositories/project_tracking.py",
"app/db/models/project_tracking.py",
"app/services/routine_sources.py",
"app/schemas/routine_sources.py",
"app/services/routine_actions.py",
"app/schemas/routine_actions.py",
"app/services/routine_delivery.py",
"app/repositories/routine_delivery.py",
"app/api/routes/v1/memory.py",
"app/db/models/eval_run.py",
"app/repositories/eval_run.py",
"app/services/eval_comparison.py",
"app/schemas/eval_threshold.py",
"app/schemas/eval_run.py",
"app/services/eval_run.py",
"app/services/eval_run_worker.py",
"app/api/routes/v1/eval_runs.py",
"app/core/cron.py",
"app/db/models/eval_suite.py",
"app/repositories/eval_suite.py",
"app/schemas/eval_suite.py",
"app/services/eval_suite_threshold.py",
"app/services/eval_suite.py",
"app/api/routes/v1/eval_suites.py",
"app/db/models/promotion_gate.py",
"app/repositories/promotion_gate.py",
"app/schemas/promotion_gate.py",
"app/services/promotion_gate.py",
# The authoring assistant: what it reads, what it proposes and how a person
# decides. Its evidence reads are the boundary that keeps it blind to evals.
"app/db/models/authoring.py",
"app/repositories/authoring.py",
"app/schemas/authoring.py",
"app/services/authoring_evidence.py",
"app/services/authoring_hunks.py",
"app/services/authoring_fields.py",
"app/services/authoring_config.py",
"app/services/authoring.py",
"app/services/authoring_worker.py",
"app/api/routes/v1/authoring.py",
"app/worker/tasks/authoring_tasks.py",
"app/worker/tasks/case_generation_tasks.py",
# Improvement sessions: the Improve pipeline run in the background up to a
# checked proposal.
"app/db/models/improvement.py",
"app/repositories/improvement.py",
"app/services/improvement_states.py",
"app/services/improvement_data.py",
"app/schemas/improvement.py",
"app/services/improvement_pipeline.py",
"app/api/routes/v1/improvements.py",
"app/worker/tasks/improvement_tasks.py",
"app/db/models/context_proposal.py",
"app/repositories/context_proposal.py",
"app/schemas/context_proposal.py",
"app/services/context_proposal.py",
"app/services/proposal_reach.py",
"app/api/routes/v1/context_changes.py",
"app/api/routes/v1/primary_conversations.py",
"app/api/routes/v1/outcomes.py",
"app/api/routes/v1/system_agents.py",
"app/worker/tasks/memory_tasks.py",
# Which past conversations an agent may search, and the full-text query over
# them. The predicate is the whole of it - a way in added here is a way in for
# every agent at once - so it is held to the same bar as the permission code
# (#789).
"app/repositories/conversation_search.py",
"app/services/conversation_search.py",
"app/db/models/conversation_task.py",
"app/schemas/conversation_task.py",
"app/repositories/conversation_task.py",
"app/services/conversation_tasks.py",
# What a coding-agent task changed, read under the conversation's own reader
# check (#2480).
"app/db/models/coding_task_change.py",
"app/schemas/coding_task_change.py",
"app/repositories/coding_task_change.py",
"app/services/coding_task_changes.py",
"app/db/models/coding_account.py",
"app/schemas/coding_account.py",
"app/repositories/coding_account.py",
"app/services/coding_accounts.py",
"app/services/personal_codex.py",
"app/services/coding_account_lease.py",
"app/services/personal_coding_snapshot.py",
"app/services/personal_coding.py",
"app/services/conversation_turns.py",
"app/worker/tasks/conversation_tasks.py",
"app/db/models/generated_media.py",
"app/repositories/generated_media.py",
"app/services/generated_media.py",
"app/api/routes/v1/generated_media.py",
"app/services/embed_generated_media.py",
"app/api/routes/v1/embed_generated_media.py",
"app/worker/tasks/generated_media_tasks.py",
# What this deployment calls itself, who may register, and whether it is open
# at all. The identity half is cosmetic; the other two are refusals - a sign-up
# policy applied on the only unauthenticated paths that mint an account, and a
# gate that decides whether the API answers at all - so a branch that never
# runs in normal operation is the whole point of them.
"app/db/models/deployment_settings.py",
"app/core/update_gate.py",
"app/schemas/updates.py",
"app/services/updates.py",
"app/repositories/diagnostics.py",
"app/schemas/diagnostics.py",
"app/services/diagnostics/*",
"app/worker/tasks/diagnostics_tasks.py",
"app/repositories/deployment_settings.py",
"app/schemas/deployment_settings.py",
"app/services/deployment_settings.py",
"app/services/signup_policy.py",
"app/core/maintenance.py",
"app/api/routes/v1/branding.py",
"app/api/routes/v1/_branding_bytes.py",
"app/api/routes/v1/_chat_file_bytes.py",
"app/api/routes/v1/admin_deployment_settings.py",
"app/db/locks.py",
"app/services/invitation_admission.py",
# One unguarded attribute in a dependency made every wrong-method request a 500
# on every route. Gated because it is a refusal path nobody exercises on purpose
# and because it goes away the moment upstream guards it.
"app/core/otel_compat.py",
# Whether a running deployment matches a security profile. A control that
# passed on a deployment that does not satisfy it is a sheet somebody puts in
# front of an auditor, so every branch of every control is held to the gate
# (#1448).
"app/services/deployment_profile.py",
# How long each class of data lives here, who may say so, and the sweep that
# applies it. A number that resolves wrongly either deletes what a deployment
# is obliged to keep or keeps what it is obliged to delete, and the sweep is
# the one code path in the platform whose whole purpose is a hard delete - so
# every branch of the precedence, every class, and every refusal is held to
# the gate (#1420).
"app/core/retention.py",
"app/repositories/retention.py",
"app/services/retention.py",
# Where `HTTPException` joins the one error envelope. It answers a 405, an
# unmatched path, and twenty-two direct raises, none of which a happy path sees.
"app/api/exception_handlers.py",
# The standalone ML services (FA-069). Held here because the surface is one
# another Urban Stack component calls with a key and no person watching: the
# refusals are the contract, the tenant scoping on a call record is what keeps
# one integrator's usage out of another's, and a branch nobody exercises on
# purpose is exactly the one an unattended caller finds.
"app/services/ml/catalog.py",
"app/services/ml/pii.py",
"app/services/ml/parsing.py",
"app/services/ml/facade.py",
"app/services/ml/audio_duration.py",
"app/services/ml/pcm_wav.py",
"app/db/models/ml_service_call.py",
"app/repositories/ml_service_call.py",
"app/schemas/ml.py",
"app/api/routes/v1/ml.py",
# Typed records shared by the console, agent tools, workflow nodes and the
# public API. Every branch is a refusal - a stale revision, a reused
# operation key, another tenant's table, an archived column - and a write path
# that skipped one would overwrite or leak a tenant's rows silently (#1782).
"app/db/models/virtual_table.py",
"app/repositories/virtual_table.py",
"app/schemas/virtual_table.py",
"app/services/virtual_tables/**",
"app/api/routes/v1/virtual_tables.py",
"app/api/routes/v1/_table_responses.py",
"app/api/routes/v1/_path_convertors.py",
# Groups, directory group mappings, and signing in with a directory (#1773).
# Authentication and membership: what they refuse is the feature.
"app/db/models/group.py",
"app/db/models/directory_mapping.py",
"app/repositories/group.py",
"app/repositories/directory_mapping.py",
"app/schemas/group.py",
"app/schemas/directory.py",
"app/services/group.py",
"app/services/group_sharing.py",
"app/services/group_spend.py",
"app/services/group_quality.py",
"app/services/directory/**",
"app/api/routes/v1/groups.py",
"app/api/routes/v1/directory_mappings.py",
"app/api/routes/v1/directory_auth.py",
"app/api/routes/v1/_directory_handoff.py",
# Discord server roles mapped to groups, and the sync that applies them (#2569).
"app/db/models/discord_role_mapping.py",
"app/repositories/discord_role_mapping.py",
"app/schemas/discord_role_mapping.py",
"app/services/discord_role_mapping.py",
"app/services/channels/discord_roles.py",
"app/worker/tasks/channel_tasks.py",
# Node contracts, registry and graph validation (#1786). Mirrors the
# capability registry's split: typed definitions in code, a graph checked
# against them before it can be published.
"app/db/models/workflow.py",
"app/repositories/workflow.py",
"app/schemas/workflow.py",
"app/services/workflow_registry.py",
"app/schemas/workflow_portable.py",
"app/services/workflow_portable.py",
"app/services/workflow_agents.py",
"app/services/workflow_execution/agent_conversations.py",
"app/workflows/**",
"app/api/routes/v1/workflows.py",
# Durable execution: run/node/attempt state machine, the dispatch outbox,
# the reconciler and the event stream (#1788).
"app/db/models/workflow_run.py",
"app/repositories/workflow_run.py",
"app/schemas/workflow_run.py",
"app/services/workflow_execution/**",
"app/worker/tasks/workflow_tasks.py",
"app/api/routes/v1/workflow_runs.py",
# What a workflow's real runs did over a window, for its runs page.
"app/schemas/workflow_stats.py",
"app/services/workflow_stats.py",
# One text-similarity scorer: text.compare, approval edit ratios and evals.
"app/services/text_similarity.py",
# A model step's evals: its cases, and the test runs that tried them.
"app/db/models/workflow_eval.py",
"app/repositories/workflow_eval.py",
"app/schemas/workflow_eval.py",
"app/services/workflow_evals.py",
"app/api/routes/v1/workflow_evals.py",
# The ways in to a workflow (#1792): its webhooks and schedules, the
# workflow-run WebSocket and the chat's door through it, and the publish
# that switches its trigger node on.
"app/db/models/workflow_exposure.py",
"app/repositories/workflow_exposure.py",
"app/schemas/workflow_exposure.py",
"app/services/workflow_exposure.py",
"app/services/workflow_webhook_test.py",
"app/services/workflow_triggers.py",
"app/services/workflow_authority.py",
"app/services/workflow_run_socket.py",
"app/api/routes/v1/workflow_exposures.py",
"app/api/routes/v1/workflow_run_socket.py",
# A run's files (#1791): what a `FileRef` resolves to, and the one lookup
# every file step reads through.
"app/db/models/workflow_file.py",
"app/repositories/workflow_file.py",
# What a `human.approval` step asks a person, and the queue deciding it.
"app/db/models/workflow_approval.py",
"app/repositories/workflow_approval.py",
"app/schemas/workflow_approval.py",
"app/api/routes/v1/workflow_approvals.py",
# A workflow's own log: the lines a step's Quick Log writes as it settles.
"app/db/models/workflow_log_entry.py",
"app/repositories/workflow_log_entry.py",
"app/schemas/workflow_log_entry.py",
# Alert rules on that log, and the worker's count of each over its window.
"app/db/models/workflow_log_alert.py",
"app/repositories/workflow_log_alert.py",
"app/schemas/workflow_log_alert.py",
"app/services/workflow_log_alerts.py",
"app/api/routes/v1/workflow_log_alerts.py",
# A table's triggers (#1785): the workflows a record added to it starts,
# and every refusal between the two - before activation, a filter miss, a
# cycle, a revoked principal - is a run that must not start.
"app/db/models/virtual_table_trigger.py",
"app/repositories/virtual_table_trigger.py",
"app/schemas/virtual_table_trigger.py",
"app/api/routes/v1/virtual_table_triggers.py",
# A saved table/kanban/list view (#1783): a console-only sub-resource of a
# table, sharing its service package but with its own model, repo, schema and
# route file. Every branch here is also a refusal - a private view another
# member owns, a rename that collides, a non-owner without `tables:edit` `ALL`.
"app/db/models/table_view.py",
"app/repositories/table_view.py",
"app/schemas/table_view.py",
"app/api/routes/v1/table_views.py",
"app/services/live_browse.py",
"app/services/live_turn.py",
"app/services/chat_room.py",
"app/services/redis_view.py",
"app/services/browse_plans.py",
"app/api/routes/v1/browse_live.py",
"app/db/models/browser_sign_in.py",
"app/repositories/browser_sign_in.py",
"app/schemas/browser_sign_in.py",
"app/services/browser_sign_in.py",
"app/services/browser_sign_in_session.py",
"app/api/routes/v1/browser_sign_ins.py",
# What the platform does for people on its own initiative, all of it in
# background jobs, and the suggestions it leaves them (#2301).
"app/services/proactive/**",
"app/services/suggestions.py",
"app/services/role_packs.py",
"app/repositories/suggestion.py",
"app/api/routes/v1/me_suggestions.py",
"app/worker/tasks/proactive_tasks.py",
# Watches over a person's own connections, and the presets they start from.
"app/services/watches.py",
"app/services/watch_sources.py",
"app/services/routine_notifications.py",
"app/schemas/routine_notifications.py",
"app/schemas/watch_sources.py",
"app/services/watch_presets.py",
"app/repositories/watch.py",
"app/api/routes/v1/me_watches.py",
]
# Mirrors `[tool.coverage.run] omit` - one definition of "ours", asserted equal
# by tests/test_coverage_gate.py.
exclude = [
"app/agents/mcp_oauth.py",
]
[tool.ty.overrides.rules]
invalid-parameter-default = "error"
invalid-argument-type = "error"
invalid-assignment = "error"
unresolved-attribute = "error"
deprecated = "error"
no-matching-overload = "error"
unused-type-ignore-comment = "error"
unsupported-operator = "error"
invalid-await = "error"
invalid-context-manager = "error"
missing-argument = "error"
# An ignore comment that has stopped suppressing anything is the failure mode
# this whole section exists to prevent, one line wide. In our own code it is an
# error, so a fixed stub or a rewritten call forces the comment out with it.
unused-ignore-comment = "error"
# The global "ignore" is for the template's optional parsers (pymupdf, docx,
# faker), which are genuinely absent in some configurations. Nothing the
# platform layer imports is optional, so here a name that does not resolve is a
# typo or a dependency somebody dropped, and both should stop the build.
import-not-found = "error"
[tool.pytest.ini_options]
testpaths = ["tests"]
# `--strict-markers` turns a marker that is not registered below into an error
# rather than a warning, so a misspelled marker name fails the run instead of
# silently dropping the test from `-m security`.
addopts = "--strict-markers"
# Dump blocked thread stacks while a stalled CI worker can still report them.
faulthandler_timeout = 180
markers = [
"security: a refusal or security-boundary test. Collected as a set so the security report can show the refusals rather than assert them (#1417) — tenant isolation, permission and grant refusals, a budget checked before the model call, an approval decided once, secret confidentiality, spec publish validation, channel sender identity, constant-time credential compare, webhook HMAC and rate limits. It is a label for the report, not a second gate: `make check` still runs everything.",
]
[tool.fastapi-fullstack]
generator_version = "0.2.17"
generated_at = "2026-07-26T18:01:04.489757+00:00"
[tool.coverage.run]
# `source` must be packages or directories — coverage silently ignores a file
# path here, which is how a whole module can sit in this list and never be
# measured. The platform layer is therefore selected with `include` globs,
# which do accept files, and CI asserts the measured set matches.
# `include` rather than `source`: `source` accepts only packages and
# directories, and silently ignores a file path — which is how a module can sit
# in this list and never be measured at all. `include` does accept files.
#
# The trade-off is that `include` only reports files that were imported, so a
# platform module nobody imports would drop out just as quietly. That is what
# tests/test_coverage_gate.py exists to catch.
include = [
"app/agents/**",
"app/commands/bootstrap.py",
"app/commands/refresh_assistant.py",
"app/commands/audit_skill_bindings.py",
"app/commands/audit_verify.py",
"app/commands/vault_rotate.py",
"app/commands/sandbox_runtimes.py",
"app/commands/invite_members.py",
"app/commands/data_protection.py",
"app/commands/mcp_audit.py",
"app/core/permissions.py",
"app/core/catalog/**",
"app/core/secret_kinds.py",
"app/core/tool_identity.py",
"app/core/vault.py",
# The audit trail, fail-closed and part of the caller's transaction. Every
# service that records a privileged action is already in this list; the trail
# they write to was not (#20).
"app/core/audit.py",
"app/core/background.py",
# The dedicated bounded pool for blocking file work, kept off the executor
# bcrypt and DNS share; its write is cancellation-safe (#1108).
"app/core/blocking.py",
# The LibreOffice conversion the RAG parser (and, later, chat) drives, owned
# here so a hung `soffice` is killed on timeout rather than orphaned (#1685).
"app/core/office_convert.py",
# The SSRF allow/deny every outbound URL a tenant chooses is checked against -
# a webhook, an MCP server address. A hole here is a request to an internal
# address the operator cannot see, which is the pair to pinned_http below (#28).
"app/core/sanitize.py",
# The bounded opening of a skill or context body that a listing card peeks at.
"app/core/excerpt.py",
# The client that dials the address it validated, for a URL the party being
# validated chose. A miss here is an SSRF the operator cannot see.
"app/core/pinned_http.py",
# Its first sync connector caller: a crawl whose every link is chosen by the
# site it reads, and the page text that decides what a sync re-embeds (#984).
"app/services/rag/connectors/web.py",
"app/services/rag/web_page.py",
# Which host is sent the Entra app's secret, its Graph token or neither.
"app/services/rag/connectors/sharepoint.py",
# The query strings a knowledge search runs, and the one place an expansion
# failure is allowed to fall back to the plain query (#1649).
"app/services/rag/query_analysis.py",
# The one size limit that runs before a body is read. Every other one in this
# codebase measures bytes that have already arrived.
"app/core/body_limit.py",
# The worker's judgement of its own event loop. It decides to `SIGKILL` the
# process it runs in, on the two stacks that have no supervisor reading a
# beat from outside - so a wrong verdict is a restart loop in production.
"app/core/watchdog.py",
# What a refusal is allowed to say about the field it refuses. One shape on
# the wire and one place that builds it - the module reads `loc` and `msg`
# and nothing else, which is what keeps a caller's own submission out of a
# response body a fifth call site would otherwise hand it (#882).
"app/core/field_errors.py",
"app/core/phone.py",
# Whose memory a row is - the three owner-key shapes, and the one place they
# are built and read. A leaf module because everything from the models to the
# capability needs it and every other home is an import cycle (#788).
"app/core/memory_keys.py",
# Which tables alembic owns and which the vector store makes at runtime. It is
# two statements, and one of them decides whether `db-check` reports real drift.
"app/db/vector_tables.py",
# Which explicit `null` on an update may reach a row. It is one function and
# it stands between a legal request and a 500 naming a database constraint
# (#637), which is the kind of thing a branch that never runs is exactly how
# it comes back.
"app/db/updates.py",
"app/services/access.py",
# How often a stranger may reach a public surface. It was configured and
# never applied for the whole life of the product (#39), which is the
# argument for holding the replacement to the bar every other refusal is
# held to rather than reporting it.
"app/services/rate_limit.py",
"app/services/agent_chat.py",
"app/services/follow_ups.py",
"app/services/mentions.py",
"app/services/agent_environment.py",
"app/services/agent_exposure.py",
# What the model is actually told about each tool, read off the built
# toolset rather than the catalog's hand-typed one-liner - the Toolbox
# panel and the exposure form's channel-lookups list both read it (#1473).
"app/services/capability_contracts.py",
"app/services/agent_trigger.py",
"app/services/trigger_events.py",
"app/services/trigger_dedupe.py",
"app/services/trigger_templates.py",
"app/schemas/trigger_template.py",
"app/services/agent_registry.py",
"app/services/agent_runner.py",
"app/services/agent_templates.py",
"app/services/agent_defaults.py",
"app/services/shipped_agents.py",
# The whole dashboard chat wire format: every frame the WebSocket sends and
# every frame it accepts. It sat outside both gates while `use-chat.ts`
# switched on its names, which is the drift #144 describes.
"app/services/agent_session.py",
# The turn loop itself, shared by the dashboard chat and the embedded
# widget since #634. One frame vocabulary for both sockets, and the sink a
# public surface filters at - so a toggle that fails open sends an agent's
# reasoning to a stranger.
"app/services/run_stream.py",
"app/services/run_reaper.py",
"app/services/run_heartbeat.py",
"app/repositories/agent_workspace.py",
"app/repositories/local_service.py",
"app/repositories/sandbox_connection.py",
"app/repositories/skill_proposal.py",
"app/services/attachments.py",
# The one subprocess in the chat attachment feature (#1591): a managed,
# killable `soffice` for DOC. Every branch is a refusal or a teardown - a
# timeout, a cancellation, a nonzero exit, an oversized output - so it is held
# to the same bar as the other security-sensitive services here.
"app/services/office_convert.py",
"app/services/chat_timeline.py",
"app/services/local_service.py",
"app/services/sandbox_connection.py",
"app/services/sandbox_runtimes.py",
"app/services/sandbox_workspace.py",
"app/services/sandboxd_compat.py",
# A `.zip` or `.tar.gz` unpacked into a workspace, and a folder zipped out
# (#2479). Every refusal here - a `../` member, a link, a device, a bomb - is
# what stops an upload from writing outside its folder.
"app/services/workspace_archive.py",
"app/services/usage_report.py",
"app/services/skill_proposal.py",
"app/services/skill_workspace.py",
"app/services/channels/attachments.py",
"app/services/channels/sms_media.py",
"app/services/channels/dedupe.py",
"app/services/channels/stream_lease.py",
"app/services/channels/feedback.py",
"app/services/channels/membership.py",
"app/services/channels/mentions.py",
# Approvals and questions offered as buttons in a chat, and the Slack app
# around the bot (#2064, #2067, #2068).
"app/services/channels/prompts.py",
# Next steps offered as buttons, a press of which is the next message (#2301).
"app/services/channels/next_steps.py",
"app/services/channels/notification_send.py",
"app/services/channels/handback.py",
"app/services/channels/continuation.py",
"app/services/parked_runs.py",
"app/services/channels/slack_answer.py",
"app/services/channels/slack_app.py",
"app/services/channels/slack_manifest.py",
"app/services/channels/telegram_format.py",
"app/services/channels/telegram_press.py",
# What the Discord adapter says and reads, without the I/O (#2079).
"app/services/channels/discord_protocol.py",
"app/repositories/channel_prompt.py",
"app/services/collection_access.py",
"app/repositories/collection_teardown.py",
"app/repositories/pending_sandbox_destroy.py",
# One visitor's turn on a public widget: who it runs as, how often they may
# ask, and what the page is allowed to put in front of the model. An
# unreachable `except BudgetExceeded` sat here for as long as it was
# ungated, which is the failure this gate exists to name (#663).
"app/services/embed_session.py",
"app/services/embed_voice_js.py",
"app/services/embedding_resolution.py",
"app/services/approvals.py",
# The read half of the audit trail. `audit:read` is the only way to see who
# did what in an organization, and the scope is the whole point of the
# service: one wrong argument here would show another tenant's actions.
"app/services/audit.py",
# An administrator acting as somebody else. Every branch here is a refusal -
# an ended row, an expired one, a token carried onto another session - and a
# refusal nobody has watched fail is the credential #1044 replaced.
"app/services/impersonation.py",
# The one check the chat socket re-runs on every frame - shared with the
# handshake so an open socket is no more permissive than a new one (#1437).
"app/services/ws_auth.py",
# The health probes are ours: the template's version reported a hardcoded
# "unknown" for the vector store and read environment variables for a
# provider check. A probe nobody has watched fail is not a probe, which is
# why this module is gated rather than reported.
"app/services/health.py",
# The job queue that replaced Prefect (#2219). The claim spends an attempt
# before the job runs and the worker never hot-loops, so a job that kills
# its process cannot loop and a database that is down cannot spin it.
"app/worker/*.py",
"app/worker/tasks/background_tasks.py",
"app/repositories/background_job.py",
"app/repositories/background_schedule.py",
"app/repositories/background_task_kind.py",
"app/repositories/background_worker.py",
"app/services/background_jobs.py",
"app/services/api_runs.py",
"app/api/routes/v1/admin_background_jobs.py",
"app/api/routes/v1/run_requests.py",
# Template-inherited, gated anyway. Its fixed `emails/compiled` path pointed
# at a directory that never existed, so every email raised
# EmailTemplateError — and every caller logs and swallows that, so
# registration returned 201 while sending nothing. A pure module whose
# silent failure costs the product all of its mail is worth the bar; there
# is nothing to mock to reach it.
"app/services/email/templates.py",
# How a collection parses, chunks and describes its documents. Template
# code decides *how* to run a parser; this decides *which* one runs, for
# whose collection, on whose model credential — which is platform layer,
# and is the difference between a setting and a policy.
"app/services/ingestion_config.py",
# What a failed ingest is allowed to say. The rest of the RAG pipeline is
# template-inherited and reported rather than gated, but this module decides
# what leaves a worker for a stored column the whole organization reads —
# the same judgement the vault and the exception handlers are held to.
"app/services/rag/failures.py",
"app/services/rag_scratch.py",
# Who hears that a run stopped on its budget or parked on an approval. The
# emails are the only signal for a run nobody was watching — a Slack
# mention, a schedule — so a silent failure here is indistinguishable from
# an agent that simply had nothing to say.
"app/services/notifications.py",
"app/services/notification_catalog.py",
"app/services/notification_center.py",
"app/services/notification_delivery.py",
"app/services/notification_routes.py",
"app/repositories/notification_route.py",
"app/schemas/notification_route.py",
"app/repositories/notification.py",
"app/api/routes/v1/notifications.py",
# The one action in this feature no organization-scoped role can reach.
"app/services/announcement.py",
"app/repositories/announcement.py",
"app/api/routes/v1/admin_announcements.py",
"app/services/mcp_catalog.py",
"app/services/portal_catalog.py",
"app/services/portals/__init__.py",
"app/services/portals/base.py",
"app/services/portals/exceptions.py",
"app/services/portals/github.py",
"app/services/portals/github_app.py",
"app/services/portals/_api.py",
"app/services/portals/_calendar.py",
"app/services/portals/_files.py",
"app/services/portals/github_oauth.py",
"app/services/portals/google_calendar.py",
"app/services/portals/google_drive.py",
"app/services/portals/mail_auth.py",
"app/services/portals/reply_metadata.py",
"app/services/reply_expectation.py",
"app/services/portals/mail_thread.py",
"app/services/portals/microsoft_files.py",
"app/services/portals/outlook_calendar.py",
"app/services/portals/registry.py",
"app/services/mcp_connection.py",
"app/services/mirage_mounts.py",
# Which providers can draw an image, derived from the SDK. What it decides is
# what the Builder offers, and a probe that quietly starts answering "no"
# would empty that picker without a single test failing.
"app/services/decision_models.py",
"app/services/image_models.py",
"app/services/model_profile.py",
# Whether an agent can run on a model. A classification that quietly widens
# hides chat models from every picker; one that narrows lets a speech model
# through publish (#2487).
"app/services/model_outputs.py",
"app/services/organization_secret.py",
"app/services/openai_codex.py",
# Same shape and the same reason as `image_models`: what a picker offers is
# decided by filtering a data file against what the platform can actually
# reach, and an entry that quietly stops being offered - or starts being
# offered and fails on the first voice note - is invisible without a test.
"app/services/mcp_listing.py",
"app/services/mcp_registry.py",
"app/services/mcp_audit.py",
"app/services/speech_to_text.py",
"app/services/speech_to_text_settings.py",
"app/services/realtime_models.py",
"app/services/provider_keys.py",
"app/repositories/voice_call.py",
"app/services/voice/**",
"app/api/routes/v1/voice.py",
"app/worker/tasks/voice_tasks.py",
"app/db/models/voice_call.py",
"app/schemas/voice.py",
"app/services/transcription.py",
"app/services/text_to_speech_models.py",
"app/services/text_to_speech/__init__.py",
"app/services/text_to_speech/clients.py",
"app/services/text_to_speech/exceptions.py",
"app/services/text_to_speech/synthesis.py",
"app/services/video_models.py",
"app/services/video_generation/__init__.py",
"app/services/video_generation/clients.py",
"app/services/video_generation/exceptions.py",
"app/services/video_generation/generation.py",
"app/services/provider_error_code.py",
"app/services/transcription_usage.py",
# The rest of `app/services/rag/` came with the template and is reported
# rather than gated. This module is ours and is only refusals - where a
# remote file may be written, and what may reach a Drive query - so the
# branch that is never taken in normal operation is the whole point of it.
"app/services/rag/remote_names.py",
# The Drive REST client that replaced Google's discovery SDK: written here,
# so held to the bar.
"app/services/rag/drive_api.py",
# What a chunk is, and therefore what a search can find. Written here rather
# than imported (#158), and the rest of the pipeline can only retrieve what
# this module decided to store - so it is held to the bar the code it
# replaced never was.
"app/services/rag/_splitters.py",
# The retrieval filter contract (FA-039): the narrowing-only business
# filters, the server-trusted tenant/authorization scope, and their
# composition. Trust-boundary validation, gated where the rest of the RAG
# pipeline is only reported.
"app/services/rag/filters.py",
"app/services/rag/overlay.py",
"app/services/rag/parent_context.py",
# Whether a citation may name a page: a Markdown file was cited as "page 1" (#2489).
"app/services/rag/citation.py",
"app/services/sharing.py",
"app/services/skills.py",
"app/services/exporting.py",
"app/services/run_export.py",
"app/services/spend.py",
"app/services/stats.py",
"app/services/transcript.py",
"app/db/models/agent_environment.py",
"app/schemas/agent_environment.py",
"app/repositories/agent_environment.py",
"app/repositories/agent_exposure.py",
"app/repositories/agent_trigger.py",
"app/repositories/ingestion_spend.py",
"app/repositories/mcp_connection.py",
"app/repositories/mcp_server_check.py",
"app/repositories/organization_secret.py",
"app/repositories/_visibility.py",
"app/repositories/resource_grant.py",
"app/api/routes/v1/_sharing_routes.py",
"app/api/routes/v1/_sharing_loaders.py",
"app/api/routes/v1/secrets.py",
"app/api/routes/v1/agent_environments.py",
"app/api/routes/v1/agent_exposures.py",
"app/api/routes/v1/agent_triggers.py",
"app/api/routes/v1/trigger_webhooks.py",
"app/api/routes/v1/local_services.py",
"app/api/routes/v1/sandbox_connections.py",
"app/api/routes/v1/skill_changes.py",
"app/api/routes/v1/workspaces.py",
"app/api/routes/v1/_workspace_bytes.py",
"app/api/routes/v1/sharing.py",
"app/api/routes/v1/stats.py",
"app/repositories/run_manifest.py",
"app/db/models/intercepted_tool_call.py",
"app/repositories/intercepted_tool_call.py",
"app/repositories/dashboard_layout.py",
"app/services/dashboard_layout.py",
"app/schemas/onboarding.py",
"app/repositories/onboarding.py",
"app/services/onboarding.py",
"app/api/routes/v1/me_onboarding.py",
"app/api/routes/v1/me_dashboard_layout.py",
"app/repositories/dashboard_preset.py",
"app/services/dashboard_preset.py",
"app/repositories/context.py",
"app/services/context.py",
"app/repositories/project.py",
"app/services/project.py",
"app/services/project_workspace_guard.py",
"app/services/project_workspace_transfer.py",
"app/worker/tasks/project_tasks.py",
"app/api/routes/v1/projects.py",
"app/api/routes/v1/context.py",
"app/db/models/dataset.py",
"app/schemas/dataset.py",
"app/schemas/dataset_helpers.py",
"app/repositories/dataset.py",
"app/repositories/_visibility.py",
"app/services/dataset.py",
"app/services/dataset_files.py",
"app/api/routes/v1/datasets.py",
"app/db/models/knowledge_gap.py",
"app/schemas/knowledge_gap.py",
"app/repositories/knowledge_gap.py",
"app/services/knowledge_gaps.py",
"app/api/routes/v1/knowledge_gaps.py",
"app/services/system_agents.py",
"app/services/system_agent_runner.py",
"app/db/models/case_score.py",
"app/schemas/scoring.py",
"app/repositories/case_score.py",
"app/services/scorers.py",
"app/services/scoring.py",
"app/db/models/case_generation.py",
"app/schemas/case_generation.py",
"app/repositories/case_generation.py",
"app/services/case_generation/__init__.py",
"app/services/case_generation/facade.py",
"app/services/case_generation/sources.py",
"app/services/case_generation/drafts.py",
"app/services/case_generation/prompt.py",
"app/services/case_generation/estimate.py",
"app/services/case_generation/generation.py",
"app/services/case_generation/worker.py",
"app/api/routes/v1/dataset_generations.py",
"app/schemas/agent_test_set.py",
"app/services/agent_test_set.py",
"app/api/routes/v1/agent_test_sets.py",
"app/db/models/artifact.py",
"app/db/models/artifact_share_request.py",
"app/schemas/artifact.py",
"app/schemas/artifact_share_request.py",
"app/repositories/artifact.py",
"app/repositories/artifact_share_request.py",
"app/services/artifact.py",
"app/services/artifact_actions.py",
"app/services/artifact_bindings.py",
"app/services/artifact_log_bindings.py",
"app/services/artifact_consent.py",
"app/services/artifact_diff.py",
"app/services/artifact_lint.py",
"app/services/artifact_ownership.py",
"app/services/artifact_public_data.py",
"app/services/artifact_request_service.py",
"app/services/artifact_requests.py",
"app/services/artifact_render.py",
"app/api/routes/v1/_artifact_bytes.py",
"app/api/routes/v1/artifacts.py",
"app/db/models/visual.py",
"app/db/models/pending_sandbox_destroy.py",
"app/schemas/visual.py",
"app/repositories/visual.py",
"app/services/visual.py",
"app/services/visual_render.py",
"app/core/map_tiles.py",
"app/services/tile_proxy.py",
"app/api/routes/v1/visuals.py",
"app/db/models/api_key.py",
"app/schemas/api_key.py",
"app/repositories/api_key.py",
"app/services/api_key.py",
"app/api/routes/v1/api_keys.py",
"app/db/models/oauth.py",
"app/schemas/oauth_server.py",
"app/repositories/oauth.py",
"app/services/oauth_server.py",
# A phone signed in from a QR the console shows: single-use, a minute, and the
# session opened only when the phone redeems it (#2097).
"app/services/device_pairing.py",
# Web Push to the browsers a person subscribed: the deployment's own signing
# key, made on first use, and a push channel through the delivery outbox.
"app/services/web_push.py",
"app/repositories/push_subscription.py",
"app/schemas/push.py",
"app/api/routes/v1/push.py",
"app/services/apns.py",
"app/services/push.py",
"app/repositories/apns_device.py",
"app/api/routes/v1/mcp_oauth.py",
"app/api/public_api.py",
"app/api/routes/v1/public_openapi.py",
"app/services/platform_mcp/__init__.py",
"app/services/platform_mcp/_api.py",
"app/services/platform_mcp/_oauth.py",
"app/services/platform_mcp/_tools.py",
"app/schemas/change_event.py",
"app/services/change_feed.py",
"app/api/routes/v1/change_events.py",
"app/services/device_hub/__init__.py",
"app/services/device_hub/_hub.py",
"app/services/device_hub/_protocol.py",
"app/services/device_hub/workspace.py",
"app/services/device_hub/services.py",
"app/services/device_hub/local_context.py",
"app/services/device_hub/exceptions.py",
"app/api/routes/v1/device_hub.py",
"app/db/models/organization_assistant.py",
"app/repositories/organization_assistant.py",
"app/schemas/assistant.py",
"app/services/assistant.py",
"app/api/routes/v1/assistant.py",
"app/schemas/resource_usage.py",
"app/services/resource_usage.py",
"app/services/knowledge_reach.py",
"app/repositories/memory.py",
"app/repositories/memory_claim.py",
"app/repositories/system_agent.py",
"app/repositories/outcome.py",
"app/services/memory/_native.py",
"app/services/memory/_claims.py",
"app/services/memory/_runtime.py",
"app/services/memory/extraction.py",
"app/services/memory/facade.py",
"app/services/primary_conversation.py",
"app/services/outcome.py",
"app/services/system_agent.py",
"app/services/routine_authority.py",
"app/services/routine_devices.py",
"app/services/routine_health.py",
"app/repositories/routine_health.py",
"app/db/models/routine_health.py",
"app/services/imessage_webhook.py",
"app/api/routes/v1/imessage_webhook.py",
"app/repositories/routine_device_wait.py",
"app/db/models/routine_device_wait.py",
"app/schemas/local_target.py",
"app/agents/routine_authority.py",
"app/agents/routine_actions.py",
"app/db/models/task_board.py",
"app/repositories/task.py",
"app/schemas/task.py",
"app/services/tasks.py",
"app/services/task_access.py",
"app/services/task_columns.py",
"app/services/task_execution.py",
"app/api/routes/v1/tasks.py",
"app/worker/tasks/task_tasks.py",
"app/services/project_tracking.py",
"app/schemas/project_tracking.py",
"app/schemas/decision_episode.py",
"app/schemas/telephony.py",
"app/db/models/telephony.py",
"app/repositories/telephony.py",
"app/services/telephony/*.py",
"app/agents/capabilities/phone_calls/*.py",
"app/repositories/project_tracking.py",
"app/db/models/project_tracking.py",
"app/services/routine_sources.py",
"app/schemas/routine_sources.py",
"app/services/routine_actions.py",
"app/schemas/routine_actions.py",
"app/services/routine_delivery.py",
"app/repositories/routine_delivery.py",
"app/api/routes/v1/memory.py",
"app/db/models/eval_run.py",
"app/repositories/eval_run.py",
"app/services/eval_comparison.py",
"app/schemas/eval_threshold.py",
"app/schemas/eval_run.py",
"app/services/eval_run.py",
"app/services/eval_run_worker.py",
"app/api/routes/v1/eval_runs.py",
"app/core/cron.py",
"app/db/models/eval_suite.py",
"app/repositories/eval_suite.py",
"app/schemas/eval_suite.py",
"app/services/eval_suite_threshold.py",
"app/services/eval_suite.py",
"app/api/routes/v1/eval_suites.py",
"app/db/models/promotion_gate.py",
"app/repositories/promotion_gate.py",
"app/schemas/promotion_gate.py",
"app/services/promotion_gate.py",
# The authoring assistant: what it reads, what it proposes and how a person
# decides. Its evidence reads are the boundary that keeps it blind to evals.
"app/db/models/authoring.py",
"app/repositories/authoring.py",
"app/schemas/authoring.py",
"app/services/authoring_evidence.py",
"app/services/authoring_hunks.py",
"app/services/authoring_fields.py",
"app/services/authoring_config.py",
"app/services/authoring.py",
"app/services/authoring_worker.py",
"app/api/routes/v1/authoring.py",
"app/worker/tasks/authoring_tasks.py",
"app/worker/tasks/case_generation_tasks.py",
# Improvement sessions: the Improve pipeline run in the background up to a
# checked proposal.
"app/db/models/improvement.py",
"app/repositories/improvement.py",
"app/services/improvement_states.py",
"app/services/improvement_data.py",
"app/schemas/improvement.py",
"app/services/improvement_pipeline.py",
"app/api/routes/v1/improvements.py",
"app/worker/tasks/improvement_tasks.py",
"app/db/models/context_proposal.py",
"app/repositories/context_proposal.py",
"app/schemas/context_proposal.py",
"app/services/context_proposal.py",
"app/services/proposal_reach.py",
"app/api/routes/v1/context_changes.py",
"app/api/routes/v1/primary_conversations.py",
"app/api/routes/v1/outcomes.py",
"app/api/routes/v1/system_agents.py",
"app/worker/tasks/memory_tasks.py",
# Which past conversations an agent may search, and the full-text query over
# them. The predicate is the whole of it - a way in added here is a way in for
# every agent at once - so it is held to the same bar as the permission code
# (#789).
"app/repositories/conversation_search.py",
"app/services/conversation_search.py",
"app/db/models/conversation_task.py",
"app/schemas/conversation_task.py",
"app/repositories/conversation_task.py",
"app/services/conversation_tasks.py",
# What a coding-agent task changed, read under the conversation's own reader
# check (#2480).
"app/db/models/coding_task_change.py",
"app/schemas/coding_task_change.py",
"app/repositories/coding_task_change.py",
"app/services/coding_task_changes.py",
"app/db/models/coding_account.py",
"app/schemas/coding_account.py",
"app/repositories/coding_account.py",
"app/services/coding_accounts.py",
"app/services/personal_codex.py",
"app/services/coding_account_lease.py",
"app/services/personal_coding_snapshot.py",
"app/services/personal_coding.py",
"app/services/conversation_turns.py",
"app/worker/tasks/conversation_tasks.py",
"app/db/models/generated_media.py",
"app/repositories/generated_media.py",
"app/services/generated_media.py",
"app/api/routes/v1/generated_media.py",
"app/services/embed_generated_media.py",
"app/api/routes/v1/embed_generated_media.py",
"app/worker/tasks/generated_media_tasks.py",
# What this deployment calls itself, who may register, and whether it is open
# at all. The identity half is cosmetic; the other two are refusals - a sign-up
# policy applied on the only unauthenticated paths that mint an account, and a
# gate that decides whether the API answers at all - so a branch that never
# runs in normal operation is the whole point of them.
"app/db/models/deployment_settings.py",
"app/core/update_gate.py",
"app/schemas/updates.py",
"app/services/updates.py",
"app/repositories/diagnostics.py",
"app/schemas/diagnostics.py",
"app/services/diagnostics/*",
"app/worker/tasks/diagnostics_tasks.py",
"app/repositories/deployment_settings.py",
"app/schemas/deployment_settings.py",
"app/services/deployment_settings.py",
"app/services/signup_policy.py",
"app/core/maintenance.py",
"app/api/routes/v1/branding.py",
"app/api/routes/v1/_branding_bytes.py",
"app/api/routes/v1/_chat_file_bytes.py",
"app/api/routes/v1/admin_deployment_settings.py",
"app/db/locks.py",
"app/services/invitation_admission.py",
# One unguarded attribute in a dependency made every wrong-method request a 500
# on every route. Gated because it is a refusal path nobody exercises on purpose
# and because it goes away the moment upstream guards it.
"app/core/otel_compat.py",
# Whether a running deployment matches a security profile. A control that
# passed on a deployment that does not satisfy it is a sheet somebody puts in
# front of an auditor, so every branch of every control is held to the gate
# (#1448).
"app/services/deployment_profile.py",
# How long each class of data lives here, who may say so, and the sweep that
# applies it. A number that resolves wrongly either deletes what a deployment
# is obliged to keep or keeps what it is obliged to delete, and the sweep is
# the one code path in the platform whose whole purpose is a hard delete - so
# every branch of the precedence, every class, and every refusal is held to
# the gate (#1420).
"app/core/retention.py",
"app/repositories/retention.py",
"app/services/retention.py",
# Where `HTTPException` joins the one error envelope. It answers a 405, an
# unmatched path, and twenty-two direct raises, none of which a happy path sees.
"app/api/exception_handlers.py",
# The standalone ML services (FA-069). Held here because the surface is one
# another Urban Stack component calls with a key and no person watching: the
# refusals are the contract, the tenant scoping on a call record is what keeps
# one integrator's usage out of another's, and a branch nobody exercises on
# purpose is exactly the one an unattended caller finds.
"app/services/ml/catalog.py",
"app/services/ml/pii.py",
"app/services/ml/parsing.py",
"app/services/ml/facade.py",
"app/services/ml/audio_duration.py",
"app/services/ml/pcm_wav.py",
"app/db/models/ml_service_call.py",
"app/repositories/ml_service_call.py",
"app/schemas/ml.py",
"app/api/routes/v1/ml.py",
# Typed records shared by the console, agent tools, workflow nodes and the
# public API. Every branch is a refusal - a stale revision, a reused
# operation key, another tenant's table, an archived column - and a write path
# that skipped one would overwrite or leak a tenant's rows silently (#1782).
"app/db/models/virtual_table.py",
"app/repositories/virtual_table.py",
"app/schemas/virtual_table.py",
"app/services/virtual_tables/**",
"app/api/routes/v1/virtual_tables.py",
"app/api/routes/v1/_table_responses.py",
"app/api/routes/v1/_path_convertors.py",
# Groups, directory group mappings, and signing in with a directory (#1773).
# Authentication and membership: what they refuse is the feature.
"app/db/models/group.py",
"app/db/models/directory_mapping.py",
"app/repositories/group.py",
"app/repositories/directory_mapping.py",
"app/schemas/group.py",
"app/schemas/directory.py",
"app/services/group.py",
"app/services/group_sharing.py",
"app/services/group_spend.py",
"app/services/group_quality.py",
"app/services/directory/**",
"app/api/routes/v1/groups.py",
"app/api/routes/v1/directory_mappings.py",
"app/api/routes/v1/directory_auth.py",
"app/api/routes/v1/_directory_handoff.py",
# Discord server roles mapped to groups, and the sync that applies them (#2569).
"app/db/models/discord_role_mapping.py",
"app/repositories/discord_role_mapping.py",
"app/schemas/discord_role_mapping.py",
"app/services/discord_role_mapping.py",
"app/services/channels/discord_roles.py",
"app/worker/tasks/channel_tasks.py",
# Node contracts, registry and graph validation (#1786). Mirrors the
# capability registry's split: typed definitions in code, a graph checked
# against them before it can be published.
"app/db/models/workflow.py",
"app/repositories/workflow.py",
"app/schemas/workflow.py",
"app/services/workflow_registry.py",
"app/schemas/workflow_portable.py",
"app/services/workflow_portable.py",
"app/services/workflow_agents.py",
"app/services/workflow_execution/agent_conversations.py",
"app/workflows/**",
"app/api/routes/v1/workflows.py",
# Durable execution: run/node/attempt state machine, the dispatch outbox,
# the reconciler and the event stream (#1788).
"app/db/models/workflow_run.py",
"app/repositories/workflow_run.py",
"app/schemas/workflow_run.py",
"app/services/workflow_execution/**",
"app/worker/tasks/workflow_tasks.py",
"app/api/routes/v1/workflow_runs.py",
# What a workflow's real runs did over a window, for its runs page.
"app/schemas/workflow_stats.py",
"app/services/workflow_stats.py",
# One text-similarity scorer: text.compare, approval edit ratios and evals.
"app/services/text_similarity.py",
# A model step's evals: its cases, and the test runs that tried them.
"app/db/models/workflow_eval.py",
"app/repositories/workflow_eval.py",
"app/schemas/workflow_eval.py",
"app/services/workflow_evals.py",
"app/api/routes/v1/workflow_evals.py",
# The ways in to a workflow (#1792): its webhooks and schedules, the
# workflow-run WebSocket and the chat's door through it, and the publish
# that switches its trigger node on.
"app/db/models/workflow_exposure.py",
"app/repositories/workflow_exposure.py",
"app/schemas/workflow_exposure.py",
"app/services/workflow_exposure.py",
"app/services/workflow_webhook_test.py",
"app/services/workflow_triggers.py",
"app/services/workflow_authority.py",
"app/services/workflow_run_socket.py",
"app/api/routes/v1/workflow_exposures.py",
"app/api/routes/v1/workflow_run_socket.py",
# A run's files (#1791): what a `FileRef` resolves to, and the one lookup
# every file step reads through.
"app/db/models/workflow_file.py",
"app/repositories/workflow_file.py",
# What a `human.approval` step asks a person, and the queue deciding it.
"app/db/models/workflow_approval.py",
"app/repositories/workflow_approval.py",
"app/schemas/workflow_approval.py",
"app/api/routes/v1/workflow_approvals.py",
# A workflow's own log: the lines a step's Quick Log writes as it settles.
"app/db/models/workflow_log_entry.py",
"app/repositories/workflow_log_entry.py",
"app/schemas/workflow_log_entry.py",
# Alert rules on that log, and the worker's count of each over its window.
"app/db/models/workflow_log_alert.py",
"app/repositories/workflow_log_alert.py",
"app/schemas/workflow_log_alert.py",
"app/services/workflow_log_alerts.py",
"app/api/routes/v1/workflow_log_alerts.py",
# A table's triggers (#1785): the workflows a record added to it starts,
# and every refusal between the two - before activation, a filter miss, a
# cycle, a revoked principal - is a run that must not start.
"app/db/models/virtual_table_trigger.py",
"app/repositories/virtual_table_trigger.py",
"app/schemas/virtual_table_trigger.py",
"app/api/routes/v1/virtual_table_triggers.py",
# A saved table/kanban/list view (#1783): a console-only sub-resource of a
# table, sharing its service package but with its own model, repo, schema and
# route file. Every branch here is also a refusal - a private view another
# member owns, a rename that collides, a non-owner without `tables:edit` `ALL`.
"app/db/models/table_view.py",
"app/repositories/table_view.py",
"app/schemas/table_view.py",
"app/api/routes/v1/table_views.py",
"app/services/live_browse.py",
"app/services/live_turn.py",
"app/services/chat_room.py",
"app/services/redis_view.py",
"app/services/browse_plans.py",
"app/api/routes/v1/browse_live.py",
"app/db/models/browser_sign_in.py",
"app/repositories/browser_sign_in.py",
"app/schemas/browser_sign_in.py",
"app/services/browser_sign_in.py",
"app/services/browser_sign_in_session.py",
"app/api/routes/v1/browser_sign_ins.py",
# What the platform does for people on its own initiative, all of it in
# background jobs, and the suggestions it leaves them (#2301).
"app/services/proactive/**",
"app/services/suggestions.py",
"app/services/role_packs.py",
"app/repositories/suggestion.py",
"app/api/routes/v1/me_suggestions.py",
"app/worker/tasks/proactive_tasks.py",
# Watches over a person's own connections, and the presets they start from.
"app/services/watches.py",
"app/services/watch_sources.py",
"app/services/routine_notifications.py",
"app/schemas/routine_notifications.py",
"app/schemas/watch_sources.py",
"app/services/watch_presets.py",
"app/repositories/watch.py",
"app/api/routes/v1/me_watches.py",
]
branch = true
omit = [
# The template's OAuth client for MCP servers: discovery, dynamic client
# registration, token exchange and refresh. Protocol plumbing we inherited
# rather than designed - holding it to 100% would mean mock-heavy tests of
# OAuth internals. It joins the gate when we take ownership of it; its
# observable contract (tokens applied, refreshed, refused) is pinned in
# tests/test_mcp_connections.py.
"app/agents/mcp_oauth.py",
]
[tool.coverage.report]
fail_under = 100
show_missing = true
skip_covered = true
exclude_also = [
"if TYPE_CHECKING:",
"raise NotImplementedError",
"@overload",
]
[tool.uv]
# aiogram 3.31 caps pydantic below 2.14, and every pydantic 2.13 pins a
# pydantic-core with no CPython 3.15 wheel, so without this the backend cannot
# be installed on the Python it runs (#2229). aiogram raises that cap with each
# pydantic minor release rather than for a break; the Telegram channel's tests
# exercise it. Drop this once aiogram allows 2.14.
#
# xai-sdk 1.20 caps packaging below 26, and virtualenv (pre-commit's) fixes
# CVE-2026-113725 only from 21.14.7, which needs packaging 26.3 - so the cap
# held a known advisory in the lock. xai-sdk reads versions with packaging's
# stable API; the xAI realtime tests exercise it. Drop this once xai-sdk allows 26.
override-dependencies = ["pydantic[email]>=2.14.3", "packaging>=26.3"]