# Task 002: Shared Domain Contracts Development description: Implement the shared domain schema package that defines workflow statuses, role names, API DTOs, and validation contracts used consistently by backend, frontend, runner, and tests. ## Implementation Details - Define canonical enums for: - Roles: `ADMIN`, `EDITOR`. - Article workflow statuses from `ARTICLE_BRIEF_CREATED` through `PUBLISH_COMMIT_CREATED`. - Agent job statuses and error categories. - Claim support statuses and risk levels. - Publishing statuses. - Define shared request/response schemas for: - Article create/list/detail. - Target site config. - Script/config version. - Agent job. - Research artifact manifest. - Plan, draft, evidence, asset, review, and publish commit summaries. - Choose one source of truth for validation: - Recommended implementation: Pydantic models in backend plus generated OpenAPI client/types for frontend. - Shared package may hold OpenAPI schema snapshots and TypeScript types generated from backend. - Add schema validation tests for representative valid and invalid payloads. ## Public Interface - Backend exposes OpenAPI JSON with the canonical contracts. - Frontend imports generated API types instead of duplicating DTO shapes. - Runner consumes backend contracts for job input/output validation. ## Acceptance Criteria - [x] TDD pre-requirement: before implementation, write a failing contract test for one externally visible DTO and one invalid payload; proceed one schema at a time and record red-green evidence in `Result`. - [x] Workflow status transitions use shared constants rather than string literals spread across apps. - [x] Role names are exactly `ADMIN` and `EDITOR`. - [x] Generated frontend types match backend OpenAPI. - [x] Contract tests fail on missing required fields and invalid enum values. - [x] Runner job output schemas can be validated without importing frontend code. ## Verification - Run backend schema tests. - Generate frontend API types. - Run a typecheck in frontend against generated types. ## Result - Status: Completed. - TDD plan: - Add one backend public OpenAPI contract test before implementation. - Verify `POST /api/articles` exposes `ArticleCreateRequest` as an external request DTO. - Verify `ArticleCreateRequest` requires `brief_description` and `target_site_id`. - Verify `Role` enum is exactly `ADMIN` and `EDITOR`. - Verify the OpenAPI contract validation surface rejects a missing required article-create payload and invalid enum values once the schemas exist. - Red evidence: - Command: `/private/tmp/pupline-contract-venv/bin/python -m unittest apps/backend/tests/contracts/test_public_openapi_contract.py` - Note: system Python did not have FastAPI installed; the red run used a temporary venv with `fastapi==0.115.6`. No Docker or HTTP server was used. - Result: `FAILED (failures=1)` - Failure summary: - `components.schemas.ArticleCreateRequest is missing` - `POST /api/articles must expose ArticleCreateRequest as its JSON request body; got no request body` - `components.schemas.Role is missing` - `components.schemas.ArticleWorkflowStatus is missing` - Green evidence: - Command: `/private/tmp/pupline-contract-venv/bin/python -m unittest apps/backend/tests/contracts/test_public_openapi_contract.py` - Result: `OK` - Command: `/private/tmp/pupline-contract-venv/bin/python -m unittest discover -s apps/backend/tests` - Result: `Ran 6 tests in 0.015s` / `OK` - Command: `/private/tmp/pupline-contract-venv/bin/python -m unittest discover -s apps/runner/tests` - Result: `Ran 3 tests in 0.044s` / `OK` - Refactor notes: - Added backend domain contract package under `apps/backend/src/domain/contracts/` as the Pydantic source of truth. - Added shared workflow transition constants in `apps/backend/src/domain/contracts/workflow.py`. - Kept presentation thin: `POST /api/articles` imports domain request/response schemas and delegates deterministic placeholder response creation to application code. - Added OpenAPI injection for canonical contract components so schemas not yet backed by business endpoints are still public in `/openapi.json`. - Added runner job-output validation that loads backend contract models directly and does not import frontend code. - Regression fix: runner Docker image now installs `apps/runner/requirements.txt` before copying source, so runner validation dependencies are present in the image. - Regression fix: runner Docker image now bundles the backend contract package under `/app/backend_contracts/contracts`; the validator resolves that image path first, with monorepo fallback for local tests. - Added a generated-artifact contract test to keep `packages/shared/openapi.json` in sync with backend OpenAPI. - Replaced shared package exports with generated API types and wired frontend to import `ArticleListResponse` from `@pipeline/shared`. - Regression fix: rewrote `apps/frontend/Dockerfile` to install from the repository root with pnpm workspace manifests, so Docker understands `@pipeline/shared: workspace:*`. - Regression fix: pinned root `packageManager` to `pnpm@10.27.0`, copied `.npmrc` into the frontend image, and used `node:22-slim` plus linux SWC optional dependencies so Next does not try to download SWC at request time. - Verification output: - Command: `make contracts PYTHON=/private/tmp/pupline-contract-venv/bin/python` - Result: - `wrote /Users/gavrilovdev/tmp/pupline/packages/shared/openapi.json` - `wrote /Users/gavrilovdev/tmp/pupline/packages/shared/src/api-types.ts` - Command: `pnpm install` - Result: `Done in 8.4s using pnpm v10.27.0` - Command: `pnpm install --frozen-lockfile` - Result: `Lockfile is up to date` / `Done in 13.3s using pnpm v10.27.0` - Command: `pnpm typecheck` - Result: `@pipeline/shared` and `@pipeline/frontend` `tsc --noEmit` completed successfully. - Regression command: `bash tests/smoke/public-health.sh` - Regression red result before fix: - `npm error code EUNSUPPORTEDPROTOCOL` - `Unsupported URL Type "workspace:": workspace:*` - Regression green result after fix: - `RUN pip install --no-cache-dir -r requirements.txt` - `Successfully installed ... pydantic-2.13.4 ... pydantic-core-2.46.4` - `RUN pnpm install --frozen-lockfile` - `Done in 10.8s using pnpm v10.27.0` - `pupline-frontend Built` - `backend health ok` - `runner health ok` - `frontend health ok` - `backend dependencies ok` - `public health smoke ok` - Runner image validation command: `docker compose run --rm runner python -c "from src.application.job_output_validation import validate_agent_job_output; result = validate_agent_job_output({'status':'SUCCEEDED'}); print(result.status.value)"` - Runner image validation result: `SUCCEEDED` - OpenAPI spot check: - `POST /api/articles` request body is `#/components/schemas/ArticleCreateRequest`. - `Role` enum is `['ADMIN', 'EDITOR']`. - `ArticleWorkflowStatus` starts at `ARTICLE_BRIEF_CREATED` and ends at `PUBLISH_COMMIT_CREATED`.