WorkflowRunException.java

package org.hammer.audio.workflow.execution;

import java.io.Serial;
import java.util.List;
import org.hammer.audio.workflow.execution.WorkflowRunModels.Violation;

/** Typed workflow-run failure suitable for stable HTTP problem mapping. */
public final class WorkflowRunException extends RuntimeException {

  @Serial private static final long serialVersionUID = 1L;

  /** Stable machine-readable failure code. */
  public enum Code {
    DUPLICATE_START_COMMAND,
    SOURCE_UNAVAILABLE,
    UNKNOWN_RUN,
    VALIDATION_FAILED,
    UNSUPPORTED_NODE,
    RESULT_NOT_AVAILABLE,
    ILLEGAL_TRANSITION,
    BACKEND_FAILURE
  }

  private final Code errorCode;
  private final String affectedRunId;
  private final String commandId;
  private final Violation[] violationDetails;

  /** Creates a typed failure without nested cause. */
  public WorkflowRunException(
      Code code, String message, String runId, String startCommandId, List<Violation> violations) {
    this(code, message, runId, startCommandId, violations, null);
  }

  /** Creates a typed failure preserving a backend or source cause. */
  public WorkflowRunException(
      Code code,
      String message,
      String runId,
      String startCommandId,
      List<Violation> violations,
      Throwable cause) {
    super(message, cause);
    this.errorCode = java.util.Objects.requireNonNull(code, "code");
    this.affectedRunId = runId;
    this.commandId = startCommandId;
    this.violationDetails =
        (violations == null ? List.<Violation>of() : violations).toArray(Violation[]::new);
  }

  public Code code() {
    return errorCode;
  }

  public String runId() {
    return affectedRunId;
  }

  public String startCommandId() {
    return commandId;
  }

  public List<Violation> violations() {
    return List.of(violationDetails.clone());
  }
}