cueloop 0.4.1

A Rust CLI for managing AI agent loops with a structured JSON task queue
Documentation
//! Queue continuation helper builders shared across machine queue documents.
//!
//! Purpose:
//! - Queue continuation helper builders shared across machine queue documents.
//!
//! Responsibilities:
//! - Build stable blocking and continuation payload fragments for validate/repair flows.
//! - Keep command/action text centralized so machine documents stay consistent.
//!
//! Non-scope:
//! - Queue IO, locking, repair execution, or undo restoration.
//! - CLI routing or JSON serialization.
//!
//!
//! Usage:
//! - Used through the crate module tree or integration test harness.
//!
//! Invariants:
//! - Returned actions remain deterministic and version-safe for machine consumers.
//! - Helpers stay focused on shared document text, not business logic branching elsewhere.

use crate::cli::machine::common::{
    machine_queue_repair_command, machine_queue_undo_dry_run_command,
    machine_queue_validate_command, machine_run_one_resume_command, machine_task_mutate_command,
};
use crate::contracts::{
    BlockingState, BlockingStatus, MachineContinuationAction, MachineContinuationSummary,
    MachineValidationWarning,
};

pub(crate) fn queue_validation_failed_state(detail: String) -> BlockingState {
    BlockingState::operator_recovery(
        BlockingStatus::Stalled,
        "queue_validate",
        "validation_failed",
        None,
        "CueLoop is stalled on queue consistency.",
        detail,
        Some(machine_queue_repair_command(true).to_string()),
    )
    .with_observed_at(crate::timeutil::now_utc_rfc3339_or_fallback())
}

pub(super) fn repair_preview_continuation(changed: bool) -> MachineContinuationSummary {
    if changed {
        return MachineContinuationSummary {
            headline: "Repair preview is ready.".to_string(),
            detail: "CueLoop found recoverable queue issues and can preserve the current queue by normalizing them.".to_string(),
            blocking: Some(
                BlockingState::operator_recovery(
                    BlockingStatus::Stalled,
                    "queue_repair",
                    "repair_available",
                    None,
                    "CueLoop found recoverable queue issues.",
                    "Preview completed successfully; apply the repair to continue from a normalized queue state.",
                    Some(machine_queue_repair_command(false).to_string()),
                )
                .with_observed_at(crate::timeutil::now_utc_rfc3339_or_fallback()),
            ),
            next_steps: vec![
                step(
                    "Apply repair",
                    machine_queue_repair_command(false),
                    "Write recoverable fixes and create an undo checkpoint first.",
                ),
                step(
                    "Preview a rollback",
                    machine_queue_undo_dry_run_command(),
                    "Inspect the restore path before applying more queue changes.",
                ),
            ],
        };
    }

    MachineContinuationSummary {
        headline: "No queue repair is needed.".to_string(),
        detail: "The queue already matches CueLoop’s continuation invariants.".to_string(),
        blocking: None,
        next_steps: vec![step(
            "Continue work",
            machine_run_one_resume_command(),
            "No recovery write is required before continuing.",
        )],
    }
}

pub(super) fn continuation_for_valid_queue(
    blocking: Option<BlockingState>,
    warnings: &[MachineValidationWarning],
) -> MachineContinuationSummary {
    if let Some(blocking) = blocking {
        if blocking.is_all_draft_queue() {
            let mut next_steps = Vec::new();
            if let Some(task_id) = blocking.task_id.as_deref() {
                next_steps.push(step(
                    "Promote first draft leaf",
                    &format!("cueloop task ready {task_id}"),
                    "Promote the first actionable draft leaf to todo.",
                ));
            }
            next_steps.push(step(
                "Inspect runnability",
                "cueloop queue explain",
                "Review queue runnability and draft activation guidance.",
            ));
            if !warnings.is_empty() {
                next_steps.push(step(
                    "Review warnings",
                    machine_queue_validate_command(),
                    "Inspect validation warnings before larger follow-up changes.",
                ));
            }
            next_steps.push(step(
                "Continue when ready",
                machine_run_one_resume_command(),
                "Resume once a leaf is todo.",
            ));

            return MachineContinuationSummary {
                headline: "Queue is waiting for draft activation.".to_string(),
                detail:
                    "No runnable tasks because all tasks are draft. Promote a leaf task to todo."
                        .to_string(),
                blocking: Some(blocking),
                next_steps,
            };
        }

        return MachineContinuationSummary {
            headline: match blocking.status {
                BlockingStatus::Waiting => "Queue continuation is waiting.".to_string(),
                BlockingStatus::Blocked => "Queue continuation is blocked.".to_string(),
                BlockingStatus::Stalled => "Queue continuation is stalled.".to_string(),
            },
            detail: if warnings.is_empty() {
                "The queue structure is valid, but CueLoop cannot continue immediately from the current runnability state.".to_string()
            } else {
                "The queue structure is valid, but warnings and current runnability should be reviewed before continuing.".to_string()
            },
            blocking: Some(blocking),
            next_steps: vec![
                step(
                    "Inspect runnability",
                    "cueloop queue explain",
                    "Review why the next task is waiting or blocked.",
                ),
                step(
                    "Continue when ready",
                    machine_run_one_resume_command(),
                    "Resume normal task flow once the queue becomes runnable.",
                ),
            ],
        };
    }

    if warnings.is_empty() {
        return MachineContinuationSummary {
            headline: "Queue continuation is ready.".to_string(),
            detail: "CueLoop can continue from the current queue state without queue-level repair."
                .to_string(),
            blocking: None,
            next_steps: vec![
                step(
                    "Continue work",
                    machine_run_one_resume_command(),
                    "Resume or continue normal task flow.",
                ),
                step(
                    "Preview additional task changes",
                    machine_task_mutate_command(true),
                    "Preview structured queue changes before writing.",
                ),
            ],
        };
    }

    MachineContinuationSummary {
        headline: "Queue continuation is ready with warnings.".to_string(),
        detail: "Warnings did not invalidate the queue, but they should be reviewed before larger follow-up changes.".to_string(),
        blocking: None,
        next_steps: vec![
                step(
                    "Review warnings",
                    machine_queue_validate_command(),
                    "Inspect the warning list from the machine validation surface.",
                ),
            step(
                "Continue carefully",
                machine_run_one_resume_command(),
                "Proceed once the warnings are understood.",
            ),
        ],
    }
}

pub(super) fn step(title: &str, command: &str, detail: &str) -> MachineContinuationAction {
    MachineContinuationAction {
        title: title.to_string(),
        command: command.to_string(),
        detail: detail.to_string(),
    }
}