Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## [Unreleased]
- Starlette `TestClient`의 기존 `httpx2==2.5.0` pin을 core 개발·테스트 의존성으로 승격하고, deprecated `httpx` fallback 경고 억제를 제거했습니다.
- 긴 이메일·첨부 본문을 의미 단위 청크로 임베딩한 뒤 기존 email/attachment 벡터 계약으로 평균화하고, 청크 요청·벡터 누적을 제한된 창으로 처리합니다. OpenAI `text-embedding-3-*`에는 저장 차원(`1536`)을 직접 요청하도록 보강했습니다. 합성 메일 fixture 5건(70청크)과 provider 요청 계약으로 1,536차원 벡터 경로를 검증했으며, 실행 시 선택한 임베딩 제공자에 본문·파싱된 첨부 텍스트를 전송할 수 있습니다. 회사 기밀 데이터는 fixture·commit·PR·log에 포함하지 않습니다.
- EmailDetail 테스트가 지원하지 않는 스레드 병합/분리 버튼을 `textContent`뿐 아니라 `aria-label`과 `title` 접근 가능 이름으로도 검출하도록 바꿔, 아이콘 전용 버튼 회귀를 놓치지 않습니다.

Expand Down
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies = [
[dependency-groups]
dev = [
"coverage==7.15.1",
"httpx2==2.5.0",
"pytest==9.1.1",
"pytest-asyncio==1.4.0",
"ruff==0.15.21",
Expand Down
1 change: 0 additions & 1 deletion backend/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[pytest]
asyncio_default_fixture_loop_scope = function
filterwarnings =
ignore:Using `httpx` with `starlette.testclient` is deprecated.*:starlette.exceptions.StarletteDeprecationWarning
ignore:You are using a Python version.*which Google will stop supporting.*:FutureWarning
ignore:Unclosed <MemoryObject:ResourceWarning
ignore:pkg_resources is deprecated:UserWarning
Expand Down
17 changes: 17 additions & 0 deletions backend/requirements-hashes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ anyio==4.14.2 \
--hash=sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f
# via
# httpx
# httpx2
# langsmith
# openai
# starlette
Expand Down Expand Up @@ -545,11 +546,16 @@ h11==0.16.0 \
--hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
# via
# httpcore
# httpcore2
# uvicorn
httpcore==1.0.9 \
--hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
--hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
# via httpx
httpcore2==2.5.0 \
--hash=sha256:5ce35188de461d31e8d000bfb8ef8bf22c6c16587a211e5571deaa5e9bdf842a \
--hash=sha256:88aa170137c17328d5ac44234f9fd10706466d5fb347f3edac4d39b91137b09d
# via httpx2
httplib2==0.32.0 \
--hash=sha256:48a0ef30a42db65d8f3399045e1d09ab0ba66e3b9efc360d07f80ea55d286025 \
--hash=sha256:dc6705cacdf3fb0a2aba7629fa33c90fd93e30035db0c157325826be177e4816
Expand All @@ -563,6 +569,10 @@ httpx==0.28.1 \
# -r backend/requirements.txt
# langsmith
# openai
httpx2==2.5.0 \
--hash=sha256:3d2d4d9cf4b61f1a1f46a95947cfdb47e80cb56a2f91c6256ac8f58e4891df41 \
--hash=sha256:e2df9cb4611021527ff8a675b1c320b610a2ec397acc8d6fe6e91df2d9b33c29
# via -r backend/requirements.txt
icalendar==7.2.0 \
--hash=sha256:32dacc396101825b82f9f1bbdf691c02be613130d5ab7a457e553fcd20959fdd \
--hash=sha256:77922b6be57dfcc2e94f93063d2fd7e948ada9b5bdf7b08bebbc684b1b66c7c4
Expand All @@ -574,6 +584,7 @@ idna==3.18 \
# anyio
# email-validator
# httpx
# httpx2
# requests
iniconfig==2.3.0 \
--hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \
Expand Down Expand Up @@ -1569,6 +1580,12 @@ tqdm==4.68.4 \
--hash=sha256:19829c9673638f2a0b8617da4cdcb927e831cd88bcfcb6e78d42a4d1af131520 \
--hash=sha256:5168118b2368f48c561afda8020fd79195b1bdb0bdf8086b88442c267a315dc2
# via openai
truststore==0.10.4 \
--hash=sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301 \
--hash=sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981
# via
# httpcore2
# httpx2
typing-extensions==4.16.0 \
--hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \
--hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5
Expand Down
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ starlette==1.3.1
uvicorn==0.51.0
pytest==9.1.1
httpx==0.28.1
httpx2==2.5.0
pydantic-settings==2.14.2
aiosmtplib==5.1.2
aioimaplib==2.0.1
Expand Down
18 changes: 16 additions & 2 deletions backend/tests/test_container_dependency_pin_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,22 @@ def test_container_provenance_dependency_pins_match_reviewed_manifests() -> None
frontend_lock = yaml.safe_load(read_repo_text("frontend/pnpm-lock.yaml"))

assert backend_pins["cryptography"] == "50.0.0"
assert backend_pins["httpx2"] == "2.5.0"
assert backend_pins["protobuf"] == "7.35.1"
assert "cryptography==50.0.0" in backend_records
assert "httpx2==2.5.0" in backend_records
assert "protobuf==7.35.1" in backend_records
assert all(
re.fullmatch(r"[0-9a-f]{64}", digest)
for pin in ("cryptography==50.0.0", "protobuf==7.35.1")
for pin in (
"cryptography==50.0.0",
"httpx2==2.5.0",
"protobuf==7.35.1",
)
for digest in backend_records[pin]
)
pytest_config = read_repo_text("backend/pytest.ini")
assert "Using `httpx` with `starlette.testclient` is deprecated" not in pytest_config

assert strix_pins["cryptography"] == "50.0.0"
assert strix_pins["protobuf"] == "6.33.6"
Expand All @@ -115,7 +123,6 @@ def test_container_provenance_dependency_pins_match_reviewed_manifests() -> None
for pin in ("cryptography==50.0.0", "protobuf==6.33.6")
for digest in strix_records[pin]
)

root_importer = frontend_lock["importers"]["."]
postcss_resolution = importer_resolution(
root_importer, "devDependencies", "postcss"
Expand Down Expand Up @@ -144,3 +151,10 @@ def test_container_provenance_dependency_pins_match_reviewed_manifests() -> None
"undici@8.9.0",
):
assert exact_lock_entry in package_records


def test_starlette_testclient_uses_httpx2_runtime() -> None:
"""Exercise Starlette's preferred TestClient transport dependency."""
from starlette import testclient

assert testclient.httpx.__name__ == "httpx2"
40 changes: 40 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions docs/doctoring/starlette-httpx2-testclient-dependency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Starlette TestClient `httpx2` dependency

## Observed failure

Protected `develop@042b0c70531b229af3acbd0421a2f23098d848b3` pins Starlette
1.3.1 but did not install `httpx2`. Importing `starlette.testclient` therefore
fell back to deprecated `httpx`; warning-as-error test runs stopped during
collection. Removing the warning filter without installing the preferred
transport would expose the defect without repairing it.

## Decision and boundary

Pin `httpx2==2.5.0` in the repository's existing combined backend
development/direct-test manifests and immutable locks. Keep application HTTP
clients on their existing `httpx` path. A runtime regression test imports
Starlette's TestClient module and verifies that its selected transport module is
`httpx2`; manifest and digest checks alone are insufficient evidence.

Starlette 1.2.0 introduced TestClient support for `httpx2`, and 1.3.0 added it
to the `full` extra. The 2.5.0 wheel in this change matches PyPI's published
SHA-256 digest `3d2d4d9cf4b61f1a1f46a95947cfdb47e80cb56a2f91c6256ac8f58e4891df41`.
PyPI records a trusted-publishing attestation from the `pydantic/httpx2`
repository at tag `v2.5.0`. These facts establish origin and integrity; they do
not transfer current-head CI or protected-merge authority.

## Verification and rollback

Run from `backend/`:

```bash
uv run --frozen pytest -q -W error tests/test_container_dependency_pin_contract.py
uv run --frozen ruff check tests/test_container_dependency_pin_contract.py
```

Rollback removes the direct pin, regenerated lock records, runtime assertion,
and obsolete-warning-filter removal together. Do not restore only the warning
suppression.

## References

Kludex. (2026). *Starlette release notes*. GitHub.
https://github.com/Kludex/starlette/blob/main/docs/release-notes.md

Python Packaging Authority. (2026). *httpx2 2.5.0 file details and provenance*.
PyPI. https://pypi.org/project/httpx2/2.5.0/

Pydantic. (2026). *HTTPX2 v2.5.0* [Source code]. GitHub.
https://github.com/pydantic/httpx2/tree/v2.5.0
Loading