fix: project Ticket mutation outputs to human keys

This commit is contained in:
2026-08-27 14:18:48 +09:00
parent 75c570962d
commit 116d610ad0
4 changed files with 210 additions and 7 deletions
+54 -1
View File
@@ -62,8 +62,9 @@ impl WorkspaceHttpObjectiveBackend {
.await
.map_err(backend_error)?;
let response = project_objective_detail(response).map_err(ToolError::ExecutionFailed)?;
let objective_ref = response.objective_ref().to_string();
Ok(ToolOutput {
summary: format!("Read objective {id}"),
summary: format!("Read objective {objective_ref}"),
content: Some(serde_json::to_string_pretty(&response).map_err(decode_error)?),
attachments: Vec::new(),
})
@@ -710,6 +711,58 @@ mod tests {
assert_eq!(link["required"], json!(["id", "ticket_id"]));
}
#[tokio::test(flavor = "multi_thread")]
async fn objective_show_summary_uses_projected_human_key() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let base_url = format!("http://{}", listener.local_addr().unwrap());
let server = thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
let mut buffer = [0_u8; 8192];
let len = stream.read(&mut buffer).unwrap();
let request = String::from_utf8_lossy(&buffer[..len]);
assert!(
request.starts_with("POST /api/w/workspace/objectives/00001INTERNAL/show HTTP/1.1")
);
let body = serde_json::json!({
"id": "00001INTERNAL",
"resource_key": "O-3",
"title": "Objective",
"body": "Body",
"state": "active",
"created_at": null,
"updated_at": null,
"linked_ticket_summaries": [],
"events": [],
"event_page": {"next_cursor": null, "has_more": false}
})
.to_string();
write!(
stream,
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
body.len(),
body
)
.unwrap();
});
let backend = WorkspaceHttpObjectiveBackend::new(Arc::new(
crate::worker::TestWorkspaceHttpClient::new("workspace", base_url),
));
let output = backend
.show(ShowObjectiveInput {
id: "00001INTERNAL".to_string(),
event_limit: None,
event_cursor: None,
})
.await
.unwrap();
server.join().unwrap();
assert_eq!(output.summary, "Read objective O-3");
assert!(!output.summary.contains("00001INTERNAL"));
assert!(!output.content.unwrap().contains("00001INTERNAL"));
}
#[tokio::test(flavor = "multi_thread")]
async fn objective_link_summaries_resolve_internal_ticket_ids_to_human_keys() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
@@ -84,6 +84,12 @@ pub(super) struct ModelObjectiveDetail {
event_page: ModelObjectiveEventPage,
}
impl ModelObjectiveDetail {
pub(super) fn objective_ref(&self) -> &str {
&self.objective
}
}
#[derive(Debug, Serialize)]
struct ModelWorkerSummary {
worker: String,
+11 -1
View File
@@ -876,12 +876,22 @@ impl WorkspaceHttpTicketBackend {
Ok(TicketBackendOperationResult::Tickets(tickets))
}
TicketBackendOperation::Show { id } => {
let ticket = Self::request(
let ticket: Ticket = Self::request(
client,
WorkspaceRequestMethod::Get,
format!("{base}/{}/record", Self::ticket_path(&id)),
None,
)?;
if !ticket
.meta
.resource_key
.as_deref()
.is_some_and(is_canonical_ticket_resource_key)
{
return Err(TicketError::Conflict(
"required Ticket human key is unavailable".to_string(),
));
}
Ok(TicketBackendOperationResult::Ticket(ticket))
}
TicketBackendOperation::Create { input } => {