@startuml Fuzzer_Class

' ── Skin ─────────────────────────────────────────────────────────────────────

skinparam linetype ortho
skinparam nodesep 35
skinparam ranksep 45

skinparam ClassBackgroundColor        #F8F7FF
skinparam ClassBorderColor            #AFA9EC
skinparam ClassBorderThickness        1.5
skinparam ClassFontName               Helvetica
skinparam ClassFontSize               11
skinparam ClassFontColor              #26215C
skinparam ClassHeaderBackgroundColor  #EDE9F7
skinparam ClassArrowColor             #7F77DD
skinparam ClassArrowThickness         1.5

skinparam InterfaceBackgroundColor    #EDE9F7
skinparam InterfaceBorderColor        #7F77DD
skinparam InterfaceFontColor          #26215C
skinparam InterfaceFontName           Helvetica
skinparam InterfaceFontSize           11

skinparam PackageBackgroundColor      #F9F8FC
skinparam PackageBorderColor          #C5C1EB
skinparam PackageFontColor            #534AB7
skinparam PackageFontSize             11
skinparam PackageFontStyle            bold

skinparam NoteBackgroundColor         #FAEEDA
skinparam NoteBorderColor             #EF9F27
skinparam ShadowingEnabled            false
skinparam RoundCorner                 8

skinparam ClassBackgroundColor<<adapter>>  #EDE9F7
skinparam ClassBorderColor<<adapter>>      #7F77DD
skinparam ClassBackgroundColor<<usecase>>  #EBF3FC
skinparam ClassBorderColor<<usecase>>      #9EB8E6
skinparam ClassBackgroundColor<<fn>>       #F0F7EE
skinparam ClassBorderColor<<fn>>           #8CC87A

' ── Component ────────────────────────────────────────────────────────────────

package "Fuzzer" {

  ' ── Inbound layer ──────────────────────────────────────────────────────────

  class Fuzzer <<adapter>> {
    - use_case : Box<FuzzerRunPort>
  }

  interface FuzzerRunPort <<inbound port>> {
    + run(Vec<RoundSignal>) : Result<Vec<FuzzReport>>
  }

  class RunFuzzerUseCase <<usecase>> {
    + runner : Box<TestRunnerPort>
    + output : Box<FuzzerOutputPort>
    + workspace_root : PathBuf
  }

  together {
    class run_fuzzer <<fn>> {
      (profile, TestRunnerPort)
      → RunnerResult
    }
    class run_coverage <<fn>> {
      (profile, TestRunnerPort)
      → CoverageResult
    }
    class enrich_coverage <<fn>> {
      (CoverageContext, workspace)
      → ()
    }
  }

  ' ── Outbound ports ─────────────────────────────────────────────────────────

  together {
    interface TestRunnerPort <<outbound port>> {
      + run_test(profile) : Result<RunnerResult>
      + run_build(profile) : Result<RunnerResult>
      + run_coverage(profile) : Result<CoverageResult>
      + collect_bugs(stdout, name) : Vec<BugInfo>
      + filter_output(stdout, name) : String
      + filter_lcov(lcov, name) : String
    }
    interface FuzzerOutputPort <<outbound port>> {
      + write_fuzz_output(name, content) : Result<()>
      + write_lcov(name, content) : Result<PathBuf>
      + write_coverage_context(name, ctx) : Result<()>
    }
  }

  ' ── Outbound adapters ──────────────────────────────────────────────────────

  together {
    class ForgeRunner <<adapter>> {
      + working_dir : PathBuf
    }
    class FileSystemFuzzerOutput <<adapter>> {
      - workspace_root : PathBuf
    }
  }

}

' ── Shared ───────────────────────────────────────────────────────────────────

package "Shared" #F1EFE8 {

  interface FuzzerEnginePort {
    + run(Vec<RoundSignal>) : Result<Vec<FuzzReport>>
  }

  together {
    class RoundSignal <<model>> {
      + round : u32
      + contract_name : String
      + source_pragma : String
    }
    class FuzzReport <<model>> {
      + outcome : FuzzOutcome
      + bugs : Vec<BugInfo>
      + lcov_path : Option<PathBuf>
    }
  }

  together {
    class RunnerResult <<model>> {
      + exit_code : i32
      + stdout : String
      + stderr : String
    }
    class CoverageResult <<model>> {
      + exit_code : i32
      + lcov_content : Option<String>
    }
  }

  together {
    class BugInfo <<model>> {
      + invariant_name : String
      + call_sequence : String
      + invariant_code : String
    }
    class CoverageContext <<model>> {
      + gaps : Vec<CoverageGap>
      + line_found : u32
      + line_hit : u32
    }
  }

}

' ── Wiring ───────────────────────────────────────────────────────────────────

' ① Inbound: shared port → adapter → internal port → use case
Fuzzer               ..|>  FuzzerEnginePort     : implements
Fuzzer               *-->  FuzzerRunPort
RunFuzzerUseCase     ..|>  FuzzerRunPort         : implements

' ② Use case owns both outbound ports
RunFuzzerUseCase     *-->  TestRunnerPort
RunFuzzerUseCase     *-->  FuzzerOutputPort

' ③ Outbound adapters implement outbound ports
ForgeRunner              ..|>  TestRunnerPort        : implements
FileSystemFuzzerOutput   ..|>  FuzzerOutputPort      : implements

' ④ Helper functions called by the use case
RunFuzzerUseCase     ..>   run_fuzzer            : calls
RunFuzzerUseCase     ..>   run_coverage          : calls
RunFuzzerUseCase     ..>   enrich_coverage       : calls
run_fuzzer           ..>   TestRunnerPort        : delegates to
run_coverage         ..>   TestRunnerPort        : delegates to

' ⑤ Data flow
RunFuzzerUseCase     ..>   RoundSignal           : receives
RunFuzzerUseCase     ..>   FuzzReport            : produces
ForgeRunner          ..>   RunnerResult          : returns
ForgeRunner          ..>   CoverageResult        : returns
ForgeRunner          ..>   BugInfo               : collects
FileSystemFuzzerOutput ..> CoverageContext       : persists

legend bottom left
  **Legend**
  ----
  <color:#7F77DD>- - ▷</color>  **..|>**  implements / realizes interface
  <color:#7F77DD>◆ ──▶</color>  ***-->**  composition — struct owns a port or field
  <color:#7F77DD>- - ▶</color>  **..>**   calls · receives · produces
endlegend

@enduml
