"""Completion rules: a reply or assistance card never finishes a node by itself.""" from __future__ import annotations from app.report_collaboration.contracts.enums import ArtifactValidationStatus, CollaborationNodeStatus class NodeCompletionBlocked(ValueError): """Raised when a caller tries to mark a node completed without a validated artifact.""" def can_complete_node( *, status: CollaborationNodeStatus, validation_status: ArtifactValidationStatus | None, awaiting_input: bool, cancelled: bool, ) -> bool: """Return whether TaskLedger may move this node to ``completed``.""" if cancelled or status in {"cancelled", "cancel_requested", "superseded"}: return False if awaiting_input or status == "awaiting_input": return False if status != "validating": return False return validation_status == "validated" def require_can_complete_node( *, status: CollaborationNodeStatus, validation_status: ArtifactValidationStatus | None, awaiting_input: bool = False, cancelled: bool = False, ) -> None: if not can_complete_node( status=status, validation_status=validation_status, awaiting_input=awaiting_input, cancelled=cancelled, ): raise NodeCompletionBlocked("node cannot complete without a validated artifact, or the current status forbids completion") def late_output_may_write_completed(*, current_status: CollaborationNodeStatus) -> bool: """Cancelled / superseded nodes must ignore late AgentScope output.""" return current_status not in {"cancelled", "superseded", "completed"}