tandem-server 0.6.4

HTTP server for Tandem engine APIs
fn promote_materialized_output(
    output: &mut Value,
    node: &crate::automation_v2::types::AutomationFlowNode,
    artifact_path: &str,
    artifact_text: &str,
    recovery_source: Option<&str>,
) {
    let accepted_candidate_source = if recovery_source.is_some() {
        "session_write_recovery"
    } else {
        "verified_output"
    };
    let content_digest = crate::sha256_hex(&[artifact_text]);
    let schema_issue = node
        .output_contract
        .as_ref()
        .and_then(|contract| contract.schema.as_ref())
        .and_then(|schema| {
            serde_json::from_str::<Value>(artifact_text)
                .map_err(|err| format!("artifact is not valid JSON: {err}"))
                .and_then(|artifact| {
                    crate::app::state::automation::automation_output_schema_validation_issue(
                        schema, &artifact,
                    )
                    .map(Err)
                    .unwrap_or(Ok(()))
                })
                .err()
        });
    let should_complete = matches!(
        node_output_status(output).as_str(),
        "blocked" | "needs_repair"
    ) && schema_issue.is_none()
        && output_only_failed_for_missing_materialized_artifact(output);

    if let Some(object) = output.as_object_mut() {
        object.insert(
            "summary".to_string(),
            json!(format!(
                "Verified workspace output `{}` for node `{}`.",
                artifact_path, node.node_id
            )),
        );
        if should_complete {
            object.insert(
                "status".to_string(),
                json!(if crate::app::state::automation_output_validator_kind(node)
                    == crate::AutomationOutputValidatorKind::CodePatch
                {
                    "done"
                } else {
                    "completed"
                }),
            );
            object.insert("blocked_reason".to_string(), Value::Null);
            object.insert("failure_kind".to_string(), Value::Null);
        }
        if let Some(issue) = schema_issue.as_ref() {
            object.insert("status".to_string(), json!("needs_repair"));
            object.insert(
                "blocked_reason".to_string(),
                json!(format!(
                    "artifact does not match output_contract.schema: {issue}"
                )),
            );
            object.insert("failure_kind".to_string(), json!("artifact_rejected"));
        }
    }

    let artifact_validation = output
        .as_object_mut()
        .and_then(|object| object.get_mut("artifact_validation"))
        .and_then(Value::as_object_mut);
    if let Some(artifact_validation) = artifact_validation {
        artifact_validation.insert(
            "accepted_candidate_source".to_string(),
            json!(accepted_candidate_source),
        );
        if let Some(issue) = schema_issue.as_ref() {
            let reason = format!("artifact does not match output_contract.schema: {issue}");
            artifact_validation.insert("rejected_artifact_reason".to_string(), json!(reason));
            artifact_validation.insert(
                "semantic_block_reason".to_string(),
                json!(format!(
                    "artifact does not match output_contract.schema: {issue}"
                )),
            );
            artifact_validation.insert("validation_outcome".to_string(), json!("needs_repair"));
            let unmet = artifact_validation
                .entry("unmet_requirements".to_string())
                .or_insert_with(|| json!([]));
            if let Some(rows) = unmet.as_array_mut() {
                if !rows
                    .iter()
                    .any(|value| value.as_str() == Some("output_schema_invalid"))
                {
                    rows.push(json!("output_schema_invalid"));
                }
            }
        } else {
            artifact_validation.insert("rejected_artifact_reason".to_string(), Value::Null);
        }
        if should_complete {
            artifact_validation.insert("semantic_block_reason".to_string(), Value::Null);
            artifact_validation.insert("unmet_requirements".to_string(), json!([]));
        }
        if let Some(validation_basis) = artifact_validation
            .entry("validation_basis".to_string())
            .or_insert_with(|| json!({}))
            .as_object_mut()
        {
            validation_basis.insert(
                "current_attempt_output_materialized".to_string(),
                json!(true),
            );
            validation_basis.insert(
                "current_attempt_output_materialized_via_filesystem".to_string(),
                json!(true),
            );
            validation_basis.insert("verified_output_materialized".to_string(), json!(true));
            validation_basis.insert("required_output_path".to_string(), json!(artifact_path));
        }
        if recovery_source.is_some() {
            artifact_validation.insert("artifact_recovered_from_session".to_string(), json!(true));
        }
    }

    let validator_summary = output
        .as_object_mut()
        .and_then(|object| object.get_mut("validator_summary"))
        .and_then(Value::as_object_mut);
    if let Some(validator_summary) = validator_summary {
        validator_summary.insert(
            "accepted_candidate_source".to_string(),
            json!(accepted_candidate_source),
        );
        if let Some(issue) = schema_issue.as_ref() {
            validator_summary.insert("outcome".to_string(), json!("needs_repair"));
            validator_summary.insert(
                "reason".to_string(),
                json!(format!(
                    "artifact does not match output_contract.schema: {issue}"
                )),
            );
            let unmet = validator_summary
                .entry("unmet_requirements".to_string())
                .or_insert_with(|| json!([]));
            if let Some(rows) = unmet.as_array_mut() {
                if !rows
                    .iter()
                    .any(|value| value.as_str() == Some("output_schema_invalid"))
                {
                    rows.push(json!("output_schema_invalid"));
                }
            }
        }
        if should_complete {
            validator_summary.insert(
                "outcome".to_string(),
                json!(if crate::app::state::automation_output_validator_kind(node)
                    == crate::AutomationOutputValidatorKind::CodePatch
                {
                    "done"
                } else {
                    "completed"
                }),
            );
            validator_summary.insert("reason".to_string(), Value::Null);
            validator_summary.insert("unmet_requirements".to_string(), json!([]));
        }
    }

    let attempt_artifact = output
        .as_object_mut()
        .and_then(|object| object.get_mut("attempt_evidence"))
        .and_then(|value| value.get_mut("artifact"))
        .and_then(Value::as_object_mut);
    if let Some(attempt_artifact) = attempt_artifact {
        attempt_artifact.insert("status".to_string(), json!("written"));
        attempt_artifact.insert("path".to_string(), json!(artifact_path));
        attempt_artifact.insert("content_digest".to_string(), json!(content_digest));
        attempt_artifact.insert(
            "accepted_candidate_source".to_string(),
            json!(accepted_candidate_source),
        );
        if let Some(recovery_source) = recovery_source {
            attempt_artifact.insert("recovery_source".to_string(), json!(recovery_source));
        }
    }
}