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

7.0 KiB

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

  • 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.
  • Workflow status transitions use shared constants rather than string literals spread across apps.
  • Role names are exactly ADMIN and EDITOR.
  • Generated frontend types match backend OpenAPI.
  • Contract tests fail on missing required fields and invalid enum values.
  • 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.