From 11b862588f7503a5b12047e1348b000587fcddde Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 8 Jul 2026 15:21:17 +0200 Subject: [PATCH] Complete DVD-WP-0002 main codebase TDD kickoff --- .claude/rules/stack-and-commands.md | 8 ++++-- SCOPE.md | 3 +- pyproject.toml | 25 +++++++++++++++++ src/dvd/__init__.py | 1 + src/dvd/adapters/__init__.py | 0 src/dvd/adapters/routing.py | 11 ++++++++ src/dvd/adapters/routing_rules.py | 8 ++++++ src/dvd/domain/__init__.py | 0 src/dvd/domain/models.py | 28 +++++++++++++++++++ tests/acceptance/test_routing_rules.py | 20 +++++++++++++ tests/unit/test_routing.py | 28 +++++++++++++++++++ .../DVD-WP-0002-main-codebase-tdd-kickoff.md | 8 ++++-- 12 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 pyproject.toml create mode 100644 src/dvd/__init__.py create mode 100644 src/dvd/adapters/__init__.py create mode 100644 src/dvd/adapters/routing.py create mode 100644 src/dvd/adapters/routing_rules.py create mode 100644 src/dvd/domain/__init__.py create mode 100644 src/dvd/domain/models.py create mode 100644 tests/acceptance/test_routing_rules.py create mode 100644 tests/unit/test_routing.py diff --git a/.claude/rules/stack-and-commands.md b/.claude/rules/stack-and-commands.md index fc1a228..911981d 100644 --- a/.claude/rules/stack-and-commands.md +++ b/.claude/rules/stack-and-commands.md @@ -6,12 +6,16 @@ ## Dev Commands ```bash +# Production codebase (src/) +python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" +.venv/bin/pytest -q tests/unit +.venv/bin/pytest -q tests/acceptance # routing-rules tests fail until implemented + # Orient on architecture and migration plan cat INTENT.md cat docs/WORKPLAN_MainCodebase_Integration.md -ls docs/architecture/adr/ -# Prototype smoke (chatgpt5 — most complete) +# Prototype reference (chatgpt5) cd prototype-chatgpt5 && pip install -e . && pytest -q # After workplan edits diff --git a/SCOPE.md b/SCOPE.md index 356d7ee..30227f0 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -28,7 +28,8 @@ direkt-vermittlung-de exists to provide the capability described in INTENT.md. ## Current State -- Pre-production: three parallel prototypes (chatgpt5, geminiNbt3pro, grok4.1) exist under /prototype-*; no unified /src codebase yet. Architecture is documented via 8 ADRs and a migration workplan, but the target production codebase has not been started. +- `/src` production skeleton started (`dvd.domain`, `dvd.adapters.routing`); unit tests green; routing-rules acceptance tests red (TDD). +- Prototypes remain under `/prototype-*` as reference; migration tracked in `docs/WORKPLAN_MainCodebase_Integration.md`. ## Getting Oriented diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..10d1022 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[project] +name = "direkt-vermittlung-de" +version = "0.1.0" +description = "DirektVermittlungDe production backend" +requires-python = ">=3.11" +dependencies = [ + "pydantic>=2.7.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", + "pytest-asyncio>=0.23.0", +] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"] + +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["src"] \ No newline at end of file diff --git a/src/dvd/__init__.py b/src/dvd/__init__.py new file mode 100644 index 0000000..9abb884 --- /dev/null +++ b/src/dvd/__init__.py @@ -0,0 +1 @@ +"""DirektVermittlungDe production codebase.""" \ No newline at end of file diff --git a/src/dvd/adapters/__init__.py b/src/dvd/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dvd/adapters/routing.py b/src/dvd/adapters/routing.py new file mode 100644 index 0000000..d77a79c --- /dev/null +++ b/src/dvd/adapters/routing.py @@ -0,0 +1,11 @@ +"""Metadata-only routing adapter (split-payload model).""" +from __future__ import annotations + +from dvd.domain.models import DocumentMetadata + + +async def route_document(meta: DocumentMetadata) -> str: + """Route using plaintext metadata only — never inspect encrypted payload.""" + if meta.doc_type.upper() == "NOTICE": + return f"{meta.authority_id}-NoticeTeam" + return f"{meta.authority_id}-DefaultTeam" \ No newline at end of file diff --git a/src/dvd/adapters/routing_rules.py b/src/dvd/adapters/routing_rules.py new file mode 100644 index 0000000..f945b6a --- /dev/null +++ b/src/dvd/adapters/routing_rules.py @@ -0,0 +1,8 @@ +"""Future routing-rules table adapter (not yet implemented).""" +from __future__ import annotations + +from dvd.domain.models import DocumentMetadata + + +async def route_document_with_rules(meta: DocumentMetadata) -> str: + raise NotImplementedError("Authority routing rules table not implemented yet") \ No newline at end of file diff --git a/src/dvd/domain/__init__.py b/src/dvd/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dvd/domain/models.py b/src/dvd/domain/models.py new file mode 100644 index 0000000..6398d6f --- /dev/null +++ b/src/dvd/domain/models.py @@ -0,0 +1,28 @@ +"""Domain models for split-payload document intake (ADR-001).""" +from __future__ import annotations + +from datetime import datetime + +from pydantic import BaseModel, Field + + +class DocumentMetadata(BaseModel): + """Plaintext routing metadata — backend routes on this only.""" + + authority_id: str = Field(..., max_length=50, alias="authorityId") + reference_number: str = Field(..., max_length=50, alias="referenceNumber") + doc_type: str = Field(..., max_length=50, alias="docType") + issued_at: datetime = Field(..., alias="issuedAt") + + model_config = {"populate_by_name": True} + + +class DocumentCreateRequest(BaseModel): + metadata: DocumentMetadata + encrypted_payload: str = Field( + ..., + description="Base64-encoded opaque blob; routing layer must not decode", + alias="encryptedPayload", + ) + + model_config = {"populate_by_name": True} \ No newline at end of file diff --git a/tests/acceptance/test_routing_rules.py b/tests/acceptance/test_routing_rules.py new file mode 100644 index 0000000..4c2c1c0 --- /dev/null +++ b/tests/acceptance/test_routing_rules.py @@ -0,0 +1,20 @@ +"""Acceptance tests for future routing-rules table (TDD red phase).""" +from datetime import UTC, datetime + +import pytest + +from dvd.domain.models import DocumentMetadata + + +@pytest.mark.asyncio +async def test_route_by_reference_prefix_from_rules_table() -> None: + """ADR-001 follow-on: authority-specific rules should override doc_type defaults.""" + meta = DocumentMetadata( + authorityId="DE-BY-001", + referenceNumber="FIN-2026-001", + docType="APPLICATION", + issuedAt=datetime(2026, 1, 15, tzinfo=UTC), + ) + from dvd.adapters.routing_rules import route_document_with_rules + + assert await route_document_with_rules(meta) == "DE-BY-001-FinanceTeam" \ No newline at end of file diff --git a/tests/unit/test_routing.py b/tests/unit/test_routing.py new file mode 100644 index 0000000..cd0801d --- /dev/null +++ b/tests/unit/test_routing.py @@ -0,0 +1,28 @@ +from datetime import UTC, datetime + +import pytest + +from dvd.adapters.routing import route_document +from dvd.domain.models import DocumentMetadata + + +@pytest.mark.asyncio +async def test_route_notice_to_notice_team() -> None: + meta = DocumentMetadata( + authorityId="DE-BY-001", + referenceNumber="AZ-123", + docType="NOTICE", + issuedAt=datetime(2026, 1, 15, tzinfo=UTC), + ) + assert await route_document(meta) == "DE-BY-001-NoticeTeam" + + +@pytest.mark.asyncio +async def test_route_default_team_for_other_doc_types() -> None: + meta = DocumentMetadata( + authorityId="DE-BY-001", + referenceNumber="AZ-456", + docType="APPLICATION", + issuedAt=datetime(2026, 1, 15, tzinfo=UTC), + ) + assert await route_document(meta) == "DE-BY-001-DefaultTeam" \ No newline at end of file diff --git a/workplans/DVD-WP-0002-main-codebase-tdd-kickoff.md b/workplans/DVD-WP-0002-main-codebase-tdd-kickoff.md index d459cb9..0cea677 100644 --- a/workplans/DVD-WP-0002-main-codebase-tdd-kickoff.md +++ b/workplans/DVD-WP-0002-main-codebase-tdd-kickoff.md @@ -4,11 +4,12 @@ type: workplan title: "Main codebase TDD integration kickoff" domain: government repo: direkt-vermittlung-de -status: ready +status: finished owner: codex topic_slug: personhood created: "2026-07-08" updated: "2026-07-08" +state_hub_workstream_id: "dde8f640-0bf7-4258-bb88-3be3f408e63f" --- # Main codebase TDD integration kickoff @@ -19,8 +20,11 @@ Start unified `/src` production codebase with ADR-governed TDD interfaces, conso ```task id: DVD-WP-0002-T01 -status: todo +status: done priority: high +state_hub_task_id: "90d0ab6a-6c86-4025-b8bf-2f63e3cd8cc3" ``` +Result 2026-07-08: Created src/dvd domain+routing; pyproject.toml; unit tests pass; acceptance routing-rules test fails (TDD red). + Create `/src` skeleton, port split-payload routing interfaces from chatgpt5 prototype, and add first failing acceptance tests per docs/WORKPLAN_MainCodebase_Integration.md.