feat: event-driven parity, Windows shell detection, dirty build timestamps, and gatekeeper integration

This commit is contained in:
Riz Ashraf committed 2026-10-08 10:52:19 +01:00
1 parent 64857f9d5e
commit f4dcf3acbf
33 files changed
+3501 -143

No files matched your search

+153
View File
@@ -772,6 +772,159 @@ impl MemoryHandler {
}
}
}
"tasks/complete" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let task_id = params.get("id").and_then(|v| v.as_str()).unwrap_or("");
if task_id.is_empty() {
return Some(crate::mcp::error(id, -32602, "Missing task id parameter"));
}
self.state.project.tasks.modify(|tasks| {
for t in tasks.iter_mut() {
if t.id == task_id {
t.status = "completed".to_string();
t.updated_at = crate::handlers::utils::now_secs();
break;
}
}
});
let unblocked = crate::handlers::reconciliation::cascade_unblock_tasks(&self.state);
let milestones = crate::handlers::reconciliation::recalculate_milestones(&self.state);
self.state.record_activity("task", &format!("Completed task via WebSocket RPC: {}", task_id), Some("completed"));
let _ = self.state.event_bus_tx.send(crate::state::GenericEvent {
topic: "task:event".to_string(),
session_id: None,
payload: serde_json::json!({
"task_id": task_id,
"status": "completed",
"unblocked_tasks": unblocked,
"updated_milestones": milestones,
}),
});
Some(crate::mcp::success(id, serde_json::json!({
"status": "success",
"unblocked_tasks": unblocked,
"updated_milestones": milestones
})))
}
"tech_debts/resolve" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let debt_id = params.get("id").and_then(|v| v.as_str()).unwrap_or("");
if debt_id.is_empty() {
return Some(crate::mcp::error(id, -32602, "Missing debt id parameter"));
}
let mut found = false;
self.state.code.tech_debts.modify(|debts| {
for d in debts.iter_mut() {
if d.id == debt_id {
d.is_resolved = true;
found = true;
break;
}
}
});
if found {
self.state.record_activity("tech_debt", &format!("Resolved tech debt via WebSocket RPC: {}", debt_id), Some("resolved"));
let _ = self.state.event_bus_tx.send(crate::state::GenericEvent {
topic: "tech_debt:event".to_string(),
session_id: None,
payload: serde_json::json!({
"id": debt_id,
"status": "resolved"
}),
});
Some(crate::mcp::success(id, serde_json::json!({ "status": "success", "message": format!("Tech debt {} resolved", debt_id) })))
} else {
Some(crate::mcp::error(id, -32602, "Tech debt not found"))
}
}
"gates/authorize" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let gate_id = params.get("id").and_then(|v| v.as_str()).unwrap_or("");
let mut found = false;
self.state.env.gates.modify(|gates| {
for g in gates.iter_mut() {
if g.id == gate_id {
g.status = "authorized".to_string();
g.reason = Some("Manually authorized via WebSocket RPC".to_string());
g.timestamp = crate::handlers::utils::now_secs();
found = true;
break;
}
}
});
if found {
self.state.record_activity("gate", &format!("Authorized push gate via WebSocket: {}", gate_id), Some("authorized"));
let _ = self.state.event_bus_tx.send(crate::state::GenericEvent {
topic: "gate:event".to_string(),
session_id: None,
payload: serde_json::json!({ "id": gate_id, "status": "authorized", "active_gate": "authorized" }),
});
Some(crate::mcp::success(id, serde_json::json!({ "status": "success" })))
} else {
Some(crate::mcp::error(id, -32602, "Gate not found"))
}
}
"gates/block" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let gate_id = params.get("id").and_then(|v| v.as_str()).unwrap_or("");
let mut found = false;
self.state.env.gates.modify(|gates| {
for g in gates.iter_mut() {
if g.id == gate_id {
g.status = "blocked".to_string();
g.reason = Some("Manually blocked via WebSocket RPC".to_string());
g.timestamp = crate::handlers::utils::now_secs();
found = true;
break;
}
}
});
if found {
self.state.record_activity("gate", &format!("Blocked push gate via WebSocket: {}", gate_id), Some("blocked"));
let _ = self.state.event_bus_tx.send(crate::state::GenericEvent {
topic: "gate:event".to_string(),
session_id: None,
payload: serde_json::json!({ "id": gate_id, "status": "blocked", "active_gate": "blocked" }),
});
Some(crate::mcp::success(id, serde_json::json!({ "status": "success" })))
} else {
Some(crate::mcp::error(id, -32602, "Gate not found"))
}
}
"gates/delete" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let gate_id = params.get("id").and_then(|v| v.as_str()).unwrap_or("");
let mut removed = false;
self.state.env.gates.modify(|gates| {
if let Some(pos) = gates.iter().position(|g| g.id == gate_id) {
gates.remove(pos);
removed = true;
}
});
if removed {
self.state.record_activity("gate", &format!("Deleted push gate via WebSocket: {}", gate_id), Some("deleted"));
let _ = self.state.event_bus_tx.send(crate::state::GenericEvent {
topic: "gate:event".to_string(),
session_id: None,
payload: serde_json::json!({ "id": gate_id, "status": "deleted" }),
});
Some(crate::mcp::success(id, serde_json::json!({ "status": "success" })))
} else {
Some(crate::mcp::error(id, -32602, "Gate not found"))
}
}
"resources/subscribe" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let uri = params.get("uri").and_then(|u| u.as_str()).unwrap_or("");
tracing::info!("Client subscribed to MCP resource: {}", uri);
Some(crate::mcp::success(id, serde_json::json!({})))
}
"resources/unsubscribe" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let uri = params.get("uri").and_then(|u| u.as_str()).unwrap_or("");
tracing::info!("Client unsubscribed from MCP resource: {}", uri);
Some(crate::mcp::success(id, serde_json::json!({})))
}
m if m.starts_with("notifications/") => None,
"ping" => Some(crate::mcp::success(id, serde_json::json!({}))),
_ => {