Skip to content

chore(deps): bump the python-dependencies group across 1 directory with 11 updates - #215

Merged
github-actions[bot] merged 3 commits into
devfrom
dependabot/uv/python-dependencies-77e0058abf
Aug 18, 2026
Merged

chore(deps): bump the python-dependencies group across 1 directory with 11 updates#215
github-actions[bot] merged 3 commits into
devfrom
dependabot/uv/python-dependencies-77e0058abf

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 16, 2026

Copy link
Copy Markdown
Contributor

Bumps the python-dependencies group with 11 updates in the / directory:

Package From To
fastapi 0.138.1 0.141.1
pydantic-settings 2.14.2 2.15.0
sqlalchemy 2.0.51 2.0.52
alembic 1.18.5 1.19.1
gitpython 3.1.54 3.1.59
pygit2 1.19.3 1.20.0
sentence-transformers 5.6.0 5.7.0
pgvector 0.4.2 0.5.0
ruff 0.15.20 0.16.2
mypy 2.1.0 2.3.0
pre-commit 4.6.0 4.6.2

Updates fastapi from 0.138.1 to 0.141.1

Release notes

Sourced from fastapi's releases.

0.141.1

Fixes

  • 🐛 Fix support for background tasks and headers from dependencies in app.frontend(). PR #16105 by @​tiangolo.

Docs

0.141.0

Features

  • ✨ Add app.frontend(check_dir="auto"), to make local development more convenient with fastapi dev. PR #16102 by @​tiangolo.

0.140.13

Fixes

Docs

0.140.12

Fixes

0.140.11

Fixes

  • 🐛 Fix response_model_* params ignored for non-generator endpoints with Iterable[..] return type. PR #15093 by @​YuriiMotov.

0.140.10

Fixes

Internal

0.140.9

Fixes

  • 🐛 Fix exclude_defaults not propagated to dict keys and values in jsonable_encoder. PR #16043 by @​MBGrao.

... (truncated)

