# adl-lims.yaml
#
# Next-Generation Sequencing LIMS — ADL Domain Definition
# ADL version: 0.3.1
#
# Resources:  Project, Sample, Instrument, Protocol, Run, Analysis
# Relations:  Sample belongsTo Project
#             Project hasMany Sample
#             Run belongsTo Instrument
#             Run belongsTo Protocol
#             Run belongsToMany Sample (via RunSamples junction)
#             Analysis belongsTo Run
#             Run hasMany Analysis
#
# ADL features exercised:
#   CRUD           — Full CRUD on all 6 resources
#   Relationships  — belongsTo (4), hasMany (3), belongsToMany (1 — Run ↔ Sample)
#   State machines — Sample workflow (7-state), Run workflow (5-state),
#                    Analysis workflow (4-state with retry loop)
#   Rich enums     — SampleType (4), SampleStatus (7), RunStatus (5),
#                    InstrumentStatus (4), AnalysisStatus (4), Organism (5),
#                    LibraryPrepKit (5), SequencingPlatform (4)
#   Types          — decimal, percentage, date, datetime, integer, email, boolean,
#                    set, url, text
#   Audit trail    — Project, Sample, Run, Analysis (track workflow transitions)
#   Search         — Project, Sample, Protocol, Run (full-text)
#   Export         — Sample, Run (data export for external reporting)
#   Permissions    — Role-based: admin, pi, lab_tech, authenticated
#
# Roles referenced:
#   admin     — Full access to all resources
#   pi        — Principal investigator — creates projects, views all data
#   lab_tech  — Lab technician — processes samples, operates instruments, runs analyses
#   authenticated — Can list/read all resources
#
# State machine — Sample:
#
#   received ──pass_qc──▶ qc_passed ──begin_library_prep──▶ library_prep
#       ▲         │                                             │
#       │    fail_qc                                   begin_sequencing
#       │         ▼                                             ▼
#       │     qc_failed                                    sequencing
#       │         │                                             │
#       └─resubmit┘                                    begin_analysis
#                                                               ▼
#                                                           analysis
#                                                               │
#                                                           complete
#                                                               ▼
#                                                           complete (final)
#
# State machine — Run:
#
#   planned ──begin_loading──▶ loading ──start_run──▶ running ──complete──▶ completed
#                                                        │
#                                                       fail
#                                                        ▼
#                                                      failed
#
# State machine — Analysis:
#
#   queued ──start──▶ running ──complete──▶ completed
#                        │
#                       fail
#                        ▼
#                      failed ──retry──▶ queued (loop)

