From a68c0322ab75f5b15e90251b779f64e748d57070 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 15 Oct 2026 08:07:54 +0000 Subject: [PATCH 1/9] build(deps): lean credit and investing domain dependency sets Split the duplicated 15-package set so credit no longer pulls yfinance/simfin/streamlit/edgartools/requests and investing no longer pulls xgboost/sklearn/joblib/fastapi/uvicorn/kaggle/pyarrow. Shared libs (pandas, click, mlflow-skinny) stay duplicated in both members per #183 (no shared/ package yet). Refs: #183 Co-authored-by: jdlsicilia --- credit/pyproject.toml | 9 ++------ investing/pyproject.toml | 17 +++++----------- uv.lock | 44 +++++++++------------------------------- 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/credit/pyproject.toml b/credit/pyproject.toml index 73b3d6c..87faf45 100644 --- a/credit/pyproject.toml +++ b/credit/pyproject.toml @@ -3,17 +3,12 @@ name = "credit" version = "0.1.0" description = "Credit Scoring System (CSS) — application default-risk ranking" requires-python = ">=3.11,<4.0" -# ponytail: full monorepo dep set until #183 lean per-domain split +# Shared with investing (duplicated until a shared/ member is justified): pandas, click, mlflow-skinny. dependencies = [ - "yfinance>=1.2.0,<2.0.0", "pandas>=3.0.1,<4.0.0", - "edgartools>=5.35.1,<6.0.0", - "pyarrow>=17.0.0,<25.0.0", - "requests>=2.34.0,<3.0.0", "click>=8.1.0,<9.0.0", - "simfin>=1.0.2,<2.0.0", "mlflow-skinny==3.14.0", - "streamlit>=1.58.0,<2.0.0", + "pyarrow>=17.0.0,<25.0.0", "kaggle>=2.2.4,<3.0.0", "xgboost>=2.0.0,<3.0.0", "scikit-learn>=1.9.1,<2.0.0", diff --git a/investing/pyproject.toml b/investing/pyproject.toml index 8d6bdbb..0b98476 100644 --- a/investing/pyproject.toml +++ b/investing/pyproject.toml @@ -3,23 +3,16 @@ name = "investing" version = "0.1.0" description = "Magic Formula / quantitative value screening on US equities" requires-python = ">=3.11,<4.0" -# ponytail: full monorepo dep set until #183 lean per-domain split +# Shared with credit (duplicated until a shared/ member is justified): pandas, click, mlflow-skinny. dependencies = [ - "yfinance>=1.2.0,<2.0.0", "pandas>=3.0.1,<4.0.0", - "edgartools>=5.35.1,<6.0.0", - "pyarrow>=17.0.0,<25.0.0", - "requests>=2.34.0,<3.0.0", "click>=8.1.0,<9.0.0", - "simfin>=1.0.2,<2.0.0", "mlflow-skinny==3.14.0", + "yfinance>=1.2.0,<2.0.0", + "edgartools>=5.35.1,<6.0.0", + "simfin>=1.0.2,<2.0.0", "streamlit>=1.58.0,<2.0.0", - "kaggle>=2.2.4,<3.0.0", - "xgboost>=2.0.0,<3.0.0", - "scikit-learn>=1.9.1,<2.0.0", - "joblib>=1.6.0,<2.0.0", - "fastapi>=0.115,<1", - "uvicorn[standard]>=0.30,<1", + "requests>=2.34.0,<3.0.0", ] [project.scripts] diff --git a/uv.lock b/uv.lock index bd13bc0..a5dfe64 100644 --- a/uv.lock +++ b/uv.lock @@ -6,13 +6,13 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", - "python_full_version < '3.11.5' and sys_platform == 'win32'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", - "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version < '3.11.5' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] @@ -529,39 +529,29 @@ version = "0.1.0" source = { editable = "credit" } dependencies = [ { name = "click" }, - { name = "edgartools" }, { name = "fastapi" }, { name = "joblib" }, { name = "kaggle" }, { name = "mlflow-skinny" }, { name = "pandas" }, { name = "pyarrow" }, - { name = "requests" }, { name = "scikit-learn" }, - { name = "simfin" }, - { name = "streamlit" }, { name = "uvicorn", extra = ["standard"] }, { name = "xgboost" }, - { name = "yfinance" }, ] [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.1.0,<9.0.0" }, - { name = "edgartools", specifier = ">=5.35.1,<6.0.0" }, { name = "fastapi", specifier = ">=0.115,<1" }, { name = "joblib", specifier = ">=1.6.0,<2.0.0" }, { name = "kaggle", specifier = ">=2.2.4,<3.0.0" }, { name = "mlflow-skinny", specifier = "==3.14.0" }, { name = "pandas", specifier = ">=3.0.1,<4.0.0" }, { name = "pyarrow", specifier = ">=17.0.0,<25.0.0" }, - { name = "requests", specifier = ">=2.34.0,<3.0.0" }, { name = "scikit-learn", specifier = ">=1.9.1,<2.0.0" }, - { name = "simfin", specifier = ">=1.0.2,<2.0.0" }, - { name = "streamlit", specifier = ">=1.58.0,<2.0.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.30,<1" }, { name = "xgboost", specifier = ">=2.0.0,<3.0.0" }, - { name = "yfinance", specifier = ">=1.2.0,<2.0.0" }, ] [[package]] @@ -986,18 +976,11 @@ source = { editable = "investing" } dependencies = [ { name = "click" }, { name = "edgartools" }, - { name = "fastapi" }, - { name = "joblib" }, - { name = "kaggle" }, { name = "mlflow-skinny" }, { name = "pandas" }, - { name = "pyarrow" }, { name = "requests" }, - { name = "scikit-learn" }, { name = "simfin" }, { name = "streamlit" }, - { name = "uvicorn", extra = ["standard"] }, - { name = "xgboost" }, { name = "yfinance" }, ] @@ -1005,18 +988,11 @@ dependencies = [ requires-dist = [ { name = "click", specifier = ">=8.1.0,<9.0.0" }, { name = "edgartools", specifier = ">=5.35.1,<6.0.0" }, - { name = "fastapi", specifier = ">=0.115,<1" }, - { name = "joblib", specifier = ">=1.6.0,<2.0.0" }, - { name = "kaggle", specifier = ">=2.2.4,<3.0.0" }, { name = "mlflow-skinny", specifier = "==3.14.0" }, { name = "pandas", specifier = ">=3.0.1,<4.0.0" }, - { name = "pyarrow", specifier = ">=17.0.0,<25.0.0" }, { name = "requests", specifier = ">=2.34.0,<3.0.0" }, - { name = "scikit-learn", specifier = ">=1.9.1,<2.0.0" }, { name = "simfin", specifier = ">=1.0.2,<2.0.0" }, { name = "streamlit", specifier = ">=1.58.0,<2.0.0" }, - { name = "uvicorn", extras = ["standard"], specifier = ">=0.30,<1" }, - { name = "xgboost", specifier = ">=2.0.0,<3.0.0" }, { name = "yfinance", specifier = ">=1.2.0,<2.0.0" }, ] @@ -1516,10 +1492,10 @@ version = "2.4.6" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", - "python_full_version < '3.11.5' and sys_platform == 'win32'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", - "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version < '3.11.5' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } @@ -2789,10 +2765,10 @@ version = "1.17.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", - "python_full_version < '3.11.5' and sys_platform == 'win32'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", - "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'win32'", + "python_full_version < '3.11.5' and sys_platform == 'emscripten'", "python_full_version < '3.11.5' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ @@ -3026,10 +3002,10 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'emscripten'", "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform == 'emscripten'", "python_full_version >= '3.11.5' and python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/5e/77/2dcfa996b01702ab8fd0763d84098f6a640d6162a328f1c04c2697579a1a/soupsieve-3.0.3.tar.gz", hash = "sha256:7dcf6022eed0399eb9934a75e020148f7a2024c37b7dfcd3cf2c5505d69c364e", size = 115923, upload-time = "2026-10-12T13:21:17.696Z" } From 425e6632be17c30bd7f1db271ab1b2c9e4fa492d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 15 Oct 2026 08:07:54 +0000 Subject: [PATCH 2/9] build(make): add per-domain install and test targets Add install-credit, install-investing, test-credit, and test-investing using uv sync --package. Keep full make install / make test / make lint. Refs: #183 Co-authored-by: jdlsicilia --- Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1bc81ce..e0f6772 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,14 @@ -.PHONY: install lint test download-fundamentals format platform-tf-check +.PHONY: install install-credit install-investing lint test test-credit test-investing download-fundamentals format platform-tf-check install: uv sync --frozen --group dev +install-credit: + uv sync --frozen --package credit --group dev + +install-investing: + uv sync --frozen --package investing --group dev + lint: uv run ruff check . uv run ruff format --check . @@ -10,6 +16,13 @@ lint: test: uv run pytest --cov=investing --cov=credit --cov-branch --cov-report=term-missing +# Optional: make test-credit COV_ARGS='--cov-report=xml' (CI) +test-credit: + uv run pytest credit/tests --cov=credit --cov-branch --cov-report=term-missing $(COV_ARGS) + +test-investing: + uv run pytest investing/tests --cov=investing --cov-branch --cov-report=term-missing $(COV_ARGS) + # Requires SEC_IDENTITY in the environment. See investing/docs/guides/download-fundamentals.md download-fundamentals: uv run download-fundamentals --universe dow30 From 3ddb61eee0508cc6d2fde3d47787383bb8f8138f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 15 Oct 2026 08:07:55 +0000 Subject: [PATCH 3/9] ci: lint full workspace then credit/investing matrix tests Split pr-ci into a full-sync lint job and a matrix that syncs each domain with --package, asserts cross-domain imports fail, and uploads per-domain coverage flags. Refs: #183 Co-authored-by: jdlsicilia --- .github/workflows/pr-ci.yml | 43 +++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 7a82429..10debab 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - ci: + lint: runs-on: ubuntu-latest steps: - name: Checkout @@ -26,20 +26,51 @@ jobs: with: python-version: "3.11" - - name: Install dependencies + - name: Install dependencies (full workspace) run: make install - name: Lint run: make lint - - name: Test with coverage - # Match [tool.coverage.run]: both packages + branch=true (mixing CLI - # statement-only --cov with pyproject branch data fails combine). - run: uv run pytest --cov=investing --cov=credit --cov-branch --cov-report=term-missing --cov-report=xml + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + domain: [credit, investing] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + python-version: "3.11" + + - name: Install ${{ matrix.domain }}-only + run: make install-${{ matrix.domain }} + + - name: Assert cross-domain deps absent + run: | + if [ "${{ matrix.domain }}" = "credit" ]; then + ! uv run python -c "import yfinance" \ + || { echo "FAIL: yfinance must not install with credit-only sync"; exit 1; } + uv run python -c "import credit" + uv run credit-css --help + else + ! uv run python -c "import xgboost" \ + || { echo "FAIL: xgboost must not install with investing-only sync"; exit 1; } + uv run python -c "import investing" + uv run score-universe --help + fi + + - name: Test ${{ matrix.domain }} with coverage + run: make test-${{ matrix.domain }} COV_ARGS='--cov-report=xml' - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} files: coverage.xml + flags: ${{ matrix.domain }} fail_ci_if_error: false From 3a0f9be97ee55de79a9aa8a254764268fa812a13 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 15 Oct 2026 08:07:55 +0000 Subject: [PATCH 4/9] docs: document one-domain uv sync and ADR-0005 lean deps Document make install-credit / install-investing and note Docker sibling COPY leftover (#188). Update ADR-0005 trade-offs for the #183 split. Refs: #183 Co-authored-by: jdlsicilia --- AGENTS.md | 13 ++++++++++--- credit/README.md | 11 ++++++++++- docs/adr/0005-poetry-to-uv-workspace.md | 10 +++++----- investing/README.md | 8 +++++++- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d771d6a..40c8c48 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,11 +52,18 @@ Write all code, comments, docstrings, documentation, commits, and PR text in **E ## Runtime - **Python:** 3.11+ (`requires-python = ">=3.11,<4.0"` in root `pyproject.toml`) -- **Dependencies:** uv workspace (`uv sync --frozen --group dev` / `make install`); members `credit` + `investing` ([ADR-0005](docs/adr/0005-poetry-to-uv-workspace.md)). CI and `make install` use `--frozen` so installs match `uv.lock`. +- **Dependencies:** uv workspace; members `credit` + `investing` with **lean per-domain deps** ([#183](https://github.com/JLaborda/SmartWealthAI/issues/183), [ADR-0005](docs/adr/0005-poetry-to-uv-workspace.md)). Shared libs (`pandas`, `click`, `mlflow-skinny`) are listed in both members. CI and Make use `--frozen` so installs match `uv.lock`. ```bash -uv sync --frozen --group dev -uv run pytest +# Full monorepo (both domains) +uv sync --frozen --group dev # or: make install +uv run pytest # or: make test / make lint + +# One domain only (credit has no yfinance; investing has no xgboost) +uv sync --frozen --package credit --group dev # or: make install-credit +uv sync --frozen --package investing --group dev # or: make install-investing +make test-credit # pytest credit/tests +make test-investing # pytest investing/tests ``` ### MLflow (demo pipeline runs) diff --git a/credit/README.md b/credit/README.md index 6e80518..e72116a 100644 --- a/credit/README.md +++ b/credit/README.md @@ -206,7 +206,16 @@ Optional `--reference` / `--recent` override the default batches; `--stable-thre ## Packaging One uv workspace at repo root: `credit` is a second installable package -(`credit/src/credit`) beside investing — `uv sync` is enough. +(`credit/src/credit`) beside investing. Full monorepo: `uv sync --frozen --group dev` +(or `make install`). **Credit-only** (no investing deps such as yfinance): + +```bash +uv sync --frozen --package credit --group dev # or: make install-credit +make test-credit +``` + +Docker images still `COPY investing/pyproject.toml` for workspace lock resolution +([#188](https://github.com/JLaborda/SmartWealthAI/issues/188)); that does not install investing packages. CLI (package scaffold [#143](https://github.com/JLaborda/SmartWealthAI/issues/143); mart stage [#144](https://github.com/JLaborda/SmartWealthAI/issues/144)): diff --git a/docs/adr/0005-poetry-to-uv-workspace.md b/docs/adr/0005-poetry-to-uv-workspace.md index 7aa063e..2eb52fa 100644 --- a/docs/adr/0005-poetry-to-uv-workspace.md +++ b/docs/adr/0005-poetry-to-uv-workspace.md @@ -6,7 +6,7 @@ status: accepted **Decision:** The monorepo uses a root **uv** workspace whose members are `credit` and `investing`. Install/run via `uv sync` / `uv run` (or `make install` / `make test` / `make lint`). Poetry is no longer the primary path; `poetry.lock` is removed. -**Why:** Domain-first layout (`credit/`, `investing/`) needs per-member `pyproject.toml` without a second installer story. uv workspaces give one lockfile (`uv.lock`), fast sync, and a clear path to lean per-domain installs later ([#183](https://github.com/JLaborda/SmartWealthAI/issues/183)). See [#182](https://github.com/JLaborda/SmartWealthAI/issues/182). +**Why:** Domain-first layout (`credit/`, `investing/`) needs per-member `pyproject.toml` without a second installer story. uv workspaces give one lockfile (`uv.lock`), fast sync, and lean per-domain installs ([#183](https://github.com/JLaborda/SmartWealthAI/issues/183)). See [#182](https://github.com/JLaborda/SmartWealthAI/issues/182). **Trade-offs:** @@ -14,12 +14,12 @@ status: accepted | --- | --- | --- | | Flat `credit/` + `investing/` workspace members | Gemini-style `domains/*` + `shared/` | Keep domain folders; avoid premature shared package | | `platform/` not a workspace member | platform as Python member | Infra/scripts only for now | -| Full dep set on both members (temporary) | Lean credit-only / investing-only deps | Owned by #183 | -| Make + CI verification | New `tests/tooling/` contract suite | Occam; existing gates enough | +| Lean per-domain deps; shared libs duplicated in both members | Full dep set on both; or a `shared/` workspace member | #183; extract `shared/` later when shared *code* or a 3rd domain appears | +| Make + CI verification (`--package` matrix) | New `tests/tooling/` contract suite | Occam; existing gates enough | **Consequences:** -- Operators use `make install`, `uv run