Commits
  • 95f8322 🔖 Release version 0.141.1 (#16106)
  • f137944 📝 Update release notes
  • d623544 🐛 Fix support for background tasks and headers from dependencies in `app.fron...
  • 1d211b9 📝 Update release notes
  • 8a1f876 📝 Document FASTAPI_ENV in FastAPI CLI guide (#16104)
  • c7e7b65 🔖 Release version 0.141.0 (#16103)
  • 6bceb84 📝 Update release notes
  • 5429fed ✨ Add app.frontend(check_dir="auto"), to make local development more conven...
  • 628663f 🔖 Release version 0.140.13 (#16096)
  • 0b54fd0 📝 Update release notes
  • Additional commits viewable in compare view

Updates pydantic-settings from 2.14.2 to 2.15.0

Release notes

Sourced from pydantic-settings's releases.

v2.15.0

Highlights

Behavior changes

  • case_sensitive now applies to init kwargs and config-file sources (#900). InitSettingsSource and the JSON/TOML/YAML config sources previously ignored case_sensitive. Since it defaults to False, case-insensitive matching is now the default for these sources — e.g. Settings(TeSt=...) now populates a test field where it previously did not. Nested keys are still matched case-sensitively.
  • Fields with unresolved forward references now emit a warning (#901). Settings sources can silently fail to resolve such fields; they now raise IncompleteFieldDefinitionWarning telling you to call model_rebuild(). If you have filterwarnings = error configured, this may surface as a new failure.
  • Non-JSON env values for strict fields now raise ValidationError (#926) instead of a less specific error.

New features

  • Show environment variable names in CLI help via cli_show_env_vars=True (#860), so generated --help output doubles as configuration documentation.
  • PYDANTIC_SETTINGS_DEBUG for debugging settings resolution (#906, #913). Set it to a truthy value with DEBUG logging enabled to see each source's contribution in priority order, which source won for each value, and which env_file/secret files were probed, loaded, or skipped — the long-standing "why isn't my .env being picked up?" question.
  • toml_table_header for regular TOML files (#882, #886, #887), letting you root settings at a nested table in any TOML file, not just pyproject.toml.
  • Traversable support for JSON/TOML/YAML file sources (#902), so you can load config packaged inside a distribution — including files inside a zip or wheel — via importlib.resources.files(...) without casting to Path.
  • GCP: project_id can come from an earlier settings source (#878), rather than only from the constructor or GOOGLE_CLOUD_PROJECT.

Bug fixes

  • Fix env vars not loading on Windows with case_sensitive=True (#894). Windows upper-cases os.environ keys, so fields raised Field required instead of picking up their values.
  • Read secret files as UTF-8 instead of the platform locale encoding (#917). On Windows code pages such as cp1252 this silently corrupted non-ASCII secrets.
  • Fix AliasPath on nested model fields not JSON-decoding env values (#898).
  • Fix case-insensitive matching for optional nested models (#905).
  • Fix dotenv extras being wrongly claimed by a complex field sharing a name prefix (#912) — e.g. dbx_token being swallowed by a db: dict field.
  • Fix nested_model_default_partial_update=True corrupting discriminated unions (#876).
  • Fix Secret subclasses crashing when loaded from the environment (#920).
  • Fix enum names not parsing through nested annotations such as Optional[Annotated[MyEnum, ...]] with env_parse_enums=True (#910).
  • An empty yaml_config_section now falls back to defaults instead of raising AttributeError: 'NoneType' object has no attribute 'keys' (#914).
  • NestedSecretsSettingsSource no longer follows symlinks pointing outside secrets_dir (#889).
  • GCP: skip the list_secrets call when case_sensitive=True (#862), lowering the required IAM permissions to just roles/secretmanager.secretAccessor.
  • AWS: types-boto3[secretsmanager] is no longer required at runtime (#880).

Documentation

  • Document JSON parsing of complex env values, plus a comma-separated-values recipe (#919).
  • Recommend an async settings loading pattern (#908).
  • Clarify behavior when an unprefixed value is present in a dotenv file (#895).
  • Clarify environment variable helper descriptions (#867) and fix assorted typos (#904).

What's Changed

... (truncated)

Commits
  • f725ca1 Prepare release 2.15.0 (#930)
  • 28f35c2 Bump the python-packages group with 4 updates (#929)
  • 9056db0 test: move function-local imports to the top of test modules (#927)
  • f077e3a fix: raise ValidationError for non-JSON env values on strict fields (#926)
  • ae25d70 fix: treat Secret subclasses as non-complex fields (#716) (#920)
  • 798dcea Bump the python-packages group with 4 updates (#924)
  • a190041 Bump the github-actions group with 4 updates (#925)
  • 5d93332 Bump the python-packages group with 4 updates (#921)
  • d2fdeda fix: read secret files as UTF-8 instead of the locale encoding (#917)
  • 2256a4e Bump the python-packages group with 3 updates (#915)
  • Additional commits viewable in compare view

Updates sqlalchemy from 2.0.51 to 2.0.52

Release notes

Sourced from sqlalchemy's releases.

2.0.52

Released: August 11, 2026

platform

  • [platform] [bug] Python 3.15 support has been added and tested, including minimal changes for full compatibility.

    References: #13477

orm

  • [orm] [bug] Fixed a result-column misalignment bug in ORM-enabled UPDATE statements where synchronize_session="fetch" is in use, either explicitly or because the statement uses constructs such as CTEs that implicitly select for it. Columns in rows returned by .returning() could be returned under incorrect keys (e.g. row[SomeClass.a] returning the value of a different column), a problem most likely to manifest under concurrent workloads. ORM DELETE statements were not affected.

    References: #13439

  • [orm] [bug] Fixed bug where a failed _orm.Session.bulk_insert_mappings(), _orm.Session.bulk_update_mappings() or _orm.Session.bulk_save_objects() call could leave the _orm.Session permanently in a "flushing" state, such as when the transaction could not be begun because a previous flush had left it needing a rollback. Unlike _orm.Session.flush(), the bulk methods set the internal flushing flag and began the transaction outside of the try/finally block that resets it, so that neither _orm.Session.rollback() nor _orm.Session.close() would clear it, and every subsequent flush would raise InvalidRequestError: Session is already flushing. Pull request courtesy Hamody We.

    References: #13485

  • [orm] [bug] Fixed issue where unpickling an ORM object that were loaded using loader options making use of wildcard tokens, such as _orm.load_only() or _orm.raiseload() with "*", would fail with KeyError or IndexError if the process doing the unpickling had not yet constructed a loader path making use of that same token. This would typically be observed when the object were unpickled in a separate process, such as with the spawn or forkserver multiprocessing start methods, the latter of which became the default on POSIX platforms as of Python 3.14. The internal collection of these tokens is now established up front, so that it is identical in every process.

... (truncated)

Commits

Updates alembic from 1.18.5 to 1.19.1

Release notes

Sourced from alembic's releases.

1.19.1

Released: August 8, 2026

bug

  • [bug] [autogenerate] Fixed bug in the check constraint detection implemented in #508 that failed to take into account column bound check constraints, leading to wrong autogenerate detections.

    References: #1842

1.19.0

Released: August 4, 2026

changed

  • [changed] [installation] Environmental updates:

    -   Trove classifiers now include Python 3.15 which is now part of CI
        integration
    
    • Python 3.14 is also added to trove classifiers which had been previously omitted

    • Implemented PEP 604 style unions in type annotations

feature

  • [feature] [autogenerate] Autogenerate now detects the addition and removal of named CHECK constraints, as part of the default autogenerate behavior. Detection is name-based only; a constraint whose name is unchanged is presumed equivalent regardless of its expression text, as reliably normalizing SQL expressions across backends for comparison purposes is not generally feasible. This behavior is implemented as a plugin named alembic.autogenerate.checkconstraint_byname, and may be disabled if not desired by excluding it from the EnvironmentContext.configure.autogenerate_plugins list. Pull request courtesy Francois van Kempen.

    References: #508

bug

... (truncated)

Commits

Updates gitpython from 3.1.54 to 3.1.59

Release notes

Sourced from gitpython's releases.

3.1.59 - Security

What's Changed

Full Changelog: gitpython-developers/GitPython@3.1.58...3.1.59

3.1.58 - Security and Fixes

What's Changed

New Contributors

Full Changelog: gitpython-developers/GitPython@3.1.57...3.1.58

3.1.57 - Security and Fixes

What's Changed

New Contributors

Full Changelog: gitpython-developers/GitPython@3.1.56...3.1.57

... (truncated)

Commits
  • 66340d7 prepare changelog prior to release
  • a5e047d Merge pull request #2211 from gitpython-developers/config-sanitize-more
  • ef7568e fix: ignore includes in submodule configuration
  • 4b4e47f fix: preserve multiline config values when writing
  • b473abb Merge pull request #2210 from gitpython-developers/fix-clone-unsafe-option
  • 5ff52cc Merge pull request #2209 from caroescm/fix-index-add-chmod
  • b68afff Block separate git directories during clone
  • 93677a0 fix: index.add() now supports filters (#2021)
  • 9729ed3 Merge pull request #2208 from gitpython-developers/security-fixes
  • ce9d8e8 prepare next release
  • Additional commits viewable in compare view

Updates pygit2 from 1.19.3 to 1.20.0

Release notes

Sourced from pygit2's releases.

1.20.0 (2026-08-08)

  • New RemoteCallbacks.custom_headers() #1465

  • New rebase API: Repository.rebase_init(...), Repository.rebase_open(...), Rebase, and RebaseOperation #1483

  • Fix Config.snapshot() for non-repository configs, allow PathLike in Config.__init__(), and improve config documentation #1468

  • Fix UnicodeDecodeError with non-UTF-8 file paths in Repository.status(), DiffFile.path, index paths, checkout callbacks, and related APIs #1451 #1469

  • Fix enums.CheckoutStrategy.CONFLICT_STYLE_ZDIFF3, which was mistakenly bound to the DIFF3 constant #1483

  • Fix crashes and reference-lifetime bugs in custom refdb backends #1471 #1474 #1475 #1476

  • Update wheels to libgit2 1.9.6 and OpenSSL 3.5.7

  • Add riscv64 wheels #1463

Breaking changes:

  • Remove deprecated pygit2.legacyenums module and GIT_* constants, use pygit2.enums instead

  • Remove deprecated support for passing str to Repository.merge(...), pass a Commit, Oid, or Reference object instead

  • Repository.merge_file_from_index(...) now returns MergeFileResult by default; pass use_deprecated=True for the previous string return, now deprecated.

  • Remove deprecated Remote.ls_remotes(...), use Remote.list_heads(...) instead

  • Remove pygit2.to_bytes(...) and pygit2.to_str(...); they were undocumented accidental public APIs

Changelog

Sourced from pygit2's changelog.

1.20.0 (2026-08-08)

  • New RemoteCallbacks.custom_headers() #1465

  • New rebase API: Repository.rebase_init(...), Repository.rebase_open(...), Rebase, and RebaseOperation #1483

  • Fix Config.snapshot() for non-repository configs, allow PathLike in Config.__init__(), and improve config documentation #1468

  • Fix UnicodeDecodeError with non-UTF-8 file paths in Repository.status(), DiffFile.path, index paths, checkout callbacks, and related APIs #1451 #1469

  • Fix enums.CheckoutStrategy.CONFLICT_STYLE_ZDIFF3, which was mistakenly bound to the DIFF3 constant #1483

  • Fix crashes and reference-lifetime bugs in custom refdb backends #1471 #1474 #1475 #1476

  • Update wheels to libgit2 1.9.6 and OpenSSL 3.5.7

  • Add riscv64 wheels #1463

Breaking changes:

  • Remove deprecated pygit2.legacyenums module and GIT_* constants, use pygit2.enums instead

  • Remove deprecated support for passing str to Repository.merge(...), pass a Commit, Oid, or Reference object instead

  • Repository.merge_file_from_index(...) now returns MergeFileResult by default; pass use_deprecated=True for the previous string return, now deprecated.

  • Remove deprecated Remote.ls_remotes(...), use Remote.list_heads(...) instead

  • Remove pygit2.to_bytes(...) and pygit2.to_str(...); they were undocumented accidental public APIs

Commits
  • ba21cf5 Release 1.20.0
  • 28be4b8 Fix refcount test failing under pytest assertion rewriting
  • 007c562 Fix NULL dereference in refdb backend exists callback
  • 5878864 Fix ownership and lifetime bugs in refdb backend callbacks
  • 2efb706 Update wheels to OpenSSL 3.5.7
  • cc48ea6 Fix ownership and error handling bugs in refdb backend callbacks
  • 997c65b Fix the refdb backend rename callback
  • eb512d2 Fix refcounting bugs in the refdb backend write callback
  • 258bf55 chore(CI): bump actions/setup-python from 6 to 7 in the actions group
  • b7fdcd6 encoding: drop public to_str, rename internal helper to path_to_str
  • Additional commits viewable in compare view

Updates sentence-transformers from 5.6.0 to 5.7.0

Release notes

Sourced from sentence-transformers's releases.

v5.7.0 - GradCache Overhaul, torch.compile Inference, and a Large Batch of Correctness Fixes

This minor version is a correctness and performance-focused release. It rebuilds all gradient-cached losses on one shared engine, fixing several silently wrong gradients and adding token-based mini-batching for up to 3.9x faster cached-loss training. It also makes model.compile() actually speed up inference, and brings a long list of fixes across embedding quantization, evaluators, hard-negative mining, community detection, and multimodal inputs.

Two changes are marked breaking (🚨): int8/uint8 embedding quantization now clips out-of-range values and floors bucket values, so int8 outputs are no longer bit-identical with earlier versions, and AdaptiveLayerLoss/Matryoshka2dLoss now weight prior-layer losses uniformly by default. There's also a forward-looking deprecation: loading models whose modules import classes from outside sentence_transformers will require trust_remote_code=True from v6.0.

Install this version with

# Training + Inference
pip install sentence-transformers[train]==5.7.0
Inference only, use one of:
pip install sentence-transformers==5.7.0
pip install sentence-transformers[onnx-gpu]==5.7.0
pip install sentence-transformers[onnx]==5.7.0
pip install sentence-transformers[openvino]==5.7.0
Multimodal dependencies (optional):
pip install sentence-transformers[image]==5.7.0
pip install sentence-transformers[audio]==5.7.0
pip install sentence-transformers[video]==5.7.0
Or combine as needed:
pip install sentence-transformers[train,onnx,image]==5.7.0

GradCache overhaul: correct gradients and token-based mini-batching (#3862)

The gradient-cached losses (CachedMultipleNegativesRankingLoss, CachedGISTEmbedLoss, CachedSpladeLoss, the Cross Encoder CachedMultipleNegativesRankingLoss, and MegaBatchMarginLoss) train with large batch sizes at constant memory by embedding in mini-batches and replaying them with cached gradients. Each loss carried its own diverged copy of that machinery. They are now all rebuilt on one shared engine, which fixed several bugs that silently corrupted gradients:

  • Cross Encoder CachedMultipleNegativesRankingLoss on GPU: the backward pass used different dropout masks than the forward pass, silently biasing gradients for every reranker trained with dropout active on CUDA or MPS. CPU training was unaffected.
  • Running two forward passes before a backward pass (e.g. in custom training loops) made CachedGISTEmbedLoss and the Cross Encoder loss backpropagate the wrong batch's gradients, because the cache was stored on the loss module. The cache now travels with each forward pass's backward hook (the .cache and .random_states loss attributes are gone as a result).
  • Pooling(include_prompt=False) (e.g. Instructor models) mutated the attention mask in place, so the backward re-embedding of every cached loss ran with a different mask than the forward pass.
  • MatryoshkaLoss(GISTEmbedLoss(...)): the guide model overwrote the cached embeddings, so only the largest Matryoshka dimension was actually trained.

Along the way, this also fixed an autocast dtype crash in the backward pass and the trainer retaining autograd graphs between logging steps when tracking loss components.

MegaBatchMarginLoss's default mini-batched version is rebuilt on the engine as well. It crashed outright on recent releases, and underneath that, its historical implementation only applied the last mini-batch's gradients. It now trains on the full batch (results will differ, for the better), works with MatryoshkaLoss, evaluates under torch.no_grad, and raises for a third input column instead of silently ignoring it.

The headline feature is mini_batch_num_tokens, available on CachedMultipleNegativesRankingLoss, CachedMultipleNegativesSymmetricRankingLoss, CachedGISTEmbedLoss, CachedSpladeLoss, and MegaBatchMarginLoss. Instead of a fixed number of sequences per mini-batch, mini-batches are greedily packed by total non-padding token count, giving near-constant work per mini-batch on variable-length data:

from sentence_transformers import SentenceTransformer
from sentence_transformers.sentence_transformer.losses import CachedMultipleNegativesRankingLoss
model = SentenceTransformer("microsoft/mpnet-base")
loss = CachedMultipleNegativesRankingLoss(model, mini_batch_num_tokens=16384)

On the PR's Natural Questions benchmark, cached-loss training with flash attention and a tuned token budget dropped from 715 to 182 seconds (3.9x) versus the previous release, with unchanged quality. The engine also trims trailing padding from each mini-batch, which alone is worth about 26% throughput on the default padded path. The updated training efficiency documentation recommends the smallest token budget that saturates your GPU. mini_batch_size keeps working everywhere as before.

... (truncated)

Commits
  • b2a9529 [fix] Name the lone suggested parameter in the mine_hard_negatives missing-ne...
  • 00e696c Release v5.7.0
  • b69081a [fix] Treat a 1-dimensional query embedding as a single query in semantic_sea...
  • c9c4250 Keep queries aligned in semantic_search_seismic when a query matches nothing ...
  • 6d331a5 [fix] Support mixed sparse/dense inputs in euclidean and manhattan similari...
  • 68c8cda [fix] Reject num_negatives larger than the range_min/range_max window in mine...
  • cfb6d43 [trainer] Fix eval DataLoader worker leak with dataloader_persistent_worker...
  • f6d3a40 Expand the community window on ties with the threshold (#3900)
  • 0b3fcbd Correct the corpus_precision values documented for semantic_search_faiss ...
  • 62724e7 Drop FAISS padding placeholders from semantic_search_faiss results (#3887)
  • Additional commits viewable in compare view

Updates pgvector from 0.4.2 to 0.5.0

Changelog

Sourced from pgvector's changelog.

0.5.0 (2026-07-06)

  • Added experimental support for type hints
  • Changed vector and halfvec types to return list for Django, SQLAlchemy, SQLModel, and Peewee
  • Changed vector type to return Vector object for Psycopg 3, Psycopg 2, asyncpg, and pg8000
  • Removed utils package (use top-level pgvector package instead)
  • Removed re-exported classes (use top-level pgvector package instead)
  • Removed dependency on NumPy
  • Dropped support for Python < 3.10
  • Dropped support for SQLAlchemy < 2
Commits

Updates ruff from 0.15.20 to 0.16.2

Release notes

Sourced from ruff's releases.

0.16.2

Release Notes

Released on 2026-08-06.

Bug fixes

  • [flake8-pyi] Avoid false positives on singledispatch functions (PYI041) (#27335)

Server

  • Register formatting capabilities dynamically to exclude TOML files (#27332)

Contributors

Install ruff 0.16.2

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://releases.astral.sh/github/ruff/releases/download/0.16.2/ruff-installer.ps1 | iex"

Download ruff 0.16.2

File Platform Checksum
ruff-aarch64-apple-darwin.tar.gz Apple Silicon macOS checksum
ruff-x86_64-apple-darwin.tar.gz Intel macOS checksum
ruff-aarch64-pc-windows-msvc.zip ARM64 Windows checksum
ruff-i686-pc-windows-msvc.zip x86 Windows checksum
ruff-x86_64-pc-windows-msvc.zip x64 Windows checksum
ruff-aarch64-unknown-linux-gnu.tar.gz ARM64 Linux checksum
ruff-i686-unknown-linux-gnu.tar.gz x86 Linux checksum
ruff-powerpc64-unknown-linux-gnu.tar.gz PPC64 Linux checksum
ruff-powerpc64le-unknown-linux-gnu.tar.gz PPC64LE Linux checksum
ruff-riscv64gc-unknown-linux-gnu.tar.gz RISCV Linux checksum
ruff-s390x-unknown-linux-gnu.tar.gz S390x Linux checksum

... (truncated)

Changelog

Sourced from ruff's changelog.

0.16.2

Released on 2026-08-06.

Bug fixes

  • [flake8-pyi] Avoid false positives on singledispatch functions (PYI041) (#27335)

Server

  • Register formatting capabilities dynamically to exclude TOML files (#27332)

Contributors

0.16.1

Released on 2026-07-30.

Preview features

  • Add an option to opt out of human-readable names (#27160)
  • [flake8-pytest-style] Make fixes safe by default and unsafe only when comments are present (PT018) (#27201)
  • [pyupgrade] Skip fix when a defaulted TypeVar precedes a non-defaulted one (UP040, UP046, UP047) (#27133)
  • [ruff] Fix false positive with unpacked arguments (RUF065) (#26959)

Bug fixes

  • Bump gen-lsp-types to gracefully handle unknown enumeration values in LSP messages (#27230)
  • [flake8-bugbear] Mark range as immutable (B008) (#27247)
  • [flake8-comprehensions] NFKC-normalize keyword names in C408 fix (#26813)
  • [flake8-return] Fix false positive when variable is read in finally clause (RET504) (#25441)
  • [pydocstyle] Skip section detection inside RST directive bodies (D214, D405, D413) (#23635)
  • [refurb] Parenthesize yield arguments in the FURB192 fix (#27192)

Rule changes

  • [flake8-pytest-style] Mark PT022 fixes as unsafe (#26440)
  • [refurb] Mark fixes that remove unknown separators as unsafe (FURB105) (#27200)

Server

  • Fix indexing of excluded nested Ruff workspaces (#27303)
  • Lint TOML files in the LSP (#26862)

... (truncated)

Commits
  • 5b48a04 Bump 0.16.2 (#27555)
  • 1b9e5fc Update Swatinem/rust-cache action to v2.9.2 (#27568)
  • c4e86fc [ty] Add helper extension methods for half-range and equality constraints (#2...
  • 17a00de [ty] Reuse primer commands in memory reports (#27553)
  • 6ea296b [ty] Normalize type labels in structured docstrings (#26923)
  • 2fc445f [ty] Diagnose invalid getattr calls (#27502)
  • 22c7823 [ty] Enable (but downrank) auto-import completion suggestions from stub-only ...
  • 05160d5 [ty] Diagnose invalid descriptor __get__ calls (

…th 11 updates

Bumps the python-dependencies group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [fastapi](https://github.com/fastapi/fastapi) | `0.138.1` | `0.141.1` |
| [pydantic-settings](https://github.com/pydantic/pydantic-settings) | `2.14.2` | `2.15.0` |
| [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) | `2.0.51` | `2.0.52` |
| [alembic](https://github.com/sqlalchemy/alembic) | `1.18.5` | `1.19.1` |
| [gitpython](https://github.com/gitpython-developers/GitPython) | `3.1.54` | `3.1.59` |
| [pygit2](https://github.com/libgit2/pygit2) | `1.19.3` | `1.20.0` |
| [sentence-transformers](https://github.com/huggingface/sentence-transformers) | `5.6.0` | `5.7.0` |
| [pgvector](https://github.com/pgvector/pgvector-python) | `0.4.2` | `0.5.0` |
| [ruff](https://github.com/astral-sh/ruff) | `0.15.20` | `0.16.2` |
| [mypy](https://github.com/python/mypy) | `2.1.0` | `2.3.0` |
| [pre-commit](https://github.com/pre-commit/pre-commit) | `4.6.0` | `4.6.2` |



Updates `fastapi` from 0.138.1 to 0.141.1
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](fastapi/fastapi@0.138.1...0.141.1)

Updates `pydantic-settings` from 2.14.2 to 2.15.0
- [Release notes](https://github.com/pydantic/pydantic-settings/releases)
- [Commits](pydantic/pydantic-settings@v2.14.2...v2.15.0)

Updates `sqlalchemy` from 2.0.51 to 2.0.52
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

Updates `alembic` from 1.18.5 to 1.19.1
- [Release notes](https://github.com/sqlalchemy/alembic/releases)
- [Changelog](https://github.com/sqlalchemy/alembic/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/alembic/commits)

Updates `gitpython` from 3.1.54 to 3.1.59
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](gitpython-developers/GitPython@3.1.54...3.1.59)

Updates `pygit2` from 1.19.3 to 1.20.0
- [Release notes](https://github.com/libgit2/pygit2/releases)
- [Changelog](https://github.com/libgit2/pygit2/blob/master/CHANGELOG.md)
- [Commits](libgit2/pygit2@v1.19.3...v1.20.0)

Updates `sentence-transformers` from 5.6.0 to 5.7.0
- [Release notes](https://github.com/huggingface/sentence-transformers/releases)
- [Commits](huggingface/sentence-transformers@v5.6.0...v5.7.0)

Updates `pgvector` from 0.4.2 to 0.5.0
- [Changelog](https://github.com/pgvector/pgvector-python/blob/master/CHANGELOG.md)
- [Commits](pgvector/pgvector-python@v0.4.2...v0.5.0)

Updates `ruff` from 0.15.20 to 0.16.2
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.20...0.16.2)

Updates `mypy` from 2.1.0 to 2.3.0
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](python/mypy@v2.1.0...v2.3.0)

Updates `pre-commit` from 4.6.0 to 4.6.2
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](pre-commit/pre-commit@v4.6.0...v4.6.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-version: 0.141.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pydantic-settings
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: sqlalchemy
  dependency-version: 2.0.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: alembic
  dependency-version: 1.19.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: gitpython
  dependency-version: 3.1.59
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: pygit2
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: sentence-transformers
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pgvector
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: ruff
  dependency-version: 0.16.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: mypy
  dependency-version: 2.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pre-commit
  dependency-version: 4.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code labels Aug 16, 2026
@github-actions
github-actions Bot enabled auto-merge (rebase) August 16, 2026 10:56
pgvector 0.5.0 ships type information, so mypy 2.3.0 now resolves
`pgvector.sqlalchemy.Vector` to a real class. That made the existing
conditional import fail three ways: the `# type: ignore` became unused,
and the `Vector = None` fallback tried to assign None to a type.

Declare `Vector: Any` up front and assign in try/except/else, so the
runtime `Vector is None` guards in embedding_service stay valid for type
checkers and remain patchable in tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@github-actions
github-actions Bot merged commit 4f4627a into dev Aug 18, 2026
14 checks passed
@dependabot
dependabot Bot deleted the dependabot/uv/python-dependencies-77e0058abf branch August 18, 2026 19:30

@JohnRDOrazio JohnRDOrazio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Group bump verified: no true major-version bumps (all minor/patch within major). Full CI green; locally verified against the merged head — uv lock --check consistent, pygit2 1.20.0 retains all legacy GIT_* constants the git module uses, OpenAPI schema builds (119 paths/151 ops/168 schemas), 1535 tests pass. Only deprecation warnings are pre-existing on dev.

damienriehl added a commit to alea-institute/ontokit-api that referenced this pull request Aug 29, 2026
* chore(deps): bump astral-sh/setup-uv in the github-actions group

Bumps the github-actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `astral-sh/setup-uv` from 8.3.0 to 8.3.2
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/d31148d669074a8d0a63714ba94f3201e7020bc3...11f9893b081a58869d3b5fccaea48c9e9e46f990)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 8.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump torch from 2.12.1 to 2.13.0

