<?xml version="1.0" encoding="UTF-8"?>
<!--
Story Context: STORY-072 - Update DTOs and API Schemas
Generated: 2025-12-01
Epic: Epic 012 - Test Environments and Known Bugs
-->

<story-context>
  <metadata>
    <story-id>story-072-dtos-and-schemas</story-id>
    <story-title>Update DTOs and API Schemas</story-title>
    <epic-id>epic-012</epic-id>
    <epic-title>Test Environments and Known Bugs</epic-title>
    <status>drafted</status>
    <prerequisites>
      <story>story-071-repository-read-paths</story>
    </prerequisites>
    <generated-date>2025-12-01</generated-date>
    <project>testio-mcp</project>
  </metadata>

  <!-- ================================================================ -->
  <!-- STORY SPECIFICATION                                               -->
  <!-- ================================================================ -->
  <story-specification>
    <user-story>
      As a **developer**,
      I want **the data transfer objects and API schemas to include test_environment and known fields**,
      so that **type safety is maintained throughout the service layer**.
    </user-story>

    <acceptance-criteria>
      <criterion id="AC1">
        <description>ServiceTestDTO includes test_environment</description>
        <given>Reviewing `ServiceTestDTO` in `src/testio_mcp/schemas/dtos.py`</given>
        <when>Schema is defined</when>
        <then>It includes `test_environment: dict[str, Any] | None` field</then>
      </criterion>

      <criterion id="AC2">
        <description>ServiceBugDTO includes known</description>
        <given>Reviewing `ServiceBugDTO` in `src/testio_mcp/schemas/dtos.py`</given>
        <when>Schema is defined</when>
        <then>It includes `known: bool = False` field</then>
      </criterion>

      <criterion id="AC3">
        <description>TestSummary and TestDetails include test_environment</description>
        <given>Reviewing `TestSummary` and `TestDetails` in `src/testio_mcp/schemas/api/tests.py`</given>
        <when>Schemas are defined</when>
        <then>Both schemas include `test_environment: dict[str, Any] | None` field</then>
      </criterion>

      <criterion id="AC4">
        <description>BugSummary includes known_bugs_count</description>
        <given>Reviewing `BugSummary` in `src/testio_mcp/schemas/api/bugs.py`</given>
        <when>Schema is defined</when>
        <then>It includes `known_bugs_count: int` field with default 0</then>
      </criterion>

      <criterion id="AC5">
        <description>RecentBug includes known</description>
        <given>Reviewing `RecentBug` in `src/testio_mcp/schemas/api/bugs.py`</given>
        <when>Schema is defined</when>
        <then>It includes `known: bool` field with default False</then>
      </criterion>
    </acceptance-criteria>

    <tasks>
      <task id="T1" status="pending">
        <title>Update Data Transfer Objects (DTOs)</title>
        <acceptance-criteria>AC1, AC2</acceptance-criteria>
        <subtasks>
          <subtask>Add `test_environment: dict[str, Any] | None` to `ServiceTestDTO` in `src/testio_mcp/schemas/dtos.py`</subtask>
          <subtask>Add `known: bool = False` to `ServiceBugDTO` in `src/testio_mcp/schemas/dtos.py`</subtask>
          <subtask>Add unit tests verifying DTO field presence and types</subtask>
        </subtasks>
      </task>

      <task id="T2" status="pending">
        <title>Update Test API Schemas</title>
        <acceptance-criteria>AC3</acceptance-criteria>
        <subtasks>
          <subtask>Add `test_environment: dict[str, Any] | None` to `TestSummary` in `src/testio_mcp/schemas/api/tests.py`</subtask>
          <subtask>Add `test_environment: dict[str, Any] | None` to `TestDetails` in `src/testio_mcp/schemas/api/tests.py`</subtask>
          <subtask>Add unit tests verifying schema serialization with new field</subtask>
        </subtasks>
      </task>

      <task id="T3" status="pending">
        <title>Update Bug API Schemas</title>
        <acceptance-criteria>AC4, AC5</acceptance-criteria>
        <subtasks>
          <subtask>Add `known_bugs_count: int = 0` to `BugSummary` in `src/testio_mcp/schemas/api/bugs.py`</subtask>
          <subtask>Add `known: bool = False` to `RecentBug` in `src/testio_mcp/schemas/api/bugs.py`</subtask>
          <subtask>Add unit tests verifying schema serialization with new fields</subtask>
        </subtasks>
      </task>

      <task id="T4" status="pending">
        <title>Update Transformers</title>
        <acceptance-criteria>All</acceptance-criteria>
        <subtasks>
          <subtask>Update `test_transformers.py` to map `test_environment` from repository data to DTOs</subtask>
          <subtask>Update `bug_transformers.py` to map `known` from repository data to DTOs</subtask>
          <subtask>Update `bug_transformers.py` to calculate `known_bugs_count` in bug summary aggregation</subtask>
          <subtask>Add unit tests verifying transformer logic for new fields</subtask>
        </subtasks>
      </task>
    </tasks>
  </story-specification>

  <!-- ================================================================ -->
  <!-- EPIC CONTEXT                                                      -->
  <!-- ================================================================ -->
  <epic-context>
    <epic-overview>
      <title>Epic 012: Test Environments and Known Bugs</title>
      <goal>Enable users to analyze test quality by environment and identify known/expected bugs</goal>
      <user-value>
        CSMs and QA leads can now:
        - Filter and group quality reports by test environment (Production, Staging, QA)
        - Distinguish between new bugs and known/expected issues
        - Make better triage decisions with complete bug context
      </user-value>
    </epic-overview>

    <epic-story-sequence>
      <story id="story-069" status="done">
        <title>Add Database Columns for Test Environment and Known Bug</title>
        <summary>Database schema updated with test_environment (JSON) and known (BOOLEAN) columns</summary>
      </story>
      <story id="story-070" status="done">
        <title>Update Repository Write Paths for New Columns</title>
        <summary>Repository layer extracts and stores test_environment and known on writes</summary>
      </story>
      <story id="story-071" status="done">
        <title>Update Repository Read Paths to Thread New Columns</title>
        <summary>Repository layer surfaces test_environment and known on reads</summary>
      </story>
      <story id="story-072" status="drafted" current="true">
        <title>Update DTOs and API Schemas</title>
        <summary>DTOs and API schemas include test_environment and known fields for type safety</summary>
      </story>
      <story id="story-073" status="backlog">
        <title>Update Services for Test Environment and Known Bugs</title>
        <summary>Service layer exposes test_environment and known_bugs_count</summary>
      </story>
      <story id="story-074" status="backlog">
        <title>Update Product Quality Report Tool</title>
        <summary>Product quality report includes test_environment information</summary>
      </story>
      <story id="story-075" status="backlog">
        <title>Add Analytics Dimensions for Test Environment and Known Bug</title>
        <summary>Analytics capabilities support test_environment and known_bug dimensions</summary>
      </story>
    </epic-story-sequence>

    <functional-requirements>
      <requirement id="FR1">
        <description>Store test environment information (id + title) in tests table</description>
        <stories>story-069, story-070, story-071</stories>
      </requirement>
      <requirement id="FR2">
        <description>Store known bug status (boolean) in bugs table</description>
        <stories>story-069, story-070, story-071</stories>
      </requirement>
      <requirement id="FR3">
        <description>Expose test_environment and known_bug as analytics dimensions</description>
        <stories>story-075</stories>
      </requirement>
      <requirement id="FR4">
        <description>Surface test_environment in get_test_summary, list_tests, get_product_quality_report</description>
        <stories>story-073, story-074</stories>
      </requirement>
      <requirement id="FR5">
        <description>Surface known_bugs_count in test summary bug statistics</description>
        <stories>story-073</stories>
      </requirement>
      <requirement id="FR7">
        <description>Fix read paths to thread new columns through repository layer</description>
        <stories>story-071</stories>
      </requirement>
    </functional-requirements>
  </epic-context>

  <!-- ================================================================ -->
  <!-- ARCHITECTURE CONTEXT                                              -->
  <!-- ================================================================ -->
  <architecture-context>
    <data-flow>
      <layer name="Database">
        <description>SQLite database with test_environment (JSON) and known (BOOLEAN) columns</description>
        <status>COMPLETE (STORY-069)</status>
      </layer>
      <layer name="Repository">
        <description>Repository layer writes and reads test_environment and known columns</description>
        <status>COMPLETE (STORY-070, STORY-071)</status>
      </layer>
      <layer name="DTOs">
        <description>Internal data transfer objects with type-safe field definitions</description>
        <status>IN PROGRESS (STORY-072 - THIS STORY)</status>
      </layer>
      <layer name="API Schemas">
        <description>External-facing API models for MCP tools and REST endpoints</description>
        <status>IN PROGRESS (STORY-072 - THIS STORY)</status>
      </layer>
      <layer name="Services">
        <description>Business logic layer exposing test_environment and known_bugs_count</description>
        <status>PENDING (STORY-073)</status>
      </layer>
      <layer name="Tools">
        <description>MCP tools surfacing test_environment and known_bugs_count to users</description>
        <status>PENDING (STORY-074)</status>
      </layer>
    </data-flow>

    <schema-layer-pattern>
      <principle>Separation of Concerns</principle>
      <description>
        The codebase maintains strict separation between internal and external data representations:

        1. **DTOs (src/testio_mcp/schemas/dtos.py):**
           - Internal service layer representations
           - Use database field names (id, not test_id)
           - Provide type safety for transformers
           - Example: ServiceTestDTO, ServiceBugDTO

        2. **API Schemas (src/testio_mcp/schemas/api/):**
           - External-facing models for MCP tools and REST endpoints
           - Use semantic field names (test_id, product_id)
           - Organized by domain (tests.py, bugs.py, products.py, features.py, shared.py)
           - Example: TestSummary, TestDetails, BugSummary, RecentBug

        3. **Transformers (src/testio_mcp/transformers/):**
           - Anti-Corruption Layer (ACL) pattern
           - Convert between DTOs and API schemas
           - Handle field name mapping (id → test_id)
           - Example: to_test_summary(), transform_api_bug_to_orm()
      </description>
    </schema-layer-pattern>

    <pydantic-patterns>
      <pattern name="Field Defaults">
        <description>Use Pydantic Field() with default values for optional fields</description>
        <example>
          test_environment: dict[str, Any] | None = Field(default=None, description="...")
          known: bool = Field(default=False, description="...")
          known_bugs_count: int = Field(default=0, description="...")
        </example>
      </pattern>

      <pattern name="Type Safety">
        <description>All fields must have strict type hints (dict[str, Any], not dict)</description>
        <rationale>Strict mypy compliance enforced project-wide</rationale>
      </pattern>

      <pattern name="Backward Compatibility">
        <description>New fields with defaults ensure existing code continues to work</description>
        <rationale>Additive changes only - no breaking modifications</rationale>
      </pattern>

      <pattern name="extra='ignore'">
        <description>DTOs use extra="ignore" for forward compatibility with API evolution</description>
        <location>ServiceTestDTO, ServiceProductDTO (dtos.py)</location>
      </pattern>
    </pydantic-patterns>

    <transformer-pattern>
      <description>
        Transformers convert between repository data and API models:

        Repository → DTO Validation → API Schema

        Example flow:
        1. Repository returns dict with 'id', 'test_environment', 'known'
        2. Transformer validates with ServiceTestDTO(**test)
        3. Transformer maps to TestSummary(test_id=dto.id, test_environment=dto.test_environment, ...)
      </description>

      <files>
        <file>src/testio_mcp/transformers/test_transformers.py</file>
        <file>src/testio_mcp/transformers/bug_transformers.py</file>
      </files>

      <key-functions>
        <function>to_test_summary(test: dict[str, Any]) → TestSummary</function>
        <function>to_test_summary_list(tests: list[dict[str, Any]]) → list[TestSummary]</function>
        <function>transform_api_bug_to_orm(api_bug: dict[str, Any]) → BugOrmData</function>
      </key-functions>
    </transformer-pattern>
  </architecture-context>

  <!-- ================================================================ -->
  <!-- CODE CONTEXT                                                      -->
  <!-- ================================================================ -->
  <code-context>
    <current-dto-structure>
      <file>src/testio_mcp/schemas/dtos.py</file>
      <schema name="ServiceTestDTO">
        <existing-fields>
          id: int
          title: str
          goal: str | None
          status: str
          review_status: str | None
          testing_type: str
          duration: int | None
          starts_at: str | None
          ends_at: str | None
        </existing-fields>
        <missing-field>test_environment: dict[str, Any] | None</missing-field>
      </schema>

      <schema name="ServiceBugDTO">
        <existing-fields>
          id: int
          title: str
          severity: str
          status: str
        </existing-fields>
        <missing-field>known: bool = False</missing-field>
      </schema>
    </current-dto-structure>

    <current-api-schema-structure>
      <file>src/testio_mcp/schemas/api/tests.py</file>
      <schema name="TestSummary">
        <existing-fields>
          test_id: int
          title: str
          goal: str | None
          status: str
          review_status: str | None
          testing_type: str
          duration: int | None
          starts_at: str | None
          ends_at: str | None
        </existing-fields>
        <missing-field>test_environment: dict[str, Any] | None</missing-field>
      </schema>

      <schema name="TestDetails">
        <existing-fields>
          id: int
          title: str
          goal: str | None
          instructions: str | None
          out_of_scope: str | None
          testing_type: str
          duration: int | None
          status: str
          review_status: str | None
          requirements_summary: list[PlatformRequirement] | None
          enable_low: bool
          enable_high: bool
          enable_critical: bool
          starts_at: str | None
          ends_at: str | None
          product: ProductInfo
          feature: FeatureInfo | None
        </existing-fields>
        <missing-field>test_environment: dict[str, Any] | None</missing-field>
      </schema>

      <file>src/testio_mcp/schemas/api/bugs.py</file>
      <schema name="BugSummary">
        <existing-fields>
          total_count: int
          by_severity: dict[str, int]
          by_status: dict[str, int]
          by_platform: dict[str, dict[str, int]]
          acceptance_rates: AcceptanceRates | None
          recent_bugs: list[RecentBug]
        </existing-fields>
        <missing-field>known_bugs_count: int = 0</missing-field>
      </schema>

      <schema name="RecentBug">
        <existing-fields>
          id: str
          title: str
          severity: str
          status: str
          created_at: str | None
        </existing-fields>
        <missing-field>known: bool = False</missing-field>
      </schema>
    </current-api-schema-structure>

    <transformer-updates-needed>
      <file>src/testio_mcp/transformers/test_transformers.py</file>
      <function name="to_test_summary">
        <current-mapping>
          test_id=dto.id,
          title=dto.title,
          goal=dto.goal,
          status=dto.status,
          review_status=dto.review_status,
          testing_type=dto.testing_type,
          duration=dto.duration,
          starts_at=dto.starts_at,
          ends_at=dto.ends_at,
        </current-mapping>
        <missing-mapping>test_environment=dto.test_environment,</missing-mapping>
      </function>

      <file>src/testio_mcp/transformers/bug_transformers.py</file>
      <note>
        BugOrmData TypedDict already includes 'known: bool | None' (added in STORY-070).
        No changes needed for bug transformers in this story.

        known_bugs_count calculation will be handled in service layer (STORY-073),
        not in transformers.
      </note>
    </transformer-updates-needed>

    <files-to-modify>
      <file priority="high">src/testio_mcp/schemas/dtos.py</file>
      <file priority="high">src/testio_mcp/schemas/api/tests.py</file>
      <file priority="high">src/testio_mcp/schemas/api/bugs.py</file>
      <file priority="medium">src/testio_mcp/transformers/test_transformers.py</file>
    </files-to-modify>

    <files-no-changes-needed>
      <file reason="BugOrmData already includes 'known' field (STORY-070)">
        src/testio_mcp/transformers/bug_transformers.py
      </file>
    </files-no-changes-needed>
  </code-context>

  <!-- ================================================================ -->
  <!-- TESTING CONTEXT                                                   -->
  <!-- ================================================================ -->
  <testing-context>
    <test-strategy>
      <unit-tests>
        <description>Schema validation and transformer logic tests</description>
        <coverage-target>100% for new fields</coverage-target>
        <test-types>
          <type>Schema serialization/deserialization with new fields</type>
          <type>Transformer mapping verification</type>
          <type>Default value validation</type>
          <type>Type safety checks (mypy compliance)</type>
        </test-types>
      </unit-tests>

      <backward-compatibility>
        <description>Existing tests must pass without modification</description>
        <rationale>New fields have defaults - additive changes only</rationale>
      </backward-compatibility>
    </test-strategy>

    <test-files-to-create>
      <file>tests/unit/test_schemas_dtos.py</file>
      <file>tests/unit/test_schemas_api_tests.py</file>
      <file>tests/unit/test_schemas_api_bugs.py</file>
      <file>tests/unit/test_transformers_test.py</file>
    </test-files-to-create>

    <test-patterns>
      <pattern name="Schema Validation">
        <description>Verify Pydantic models accept new fields and serialize correctly</description>
        <example>
          def test_service_test_dto_with_test_environment():
              dto = ServiceTestDTO(
                  id=123,
                  title="Test",
                  status="running",
                  testing_type="rapid",
                  test_environment={"id": 1, "title": "Production"}
              )
              assert dto.test_environment == {"id": 1, "title": "Production"}
        </example>
      </pattern>

      <pattern name="Default Values">
        <description>Verify fields have correct defaults when omitted</description>
        <example>
          def test_service_bug_dto_known_defaults_to_false():
              dto = ServiceBugDTO(id=1, title="Bug", severity="high", status="open")
              assert dto.known is False
        </example>
      </pattern>

      <pattern name="Transformer Mapping">
        <description>Verify transformers correctly map new fields</description>
        <example>
          def test_to_test_summary_maps_test_environment():
              test = {
                  "id": 123,
                  "title": "Test",
                  "status": "running",
                  "testing_type": "rapid",
                  "test_environment": {"id": 1, "title": "Production"}
              }
              summary = to_test_summary(test)
              assert summary.test_environment == {"id": 1, "title": "Production"}
        </example>
      </pattern>
    </test-patterns>
  </testing-context>

  <!-- ================================================================ -->
  <!-- LEARNINGS FROM PREVIOUS STORIES                                   -->
  <!-- ================================================================ -->
  <previous-story-learnings>
    <story id="story-071" status="done">
      <title>Update Repository Read Paths to Thread New Columns</title>
      <key-insights>
        <insight>Repository layer now provides test_environment and known in output dictionaries</insight>
        <insight>Column values override JSON blob values (column is source of truth)</insight>
        <insight>NULL handling: Repository gracefully handles NULL columns (preserves JSON value)</insight>
        <insight>11 new unit tests added, all passing. Zero regressions (621 unit tests pass)</insight>
      </key-insights>

      <files-modified>
        <file>src/testio_mcp/repositories/test_repository.py</file>
        <file>src/testio_mcp/repositories/bug_repository.py</file>
      </files-modified>

      <critical-dependency>
        This story (072) completes the type safety layer by ensuring DTOs and API schemas
        match the data structure returned by repositories.
      </critical-dependency>

      <review-status>Approved by senior developer (leoric). All ACs met. Ready for next story.</review-status>
    </story>

    <story id="story-070" status="done">
      <title>Update Repository Write Paths for New Columns</title>
      <key-insights>
        <insight>test_environment is sanitized to only store {id, title} - no PII or nested objects</insight>
        <insight>BugOrmData TypedDict updated to include known: bool with default False</insight>
        <insight>Transformer pattern established for extracting fields from API responses</insight>
      </key-insights>

      <files-modified>
        <file>src/testio_mcp/transformers/bug_transformers.py</file>
        <file>src/testio_mcp/repositories/test_repository.py</file>
        <file>src/testio_mcp/repositories/bug_repository.py</file>
      </files-modified>
    </story>

    <story id="story-069" status="done">
      <title>Add Database Columns for Test Environment and Known Bug</title>
      <key-insights>
        <insight>Database schema updated with test_environment (JSON) and known (BOOLEAN) columns</insight>
        <insight>Migration includes backfill from existing JSON blobs</insight>
        <insight>server_default="0" for known column handles concurrent inserts during migration</insight>
      </key-insights>
    </story>
  </previous-story-learnings>

  <!-- ================================================================ -->
  <!-- CODING STANDARDS                                                  -->
  <!-- ================================================================ -->
  <coding-standards>
    <type-safety>
      <requirement>All fields must have strict type hints (dict[str, Any], not dict)</requirement>
      <requirement>Strict mypy compliance enforced project-wide</requirement>
      <requirement>No implicit Any types allowed</requirement>
    </type-safety>

    <pydantic-standards>
      <requirement>Use Field() for all fields with descriptions</requirement>
      <requirement>Provide default values for optional fields</requirement>
      <requirement>Use ConfigDict(extra="ignore") for DTOs (forward compatibility)</requirement>
      <requirement>Document validation strategy in docstrings</requirement>
    </pydantic-standards>

    <testing-standards>
      <requirement>Unit tests for all new schema fields</requirement>
      <requirement>Verify serialization/deserialization</requirement>
      <requirement>Test default values</requirement>
      <requirement>Ensure backward compatibility (existing tests pass)</requirement>
      <requirement>Coverage target: ≥85% overall, ≥90% for services</requirement>
    </testing-standards>

    <documentation-standards>
      <requirement>Google-style docstrings for all public classes</requirement>
      <requirement>Field descriptions in Pydantic Field()</requirement>
      <requirement>Update schema module docstrings if needed</requirement>
    </documentation-standards>

    <code-quality>
      <requirement>ruff format (no changes)</requirement>
      <requirement>ruff check (no errors)</requirement>
      <requirement>mypy src/ (zero errors)</requirement>
      <requirement>pytest -m unit (all tests pass)</requirement>
    </code-quality>
  </coding-standards>

  <!-- ================================================================ -->
  <!-- REFERENCES                                                        -->
  <!-- ================================================================ -->
  <references>
    <epic-doc>docs/epics/epic-012-polish.md</epic-doc>
    <story-doc>docs/sprint-artifacts/story-072-dtos-and-schemas.md</story-doc>
    <architecture-doc>docs/architecture/ARCHITECTURE.md</architecture-doc>
    <coding-standards-doc>docs/architecture/CODING-STANDARDS.md</coding-standards-doc>
    <testing-doc>docs/architecture/TESTING.md</testing-doc>

    <related-stories>
      <story>docs/sprint-artifacts/story-071-repository-read-paths.md</story>
      <story>docs/stories/story-070-repository-write-paths.md</story>
      <story>docs/stories/story-069-database-columns.md</story>
    </related-stories>

    <adrs>
      <adr>docs/architecture/adrs/ADR-006-service-layer-pattern.md</adr>
    </adrs>
  </references>

  <!-- ================================================================ -->
  <!-- IMPLEMENTATION NOTES                                              -->
  <!-- ================================================================ -->
  <implementation-notes>
    <note priority="high">
      <title>Data Flow Complete After This Story</title>
      <description>
        After STORY-072, the data flow for test_environment and known is complete
        through the type system:

        Database (STORY-069)
          → Repository Write (STORY-070)
          → Repository Read (STORY-071)
          → DTOs (STORY-072 - THIS STORY)
          → API Schemas (STORY-072 - THIS STORY)
          → Services (STORY-073 - NEXT)
          → Tools (STORY-074)

        This story ensures type safety at the DTO and API schema layers.
      </description>
    </note>

    <note priority="medium">
      <title>Transformer Updates Minimal</title>
      <description>
        Only test_transformers.py needs updates to map test_environment.
        bug_transformers.py already handles 'known' field (added in STORY-070).

        known_bugs_count is a derived field calculated in service layer (STORY-073),
        not a direct mapping in transformers.
      </description>
    </note>

    <note priority="medium">
      <title>Backward Compatibility Guaranteed</title>
      <description>
        All new fields have default values:
        - test_environment: dict[str, Any] | None = None
        - known: bool = False
        - known_bugs_count: int = 0

        This ensures existing code continues to work without modification.
        Existing tests should pass without changes.
      </description>
    </note>

    <note priority="low">
      <title>Future Tech Debt</title>
      <description>
        Epic 012 documents architectural debt regarding "Repository Read Pattern
        Standardization" (epic-012-polish.md lines 335-421). The current "override"
        pattern is acknowledged as a workaround. A future tech debt story is suggested
        to standardize the read pattern. This is a strategic decision, not a defect.
      </description>
    </note>
  </implementation-notes>

</story-context>
