From b7075edc13350e3d697cedb3ec73d6d6ca62d206 Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Fri, 16 Oct 2026 12:46:05 -0500 Subject: [PATCH 1/2] chore(ci): configure ruff, mypy, pytest coverage and import-linter Encode the quality rules from spec section 9 and the dependency rules from ADR-0001. - ruff targets py311 with a line length of 100 and the rule sets E, F, W, I, B, UP, SIM, RUF and D (Google docstrings). Docstring rules are relaxed under tests. - mypy runs in strict mode on packages/. - pytest measures coverage for both packages, with an 80% floor. - import-linter enforces three contracts: - types and ports never import adapters; - the engine never imports agentic_rag_code; - the engine follows the layer order from the spec. - Skeleton engine subpackages exist so that the contracts resolve. The middle layer lists ingest, retrieval, generation, agent and eval as non-independent siblings (`:`), because the agent runtime and the eval runner have to call retrieval. Refs #3 --- .../src/agentic_rag/adapters/__init__.py | 1 + .../engine/src/agentic_rag/agent/__init__.py | 1 + .../engine/src/agentic_rag/eval/__init__.py | 1 + .../src/agentic_rag/evidence/__init__.py | 1 + .../src/agentic_rag/generation/__init__.py | 1 + .../engine/src/agentic_rag/ingest/__init__.py | 1 + .../engine/src/agentic_rag/ports/__init__.py | 1 + .../src/agentic_rag/retrieval/__init__.py | 1 + .../engine/src/agentic_rag/types/__init__.py | 1 + pyproject.toml | 48 +++++++++++++++++++ 10 files changed, 57 insertions(+) create mode 100644 packages/engine/src/agentic_rag/adapters/__init__.py create mode 100644 packages/engine/src/agentic_rag/agent/__init__.py create mode 100644 packages/engine/src/agentic_rag/eval/__init__.py create mode 100644 packages/engine/src/agentic_rag/evidence/__init__.py create mode 100644 packages/engine/src/agentic_rag/generation/__init__.py create mode 100644 packages/engine/src/agentic_rag/ingest/__init__.py create mode 100644 packages/engine/src/agentic_rag/ports/__init__.py create mode 100644 packages/engine/src/agentic_rag/retrieval/__init__.py create mode 100644 packages/engine/src/agentic_rag/types/__init__.py diff --git a/packages/engine/src/agentic_rag/adapters/__init__.py b/packages/engine/src/agentic_rag/adapters/__init__.py new file mode 100644 index 0000000..26e2a7e --- /dev/null +++ b/packages/engine/src/agentic_rag/adapters/__init__.py @@ -0,0 +1 @@ +"""Backend adapters implementing the ports, each behind an optional extra.""" diff --git a/packages/engine/src/agentic_rag/agent/__init__.py b/packages/engine/src/agentic_rag/agent/__init__.py new file mode 100644 index 0000000..3be43c5 --- /dev/null +++ b/packages/engine/src/agentic_rag/agent/__init__.py @@ -0,0 +1 @@ +"""Declarative agent runtime: task graphs, tools and retrieval policies.""" diff --git a/packages/engine/src/agentic_rag/eval/__init__.py b/packages/engine/src/agentic_rag/eval/__init__.py new file mode 100644 index 0000000..03cd792 --- /dev/null +++ b/packages/engine/src/agentic_rag/eval/__init__.py @@ -0,0 +1 @@ +"""Evaluation: golden sets, IR metrics and the eval runner.""" diff --git a/packages/engine/src/agentic_rag/evidence/__init__.py b/packages/engine/src/agentic_rag/evidence/__init__.py new file mode 100644 index 0000000..eee429b --- /dev/null +++ b/packages/engine/src/agentic_rag/evidence/__init__.py @@ -0,0 +1 @@ +"""Evidence pack construction: deduplication, ordering and token budgeting.""" diff --git a/packages/engine/src/agentic_rag/generation/__init__.py b/packages/engine/src/agentic_rag/generation/__init__.py new file mode 100644 index 0000000..33465e5 --- /dev/null +++ b/packages/engine/src/agentic_rag/generation/__init__.py @@ -0,0 +1 @@ +"""Generation helpers: prompt registry, context formatting and citation checks.""" diff --git a/packages/engine/src/agentic_rag/ingest/__init__.py b/packages/engine/src/agentic_rag/ingest/__init__.py new file mode 100644 index 0000000..d874bb3 --- /dev/null +++ b/packages/engine/src/agentic_rag/ingest/__init__.py @@ -0,0 +1 @@ +"""Ingestion pipeline: parsing, cleaning, chunking and section trees.""" diff --git a/packages/engine/src/agentic_rag/ports/__init__.py b/packages/engine/src/agentic_rag/ports/__init__.py new file mode 100644 index 0000000..bea0835 --- /dev/null +++ b/packages/engine/src/agentic_rag/ports/__init__.py @@ -0,0 +1 @@ +"""Protocol definitions for every swappable backend.""" diff --git a/packages/engine/src/agentic_rag/retrieval/__init__.py b/packages/engine/src/agentic_rag/retrieval/__init__.py new file mode 100644 index 0000000..7f24582 --- /dev/null +++ b/packages/engine/src/agentic_rag/retrieval/__init__.py @@ -0,0 +1 @@ +"""Retrieval pipeline: hybrid search, fusion, reranking and parent expansion.""" diff --git a/packages/engine/src/agentic_rag/types/__init__.py b/packages/engine/src/agentic_rag/types/__init__.py new file mode 100644 index 0000000..026f4bb --- /dev/null +++ b/packages/engine/src/agentic_rag/types/__init__.py @@ -0,0 +1 @@ +"""Domain-neutral data model: documents, chunks, citations and evidence.""" diff --git a/pyproject.toml b/pyproject.toml index 9ae8402..570c31a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,3 +17,51 @@ members = ["packages/*"] [tool.uv.sources] agentic-rag = { workspace = true } agentic-rag-code = { workspace = true } + +[tool.ruff] +target-version = "py311" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "D"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"**/tests/**" = ["D"] + +[tool.mypy] +strict = true +files = ["packages"] +python_version = "3.11" + +[tool.pytest.ini_options] +testpaths = ["packages"] +addopts = "--cov=agentic_rag --cov=agentic_rag_code --cov-report=term-missing --cov-fail-under=80" + +[tool.importlinter] +root_packages = ["agentic_rag", "agentic_rag_code"] + +[[tool.importlinter.contracts]] +name = "Types and ports never import adapters" +type = "forbidden" +source_modules = ["agentic_rag.types", "agentic_rag.ports"] +forbidden_modules = ["agentic_rag.adapters"] + +[[tool.importlinter.contracts]] +name = "The engine never imports the code-rag extension" +type = "forbidden" +source_modules = ["agentic_rag"] +forbidden_modules = ["agentic_rag_code"] + +[[tool.importlinter.contracts]] +name = "Engine layers" +type = "layers" +layers = [ + "agentic_rag.adapters", + "agentic_rag.ingest : agentic_rag.retrieval : agentic_rag.generation : agentic_rag.agent : agentic_rag.eval", + "agentic_rag.evidence", + "agentic_rag.ports", + "agentic_rag.types", +] From bea79c43a54f2391ce5eb6e598a52fcc8033b099 Mon Sep 17 00:00:00 2001 From: Luca Ostinelli Date: Fri, 16 Oct 2026 12:46:40 -0500 Subject: [PATCH 2/2] chore(ci): add the CI workflow with a single quality gate and Dependabot Every pull request and every push to main runs four jobs: - lint: ruff check and format; - typecheck: mypy; - import-contracts: import-linter; - test: pytest with coverage, plus a markdown coverage summary. The quality-gate job aggregates them and fails if any of them did not succeed. It is meant to be the only required status check. Dependabot updates uv dependencies and GitHub Actions weekly on Mondays (Europe/Rome). Minor and patch updates are grouped, and major updates arrive as separate PRs. Refs #4 --- .github/dependabot.yml | 25 +++++++++++++ .github/workflows/ci.yml | 79 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d31f593 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,25 @@ +version: 2 +updates: + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + timezone: "Europe/Rome" + commit-message: + prefix: "chore(deps)" + groups: + python-minor-patch: + update-types: ["minor", "patch"] + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + timezone: "Europe/Rome" + commit-message: + prefix: "chore(ci)" + groups: + actions-minor-patch: + update-types: ["minor", "patch"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9b6f91c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + - run: uv sync --locked + - name: Ruff lint + run: uv run ruff check . + - name: Ruff format + run: uv run ruff format --check . + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + - run: uv sync --locked + - name: mypy + run: uv run mypy + + import-contracts: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + - run: uv sync --locked + - name: import-linter + run: uv run lint-imports + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + - run: uv sync --locked + - name: pytest + run: uv run pytest + - name: Coverage summary + if: always() + run: uv run coverage report --format=markdown >> "$GITHUB_STEP_SUMMARY" + + quality-gate: + if: always() + needs: [lint, typecheck, import-contracts, test] + runs-on: ubuntu-latest + steps: + - name: Fail unless every required job succeeded + if: >- + contains(needs.*.result, 'failure') || + contains(needs.*.result, 'cancelled') || + contains(needs.*.result, 'skipped') + run: | + echo "Required job results: ${{ join(needs.*.result, ', ') }}" + exit 1 + - name: All required jobs succeeded + run: echo "quality-gate passed"