From 4ebd98bb991a8ce6d0f041dc0af99f7a0d02c00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 16 Apr 2026 22:51:00 -0400 Subject: [PATCH] fix(automation): surface malformed condition JSON instead of silently returning false P2-46: Previous bare try/catch hid parse errors, making rules silently non-firing. Let JSON.parse throw; the caller (evaluateAutomationRules) already logs and records the failure. --- src/main/automation/automation-conditions.ts | 60 ++++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/main/automation/automation-conditions.ts b/src/main/automation/automation-conditions.ts index c7ce40f0..12f3fdc4 100644 --- a/src/main/automation/automation-conditions.ts +++ b/src/main/automation/automation-conditions.ts @@ -10,39 +10,37 @@ export function matchesEventConfig( if (sourceDepartment && ctx.department && sourceDepartment !== ctx.department) { return false; } - try { - const conditions = JSON.parse(configJson) as Record; - if (Object.keys(conditions).length === 0) return true; - if (conditions.status && ctx.newStatus && conditions.status !== ctx.newStatus) return false; - if (conditions.previousStatus && ctx.previousStatus && conditions.previousStatus !== ctx.previousStatus) return false; - if (conditions.priority) { - try { - const task = ctx.taskId ? db.getTask(ctx.taskId) : null; - if (task && task.priority !== conditions.priority) return false; - // If task was deleted, skip this condition instead of rejecting the rule. - } catch { - // Entity missing — treat as condition not applicable, continue matching. - } + // Surface malformed condition JSON to the caller (automation service) so the + // operator sees it in logs instead of silently filtering out the rule. + const conditions = JSON.parse(configJson) as Record; + if (Object.keys(conditions).length === 0) return true; + if (conditions.status && ctx.newStatus && conditions.status !== ctx.newStatus) return false; + if (conditions.previousStatus && ctx.previousStatus && conditions.previousStatus !== ctx.previousStatus) return false; + if (conditions.priority) { + try { + const task = ctx.taskId ? db.getTask(ctx.taskId) : null; + if (task && task.priority !== conditions.priority) return false; + // If task was deleted, skip this condition instead of rejecting the rule. + } catch { + // Entity missing — treat as condition not applicable, continue matching. } - if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) { - try { - const agent = db.getAgent(ctx.agentId); - if (agent.budgetMonthlyUsd > 0) { - const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd; - if (utilization < (conditions.budgetThresholdPct as number) / 100) return false; - } - // If budget is 0 (unlimited), skip budget condition instead of rejecting. - } catch { - // Agent missing — treat as condition not applicable, continue matching. - } - } - if (conditions.taskId && ctx.taskId && conditions.taskId !== ctx.taskId) return false; - if (conditions.projectId && ctx.projectId && conditions.projectId !== ctx.projectId) return false; - if (conditions.agentId && ctx.agentId && conditions.agentId !== ctx.agentId) return false; - return true; - } catch { - return false; } + if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) { + try { + const agent = db.getAgent(ctx.agentId); + if (agent.budgetMonthlyUsd > 0) { + const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd; + if (utilization < (conditions.budgetThresholdPct as number) / 100) return false; + } + // If budget is 0 (unlimited), skip budget condition instead of rejecting. + } catch { + // Agent missing — treat as condition not applicable, continue matching. + } + } + if (conditions.taskId && ctx.taskId && conditions.taskId !== ctx.taskId) return false; + if (conditions.projectId && ctx.projectId && conditions.projectId !== ctx.projectId) return false; + if (conditions.agentId && ctx.agentId && conditions.agentId !== ctx.agentId) return false; + return true; } export function matchesConditions(