domain:
  name: lims
  version: "1.0.0"
  description: >
    Next-generation sequencing laboratory information management system.
    Tracks the complete NGS workflow from sample receipt through quality
    control, library preparation, sequencing on high-throughput instruments,
    and bioinformatics analysis. Supports Illumina (NovaSeq, NextSeq, MiSeq)
    and Oxford Nanopore platforms. Designed for a core facility processing
    50-200 samples per week across multiple research groups.

  api: {}

  # ─── Shared objects ────────────────────────────────────────────
  objects:
    Timestamp:
      createdAt: { type: datetime, autoNow: create }
      updatedAt: { type: datetime, autoNow: update }

  # ─── Shared properties ─────────────────────────────────────────
  properties:
    slug:
      type: slug
      unique: true
      maxLength: 255

  # ─── Enums ─────────────────────────────────────────────────────
  enums:
    SampleType:
      values:
        dna:
          label: "Genomic DNA"
          description: "Extracted genomic DNA for whole-genome or targeted sequencing"
          color: "#3b82f6"
        rna:
          label: "Total RNA"
          description: "Extracted total RNA for RNA-seq or small RNA sequencing"
          color: "#10b981"
        cfDNA:
          label: "Cell-free DNA"
          description: "Circulating cell-free DNA from plasma for liquid biopsy"
          color: "#8b5cf6"
        ffpe:
          label: "FFPE"
          description: "Formalin-fixed paraffin-embedded tissue DNA/RNA"
          color: "#f59e0b"

    SampleStatus:
      values:
        received:
          label: "Received"
          description: "Sample received and accessioned into the system"
          color: "#94a3b8"
        qc_passed:
          label: "QC Passed"
          description: "Sample passed quality control — ready for library prep"
          color: "#3b82f6"
        qc_failed:
          label: "QC Failed"
          description: "Sample failed QC — insufficient quality or quantity"
          color: "#ef4444"
        library_prep:
          label: "Library Prep"
          description: "Library preparation in progress"
          color: "#8b5cf6"
        sequencing:
          label: "Sequencing"
          description: "On the sequencer — data generation in progress"
          color: "#f59e0b"
        analysis:
          label: "Analysis"
          description: "Bioinformatics analysis in progress"
          color: "#06b6d4"
        complete:
          label: "Complete"
          description: "All processing finished — results delivered"
          color: "#10b981"
          final: true

    RunStatus:
      values:
        planned:
          label: "Planned"
          description: "Run is scheduled but not yet started"
          color: "#94a3b8"
        loading:
          label: "Loading"
          description: "Flow cell is being loaded with libraries"
          color: "#f59e0b"
        running:
          label: "Running"
          description: "Sequencer is actively generating data"
          color: "#3b82f6"
        completed:
          label: "Completed"
          description: "Sequencing finished — data available for analysis"
          color: "#10b981"
        failed:
          label: "Failed"
          description: "Run failed — check instrument logs and flow cell"
          color: "#ef4444"

    InstrumentStatus:
      values:
        available:
          label: "Available"
          description: "Instrument is operational and ready for use"
          color: "#10b981"
        in_use:
          label: "In Use"
          description: "Currently running a sequencing experiment"
          color: "#3b82f6"
        maintenance:
          label: "Maintenance"
          description: "Scheduled or unscheduled maintenance in progress"
          color: "#f59e0b"
        offline:
          label: "Offline"
          description: "Instrument is down — repair or decommission pending"
          color: "#ef4444"

    AnalysisStatus:
      values:
        queued:
          label: "Queued"
          description: "Waiting for compute resources"
          color: "#94a3b8"
        running:
          label: "Running"
          description: "Pipeline is actively processing data"
          color: "#3b82f6"
        completed:
          label: "Completed"
          description: "Analysis finished — results available"
          color: "#10b981"
        failed:
          label: "Failed"
          description: "Pipeline failed — check error log for details"
          color: "#ef4444"

    Organism:
      values:
        human:
          label: "Human (Homo sapiens)"
          description: "GRCh38/hg38 reference genome"
          color: "#3b82f6"
        mouse:
          label: "Mouse (Mus musculus)"
          description: "GRCm39/mm39 reference genome"
          color: "#94a3b8"
        rat:
          label: "Rat (Rattus norvegicus)"
          description: "mRatBN7.2/rn7 reference genome"
          color: "#94a3b8"
        ecoli:
          label: "E. coli (K-12)"
          description: "Standard laboratory strain"
          color: "#f59e0b"
        custom:
          label: "Custom / Other"
          description: "Non-standard organism — specify in project notes"
          color: "#8b5cf6"

    LibraryPrepKit:
      values:
        nextera_dna_flex:
          label: "Nextera DNA Flex"
          description: "Illumina tagmentation-based DNA library prep (1-500ng input)"
          color: "#3b82f6"
        truseq_stranded_mrna:
          label: "TruSeq Stranded mRNA"
          description: "Illumina poly-A selection RNA-seq library prep"
          color: "#10b981"
        truseq_stranded_total_rna:
          label: "TruSeq Stranded Total RNA"
          description: "Illumina ribo-depletion total RNA library prep"
          color: "#8b5cf6"
        twist_exome:
          label: "Twist Bioscience Exome"
          description: "Twist Human Core Exome panel for whole-exome sequencing"
          color: "#f59e0b"
        nanopore_ligation:
          label: "ONT Ligation Sequencing"
          description: "Oxford Nanopore ligation-based long-read library prep"
          color: "#94a3b8"

    SequencingPlatform:
      values:
        novaseq_6000:
          label: "NovaSeq 6000"
          description: "Illumina high-throughput, up to 6 Tb per run"
          color: "#3b82f6"
        nextseq_2000:
          label: "NextSeq 2000"
          description: "Illumina mid-throughput, XLEAP chemistry"
          color: "#8b5cf6"
        miseq:
          label: "MiSeq"
          description: "Illumina benchtop, ideal for amplicon and small genome"
          color: "#10b981"
        minion:
          label: "MinION"
          description: "Oxford Nanopore portable long-read sequencer"
          color: "#f59e0b"

  # ─── Resources ─────────────────────────────────────────────────
  resources:

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Project                                                     ║
    # ╚══════════════════════════════════════════════════════════════╝
    Project:
      description: >
        A sequencing project grouping related samples from a single
        research group or grant. Projects define the organism, sequencing
        objectives, and timeline. All samples within a project share the
        same organism and are tracked toward a common delivery date.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 200

        slug:
          $ref: '#/properties/slug'
          from: name

        description:
          type: text
          maxLength: 3000
          description: "Research objectives, sample types expected, and special requirements"

        organism:
          $ref: '#/enums/Organism'
          required: true

        referenceGenome:
          type: string
          maxLength: 50
          description: "Reference genome build (e.g. GRCh38, GRCm39, custom assembly)"

        piName:
          type: string
          required: true
          maxLength: 200

        piEmail:
          type: email

        labName:
          type: string
          maxLength: 200
          description: "Research group or lab name"

        department:
          type: string
          maxLength: 200
          description: "Academic department"

        grantNumber:
          type: string
          maxLength: 50
          description: "Funding grant award number for billing"

        sequencingObjective:
          type: string
          maxLength: 200
          description: "e.g. Whole Genome 30x, RNA-seq (polyA), Exome, 16S Amplicon"

        targetSampleCount:
          type: integer
          description: "Expected number of samples"

        samplesReceived:
          type: integer
          default: 0
          description: "Number of samples received so far"

        samplesCompleted:
          type: integer
          default: 0
          description: "Number of samples fully processed"

        startDate:
          type: date

        targetDate:
          type: date
          description: "Desired completion date"

        active:
          type: boolean
          default: true

        notes:
          type: text
          maxLength: 5000
          description: "Special handling instructions, billing notes, communication log"

        $merge:
          - { $ref: '#/objects/Timestamp' }

      relationships:
        samples:
          type: hasMany
          target: Sample
          foreignKey: projectId

      indexes:
        - fields: [organism]
          description: "Filter projects by organism"
        - fields: [piName]
          description: "Filter projects by PI"
        - fields: [active]
          description: "Filter active/completed projects"

      features:
        search: true
        audit: true

      audit:
        track: [name, organism, piName, targetSampleCount, samplesReceived, samplesCompleted, active]
        exclude: [updatedAt]

      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [pi, admin]
        update: [owner, pi, admin]
        delete: [admin]

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Sample                                                      ║
    # ╚══════════════════════════════════════════════════════════════╝
    Sample:
      description: >
        A biological sample progressing through the NGS workflow.
        Samples arrive at the core facility, undergo quality control,
        library preparation, sequencing, and bioinformatics analysis.
        Each stage is a state machine transition. Samples carry
        quantitative QC metrics (concentration, volume, quality score)
        and are linked to a project and optionally to specific library
        prep kits.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 200
          description: "Lab-assigned sample identifier"

        externalId:
          type: string
          maxLength: 100
          description: "Client's own sample ID for cross-referencing"

        sampleType:
          $ref: '#/enums/SampleType'
          required: true

        status:
          $ref: '#/enums/SampleStatus'
          default: received

        organism:
          $ref: '#/enums/Organism'
          description: "Overrides project organism if different"

        tissue:
          type: string
          maxLength: 100
          description: "Tissue or cell type (e.g. whole blood, liver, tumor, PBMC)"

        preservationMethod:
          type: string
          maxLength: 100
          description: "e.g. Fresh frozen, FFPE, RNAlater, ethanol-fixed"

        collectionDate:
          type: date
          description: "Date the sample was collected from the subject"

        receivedAt:
          type: datetime
          description: "When the sample arrived at the core facility"

        # ─── QC Metrics ───────────────────────────────────────────
        concentration:
          type: decimal
          description: "Concentration in ng/µL (measured by Qubit or NanoDrop)"

        volume:
          type: decimal
          description: "Volume in µL"

        totalAmount:
          type: decimal
          description: "Total mass in ng (concentration × volume)"

        a260_280:
          type: decimal
          description: "A260/A280 absorbance ratio — purity indicator (DNA ~1.8, RNA ~2.0)"

        a260_230:
          type: decimal
          description: "A260/A230 absorbance ratio — contaminant indicator (ideal 2.0-2.2)"

        rin:
          type: decimal
          description: "RNA Integrity Number (1-10, RNA only, measured by Bioanalyzer)"

        din:
          type: decimal
          description: "DNA Integrity Number (1-10, DNA only, measured by TapeStation)"

        fragmentSize:
          type: integer
          description: "Average fragment size in bp (from Bioanalyzer or TapeStation)"

        qcNotes:
          type: text
          maxLength: 2000
          description: "QC observations, Bioanalyzer trace notes, degradation assessment"

        qcPassedAt:
          type: datetime
          description: "When QC was completed"

        qcPerformedBy:
          type: string
          maxLength: 100
          description: "Username of the technician who performed QC"

        # ─── Library Prep ─────────────────────────────────────────
        libraryPrepKit:
          $ref: '#/enums/LibraryPrepKit'
          description: "Kit used for library preparation"

        libraryPrepDate:
          type: date

        indexSequence:
          type: string
          maxLength: 50
          description: "i7/i5 index barcode sequence for demultiplexing (e.g. ATCACG-TTAGGC)"

        libraryConcentration:
          type: decimal
          description: "Post-prep library concentration in nM"

        libraryFragmentSize:
          type: integer
          description: "Post-prep average fragment size in bp"

        libraryPrepNotes:
          type: text
          maxLength: 2000

        # ─── Project Link ─────────────────────────────────────────
        projectId:
          type: uuid
          required: true

        projectName:
          type: string
          maxLength: 200
          description: "Denormalized project name for display"

        # ─── General ──────────────────────────────────────────────
        notes:
          type: text
          maxLength: 3000
          description: "General notes, special handling, client communications"

        completedAt:
          type: datetime
          description: "When all processing was finished"

        $merge:
          - { $ref: '#/objects/Timestamp' }

      relationships:
        project:
          type: belongsTo
          target: Project
          foreignKey: projectId

      indexes:
        - fields: [projectId]
          description: "List samples by project"
        - fields: [status]
          description: "Filter by processing status"
        - fields: [sampleType]
          description: "Filter by sample type"
        - fields: [organism]
          description: "Filter by organism"
        - fields: [projectId, status]
          description: "Composite: project samples by status"
        - fields: [libraryPrepKit]
          description: "Filter by prep kit"

      features:
        audit: true
        search: true
        export: true

      audit:
        track: [name, status, concentration, volume, rin, din, qcNotes, libraryPrepKit, indexSequence]
        exclude: [updatedAt]

      # ─── State machine ──────────────────────────────────────────
      stateMachine:
        field: status
        initial: received

        states:
          received:
            description: "Sample accessioned — awaiting QC"
            transitions:
              pass_qc:
                to: qc_passed
                guards: [lab_tech, admin]
                sets:
                  qcPassedAt: now()
              fail_qc:
                to: qc_failed
                guards: [lab_tech, admin]
                sets:
                  qcPassedAt: now()

          qc_passed:
            description: "QC passed — ready for library preparation"
            transitions:
              begin_library_prep:
                to: library_prep
                guards: [lab_tech, admin]

          qc_failed:
            description: "QC failed — sample may need re-extraction or replacement"
            transitions:
              resubmit:
                to: received
                guards: [lab_tech, admin]
                sets:
                  qcPassedAt: null
                  qcNotes: null
                  concentration: null
                  volume: null
                  totalAmount: null
                  a260_280: null
                  a260_230: null
                  rin: null
                  din: null

          library_prep:
            description: "Library preparation in progress"
            transitions:
              begin_sequencing:
                to: sequencing
                guards: [lab_tech, admin]

          sequencing:
            description: "Sample is on the sequencer"
            transitions:
              begin_analysis:
                to: analysis
                guards: [lab_tech, admin]

          analysis:
            description: "Bioinformatics analysis in progress"
            transitions:
              complete:
                to: complete
                guards: [lab_tech, admin]
                sets:
                  completedAt: now()

          complete:
            description: "All processing finished — results delivered to PI"

      # ─── Permissions ─────────────────────────────────────────────
      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [lab_tech, pi, admin]
        update: [lab_tech, admin]
        delete: [admin]

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Instrument                                                  ║
    # ╚══════════════════════════════════════════════════════════════╝
    Instrument:
      description: >
        A sequencing instrument in the core facility. Each instrument
        has a model, serial number, platform type, and operational
        status. Instruments are linked to sequencing runs. Maintenance
        schedules are tracked to minimize downtime.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 100
          description: "Friendly name (e.g. NovaSeq-1, MiSeq-Bench3)"

        platform:
          $ref: '#/enums/SequencingPlatform'
          required: true

        model:
          type: string
          required: true
          maxLength: 100
          description: "Full model name (e.g. NovaSeq 6000, NextSeq 2000, MinION Mk1C)"

        serialNumber:
          type: string
          unique: true
          maxLength: 50

        status:
          $ref: '#/enums/InstrumentStatus'
          default: available

        location:
          type: string
          maxLength: 100
          description: "Room and bench location (e.g. Sequencing Core, Room 312, Bench A)"

        ipAddress:
          type: string
          maxLength: 45
          description: "Network IP for instrument control software"

        softwareVersion:
          type: string
          maxLength: 50
          description: "Current control software version"

        purchaseDate:
          type: date

        warrantyExpires:
          type: date

        lastMaintenanceDate:
          type: date

        nextMaintenanceDate:
          type: date

        totalRuns:
          type: integer
          default: 0
          description: "Cumulative number of runs on this instrument"

        notes:
          type: text
          maxLength: 3000
          description: "Maintenance history, known issues, performance notes"

        $merge:
          - { $ref: '#/objects/Timestamp' }

      relationships:
        runs:
          type: hasMany
          target: Run
          foreignKey: instrumentId

      indexes:
        - fields: [platform]
          description: "Filter by sequencing platform"
        - fields: [status]
          description: "Filter by operational status"

      features:
        audit: true

      audit:
        track: [name, status, softwareVersion, lastMaintenanceDate, nextMaintenanceDate, totalRuns]
        exclude: [updatedAt]

      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [admin]
        update: [lab_tech, admin]
        delete: [admin]

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Protocol                                                    ║
    # ╚══════════════════════════════════════════════════════════════╝
    Protocol:
      description: >
        Standard operating procedures for library preparation and
        sequencing. Each protocol has a name, version, and description
        of the workflow steps. Protocols specify which sample types
        they support and estimated processing time. Lab technicians
        select a protocol when setting up a sequencing run.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 200
          description: "Protocol name (e.g. WGS 30x Human, RNA-seq polyA, 16S V3-V4)"

        version:
          type: string
          required: true
          maxLength: 20
          description: "Protocol version (e.g. 3.1, 2024-Q4)"

        category:
          type: string
          maxLength: 100
          description: "e.g. Whole Genome, RNA-seq, Exome, Amplicon, Metagenomics"

        description:
          type: text
          maxLength: 5000
          description: "Detailed protocol steps, reagent list, and quality checkpoints"

        applicableSampleTypes:
          type: set
          description: "Which sample types this protocol supports (dna, rna, cfDNA, ffpe)"

        applicablePlatforms:
          type: set
          description: "Which sequencing platforms this protocol targets"

        recommendedKit:
          $ref: '#/enums/LibraryPrepKit'
          description: "Recommended library prep kit for this protocol"

        inputRequirements:
          type: text
          maxLength: 1000
          description: "Minimum input requirements (e.g. 100ng DNA, RIN >= 7, DIN >= 6)"

        estimatedDuration:
          type: string
          maxLength: 50
          description: "Estimated hands-on time (e.g. 4 hours, 2 days)"

        estimatedTurnaround:
          type: string
          maxLength: 50
          description: "Total turnaround including sequencing (e.g. 5 business days)"

        readConfiguration:
          type: string
          maxLength: 50
          description: "Default read length and type (e.g. 2×150bp PE, 1×75bp SE, 2×250bp PE)"

        targetCoverage:
          type: string
          maxLength: 50
          description: "Target coverage or read depth (e.g. 30x, 50M reads, 100k reads/sample)"

        active:
          type: boolean
          default: true
          description: "Whether this protocol is currently in use"

        author:
          type: string
          maxLength: 200
          description: "Protocol developer or responsible scientist"

        approvedDate:
          type: date
          description: "Date the protocol was approved for production use"

        $merge:
          - { $ref: '#/objects/Timestamp' }

      indexes:
        - fields: [category]
          description: "Filter by protocol category"
        - fields: [active]
          description: "Filter active protocols"

      features:
        search: true

      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [admin]
        update: [admin]
        delete: [admin]

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Run                                                         ║
    # ╚══════════════════════════════════════════════════════════════╝
    Run:
      description: >
        A sequencing run on an instrument. Each run processes one or
        more samples (via the RunSamples junction table), follows a
        protocol, and produces raw sequencing data. Runs progress
        through a state machine: planned → loading → running →
        completed (or failed). QC metrics (total reads, pass filter
        percentage, Q30 score) are recorded after the run completes.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 200
          description: "Run identifier (e.g. NS6K-20260315-WGS-001)"

        flowCellId:
          type: string
          maxLength: 50
          description: "Flow cell barcode (e.g. HXXXXXXXX)"

        flowCellType:
          type: string
          maxLength: 50
          description: "Flow cell type (e.g. SP, S1, S2, S4 for NovaSeq)"

        status:
          $ref: '#/enums/RunStatus'
          default: planned

        instrumentId:
          type: uuid
          required: true

        instrumentName:
          type: string
          maxLength: 100
          description: "Denormalized instrument name"

        protocolId:
          type: uuid

        protocolName:
          type: string
          maxLength: 200
          description: "Denormalized protocol name"

        operatorId:
          type: string
          maxLength: 100
          description: "Username of the lab technician running the instrument"

        operatorName:
          type: string
          maxLength: 200

        readLength:
          type: string
          maxLength: 50
          description: "Actual read configuration (e.g. 2×150bp, 1×300bp)"

        indexReads:
          type: string
          maxLength: 50
          description: "Index read configuration (e.g. 8+8bp, 10+10bp)"

        loadingConcentration:
          type: decimal
          description: "Library loading concentration in pM"

        phiXPercent:
          type: percentage
          description: "Percentage of PhiX spike-in control"

        # ─── Post-run QC metrics ──────────────────────────────────
        totalReads:
          type: integer
          description: "Total clusters / reads generated (millions)"

        readsPassFilter:
          type: integer
          description: "Reads passing quality filter (millions)"

        passFilterPercent:
          type: percentage
          description: "Percentage of clusters passing filter"

        q30Percent:
          type: percentage
          description: "Percentage of bases with quality score >= Q30"

        meanQuality:
          type: decimal
          description: "Mean quality score across all bases"

        errorRate:
          type: decimal
          description: "Estimated sequencing error rate (percentage)"

        yieldGb:
          type: decimal
          description: "Total data yield in gigabases"

        clusterDensity:
          type: decimal
          description: "Cluster density (K/mm²) — Illumina only"

        # ─── Timing ───────────────────────────────────────────────
        scheduledDate:
          type: date
          description: "Planned run date"

        startedAt:
          type: datetime

        completedAt:
          type: datetime

        estimatedCompletion:
          type: datetime
          description: "Estimated completion time (calculated from run parameters)"

        # ─── General ──────────────────────────────────────────────
        outputPath:
          type: string
          maxLength: 500
          description: "Path to raw data output directory on the file server"

        notes:
          type: text
          maxLength: 3000
          description: "Run observations, issues encountered, post-run assessment"

        $merge:
          - { $ref: '#/objects/Timestamp' }

      relationships:
        instrument:
          type: belongsTo
          target: Instrument
          foreignKey: instrumentId

        protocol:
          type: belongsTo
          target: Protocol
          foreignKey: protocolId

        samples:
          type: belongsToMany
          target: Sample
          through: RunSamples
          foreignKey: runId
          otherKey: sampleId

        analyses:
          type: hasMany
          target: Analysis
          foreignKey: runId

      indexes:
        - fields: [instrumentId]
          description: "List runs by instrument"
        - fields: [status]
          description: "Filter by run status"
        - fields: [protocolId]
          description: "List runs by protocol"
        - fields: [operatorId]
          description: "List runs by operator"
        - fields: [scheduledDate]
          description: "Sort by scheduled date"
        - fields: [instrumentId, status]
          description: "Composite: instrument runs by status"

      features:
        audit: true
        search: true
        export: true

      audit:
        track: [name, status, instrumentId, totalReads, q30Percent, passFilterPercent, yieldGb]
        exclude: [updatedAt]

      # ─── State machine ──────────────────────────────────────────
      stateMachine:
        field: status
        initial: planned

        states:
          planned:
            description: "Run is scheduled — loading date set, samples selected"
            transitions:
              begin_loading:
                to: loading
                guards: [lab_tech, admin]

          loading:
            description: "Flow cell is being loaded with pooled libraries"
            transitions:
              start_run:
                to: running
                guards: [lab_tech, admin]
                sets:
                  startedAt: now()

          running:
            description: "Sequencer is actively generating data"
            transitions:
              complete:
                to: completed
                guards: [lab_tech, admin]
                sets:
                  completedAt: now()
              fail:
                to: failed
                guards: [lab_tech, admin]
                sets:
                  completedAt: now()

          completed:
            description: "Run finished successfully — data ready for analysis"

          failed:
            description: "Run failed — check instrument logs and flow cell integrity"

      # ─── Permissions ─────────────────────────────────────────────
      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [lab_tech, admin]
        update: [lab_tech, admin]
        delete: [admin]

    # ╔══════════════════════════════════════════════════════════════╗
    # ║  Analysis                                                    ║
    # ╚══════════════════════════════════════════════════════════════╝
    Analysis:
      description: >
        A bioinformatics analysis of sequencing run data. Each analysis
        runs a specific pipeline (e.g. GATK, bcl2fastq, nf-core/rnaseq)
        on the data from a sequencing run. Analyses progress through a
        state machine: queued → running → completed (or failed with
        retry option). Failed analyses can be retried, which clears
        the error state and re-queues the job.

      object:
        id:
          type: uuid
          primaryKey: true

        name:
          type: string
          required: true
          maxLength: 200
          description: "Analysis identifier (e.g. WGS-Alignment-Run001, RNAseq-DGE-Proj42)"

        pipeline:
          type: string
          required: true
          maxLength: 100
          description: "Pipeline name (e.g. bcl2fastq, GATK-HaplotypeCaller, nf-core/rnaseq, nf-core/sarek)"

        pipelineVersion:
          type: string
          maxLength: 20
          description: "Pipeline version (e.g. 3.12.0, 2024.1)"

        referenceGenome:
          type: string
          maxLength: 50
          description: "Reference genome used (e.g. GRCh38, GRCm39)"

        status:
          $ref: '#/enums/AnalysisStatus'
          default: queued

        runId:
          type: uuid
          required: true

        runName:
          type: string
          maxLength: 200
          description: "Denormalized run name"

        submittedBy:
          type: string
          maxLength: 100
          description: "Username of the person who submitted the analysis"

        submittedByName:
          type: string
          maxLength: 200

        # ─── Compute ──────────────────────────────────────────────
        computeNode:
          type: string
          maxLength: 100
          description: "HPC node or cloud instance running the analysis"

        cpuHours:
          type: decimal
          description: "CPU hours consumed"

        peakMemoryGb:
          type: decimal
          description: "Peak memory usage in GB"

        wallTimeHours:
          type: decimal
          description: "Wall-clock time in hours"

        # ─── Timing ───────────────────────────────────────────────
        startedAt:
          type: datetime

        completedAt:
          type: datetime

        # ─── Results ──────────────────────────────────────────────
        outputPath:
          type: string
          maxLength: 500
          description: "Path to analysis output directory"

        reportUrl:
          type: url
          description: "URL to the MultiQC or analysis report"

        totalVariants:
          type: integer
          description: "Number of variants called (for variant calling pipelines)"

        alignmentRate:
          type: percentage
          description: "Percentage of reads aligned to reference"

        duplicationRate:
          type: percentage
          description: "Percentage of duplicate reads"

        meanCoverage:
          type: decimal
          description: "Mean genome coverage (for WGS/WES)"

        # ─── Error handling ───────────────────────────────────────
        errorLog:
          type: text
          maxLength: 10000
          description: "Error output from the pipeline (captured on failure)"

        retryCount:
          type: integer
          default: 0
          description: "Number of times this analysis has been retried"

        notes:
          type: text
          maxLength: 3000

        $merge:
          - { $ref: '#/objects/Timestamp' }

      relationships:
        run:
          type: belongsTo
          target: Run
          foreignKey: runId

      indexes:
        - fields: [runId]
          description: "List analyses by run"
        - fields: [status]
          description: "Filter by analysis status"
        - fields: [pipeline]
          description: "Filter by pipeline"
        - fields: [runId, status]
          description: "Composite: run analyses by status"

      features:
        audit: true

      audit:
        track: [name, status, pipeline, startedAt, completedAt, errorLog, retryCount]
        exclude: [updatedAt]

      # ─── State machine ──────────────────────────────────────────
      stateMachine:
        field: status
        initial: queued

        states:
          queued:
            description: "Waiting for compute resources on the HPC cluster"
            transitions:
              start:
                to: running
                guards: [lab_tech, admin]
                sets:
                  startedAt: now()

          running:
            description: "Pipeline is actively processing sequencing data"
            transitions:
              complete:
                to: completed
                guards: [lab_tech, admin]
                sets:
                  completedAt: now()
              fail:
                to: failed
                guards: [lab_tech, admin]
                sets:
                  completedAt: now()

          completed:
            description: "Analysis complete — results available at outputPath"

          failed:
            description: "Pipeline failed — check errorLog for diagnostics"
            transitions:
              retry:
                to: queued
                guards: [lab_tech, admin]
                sets:
                  startedAt: null
                  completedAt: null
                  errorLog: null

      # ─── Permissions ─────────────────────────────────────────────
      permissions:
        list:   [authenticated]
        read:   [authenticated]
        create: [lab_tech, admin]
        update: [lab_tech, admin]
        delete: [admin]
