Skip to content

fix(flows): case_escalation re-fire guard must use status, not the boolean is_escalated#435

Merged
os-zhuang merged 1 commit into
mainfrom
fix/case-escalation-loop-guard
Jul 6, 2026
Merged

fix(flows): case_escalation re-fire guard must use status, not the boolean is_escalated#435
os-zhuang merged 1 commit into
mainfrom
fix/case-escalation-loop-guard

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

case_escalation fires on record-after-update and its action writes back to the same case (is_escalated=true, status='escalated', …), re-triggering record-after-update. The guard record.is_escalated != true was meant to stop the second fire, but a boolean persists as integer 1 on SQLite/libsql and CEL 1 != true is true — the guard never trips and the flow loops forever. On 2026-07-06 this wedged a new env's first-boot seed and left the workspace unopenable.

What

Gate on record.status != "escalated"status is a string enum set by the same escalation write, so it reliably suppresses the re-fire regardless of how the driver stores booleans. (The platform now also coerces booleans for flow conditions + guards against self-trigger loops — objectstack-ai/objectstack#2661 — but the template must not depend on that.)

task_urgent_alert also guards on a boolean (is_completed != true) but fires on record-after-create with a notify-only body (no self-write), so it cannot loop; left as-is.

🤖 Generated with Claude Code

…olean is_escalated

The escalation flow fires on `record-after-update` and its action writes back to
the same case (`is_escalated=true, status='escalated', …`), which re-triggers
record-after-update. The guard `record.is_escalated != true` was meant to stop
the second fire, but a `boolean` field persists as integer `1` on SQLite/libsql
and CEL `1 != true` is `true`, so the guard never trips and the flow loops
forever — on 2026-07-06 this infinite cascade during a new env's first-boot seed
wedged the whole per-env kernel build, leaving the workspace unopenable.

Gate on `record.status != "escalated"` instead: `status` is a string enum set by
the same escalation write, so it reliably suppresses the re-fire regardless of
how the driver stores booleans. (The platform also now coerces booleans for flow
conditions + guards against self-trigger loops, but the template must not depend
on that.)

task_urgent_alert also guards on a boolean (`is_completed != true`) but fires on
record-after-CREATE with a notify-only body (no self-write), so it cannot loop;
left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit d13709d into main Jul 6, 2026
5 checks passed
@os-zhuang
os-zhuang deleted the fix/case-escalation-loop-guard branch July 6, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant