///|
/// Typed command descriptors used by service and CLI adapters. Keeping command
/// validation in the library gives HTTP, queue, and command-line front ends a
/// single contract for safe execution and structured reporting.
pub(all) enum WorkflowCommandKind {
WorkflowScan
WorkflowRedact
WorkflowReview
WorkflowExport
WorkflowValidate
WorkflowBenchmark
} derive(Debug, Eq)
///|
pub(all) struct WorkflowCommand {
command_id : String
kind : WorkflowCommandKind
document_id : String
payload_checksum : String
requested_by : String
purpose : String
dry_run : Bool
arguments : Map[String, String]
} derive(Debug)
///|
pub(all) struct WorkflowCommandResult {
command_id : String
accepted : Bool
status : String
message : String
output_checksum : String
metrics : Map[String, Int]
} derive(Debug)
///|
pub fn workflow_command_kind_name(kind : WorkflowCommandKind) -> String {
match kind {
WorkflowScan => "scan"
WorkflowRedact => "redact"
WorkflowReview => "review"
WorkflowExport => "export"
WorkflowValidate => "validate"
WorkflowBenchmark => "benchmark"
}
}
///|
pub fn workflow_command(
command_id : String,
kind : WorkflowCommandKind,
document_id : String,
payload_checksum : String,
requested_by : String,
purpose : String,
) -> WorkflowCommand {
{
command_id,
kind,
document_id,
payload_checksum,
requested_by,
purpose,
dry_run: false,
arguments: Map([]),
}
}
///|
pub fn WorkflowCommand::dry_run(self : WorkflowCommand) -> WorkflowCommand {
{ ..self, dry_run: true }
}
///|
pub fn WorkflowCommand::with_argument(
command : WorkflowCommand,
key : String,
value : String,
) -> WorkflowCommand {
command.arguments[key] = value
command
}
///|
pub fn WorkflowCommand::argument(
self : WorkflowCommand,
key : String,
) -> String {
self.arguments.get_or_default(key, "")
}
///|
pub fn WorkflowCommand::is_valid(self : WorkflowCommand) -> Bool {
self.command_id.length() > 0 &&
self.document_id.length() > 0 &&
self.payload_checksum.length() > 0 &&
self.requested_by.length() > 0 &&
self.purpose.length() > 0
}
///|
pub fn WorkflowCommand::checksum(self : WorkflowCommand) -> String {
stable_hash(
self.command_id +
":" +
workflow_command_kind_name(self.kind) +
":" +
self.document_id +
":" +
self.payload_checksum,
)
}
///|
pub fn workflow_command_result(
command : WorkflowCommand,
accepted : Bool,
status : String,
message : String,
output_checksum : String,
) -> WorkflowCommandResult {
{
command_id: command.command_id,
accepted,
status,
message,
output_checksum,
metrics: Map([]),
}
}
///|
pub fn WorkflowCommandResult::with_metric(
result : WorkflowCommandResult,
key : String,
value : Int,
) -> WorkflowCommandResult {
result.metrics[key] = if value < 0 { 0 } else { value }
result
}
///|
pub fn WorkflowCommandResult::is_success(self : WorkflowCommandResult) -> Bool {
self.accepted && self.status == "succeeded"
}
///|
pub fn WorkflowCommandResult::to_json(self : WorkflowCommandResult) -> String {
"{" +
"\"command_id\":\"" +
json_escape(self.command_id) +
"\"," +
"\"accepted\":" +
self.accepted.to_string() +
"," +
"\"status\":\"" +
json_escape(self.status) +
"\"," +
"\"message\":\"" +
json_escape(self.message) +
"\"," +
"\"output_checksum\":\"" +
json_escape(self.output_checksum) +
"\"}"
}
///|
pub fn validate_workflow_command(
command : WorkflowCommand,
) -> WorkflowCommandResult {
if !command.is_valid() {
workflow_command_result(
command, false, "rejected", "required command fields are missing", "",
)
} else if command.kind == WorkflowExport && command.purpose == "" {
workflow_command_result(
command, false, "rejected", "export purpose is required", "",
)
} else {
workflow_command_result(
command,
true,
"accepted",
"command passed validation",
command.checksum(),
)
}
}
///|
pub fn complete_workflow_command(
command : WorkflowCommand,
output_checksum : String,
metrics : Map[String, Int],
) -> WorkflowCommandResult {
let result = workflow_command_result(
command, true, "succeeded", "command completed", output_checksum,
)
for key, value in metrics {
result.metrics[key] = value
}
result
}
///|
pub fn fail_workflow_command(
command : WorkflowCommand,
message : String,
) -> WorkflowCommandResult {
workflow_command_result(command, false, "failed", message, "")
}
///|
pub fn workflow_command_batch(
commands : Array[WorkflowCommand],
) -> Map[String, Int] {
let counts : Map[String, Int] = Map([])
for command in commands {
let key = workflow_command_kind_name(command.kind)
counts[key] = counts.get_or_default(key, 0) + 1
}
counts
}
///|
pub fn workflow_commands_are_unique(commands : Array[WorkflowCommand]) -> Bool {
let seen : Map[String, Bool] = Map([])
let mut unique = true
for command in commands {
if seen.contains(command.command_id) {
unique = false
}
seen[command.command_id] = true
}
unique
}
///|
pub fn workflow_command_requires_payload(kind : WorkflowCommandKind) -> Bool {
kind == WorkflowScan || kind == WorkflowRedact || kind == WorkflowValidate
}
///|
pub fn workflow_command_is_mutating(kind : WorkflowCommandKind) -> Bool {
kind == WorkflowRedact || kind == WorkflowReview || kind == WorkflowExport
}
///|
pub fn workflow_command_safe_for_dry_run(command : WorkflowCommand) -> Bool {
command.dry_run || !workflow_command_is_mutating(command.kind)
}
///|
pub fn workflow_command_summary(command : WorkflowCommand) -> String {
[
"command_id=" + command.command_id,
"kind=" + workflow_command_kind_name(command.kind),
"document_id=" + command.document_id,
"requested_by=" + command.requested_by,
"purpose=" + command.purpose,
"dry_run=" + command.dry_run.to_string(),
"valid=" + command.is_valid().to_string(),
"checksum=" + command.checksum(),
].join("\n")
}
///|
pub fn workflow_command_kind_counts(
commands : Array[WorkflowCommand],
) -> Map[String, Int] {
let counts : Map[String, Int] = Map([])
for command in commands {
let name = workflow_command_kind_name(command.kind)
counts[name] = counts.get_or_default(name, 0) + 1
}
counts
}
///|
pub fn workflow_result_checksum(result : WorkflowCommandResult) -> String {
stable_hash(
result.command_id + ":" + result.status + ":" + result.output_checksum,
)
}
///|
pub fn workflow_command_id_set(
commands : Array[WorkflowCommand],
) -> Array[String] {
commands.map(fn(command) { command.command_id })
}