Files
content-factory/tasks/002-shared-domain-contracts.md
T

115 lines
7.0 KiB
Markdown

# 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`.