openapi: 3.1.0
info:
  title: Test Suite Governance Logical Contract
  version: 1.0.0
  summary: Logical run and reporting contract for checked-in test lanes.
  description: |
    This is a logical contract for repository tooling, not a promise that a new
    HTTP service will be introduced. It documents the expected semantics of lane
    execution and lane reporting so commands, tasks, or wrappers can remain
    consistent as the implementation evolves.
x-logical-contract: true
servers:
  - url: https://tenantatlas.local/logical
paths:
  /test-lanes/{laneId}/runs:
    post:
      summary: Start a lane run through a checked-in command entry point.
      operationId: startTestLaneRun
      parameters:
        - name: laneId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/LaneId'
      responses:
        '202':
          description: Lane execution accepted and resolved to a checked-in command path.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LaneRunAccepted'
  /test-lanes/{laneId}/reports/latest:
    get:
      summary: Read the most recent report produced for a lane.
      operationId: getLatestTestLaneReport
      parameters:
        - name: laneId
          in: path
          required: true
          schema:
            $ref: '#/components/schemas/LaneId'
      responses:
        '200':
          description: Latest report summary for the requested lane.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LaneReport'
components:
  schemas:
    LaneId:
      type: string
      enum:
        - fast-feedback
        - confidence
        - heavy-governance
        - browser
        - profiling
        - junit
    GovernanceClass:
      type: string
      enum:
        - fast
        - confidence
        - heavy
        - support
    ArtifactMode:
      type: string
      enum:
        - summary
        - junit-xml
        - profile-top
        - budget-report
    BudgetStatus:
      type: string
      enum:
        - within-budget
        - warning
        - over-budget
    BudgetBaselineSource:
      type: string
      enum:
        - measured-current-suite
        - measured-lane
    BudgetEnforcement:
      type: string
      enum:
        - report-only
        - warn
        - hard-fail
    BudgetLifecycleState:
      type: string
      enum:
        - draft
        - measured
        - documented
        - enforced
    LaneRunAccepted:
      type: object
      additionalProperties: false
      required:
        - laneId
        - governanceClass
        - commandRef
        - parallelMode
        - artifactModes
        - includedFamilies
        - excludedFamilies
        - budgetThresholdSeconds
        - budgetBaselineSource
        - budgetEnforcement
        - budgetLifecycleState
      properties:
        laneId:
          $ref: '#/components/schemas/LaneId'
        governanceClass:
          $ref: '#/components/schemas/GovernanceClass'
        commandRef:
          type: string
          description: Checked-in command reference, such as a Composer script name.
        parallelMode:
          type: string
          enum:
            - required
            - optional
            - forbidden
        artifactModes:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ArtifactMode'
        includedFamilies:
          type: array
          minItems: 1
          items:
            type: string
        excludedFamilies:
          type: array
          items:
            type: string
        nonBrowserFeatureIntegrationSelectorCount:
          type: integer
          minimum: 0
        includedSelectors:
          type: array
          items:
            type: string
        excludedSelectors:
          type: array
          items:
            type: string
        budgetThresholdSeconds:
          type: integer
          minimum: 1
        budgetBaselineSource:
          $ref: '#/components/schemas/BudgetBaselineSource'
        budgetEnforcement:
          $ref: '#/components/schemas/BudgetEnforcement'
        budgetLifecycleState:
          $ref: '#/components/schemas/BudgetLifecycleState'
        baselineDeltaTargetPercent:
          type: integer
          minimum: 1
          maximum: 100
        artifactDirectory:
          type: string
          const: storage/logs/test-lanes
          description: App-root relative directory for emitted artifacts. The first slice fixes this to storage/logs/test-lanes.
      allOf:
        - if:
            properties:
              laneId:
                const: fast-feedback
          then:
            required:
              - baselineDeltaTargetPercent
            properties:
              baselineDeltaTargetPercent:
                const: 50
              parallelMode:
                const: required
              excludedFamilies:
                allOf:
                  - contains:
                      const: browser
                  - contains:
                      const: heavy-governance
        - if:
            properties:
              laneId:
                const: confidence
          then:
            properties:
              parallelMode:
                const: required
              includedFamilies:
                allOf:
                  - contains:
                      const: unit
                  - contains:
                      const: non-browser-feature-integration
              nonBrowserFeatureIntegrationSelectorCount:
                minimum: 1
              excludedFamilies:
                allOf:
                  - contains:
                      const: browser
                  - contains:
                      const: heavy-governance
        - if:
            properties:
              laneId:
                const: profiling
          then:
            properties:
              governanceClass:
                const: support
              parallelMode:
                const: forbidden
              artifactModes:
                contains:
                  const: profile-top
        - if:
            properties:
              laneId:
                const: junit
          then:
            properties:
              governanceClass:
                const: support
              artifactModes:
                contains:
                  const: junit-xml
        - if:
            properties:
              laneId:
                const: browser
          then:
            properties:
              includedFamilies:
                contains:
                  const: browser
    LaneReport:
      type: object
      additionalProperties: false
      required:
        - laneId
        - finishedAt
        - wallClockSeconds
        - budgetThresholdSeconds
        - budgetBaselineSource
        - budgetEnforcement
        - budgetLifecycleState
        - budgetStatus
        - slowestEntries
        - familyBudgetEvaluations
        - artifacts
      properties:
        laneId:
          $ref: '#/components/schemas/LaneId'
        finishedAt:
          type: string
          format: date-time
        wallClockSeconds:
          type: number
          minimum: 0
        budgetThresholdSeconds:
          type: integer
          minimum: 1
        budgetBaselineSource:
          $ref: '#/components/schemas/BudgetBaselineSource'
        budgetEnforcement:
          $ref: '#/components/schemas/BudgetEnforcement'
        budgetLifecycleState:
          $ref: '#/components/schemas/BudgetLifecycleState'
        baselineDeltaTargetPercent:
          type: integer
          minimum: 1
          maximum: 100
        budgetStatus:
          $ref: '#/components/schemas/BudgetStatus'
        slowestEntries:
          type: array
          minItems: 10
          items:
            $ref: '#/components/schemas/SlowEntry'
        familyBudgetEvaluations:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/FamilyBudgetEvaluation'
        artifacts:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/ArtifactRecord'
      allOf:
        - if:
            properties:
              laneId:
                const: fast-feedback
          then:
            properties:
              baselineDeltaTargetPercent:
                const: 50
        - if:
            properties:
              laneId:
                const: profiling
          then:
            properties:
              artifacts:
                contains:
                  type: object
                  required:
                    - artifactMode
                  properties:
                    artifactMode:
                      const: profile-top
        - if:
            properties:
              laneId:
                const: junit
          then:
            properties:
              artifacts:
                contains:
                  type: object
                  required:
                    - artifactMode
                  properties:
                    artifactMode:
                      const: junit-xml
    SlowEntry:
      type: object
      additionalProperties: false
      required:
        - subject
        - durationSeconds
        - laneId
      properties:
        subject:
          type: string
        durationSeconds:
          type: number
          minimum: 0
        laneId:
          $ref: '#/components/schemas/LaneId'
        familyId:
          type: string
    ArtifactRecord:
      type: object
      additionalProperties: false
      required:
        - artifactMode
        - relativePath
      properties:
        artifactMode:
          $ref: '#/components/schemas/ArtifactMode'
        relativePath:
          type: string
          pattern: ^storage/logs/test-lanes/
        machineReadable:
          type: boolean
    FamilyBudgetEvaluation:
      type: object
      additionalProperties: false
      required:
        - familyId
        - thresholdSeconds
        - baselineSource
        - enforcement
        - lifecycleState
        - measuredSeconds
        - budgetStatus
      properties:
        familyId:
          type: string
        thresholdSeconds:
          type: integer
          minimum: 1
        baselineSource:
          $ref: '#/components/schemas/BudgetBaselineSource'
        enforcement:
          $ref: '#/components/schemas/BudgetEnforcement'
        lifecycleState:
          $ref: '#/components/schemas/BudgetLifecycleState'
        measuredSeconds:
          type: number
          minimum: 0
        budgetStatus:
          $ref: '#/components/schemas/BudgetStatus'
        matchedSelectors:
          type: array
          items:
            type: string