fix: preserve intake and companion ticket workflows
This commit is contained in:
@@ -18,7 +18,7 @@ use crate::runtime::dir::RuntimeDir;
|
||||
use crate::segment_log_sink::SegmentLogSink;
|
||||
use crate::shared_state::WorkerSharedState;
|
||||
use crate::shutdown_after_idle::{
|
||||
ShutdownAfterIdleRequest, TicketMarkReadyShutdownHook, is_ticket_intake_role,
|
||||
ShutdownAfterIdleRequest, TicketIntakeReadyShutdownHook, is_ticket_intake_role,
|
||||
take_shutdown_request_after_status,
|
||||
};
|
||||
use crate::spawn::registry::SpawnedWorkerRegistry;
|
||||
@@ -423,11 +423,11 @@ impl WorkerController {
|
||||
.await?;
|
||||
|
||||
// Intake role Workers self-terminate only after a successful
|
||||
// TicketMarkReady turn has fully settled back to Idle. The request
|
||||
// TicketIntakeReady turn has fully settled back to Idle. The request
|
||||
// is transient controller state, not model-visible context or ticket
|
||||
// claim metadata.
|
||||
let shutdown_after_idle = ShutdownAfterIdleRequest::default();
|
||||
worker.add_post_tool_call_hook(TicketMarkReadyShutdownHook::new(
|
||||
worker.add_post_tool_call_hook(TicketIntakeReadyShutdownHook::new(
|
||||
shutdown_after_idle.clone(),
|
||||
is_ticket_intake_role(worker.runtime_ticket_role()),
|
||||
));
|
||||
|
||||
@@ -381,6 +381,8 @@ const READ_ONLY_TOOL_NAMES: &[&str] = &["QueryTicket", "ShowTicket"];
|
||||
const AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||
"TicketCreate",
|
||||
"TicketEditItem",
|
||||
"TicketMarkReady",
|
||||
"TicketQueue",
|
||||
"TicketClose",
|
||||
"TicketRelationRecord",
|
||||
"TicketRelationRemove",
|
||||
@@ -388,7 +390,7 @@ const AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||
|
||||
const THREAD_TOOL_NAMES: &[&str] = &["TicketComment"];
|
||||
|
||||
const INTAKE_TOOL_NAMES: &[&str] = &["TicketMarkReady"];
|
||||
const INTAKE_TOOL_NAMES: &[&str] = &["TicketIntakeReady"];
|
||||
|
||||
#[cfg(test)]
|
||||
const WORKSPACE_AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||
@@ -397,6 +399,8 @@ const WORKSPACE_AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||
"QueryTicket",
|
||||
"ShowTicket",
|
||||
"TicketComment",
|
||||
"TicketMarkReady",
|
||||
"TicketQueue",
|
||||
"TicketClose",
|
||||
"TicketRelationRecord",
|
||||
"TicketRelationRemove",
|
||||
@@ -407,7 +411,6 @@ const WORKFLOW_TOOL_NAMES: &[&str] = &[
|
||||
"QueryTicket",
|
||||
"ShowTicket",
|
||||
"TicketComment",
|
||||
"TicketQueue",
|
||||
"TicketWorkflowState",
|
||||
"TicketClose",
|
||||
"TicketDependencyCheck",
|
||||
@@ -418,7 +421,6 @@ const WORKFLOW_TOOL_NAMES: &[&str] = &[
|
||||
];
|
||||
|
||||
const WORKFLOW_ADDITIONAL_TOOL_NAMES: &[&str] = &[
|
||||
"TicketQueue",
|
||||
"TicketWorkflowState",
|
||||
"TicketClose",
|
||||
"TicketDependencyCheck",
|
||||
@@ -1295,13 +1297,13 @@ mod tests {
|
||||
assert_eq!(show.name, "ShowTicket");
|
||||
assert!(show.input_schema["properties"]["event_limit"].is_object());
|
||||
let tool_names = TicketFeatureAccess::workspace_authoring().tool_names();
|
||||
assert_eq!(tool_names.len(), 8);
|
||||
assert_eq!(tool_names.len(), 10);
|
||||
assert!(
|
||||
tool_names.len() < 13,
|
||||
"authoring catalog must stay below the prior broad catalog"
|
||||
);
|
||||
let workflow_names = TicketFeatureAccess::workflow().tool_names();
|
||||
assert_eq!(workflow_names.len(), 11);
|
||||
assert_eq!(workflow_names.len(), 10);
|
||||
assert!(
|
||||
workflow_names.len() < 12,
|
||||
"workflow catalog must stay below the prior broad catalog"
|
||||
@@ -1382,7 +1384,7 @@ mod tests {
|
||||
.collect::<Vec<_>>();
|
||||
assert!(workspace_tools.contains(&"TicketCreate"));
|
||||
assert!(workspace_tools.contains(&"TicketEditItem"));
|
||||
assert!(!workspace_tools.contains(&"TicketQueue"));
|
||||
assert!(workspace_tools.contains(&"TicketQueue"));
|
||||
assert!(!workspace_tools.contains(&"TicketWorkflowState"));
|
||||
|
||||
let orchestration =
|
||||
@@ -1398,7 +1400,7 @@ mod tests {
|
||||
assert!(orchestration_tools.contains(&"TicketRelationRecord"));
|
||||
assert!(orchestration_tools.contains(&"TicketOrchestrationPlanRecord"));
|
||||
assert!(!orchestration_tools.contains(&"TicketEditItem"));
|
||||
assert!(orchestration_tools.contains(&"TicketQueue"));
|
||||
assert!(!orchestration_tools.contains(&"TicketQueue"));
|
||||
|
||||
let work_report =
|
||||
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::work_report());
|
||||
@@ -1511,8 +1513,9 @@ language = "Japanese"
|
||||
assert_eq!(installed, WORKSPACE_AUTHORING_TOOL_NAMES);
|
||||
assert!(installed.iter().any(|tool| *tool == "TicketCreate"));
|
||||
assert!(installed.iter().any(|tool| *tool == "TicketEditItem"));
|
||||
assert!(!installed.iter().any(|tool| *tool == "TicketQueue"));
|
||||
assert!(!installed.iter().any(|tool| *tool == "TicketMarkReady"));
|
||||
assert!(installed.iter().any(|tool| *tool == "TicketQueue"));
|
||||
assert!(installed.iter().any(|tool| *tool == "TicketMarkReady"));
|
||||
assert!(!installed.iter().any(|tool| *tool == "TicketIntakeReady"));
|
||||
assert!(!installed.iter().any(|tool| *tool == "TicketWorkflowState"));
|
||||
assert!(
|
||||
!installed
|
||||
|
||||
@@ -9,7 +9,7 @@ use ticket::config::TicketRole;
|
||||
|
||||
use crate::hook::{Hook, HookPostToolAction, PostToolCall, ToolResultSummary};
|
||||
|
||||
const TICKET_MARK_READY_TOOL_NAME: &str = "TicketMarkReady";
|
||||
const TICKET_INTAKE_READY_TOOL_NAME: &str = "TicketIntakeReady";
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct ShutdownAfterIdleRequest {
|
||||
@@ -42,12 +42,12 @@ pub(crate) fn take_shutdown_request_after_status(
|
||||
status == WorkerStatus::Idle && shutdown_after_idle.take()
|
||||
}
|
||||
|
||||
pub(crate) struct TicketMarkReadyShutdownHook {
|
||||
pub(crate) struct TicketIntakeReadyShutdownHook {
|
||||
shutdown_after_idle: ShutdownAfterIdleRequest,
|
||||
eligible_ticket_intake_role: bool,
|
||||
}
|
||||
|
||||
impl TicketMarkReadyShutdownHook {
|
||||
impl TicketIntakeReadyShutdownHook {
|
||||
pub(crate) fn new(
|
||||
shutdown_after_idle: ShutdownAfterIdleRequest,
|
||||
eligible_ticket_intake_role: bool,
|
||||
@@ -60,7 +60,7 @@ impl TicketMarkReadyShutdownHook {
|
||||
|
||||
fn observe_tool_result(&self, info: &ToolResultSummary) {
|
||||
if self.eligible_ticket_intake_role
|
||||
&& info.tool_name == TICKET_MARK_READY_TOOL_NAME
|
||||
&& info.tool_name == TICKET_INTAKE_READY_TOOL_NAME
|
||||
&& !info.is_error
|
||||
{
|
||||
self.shutdown_after_idle.request();
|
||||
@@ -69,7 +69,7 @@ impl TicketMarkReadyShutdownHook {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Hook<PostToolCall> for TicketMarkReadyShutdownHook {
|
||||
impl Hook<PostToolCall> for TicketIntakeReadyShutdownHook {
|
||||
async fn call(&self, info: &ToolResultSummary) -> HookPostToolAction {
|
||||
self.observe_tool_result(info);
|
||||
HookPostToolAction::Continue
|
||||
@@ -98,9 +98,9 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_ticket_intake_ready_schedules_shutdown_after_idle_for_intake_role() {
|
||||
let request = ShutdownAfterIdleRequest::default();
|
||||
let hook = TicketMarkReadyShutdownHook::new(request.clone(), true);
|
||||
let hook = TicketIntakeReadyShutdownHook::new(request.clone(), true);
|
||||
|
||||
hook.observe_tool_result(&tool_result(TICKET_MARK_READY_TOOL_NAME, false));
|
||||
hook.observe_tool_result(&tool_result(TICKET_INTAKE_READY_TOOL_NAME, false));
|
||||
|
||||
assert!(request.is_requested());
|
||||
assert!(request.take());
|
||||
@@ -110,9 +110,9 @@ mod tests {
|
||||
#[test]
|
||||
fn failed_ticket_intake_ready_does_not_schedule_shutdown_after_idle() {
|
||||
let request = ShutdownAfterIdleRequest::default();
|
||||
let hook = TicketMarkReadyShutdownHook::new(request.clone(), true);
|
||||
let hook = TicketIntakeReadyShutdownHook::new(request.clone(), true);
|
||||
|
||||
hook.observe_tool_result(&tool_result(TICKET_MARK_READY_TOOL_NAME, true));
|
||||
hook.observe_tool_result(&tool_result(TICKET_INTAKE_READY_TOOL_NAME, true));
|
||||
|
||||
assert!(!request.is_requested());
|
||||
}
|
||||
@@ -120,9 +120,9 @@ mod tests {
|
||||
#[test]
|
||||
fn non_intake_role_does_not_schedule_shutdown_after_idle() {
|
||||
let request = ShutdownAfterIdleRequest::default();
|
||||
let hook = TicketMarkReadyShutdownHook::new(request.clone(), false);
|
||||
let hook = TicketIntakeReadyShutdownHook::new(request.clone(), false);
|
||||
|
||||
hook.observe_tool_result(&tool_result(TICKET_MARK_READY_TOOL_NAME, false));
|
||||
hook.observe_tool_result(&tool_result(TICKET_INTAKE_READY_TOOL_NAME, false));
|
||||
|
||||
assert!(!request.is_requested());
|
||||
}
|
||||
@@ -130,7 +130,7 @@ mod tests {
|
||||
#[test]
|
||||
fn other_successful_tools_do_not_schedule_shutdown_after_idle() {
|
||||
let request = ShutdownAfterIdleRequest::default();
|
||||
let hook = TicketMarkReadyShutdownHook::new(request.clone(), true);
|
||||
let hook = TicketIntakeReadyShutdownHook::new(request.clone(), true);
|
||||
|
||||
hook.observe_tool_result(&tool_result("ShowTicket", false));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user