diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 7a82429..3c4ab9c 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,53 @@ 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 }} + + # Bare `uv run` re-syncs the full workspace (root depends on both members). + # Use --no-sync after --package sync, or uv run --package, for isolation. + - name: Assert cross-domain deps absent + run: | + if [ "${{ matrix.domain }}" = "credit" ]; then + ! uv run --no-sync python -c "import yfinance" \ + || { echo "FAIL: yfinance must not install with credit-only sync"; exit 1; } + uv run --no-sync python -c "import credit" + uv run --no-sync credit-css --help + else + ! uv run --no-sync python -c "import xgboost" \ + || { echo "FAIL: xgboost must not install with investing-only sync"; exit 1; } + uv run --no-sync python -c "import investing" + uv run --no-sync 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 diff --git a/AGENTS.md b/AGENTS.md index d771d6a..357287c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,11 +52,20 @@ 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 (keeps --package; bare uv run would re-sync both) +make test-investing # pytest investing/tests +# After a domain-only sync, use `uv run --no-sync …` or `uv run --package …` +# so a plain `uv run` does not reinstall the other member. ``` ### MLflow (demo pipeline runs) diff --git a/Makefile b/Makefile index 1bc81ce..7384179 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,15 @@ lint: test: uv run pytest --cov=investing --cov=credit --cov-branch --cov-report=term-missing +# uv run without --package re-syncs the full workspace (root depends on both +# members). Keep --package so credit-only / investing-only envs stay lean. +# Optional: make test-credit COV_ARGS='--cov-report=xml' (CI) +test-credit: + uv run --frozen --package credit --group dev pytest credit/tests --cov=credit --cov-branch --cov-report=term-missing $(COV_ARGS) + +test-investing: + uv run --frozen --package investing --group dev 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 diff --git a/credit/README.md b/credit/README.md index 6e80518..7470141 100644 --- a/credit/README.md +++ b/credit/README.md @@ -206,18 +206,29 @@ 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): -CLI (package scaffold [#143](https://github.com/JLaborda/SmartWealthAI/issues/143); mart stage [#144](https://github.com/JLaborda/SmartWealthAI/issues/144)): +```bash +uv sync --frozen --package credit --group dev # or: make install-credit +make test-credit +# After a domain-only sync, keep --no-sync (or --package credit) so bare uv run +# does not re-sync both workspace members. +``` + +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 after credit-only sync (package scaffold [#143](https://github.com/JLaborda/SmartWealthAI/issues/143); mart stage [#144](https://github.com/JLaborda/SmartWealthAI/issues/144)): ```bash -uv run credit-css --help -uv run credit-css build-application-mart \ +uv run --no-sync credit-css --help +uv run --no-sync credit-css build-application-mart \ --source credit/tests/fixtures/application_source/applications.csv \ --output-dir data/credit/application_mart -uv run credit-css fit --help -uv run credit-css score --help -uv run python -m credit build-application-mart --help +uv run --no-sync credit-css fit --help +uv run --no-sync credit-css score --help +uv run --no-sync python -m credit build-application-mart --help ``` Hermetic CI uses fixtures under `credit/tests/fixtures/` (including `scoring_mart/` for #145). The mart README sidecar documents **target** → **bad**/**good** and the develop/holdout policy. diff --git a/credit/pyproject.toml b/credit/pyproject.toml index 73b3d6c..0ff21a7 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", @@ -26,6 +21,15 @@ dependencies = [ credit-css = "credit.cli:main" credit-css-serve = "credit.api:main" +[dependency-groups] +# Required for `uv sync --package credit --group dev` (uv does not inherit root groups). +dev = [ + "pytest>=9.0.3,<10.0.0", + "pytest-cov>=6.0.0,<7.0.0", + "ruff>=0.15.14,<0.16.0", + "httpx>=0.28.0,<1.0.0", # FastAPI/Starlette TestClient +] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" 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