Bumps [torch](https://github.com/pytorch/pytorch) from 2.12.1 to 2.13.0.
- [Release notes](https://github.com/pytorch/pytorch/releases)
- [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md)
- [Commits](https://github.com/pytorch/pytorch/compare/v2.12.1...v2.13.0)

---
updated-dependencies:
- dependency-name: torch
  dependency-version: 2.13.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump transformers from 5.3.0 to 5.5.0

Bumps [transformers](https://github.com/huggingface/transformers) from 5.3.0 to 5.5.0.
- [Release notes](https://github.com/huggingface/transformers/releases)
- [Commits](https://github.com/huggingface/transformers/compare/v5.3.0...v5.5.0)

---
updated-dependencies:
- dependency-name: transformers
  dependency-version: 5.5.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): update uvicorn[standard] requirement

Updates the requirements on [uvicorn[standard]](https://github.com/Kludex/uvicorn) to permit the latest version.
- [Release notes](https://github.com/Kludex/uvicorn/releases)
- [Changelog](https://github.com/Kludex/uvicorn/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/uvicorn/compare/0.49.0...0.51.0)

---
updated-dependencies:
- dependency-name: uvicorn[standard]
  dependency-version: 0.50.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump gitpython from 3.1.50 to 3.1.54

Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.50 to 3.1.54.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.50...3.1.54)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.54
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [docker/login-action](https://github.com/docker/login-action).


Updates `actions/checkout` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

Updates `docker/login-action` from 4.4.0 to 4.5.1
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/af1e73f918a031802d376d3c8bbc3fe56130a9b0...abd2ef45e78c5afb21d64d4ca52ee8550d9572c7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: docker/login-action
  dependency-version: 4.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump astral-sh/setup-uv from 8.3.2 to 9.0.0

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.2 to 9.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/11f9893b081a58869d3b5fccaea48c9e9e46f990...c771a70e6277c0a99b617c7a806ffedaca235ff9)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump docker/login-action in the github-actions group

Bumps the github-actions group with 1 update: [docker/login-action](https://github.com/docker/login-action).


Updates `docker/login-action` from 4.5.1 to 4.6.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/abd2ef45e78c5afb21d64d4ca52ee8550d9572c7...dbcb813823bdd20940b903addbd779551569679f)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml](https://github.com/google/osv-scanner-action) and [google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml](https://github.com/google/osv-scanner-action).


Updates `google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml` from 2.3.8 to 2.5.0
- [Release notes](https://github.com/google/osv-scanner-action/releases)
- [Commits](https://github.com/google/osv-scanner-action/compare/9a498708959aeaef5ef730655706c5a1df1edbc2...8deb546fdb875b9996d27d4950be7312dac076a1)

Updates `google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml` from 2.3.8 to 2.5.0
- [Release notes](https://github.com/google/osv-scanner-action/releases)
- [Commits](https://github.com/google/osv-scanner-action/compare/9a498708959aeaef5ef730655706c5a1df1edbc2...8deb546fdb875b9996d27d4950be7312dac076a1)

---
updated-dependencies:
- dependency-name: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump astral-sh/setup-uv from 9.0.0 to 10.0.0

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 9.0.0 to 10.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/c771a70e6277c0a99b617c7a806ffedaca235ff9...ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump setuptools from 81.0.0 to 83.0.0 (#193)

Bumps [setuptools](https://github.com/pypa/setuptools) from 81.0.0 to 83.0.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v81.0.0...v83.0.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 83.0.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John R. D'Orazio <priest@johnromanodorazio.com>

* chore(deps): bump cryptography from 48.0.1 to 50.0.0 (#200)

Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.1 to 50.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/48.0.1...50.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 50.0.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John R. D'Orazio <priest@johnromanodorazio.com>

* chore(deps): bump the python-dependencies group across 1 directory with 11 updates

Bumps the python-dependencies group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [fastapi](https://github.com/fastapi/fastapi) | `0.138.1` | `0.141.1` |
| [pydantic-settings](https://github.com/pydantic/pydantic-settings) | `2.14.2` | `2.15.0` |
| [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) | `2.0.51` | `2.0.52` |
| [alembic](https://github.com/sqlalchemy/alembic) | `1.18.5` | `1.19.1` |
| [gitpython](https://github.com/gitpython-developers/GitPython) | `3.1.54` | `3.1.59` |
| [pygit2](https://github.com/libgit2/pygit2) | `1.19.3` | `1.20.0` |
| [sentence-transformers](https://github.com/huggingface/sentence-transformers) | `5.6.0` | `5.7.0` |
| [pgvector](https://github.com/pgvector/pgvector-python) | `0.4.2` | `0.5.0` |
| [ruff](https://github.com/astral-sh/ruff) | `0.15.20` | `0.16.2` |
| [mypy](https://github.com/python/mypy) | `2.1.0` | `2.3.0` |
| [pre-commit](https://github.com/pre-commit/pre-commit) | `4.6.0` | `4.6.2` |



Updates `fastapi` from 0.138.1 to 0.141.1
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.138.1...0.141.1)

Updates `pydantic-settings` from 2.14.2 to 2.15.0
- [Release notes](https://github.com/pydantic/pydantic-settings/releases)
- [Commits](https://github.com/pydantic/pydantic-settings/compare/v2.14.2...v2.15.0)

Updates `sqlalchemy` from 2.0.51 to 2.0.52
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

Updates `alembic` from 1.18.5 to 1.19.1
- [Release notes](https://github.com/sqlalchemy/alembic/releases)
- [Changelog](https://github.com/sqlalchemy/alembic/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/alembic/commits)

Updates `gitpython` from 3.1.54 to 3.1.59
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.54...3.1.59)

Updates `pygit2` from 1.19.3 to 1.20.0
- [Release notes](https://github.com/libgit2/pygit2/releases)
- [Changelog](https://github.com/libgit2/pygit2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/libgit2/pygit2/compare/v1.19.3...v1.20.0)

Updates `sentence-transformers` from 5.6.0 to 5.7.0
- [Release notes](https://github.com/huggingface/sentence-transformers/releases)
- [Commits](https://github.com/huggingface/sentence-transformers/compare/v5.6.0...v5.7.0)

Updates `pgvector` from 0.4.2 to 0.5.0
- [Changelog](https://github.com/pgvector/pgvector-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgvector/pgvector-python/compare/v0.4.2...v0.5.0)

Updates `ruff` from 0.15.20 to 0.16.2
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.15.20...0.16.2)

Updates `mypy` from 2.1.0 to 2.3.0
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v2.1.0...v2.3.0)

Updates `pre-commit` from 4.6.0 to 4.6.2
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.6.0...v4.6.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-version: 0.141.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pydantic-settings
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: sqlalchemy
  dependency-version: 2.0.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: alembic
  dependency-version: 1.19.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: gitpython
  dependency-version: 3.1.59
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: pygit2
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: sentence-transformers
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pgvector
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: ruff
  dependency-version: 0.16.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: mypy
  dependency-version: 2.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
- dependency-name: pre-commit
  dependency-version: 4.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(types): keep pgvector Vector fallback typed as Any (#215)

pgvector 0.5.0 ships type information, so mypy 2.3.0 now resolves
`pgvector.sqlalchemy.Vector` to a real class. That made the existing
conditional import fail three ways: the `# type: ignore` became unused,
and the `Vector = None` fallback tried to assign None to a type.

Declare `Vector: Any` up front and assign in try/except/else, so the
runtime `Vector is None` guards in embedding_service stay valid for type
checkers and remain patchable in tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: clear the two Starlette deprecation warnings (#218)

Starlette 1.3.1 deprecated `HTTP_413_REQUEST_ENTITY_TOO_LARGE` in favour
of `HTTP_413_CONTENT_TOO_LARGE`. Both resolve to 413, so the swap at the
single call site in the project-import size guard is warning-only with no
behaviour change.

`starlette.testclient` now prefers `httpx2` and warns when it falls back
to `httpx`. Add `httpx2` to the dev group so TestClient picks it up.
`httpx` stays a runtime dependency — auth, github_service, user_service,
the OpenAI embedding provider and sitemap_notifier all use it directly,
and several test modules mock `httpx.Response` / `httpx.HTTPStatusError`
against that runtime code.

The test suite now runs with zero deprecation warnings.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(08-01): add AUTH_MODE config and anonymous user for three auth modes

- Add auth_mode field (required/optional/disabled) to Settings class in config.py
- Add ANONYMOUS_USER constant to auth.py (id=anonymous, roles=[viewer])
- Add early returns in get_current_user, get_current_user_optional, get_current_user_with_token for disabled mode
- Optional mode: RequiredUser still 401s (write protection), OptionalUser returns None (browse works)

* test(08-01): add tests for all three auth modes (required, optional, disabled)

- Test ANONYMOUS_USER properties (id, roles, type, not superadmin)
- Test disabled mode: all three auth functions return ANONYMOUS_USER
- Test required mode: get_current_user raises 401, get_current_user_optional returns None
- Test optional mode: RequiredUser raises 401 (write protection), OptionalUser returns None (browse works)

* harden(auth): Literal auth_mode + disabled-ignores-credentials test

/ce:review (PR-2) follow-ups:
- config.py: auth_mode str -> Literal[required|optional|disabled] so
  pydantic-settings rejects config typos at startup (fail-fast) instead of
  silently falling through to required behavior.
- test_auth_disabled.py: add test_disabled_ignores_valid_credentials proving
  disabled mode ignores even a valid Bearer token (everyone anonymous viewer).

WS-auth parity (authenticate_ws does not consult auth_mode) is intentionally
NOT changed here — it is new work beyond the extracted phase-08 slice; noted
on the PR for a follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(auth): WebSocket auth honors auth_mode (HTTP parity)

authenticate_ws hard-required a token regardless of settings.auth_mode,
so a disabled/optional deployment admitted anonymous callers on its HTTP
API but rejected them at the WebSocket handshake. Resolve caller identity
per auth_mode mirroring core.auth:

- disabled  -> ANONYMOUS_USER, no token required
- optional  -> absent/invalid token downgrades to anonymous (get_current_user_optional)
- required  -> valid token mandatory (unchanged)

Project-access check still gates private projects for anonymous callers.
+7 parity tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* feat(10-01): anonymous token module, extended model, and schemas

- Add ontokit/core/anonymous_token.py with HMAC-signed create/verify functions (24h TTL, anon: prefix to prevent token type confusion with beacon tokens)
- Extend SuggestionSession model with is_anonymous, submitter_name, submitter_email, and client_ip columns
- Add ontokit/schemas/anonymous_suggestion.py with AnonymousSessionCreateResponse, AnonymousSubmitRequest (honeypot field aliased as 'website'), and AnonymousSubmitResponse

* feat(10-01): anonymous suggestion endpoints, service methods, and rate limiting

- Add ontokit/api/routes/anonymous_suggestions.py with create, save, submit, discard, and beacon endpoints
- All endpoints gate on AUTH_MODE != 'required' (return 403 otherwise)
- Rate limit: create endpoint checks for >= 5 anonymous sessions from same IP in last hour, returns 429
- Save/submit/discard authenticate via X-Anonymous-Token header using verify_anonymous_token()
- Submit endpoint silently returns fake success for honeypot-filled bot requests
- Add create_anonymous_session, save_anonymous, submit_anonymous, discard_anonymous methods to SuggestionService
- Register anonymous_suggestions router in ontokit/api/routes/__init__.py under /projects prefix

* feat(10-01): update review summaries to show anonymous submitter info

- Add is_anonymous field to SuggestionSessionSummary schema
- _build_summary: use submitter_name/email (credit info) over user_name/email for anonymous sessions
- _create_pr_for_session: show 'Submitted anonymously' or 'Submitted by {name}' for anonymous sessions
- Existing authenticated session summaries are unchanged (backward compatible)

* feat: add alembic migration for anonymous suggestion fields

Adds is_anonymous, submitter_name, submitter_email, client_ip columns
to suggestion_sessions table. These were added to the model in plan
10-01 but the migration was missing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* harden(pr-7): fix dead anonymous beacon + public-project gate + endpoint tests

- BLOCKER-class functional fix: the lineage's anonymous beacon route passed
  data.session_id where beacon_save expects a BEACON token — verify_beacon_token
  (session_id) always returned None, so the endpoint 401'd on every request
  (fails closed; dead code). New SuggestionService.beacon_save_anonymous binds
  the route-verified X-Anonymous-Token session to the payload session (403 on
  mismatch), re-checks is_anonymous (an anonymous token must never flush an
  authenticated session), and shares the branch-locked flush via _beacon_flush.
- Security gate: create_anonymous_session now requires project.is_public —
  anonymous users could previously create suggestion branches/PRs against
  PRIVATE projects (rate limiting was the only guard).
- Tests (lineage shipped none): 11 new — AUTH_MODE 403 sweep, garbage-token
  401s, verified-session forwarding (save + beacon), honeypot silent fake
  success without service call, service-level private-project/mismatch/
  authenticated-session guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-7): apply /ce:review findings — global IP rate limit, anonymous branch reaper, honeypot de-disclosure

- HIGH: 5-sessions/hour rate limit was scoped per (project_id, IP) — one IP
  could mint 5 sessions+branches on EVERY public project per hour. Now global
  per-IP (the docstring's original intent).
- HIGH: anonymous git branches were never garbage-collected — sessions with
  changes_count==0 matched no sweep at all, and the authed sweep's access
  re-check always fails for the anonymous pseudo-user, discarding WITHOUT
  branch delete (orphaned-branch leak = unbounded unauthenticated git-storage
  abuse). New reap_stale_anonymous_sessions (atomic claim, discard + force
  branch delete at 24h = token TTL, includes zero-change sessions), wired
  into the worker cron next to the authed sweep, which now excludes
  anonymous sessions by design.
- MEDIUM: honeypot semantics were disclosed verbatim in the public OpenAPI
  schema (field description, model docstring, route docstring) — now reads
  as an ordinary optional website URL; mechanism documented in code comments
  only. OpenAPI scan pins no leak.
- MEDIUM (tests): new test_anonymous_token.py — TTL expiry, tamper, malformed,
  insecure-secret guard, and BOTH directions of anonymous<->beacon cross-token
  rejection (same-secret confusion). Reaper behavioral tests (branch delete +
  concurrent-claim skip). Worker test reconciled.

Follow-ups noted on the PR (not blocking): is_public re-check on session
lifecycle, PR title/body sanitization, content max_length, proxy-topology
note for request.client.host, rate-limit TOCTOU.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* test(auth): isolate anonymous token secret

* feat(11-01): DB models, schemas, and Alembic migration for LLM config

- Add ProjectLLMConfig model with provider, encrypted key, model_tier, base_url, budget fields
- Add LLMAuditLog model with token counts, cost_estimate_usd, is_byo_key flag
- Add can_self_merge_structural boolean to ProjectMember model
- Add LLMProviderType enum (13 providers) and LLM Pydantic schemas in schemas/llm.py
- Add Alembic migration u9v0w1x2y3a4 — applies cleanly against live DB
- Export new models from ontokit/models/__init__.py

* feat(11-01): LLM provider registry, crypto helpers, pricing, and SSRF validator

- Port 13-provider registry from folio-enrich with get_provider() factory
- OpenAICompatProvider handles 9 providers via OpenAI SDK base_url param
- AnthropicProvider, GoogleProvider (httpx REST), CohereProvider, GitHubModelsProvider
- All chat() methods return (text, input_tokens, output_tokens) for audit logging
- crypto.py: Fernet encrypt_secret/decrypt_secret using same pattern as embedding_service.py
- pricing.py: LiteLLM pricing fetch with 7-day module-level cache, graceful stale fallback
- ssrf.py: validate_base_url() blocks private IPs, metadata endpoint, non-https for cloud
- Add openai, anthropic, google-generativeai, cohere to pyproject.toml dependencies

* feat(pr-3): LLM config/test-connection/usage routes + shared audit module

Carved from phase-11 commit 9d1d7ca (mixed PR-3/PR-4). Includes the PR-3
routes only — config GET/PUT, test-connection, usage, and public
providers/known-models — and registers them. The PR-4 routes (GET /llm/status
and the can_self_merge_structural member-flags PATCH) are dropped here and ship
with cost-controls (PR-4). audit.py (from PR-4 commit 119e501) is pulled in as
the shared backing for GET /llm/usage; budget/rate_limiter/role_gates stay in PR-4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-3): refuse default secret for API-key encryption + CI lint/type fixes

- crypto.py: _get_fernet() now hard-fails in production (and warns in dev) when
  SECRET_KEY is the shipped 'change-me-in-production' default. Because this key
  encrypts user/BYO provider API keys, a publicly-known secret would make them
  trivially decryptable — equivalent to plaintext. (Addresses the session-4
  hardcoded-secret class of finding for the BYOK lineage.)
- registry.py: fix latent UnboundLocalError — the unknown-provider handler
  interpolated an unbound 'provider_type'; use 'name'. Also raise ... from None.
- ruff/mypy: StrEnum for LLMProviderType (UP042); dict[str, ...] type args;
  provider _client: Any annotations; ssrf addr str(); B904 raise-from. Net-new
  files must pass CI's authoritative mypy + ruff.
- tests: real secrets tests (crypto round-trip, response never exposes key,
  write-only api_key, production default-secret block, audit metadata-only)
  replacing the phase-11 skipped stubs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* test(pr-3): public LLM catalogue endpoint tests (providers/known-models)

Exercises the two no-auth PR-3 routes through the real FastAPI app (route
registration + response_model serialization), asserts no secret material in the
public catalogue, and confirms project-scoped /llm/config is auth-gated. This is
the PR-3 surface verifiable without an authenticated, seeded project.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-3): apply /ce:review findings (secret-key scope, SSRF, usage TZ)

Kieran-python review (no BLOCKER):
- H1: secret-key guard now hard-fails in ANY deployed env (staging + production),
  not just production. Staging is shared/reachable and may hold real tenant keys;
  a log warning there is not a control. Uses "not settings.is_development".
- H2: SSRF was write-time only (TOCTOU / DNS-rebinding). Re-validate base_url in
  test-connection immediately before the outbound call (the only PR-3 outbound
  path); harden metadata detection to catch IPv4-mapped IPv6 (::ffff:169.254...)
  and AWS IPv6 IMDS (fd00:ec2::254), and unwrap mapped IPv6 in private-IP checks.
  Full connect-time IP pinning + redirect disabling + the PR-5 chat path are
  noted as follow-ups.
- M1: usage aggregation compared a timestamptz column against a naive UTC
  wall-clock (NOW AT TIME ZONE UTC), shifting month/day boundaries under a
  non-UTC DB session. Now uses tz-aware date_trunc(field, now, 'UTC') and
  now - interval, so totals + budget% are correct regardless of session TimeZone.
- L1: a base_url-only update on an existing local (Ollama) config is no longer
  rejected -- effective provider falls back to the stored row.

Tests: staging block (parametrized), SSRF metadata/private-IP detection incl.
IPv6 forms. ruff + mypy clean; full suite 1547 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-3): SSRF connect-time pinning + Fernet MultiFernet rotation

Two queued follow-ups from the PR-3 /ce:review:

SSRF connect-time re-validation (kills the resolve-then-connect TOCTOU):
- ssrf.resolve_and_validate() resolves + validates every address at call time
- SSRFProtectedTransport re-validates the target host on every request,
  immediately before the socket opens, so a base_url that passed validation
  at config-save can't later DNS-rebind to a private/metadata IP
- secure_async_client() wires that transport and disables redirect following
  (a 3xx to a private IP can't bypass the guard)
- wired into every provider that dials a project-controlled base_url:
  cohere/google/github_models (raw httpx) + openai_compat (openai SDK
  http_client); local providers pass allow_private=True (metadata still blocked)

Fernet key rotation via MultiFernet:
- crypto._get_fernet() now builds a MultiFernet: current SECRET_KEY encrypts,
  retired keys in SECRET_KEY_PREVIOUS decrypt only -> zero-downtime rotation
- rotate_secret() re-encrypts stored ciphertext onto the current key
- the shipped insecure default is never trusted as a rotation key
- new config.secret_key_previous (comma-separated)

+13 tests (SSRF resolver/rebinding/redirects; rotation round-trip/drop/migrate).
Full suite 1575 green; ruff + mypy-strict clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* feat(pr-4): cost controls & role gating — budget/rate-limit/role-gate services + status/member-flags routes

Reintroduces the PR-4 pieces deferred from PR-3:
- services/llm/{budget,rate_limiter,role_gates}.py carved from the phase-11
  lineage (119e501); __init__.py re-exports restored
- routes/llm.py: GET /projects/{id}/llm/status (member-readable, advisory —
  dispatch re-checks in PR-5) + PATCH /projects/{id}/members/{uid}/flags
  (owner/admin-only ROLE-03 toggle), hand-inserted from 9d1d7ca so PR-3's
  hardening (TOCTOU SSRF re-validation, effective_provider fallback) stays
- MemberFlagsUpdate/Response schemas moved to schemas/llm.py (lineage had
  them inline in the routes file with an aliased BaseModel import)
- Activates the inert PR-3 carry-alongs: LLMStatusResponse schema and the
  can_self_merge_structural column/migration

Deviations from lineage (hardening folded in):
- budget.py time windows now tz-aware func.date_trunc(..., 'UTC') — mirrors
  the PR-3 review fix already applied to audit.py (naive-UTC text() SQL
  mis-windows spend against timestamptz)
- rate_limiter.py: UTC day key (was server-local date.today()) so the rate
  window aligns with budget/audit windows; EXPIRE now NX+unconditional,
  repairing keys left TTL-less by a crash between INCR and EXPIRE (which
  permanently rate-limited the user once over cap)
- /llm/status: no-access roles report daily_remaining=0 (lineage reported
  null, which the schema documents as 'uncapped')
- member-flags PATCH logs privilege changes (actor/target/value, metadata
  only) — it grants structural self-merge and previously left no trace
- mypy strict: BudgetStatus TypedDict + typed role descriptors; dead lineage
  imports/_MemberFlagsUpdate class dropped

Net-new tests (lineage had only skip-stubs): rate-limit matrix incl. NX TTL
+ documented fail-open pin; budget matrix incl. BYO-exclusion asserted
against compiled SQL + daily-before-monthly ordering; role-gate matrix incl.
anonymous override + RATE_LIMITS consistency; status/member-flags route
tests (auth boundaries 401/403/404/422, viewer=0, owner=null, exhaustion,
local-provider, no-key-leak). 55 tests; full suite 1617.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-4): apply /ce:review findings (budget-pct scale, daily-remaining invariant, query consolidation + tests)

Completes the /ce:review pass on the cost-control layer (was session-capped
mid-review). Two reviewers (kieran-python + security-sentinel) — SHIP, no
BLOCKER/HIGH.

Service/route hardening (budget/rate_limiter/llm routes):
- budget.py: get_budget_status consolidated 3 queries -> 1 round-trip
  (FILTER'd SUMs over month/day/week, LEAST(month_start, week_ago) outer
  bound) since it backs the member-polled /llm/status; project_id typed
  str -> uuid.UUID; tz-aware date_trunc(..., 'UTC') windows.
- budget.py: budget_consumed_pct now on the 0-100 scale to match
  LLMUsageResponse / get_llm_usage (both *100) — the bare-fraction was a
  latent 100x mis-render for the first consumer of the snapshot. [MEDIUM]
- rate_limiter.py: typed RateLimitRedis Protocol (drops the object +
  type:ignore); _REDIS_INFRA_ERRORS narrows the fail-open catch so a
  mis-wired client raises instead of silently disabling metering; EXPIRE
  NX repairs TTL-less keys.
- llm.py: explicit `role is not None` superadmin fallback (empty-string
  role cannot escalate to admin); unknown provider -> unconfigured, not
  500; spend telemetry gated (no-access roles see caps/booleans, not
  spend/burn); daily_remaining is now an unconditional static per-role
  lookup so a no-access role reports 0 even on an unconfigured project
  (the null-reads-as-uncapped invariant must not hinge on config). [LOW]
- role_gates.py: hoisted the per-role descriptor map to a module-level
  constant (was rebuilt on every gate call). [LOW]
- models/__init__.py: import ordering (isort).

Tests reconciled + strengthened (no orphaned red):
- Updated the budget-status and status-route test doubles to the single
  consolidated-query shape (result.one().monthly/.daily/.week).
- Added a compiled-SQL pin for get_budget_status (BYO exclusion, UTC
  date_trunc, LEAST outer bound, 3 FILTER aggregates). [LOW]
- Added a viewer-on-unconfigured-project regression (daily_remaining == 0).
- Full suite 1619 passed; mypy strict 0; ruff 0 on all PR-4 files.

Non-blocking follow-up noted on the PR: alert on the rate-limiter fail-open
WARNING so a sustained Redis outage is visible (the fail-open is bounded —
not yet wired into enforcement here; PR-5 wires it, backstopped by the
non-fail-open DB budget layer).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-4): actionable, unified rate-limiter fail-open alert

Both limiter fail-open paths (check_rate_limit, get_remaining_calls) now emit
ONE stable, greppable signal — FAIL_OPEN_EVENT=llm_rate_limiter_fail_open — with
structured extra fields (event/operation/project_id/user_id) and the triggering
error, so ops can alert on a single marker instead of two ad-hoc log lines.
Parity verified: budget layer fails CLOSED (non-fail-open backstop); route-level
fail-open alerting lands with PR-5. +2 tests assert the marker fires on both paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* feat(pr-5): duplicate-check dependency slice (composite scoring + cross-branch search)

Minimal phase-12 chain needed by the suggestion generation pipeline:
- DuplicateRejection model + duplicate_check schemas (from 8226d9e)
- StructuralSimilarityService — folio-python Jaccard, fail-soft to 0.0 (from 9a97712)
- EmbeddingService.semantic_search_all_branches + SemanticSearchResultWithBranch
  schema (from e4f8a71), ported to the CAST(:param AS vector) bindparam style this
  branch already uses (::vector after a bindparam is silently dropped by
  SQLAlchemy text())
- DuplicateCheckService with 40/40/20 exact/semantic/structural scoring (from
  a884e27); reviewer-flow hunks (suggestion.py reject-linking fields,
  suggestion_service.py) dropped — they belong to the PR-6 review flow
- HNSW index + duplicate_rejections migration (from 11d1d3a); also repairs the
  orphaned u9v0w1x2y3a4 down_revision (t8u9v0w1x2y3 was never extracted) to
  chain onto 47cc27515626 so alembic history is a single linear head again

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* feat(pr-5): core suggestion generation engine (children/siblings/parents/annotations/edges)

Extracted from the phase-13 lineage (029d309, 220c88c, 4b54f96, c6d3cb7,
3399045, 481284a + final-state tests from 3b1874a/1fa317e):

- schemas/generation.py: request/response contract, Provenance literal,
  GeneratedSuggestion with confidence + provenance + embedded validation and
  duplicate results
- services/validation_service.py: VALID-01..06 rules, mint_iri,
  detect_project_namespace
- services/context_assembler.py: ~2-4K-token ontology context assembly
  (quality_filter.py from the same lineage commit dropped — PR-6 reviewer
  concern, nothing in the generate path imports it)
- services/llm/prompts/: 5 prompt templates + typed PROMPT_BUILDERS dispatch
- services/suggestion_generation_service.py: context -> LLM -> parse ->
  validate -> dedup pipeline; confidence normalization (clamp 0-1, /100 for
  0-100 scales); dedup failures fail-soft to verdict=pass
- api/routes/generation.py: POST generate-suggestions + validate-entity;
  generate enforces role gate (403), rate limit (429, Redis fail-open),
  budget (402) BEFORE any LLM call — closes the PR-4 follow-up where the
  rate limiter shipped without enforcement
- tests: generation schemas, entity validation, suggestion generation
  (GEN-01..09), context assembler; quality_filter-only tests trimmed

Typing tightened for this branch's strict mypy gate (dict type args,
typed PROMPT_BUILDERS, cast on JSON parse, isinstance guard on confidence).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* feat(pr-5): carry model + prompt-template provenance on each suggestion

Per-suggestion provenance now carries model + prompt identity, not just the
audit log (D-08: metadata only — raw prompt text is never persisted):

- GeneratedSuggestion gains model: str | None and prompt_template: str | None
- SuggestionGenerationService.generate() accepts model_id and stamps
  model=model_id, prompt_template=<PROMPT_BUILDERS key> on every suggestion
- generate-suggestions route threads config.model into the pipeline
- tests pin: provenance='llm-proposed', confidence in [0,1], model id, and
  prompt_template key on every generated suggestion; model stays None when
  no model_id is threaded (user-written paths)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* test(pr-5): generation route enforcement tests (403/429/402/400/404/422/502 + success shape)

Route-level coverage for the enforcement boundary, which the extracted
lineage left thin:

- 401/403 unauthenticated + non-member + viewer role-gate
- 429 rate-limit exceeded — pins that check_rate_limit fires BEFORE any
  provider construction or budget check (the PR-4 'shipped but not wired'
  follow-up), plus the documented Redis fail-open path
- 402 budget exhausted / daily cap, provider never constructed
- 400 missing LLM config, 422 batch_size bounds, 404 unknown project and
  unknown class_iri, 502 provider auth error
- success shape pins provenance/model/prompt_template on the wire and that
  config.model is threaded into the pipeline as model_id

Also fixes the endpoints' user dependency: the lineage's
'Annotated[RequiredUser, Depends()]' double-wrap breaks on this branch's
FastAPI (CurrentUser got re-interpreted as query params -> every request
422'd); switched to the bare 'user: RequiredUser' style the rest of this
repo's routes use. Also isort-fixes the u9v0w1x2y3a4 migration touched by
the chain repair.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* fix(pr-5): make rate-limiter fail-open path alertable (PR-4 follow-up)

The generation route wires rate-limit enforcement, but its route-level
fail-open path (Redis pool absent -> _get_redis returns None -> rate check
skipped) was silent, so ops could not alert on unmetered LLM traffic.

Now both fail-open layers log at WARNING: _get_redis narrows to
Import/AttributeError and warns on an absent pool, and the route logs the
bypass with user/project context when it skips the check. Behaviour is
unchanged (still fail-open; DB budget cap in step 5 remains the
non-fail-open backstop) -- only observability is added. Extends the
redis-unavailable route test to assert the WARNING fires.

Closes the PR-4 rate-limiter fail-open review follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* fix(pr-5): apply /ce:review findings — per-type suggestion parsing + provenance/robustness

BLOCKER: edges and annotations returned empty, validation-failing stubs and
dropped all type-specific payload — the pipeline parsed a single generic
raw["label"] that the edge/annotation prompts never emit. Replace with a
per-type dispatch (_parse_typed):
  - edges: parse target_label/target_iri/relationship_type; flag non-controlled
    relationship_type and null target with GEN-05 validation errors instead of
    silently dropping; label = target_label.
  - annotations: parse property_iri/value/lang; carry the focus class IRI;
    display label falls back to value.
  - parents (HIGH): link an existing parent by its IRI instead of minting a
    duplicate node; correct IS-A direction (web links class_iri -> parent).
  - siblings: parent to the focus class's shared parents, not to the class
    itself; children unchanged.
Move the type-specific fields (property_iri/value/lang/target_iri/
relationship_type) onto the flat base GeneratedSuggestion so list[...] response
serialization carries them and the shape matches the web contract.

HIGH: refuse generation with 400 when config.model is empty (every suggestion
must carry a resolvable model id for provenance — D-08).
HIGH: rewrite the edge/annotation/parent tests with real prompt-output shapes
and assert the payload round-trips (the old fixtures injected a "label" key,
masking the blocker).
MEDIUM: unexpected pipeline exceptions now surface as 500 instead of being
masked as an empty-200; only transient TimeoutError/ConnectionError fail soft.
MEDIUM: NaN/inf confidence -> None (clamping had promoted NaN to 1.0).
LOW: 502 returns a generic client message (provider detail logged server-side
only, not echoed); dedup-unavailable logs an alertable distinct WARNING;
provider param is honestly typed (Optional + runtime guard, no type: ignore).

Full pytest 1688 passed; ruff + mypy(strict) clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* harden(pr-5): route rate-limit fail-open uses the unified alert marker

The generation route's Redis-pool-absent bypass now emits the same
FAIL_OPEN_EVENT=llm_rate_limiter_fail_open signal (with structured extra) as the
limiter-internal fail-open paths, so all three degraded paths alert as one event.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* harden(pr-5): drop dead DuplicateCheckRequest.branch field

Duplicate detection always searches across ALL branches (DEDUP-08), so the
request's branch field was silently ignored. Remove it (nothing sends it — the
duplicate-check route lands in PR-6 with no branch consumer) and document why no
per-request branch scope exists. The candidate's found-on branch is still
reported via DuplicateCandidate.branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* feat(12-04): add duplicate-check API endpoint and implement all 7 tests

- New route: POST /projects/{project_id}/duplicate-check (DuplicateCheckResponse)
- Router registered in __init__.py alongside validation, llm, and other routes
- Replace 7 Wave 0 skip stubs with real async unit tests covering DEDUP-04/05/06/07/08 + D-01/D-09/D-13
- Tests mock semantic_search_all_branches + compute_similarity to control scores
- All 7 tests pass; full unit suite: 162 passed, 14 skipped

* harden(pr-6): access-gate the duplicate-check route + schema-parity entity_type

- Route had NO auth dependency in the lineage — any caller could run
  duplicate checks (label + embedding similarity reads) against private
  projects. Now mirrors /search/semantic: OptionalUser +
  project_service.get (public readable by anyone incl. anonymous;
  private requires membership; 403/404 before any scoring work).
- Re-added entity_type to DuplicateCheckService.check() — PR-5's carve
  dropped it while DuplicateCheckRequest still advertises the field and
  the route forwards it (would have raised TypeError at runtime).
  Accepted-but-unused, documented as type-agnostic scoring.
- New tests/unit/test_duplicate_check_route.py (5 endpoint tests:
  private-denied-before-check, 404, anonymous-public-read, request
  wiring incl. entity_type, 422) + ruff fixes in the lineage test file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* test(pr-6): pin branch-field accepted-but-ignored contract (/ce:review MEDIUM visibility)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELAutiUDrKdGf2vZAwgt9Q

* test(pr-6): pin DuplicateCheckRequest.branch removal contract

Update the branch-ignored contract pin to assert the field is gone from the
request model (removed on PR-5) and that a stray branch key in the body is
ignored and never forwarded to the all-branches search.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eQ2CoAwpGv9Q8vBhuGGtH

* fix(llm): synthesize reviewed T3 hardening

* feat(trust): add contribution ladder and guarded auto-accept

* feat(dedup): add auditable distinct-entity decisions

* fix(pr): enforce open source-branch idempotency

* fix(review): preflight open PR uniqueness

* fix(pr): serialize lifecycle invariants

* feat(pr): expose retryable GitHub mirror status

* fix(review): harden GitHub mirror reconciliation

* fix(review): close API lifecycle race gaps

* feat(pr-party): data model and migrations (U1)

Four tables (reviewer, credential, pr, action) with the constraints that
retire the prototype's state bugs: (repo_full_name, pr_number) natural key
(pr_node_id is enrichment only), a partial unique index allowing one
non-failed action per (reviewer, pr, head_sha, kind) so failed actions are
retryable (C6) while replays collapse, and the pr_party_ready
once-per-revision partial index (R22). Notification and LLMAuditLog project
columns go nullable for the project-less surface (KTD11/KTD20/KTD17).
Live-PG round-trip and constraint behavior verified on a scratch DB.

* feat(pr-party): GitHub client layer with domain split and error taxonomy (U3)

Generation/actuation factory split (generation mode refuses writes before
any HTTP call — KTD13); non-PENDING review assertion with pinned commit_id,
merge sha with 409→StaleCardError, tri-state mergeability (null=computing),
check-runs rollup with an explicit NONE state, org search over issue-shaped
items, 401→TokenExpiredError / 422→SelfApprovalError / rate-limit taxonomy,
and token-expiration header capture for credential lifecycle (KTD13).

* feat(pr-party): reviewer registry, credentials, and capability surface (U2)

Config-driven registry (PR_PARTY_REVIEWERS reconciled at startup with
best-effort node-id resolution; empty config means unconfigured, never
delete-everything), MultiFernet credential storage with domain-separated
key derivation and validate-the-typed-token rotation (KTD12/KTD13),
/pr-party/me capability payload with credential + generation-token health,
settings routes for merge default and ntfy topic (secret, never in the
capability payload), and the AUTH_MODE=disabled router gate (KTD19).
Contributor-PAT retirement carve-out documented in user_settings.

* feat(pr-party): intake — org webhook receiver, two-stage sweep, cycle wiring (U4)

Sweep-first intake per KTD14: discovery search then detail/check fetches
only for changed rows, upserting on (repo_full_name, pr_number) with strict
column ownership (brief columns preserved across refresh — the B3 class).
HMAC-verified org webhook with delivery-id dedupe converging on the same
upsert path; author classification against the registry (counterpart =
any principal, own derived per-caller at read time — model docstring
aligned); 90-minute brewing timeout to ready-with-warning (R17);
missing_since stamping with consecutive-miss tolerance (C7); conditional
arq cron; API-side Redis settings unified with the worker's DSN handling.

* feat(pr-party): tool-denied brief generation worker (U5)

arq task (max_tries=1, explicit timeout) generating structured plain-string
briefs through the generation-domain client only — tool-denial asserted at
the seam and mutation-tested (A1/KTD17). Untrusted-data delimiters, diff
cap with truncation flag, CE artifacts fetched only as own-repo contents
paths (SSRF-constrained), server-side links allowlist, instruction-echo
rejection with one retry, fail-closed daily budget in Redis, approved-
provider gate, audit with null project. Brief failure still releases the
card (ready_at stamped; links render from PR facts).

* feat(pr-party): queue and card read API (U15)

Reviewer-gated read surface with per-caller author projection (own derived
from stored identity, node-id-first), server-computed readiness with plain-
language reasons (R17/KTD19), head-scoped other-reviewer state and staleness
(R24), discuss-live park derivation pinned as the U6 contract, plain-string
brief serialization (R21), deep links from PR facts, Cache-Control:
no-store on both endpoints. 403s carry no PR data; 401 unauthenticated.

* feat(pr-party): PR title flows GitHub -> intake -> queue cards (U15 follow-up)

Nullable title column (migration amended in place; branch is local-only),
written by both intake stages with truncation, serialized on queue and card
payloads. Adds a DDL-equals-model drift test for pr_party_pr.

* feat(pr-party): verdict endpoint with durable idempotency and degraded fallback (U6)

KTD16 implemented exactly: server-side readiness re-check with override
recording (R26/AE6), pre-flight from the server-side card row only (A4),
pending-insert-before-GitHub-call with replay/in-flight/reclaim/re-open
semantics (C2/C6), merge as its own claim authorized by an approval at the
current head (R18/R11), discuss-live parks without GitHub (R9) plus unpark,
TokenExpired -> degraded intent with deep link and credential marking (R12),
scrubbed error storage, and a fail-closed daily action limiter (actuation
503s on Redis loss; credential PUT deliberately fails open).

* feat(pr-party): ready notifications — in-app bell plus ntfy ping (U9)

One pr_party_ready notification per routed reviewer per PR revision,
enforced by the U1 partial unique index via SAVEPOINT no-op (R22); author
excluded node-id-first; target_url is the card deep link and the ntfy
payload carries a fixed title and link only — no PR-derived text (KTD20/
R27). Notification schemas accept null project fields so a mixed bell page
serializes (the F5 regression case). Hook registered idempotently in both
the API lifespan and the arq worker startup.

* feat(pr-party): Q&A, deliberation notes, and the contained org answerer (U7)

Questions, notes (R14), and CodeRabbit re-triggers post as the reviewer via
the actuation domain, with compose-for-copy degraded fallback (R12/KTD18);
answers bind only by linkage, never author identity (C4); qa_thread serves
live from GitHub comments through the generation client (KD5 — GitHub stays
canonical; questions persist no app rows). The org answerer workflow ships
as a carried asset for a catholicos/.github fork PR: OWNER/MEMBER gate,
least-privilege permissions, no PR-head checkout, single-comment allowed
tools — all yml-asserted in tests (A2/KTD18).

* feat(pr-party): reconciler — convergence with live GitHub state (U8)

Third sweep stage: abandoned pending reviews back-fill from a matching
GitHub review (C2 healing, no duplicate post, commit_id-scoped per C1);
degraded intents confirm by reviewer node id (rename-proof, C3) with
derived nag state; dismissed approvals flip their action row failed so
merge authorization loses them (R20); missing_since rows verify via detail
fetch before retiring (C7 eventual-consistency); closed/merged projections
archive by row deletion after 14 days. Per-item isolation keeps a broken
repair pass from costing the committed facts refresh.

* fix(pr-party): apply code-review round — actuation, reconcile, intake, webhook, containment

Review fixes: reclaimed pending reviews adopt an existing GitHub review
instead of re-posting; back-fill and degraded confirmation are verdict/
state-aware; degraded-intent replay retries once a credential is repaired
and always carries the deep link; httpx transport errors join the error
taxonomy; scrubbed error prose everywhere credentials store it; DB-cascade
via passive_deletes; pr_id index; discovery page-cap reports incomplete;
failed webhook handlers release the delivery claim for redelivery; stale
snapshots cannot roll facts backwards; HMAC compares bytes (non-ASCII
header 401s); org answerer delimiters are per-run nonce-suffixed; sweep
cron gets an explicit timeout. Consolidations: shared split_repo,
default_generation_client, parse_dt/to_int, emit_ready_transition,
CredentialResolver/mark_credential_dead; ReviewerReconcileResult rename.

* fix(pr-party): cross-model review round — atomic action claim and per-run brief delimiter

The first independent-model pass over this branch (Codex, adversarial scope)
found two issues the eight in-family personas missed, plus the recorded API
residuals B6/B9/B12/B13/B14/B15/B16/B17 and A2.

- N1: existing action rows are now claimed with SELECT ... FOR UPDATE held
  through the pending commit. Without it, two requests could read the same
  stale pending, failed, or degraded-intent row, both flip it to pending, and
  both call create_review — posting two real reviews on a colleague's PR. The
  partial unique index cannot catch this because both transactions UPDATE the
  same row, so it is distinct from B12's first-INSERT race. Concurrency tests
  cover all three retry paths.
- N2: brief prompts mint an unpredictable per-run delimiter after context
  collection and neutralize delimiter-like text case-insensitively. The fixed
  lowercase tokens could be forged with case variants that read as the same
  boundary to an LLM.
- B9: sweep and reconcile roll the shared session back per item, so a flush
  failure no longer poisons every subsequent item with PendingRollbackError.
- B12: unique live-fingerprint violations map to the intended 409 instead of a 500.
- B14: webhook events for repos outside pr_party_org are ignored before processing.
- B15: app-origin links no longer admitted from LLM output; origins compared by
  parsed host and port rather than a reconstructed string.
- B17: PRFacts.mergeable_known stops a webhook's asynchronous null from
  overwriting sweep-established mergeable_state.
- B13: dismissal fallback prefers a standing review over feed ordering.
- B16: action ordering matches its documented verdict-first contract.
- B6: the migration's downgrade deletes only pr-party rows, not every
  NULL-project audit row.
- A2: draft, merged, and closed refusals each say what actually happened.

Gates: 2547 tests green, mypy strict clean, ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CkjoGjpJCeTFqnu1eMiZp4

* fix(llm-review-r2): 2 — degrade unavailable brief pricing

* feat(ops): add safe PR Party credential rewrap (D5)

* fix(pr-party): align replay tests with synthesis topology

* style(pr-party): format synthesized T5 files

* feat(translations): provenance store — translation_records model, migration, value-hash util (U1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): per-instance settings, palette, config routes (U2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): primary provider/model on translation config (U2 ext)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): consensus/confidence engine with scoring, egress, metering contracts (U3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): compact owl:Axiom provenance annotations with record digest (U4)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): gated auto-commit path — shared branch lock, apply-time revalidation, split author/committer, coalesced index refresh (U5)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): coverage + provenance queries under annotation-plus-hash rule (U8)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): mint trigger, label-diff jobs, on-demand endpoint, abuse controls (U6)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): native-speaker reviewer tags + object-scoped confirmation API (U9)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* feat(translations): backfill with per-call cost preview, era scoping, one-active-job lifecycle (U7)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* test(translations): real-seam integration proof AE1-AE5 + in-flight race; fix 4 integration bugs it caught (U14)

Fixes found by the proof: async lazy-load MissingGreenlet in translation jobs
and confirmation route; provisional gate ignored at commit eligibility;
machine commit identities wrong at the author/committer seam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* refactor(translations): simplification pass — slot-indexed coverage, era fast path, single-hash labels, shared BCP 47 validation (ce-simplify)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* fix(translations): review fix wave — advisory-lock parity, confirm/reject revalidation, scoring floors, quota fan-out, era idempotency, lifecycle hardening, canonical serialization (ce-code-review)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNjtnCqGME93jF6xQZNH7q

* fix(types): satisfy pyright on translation modules (base gate now enforced)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(translations): chain T6 migrations after T5

* style(translations): format synthesized T6 files

* fix(translations): close metering and reviewer gaps

* fix(index): stop dropping altLabel, prefLabel and title from class detail

get_class_detail had two queries that were meant to be complements and were not:

- the labels query returned ONLY rdfs:label
- the annotations query excluded ALL of LABEL_PROPERTIES, commented "already
  returned via IndexedLabel"

LABEL_PROPERTIES holds five entries: rdfs:label, skos:prefLabel, skos:altLabel,
dcterms:title and dc:title. The "already returned" claim was true only for
rdfs:label. The other four were excluded from annotations and never added to
labels, so they fell through both and vanished from the response.

Downstream this was destructive, not merely lossy: ontokit-web regenerated the
class Turtle block from the partial payload, so the four properties were deleted
on disk. One observed edit on DEV removed 13 skos:altLabel values across nine
languages while the user was changing a single comment.

The exclusion set is now exactly the properties this response genuinely returns
elsewhere — rdfs:label and rdfs:comment — defined once beside the queries that
justify it, with the invariant stated: excluded from annotations iff returned
elsewhere in this response. LABEL_PROPERTIES is unchanged; it remains correct for
label resolution and preference ordering.

Only the indexed path was affected. The RDFLib fallback iterates
ANNOTATION_PROPERTIES, which already includes these, and was correct — which is
precisely why this survived testing: cold-start passes, a warm index loses data.

Adds a parity test asserting both paths return the same predicate/value/language
set for the same fixture. That test, not the one-line fix, is the real regression
guard. A known remaining divergence for predicates outside ANNOTATION_PROPERTIES
(e.g. skos:related) is tracked as CatholicOS/ontokit-api#212.

Both tests fail before this change and pass after.

Part of CatholicOS/ontokit-web#361.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q15MzMCFSEu2YYKczpmum3

* fix(ontology): unify annotation classification paths

* feat(audit): snapshot columns on suggestion_outcomes — migration + model (U1)

Six nullable no-default columns (tier, role, submitter/decider display
identity, captured-at discriminator), keyset audit index, non-destructive
downgrade. R3/R4/R9, KTD2.

Co-Authored-By: Codex via ce-work cross-model <noreply@openai.com>

* feat(audit): capture submitter snapshot at the record_outcome seam (U2)

Tier/role resolved from the threaded project (no second fetch), keyed on
the session submitter; captured_at written unconditionally; identity
copied for every submitter; non-throwing degrade with PII-free warning.
R1/R2/R5/R6/R8, KTD1/KTD3. Covers AE1-AE4, AE6/AE7 at unit level.

Co-Authored-By: Codex via ce-work cross-model <noreply@openai.com>

* test(audit): preserve bulk review query contract

* feat(audit): trust outcomes audit endpoint with keyset pagination (U3)

GET /{project_id}/trust/outcomes — owner/admin gated, cursor on
(created_at, id) desc, explicit project_id predicate (tenant isolation),
{items, total, next_cursor}, values served as stored. R4/R7/R8, KTD5.

Co-Authored-By: Codex via ce-work cross-model <noreply@openai.com>

* test(audit): real-seam integration proof AE1-AE7 (U4)

Migrated live Postgres + real services: reject/accept snapshots, sweep
path, anonymous attribution, pre-feature nulls, pre-promotion tier at
threshold crossing, degraded capture, authz + tenant isolation.

Co-Authored-By: Codex via ce-work cross-model <noreply@openai.com>

* fix(audit): review fix wave — role width, pure-refusal downgrade, cursor proof, contract docs (ce-code-review)

snapshot_role widened to String(50) matching ProjectMember.role (codex
peer finding — DB-valid long role aborted the outcome flush post-merge);
downgrade raises before any DDL (drop_index was rolled back by its own
raise); redundant post-commit project reload removed and project_id
derived from the threaded project; next_cursor documented as the sole
end-of-list signal; degradation log carries exception class name only;
real two-page cursor walk + tie-breaker, deeper malformed-cursor
branches, editor-role 403, AE7 caplog + anonymity distinctness.

Co-Authored-By: Codex via codex MCP <noreply@openai.com>

* fix(audit): bind cursors to their project

* style(audit): format synthesized audit files

* fix(audit): preserve bulk approval project contract

* feat(demo): prepare credential-separated refresh

* feat(demo): isolate resettable project targets

* fix(demo): expose provisioned ontology availability

* fix(demo): expose exact resettable repository (U9)

* fix(demo): align synthesis migration and contracts

* fix(review): bound demo project discovery

* fix(review): harden demo activation lifecycle

* fix(ci): migrate integration database before tests

* ci: make codecov uploads best-effort

* style: apply current Ruff formatting

* fix(types): snapshot LLM budget limits at route boundary

* fix(types): separate strict suggestion constructors

* fix(types): bind pinned DNS backend to interface

* fix(ci): align mypy with Python 3.13

* fix(db): allow reviewed suggestion session states

* test(translations): patch reservation rate-limit seam

* security(actions): pin Claude answerer commit

* security(sql): document embedding query invariants

* security(logs): minimize identity failure detai…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant