Automations
Automations let you trigger actions automatically when events happen in Craft Agent. You can run shell commands, send prompts to start new sessions, or schedule recurring workflows — all configured in a single JSON file.
Getting Started
Section titled “Getting Started”The easiest way to set up automations is to describe what you want in plain language. Here are some prompts you can try:
| What you want | What to say |
|---|---|
| Scheduled briefing | “Set up a daily standup briefing every weekday at 9am” |
| Desktop notifications | “Notify me with a macOS notification when a session is labelled urgent” |
| Audit logging | “Log all permission mode changes to a file” |
| Auto-label | “When a session starts, run a command that checks the working directory and logs it” |
| Recurring report | “Every Friday at 5pm, create a session that summarises this week’s completed tasks” |
| Webhook integration | “When I flag a session, send a curl request to my webhook URL” |
Your First Automation
Section titled “Your First Automation”Here’s the simplest possible automation — it logs a message every time a label is added to a session:
{ "version": 2, "automations": { "LabelAdd": [ { "actions": [ { "type": "command", "command": "echo \"Label added: $CRAFT_LABEL\" >> ~/craft-automations.log" } ] } ] }}Save this as ~/.craft-agent/workspaces/{workspaceId}/automations.json and it starts working immediately — no restart needed.
Scheduling a Prompt
Section titled “Scheduling a Prompt”Want Craft Agent to do something for you on a schedule? Use a prompt action with a cron expression:
{ "version": 2, "automations": { "SchedulerTick": [ { "cron": "0 9 * * 1-5", "timezone": "America/New_York", "labels": ["Scheduled"], "actions": [ { "type": "prompt", "prompt": "Check @github for any new issues assigned to me and summarise them" } ] } ] }}This creates a new session every weekday at 9 AM, activates the GitHub source, and asks the agent to check your issues. The session is automatically labelled “Scheduled” for easy filtering.
How Automations Work
Section titled “How Automations Work”When an event fires (e.g., a label is added, a tool runs, or a cron schedule matches), Craft Agent checks your configuration for matching entries and executes them.
There are two types of actions:
- Command actions — execute shell commands with event data available as environment variables
- Prompt actions — send a prompt to Craft Agent, creating a new session (App events only)
Configuration File
Section titled “Configuration File”Automations are configured per-workspace in automations.json:
~/.craft-agent/workspaces/{workspaceId}/automations.jsonBasic Structure
Section titled “Basic Structure”{ "version": 2, "automations": { "EventName": [ { "matcher": "regex-pattern", "actions": [ { "type": "command", "command": "echo 'Hello'" } ] } ] }}Each event type maps to an array of matchers, and each matcher contains an array of actions to execute when the matcher’s pattern matches the event value.
Managing Automations
Section titled “Managing Automations”In the UI
Section titled “In the UI”Automations are listed in the sidebar under Automations. From here you can:
- Enable / Disable — Toggle individual automations on or off without removing them
- Duplicate — Create a copy of an existing automation with a “Copy” suffix
- Delete — Remove an automation permanently
- Test — Manually trigger an automation to verify it works before waiting for the real event. The test runner resolves
@mentions, enables sources, and uses the configuredllmConnection/model— the same code path as the scheduler. - Execution history — Each automation records success and failure to a timeline, viewable in the detail page. History is stored in
automations-history.jsonland retained to the last 20 runs per automation (max 1000 entries total).
Batch operations are supported — select multiple automations and enable, disable, or delete them together. The list is sorted by most recent execution for quick access to active automations.
In the Config File
Section titled “In the Config File”You can also manage automations by editing automations.json directly. Changes take effect immediately — no restart required.
Enabling and disabling: Set "enabled": false on any matcher to temporarily disable it without removing the configuration. Omit the field or set it to true to re-enable.
{ "matcher": "^urgent$", "enabled": false, "actions": [ { "type": "command", "command": "notify-send 'Urgent!'" } ]}Deleting: Remove the matcher entry from automations.json. If an event type has no remaining matchers, you can remove the entire event key.
Events
Section titled “Events”App Events
Section titled “App Events”Triggered by Craft Agent itself. Support both command and prompt actions.
| Event | Trigger | Match Value |
|---|---|---|
LabelAdd | Label added to session | Label ID (e.g., urgent) |
LabelRemove | Label removed from session | Label ID |
LabelConfigChange | Label configuration changed | Always matches |
PermissionModeChange | Permission mode changed | New mode name |
FlagChange | Session flagged/unflagged | true or false |
SessionStatusChange | Session status changed | New status (e.g., done) |
SchedulerTick | Runs every minute | Uses cron matching |
Agent Events
Section titled “Agent Events”Passed to the Claude SDK. Only command actions are supported (no prompt actions).
| Event | Trigger | Match Value |
|---|---|---|
PreToolUse | Before a tool executes | Tool name |
PostToolUse | After a tool succeeds | Tool name |
PostToolUseFailure | After a tool fails | Tool name |
UserPromptSubmit | User submits a prompt | Event data JSON |
SessionStart | Session starts | Event data JSON |
SessionEnd | Session ends | Event data JSON |
Stop | Agent stops | Event data JSON |
SubagentStart | Subagent spawned | Event data JSON |
SubagentStop | Subagent completes | Event data JSON |
Notification | Notification received | Event data JSON |
PreCompact | Before context compaction | Event data JSON |
PermissionRequest | Permission requested | Event data JSON |
Setup | Agent setup/initialization | Event data JSON |
Action Types
Section titled “Action Types”Command Actions
Section titled “Command Actions”Execute a shell command when the event fires. Event data is available through environment variables.
{ "type": "command", "command": "echo \"Label $CRAFT_LABEL was added\" >> ~/automation-log.txt", "timeout": 60000}| Property | Type | Default | Description |
|---|---|---|---|
type | "command" | Required | Action type |
command | string | Required | Shell command to execute |
timeout | number | 60000 | Timeout in milliseconds |
Prompt Actions
Section titled “Prompt Actions”Send a prompt to Craft Agent, creating a new session. Only works with App events.
{ "type": "prompt", "prompt": "Run the @weather skill and summarize today's forecast"}| Property | Type | Default | Description |
|---|---|---|---|
type | "prompt" | Required | Action type |
prompt | string | Required | Prompt text to send |
llmConnection | string | Workspace default | LLM connection slug (configured in AI Settings) |
model | string | Workspace default | Model ID for the created session |
thinkingLevel | "off" | "low" | "medium" | "high" | "xhigh" | "max" | Workspace default | Thinking level for the spawned session |
Features:
- Use
@mentionsto reference sources or skills (e.g.,@github,@linear) - Environment variables are expanded (e.g.,
$CRAFT_LABEL,${CRAFT_SESSION_NAME}) - Mentioned sources are automatically activated for the new session
Per-Action Overrides: You can override the AI provider, model, and thinking level for the session created by a prompt action. Each field is independent — omit any of them to inherit the workspace default.
{ "type": "prompt", "prompt": "Quick code review of recent changes", "llmConnection": "my-copilot-connection", "model": "gemini-2.5-flash", "thinkingLevel": "high"}The llmConnection value is the slug of an LLM connection configured in AI Settings. The model value is a model ID supported by the provider. If either is invalid or not found, it gracefully falls back to the workspace default.
Thinking level resolution: thinkingLevel on the action takes precedence; if omitted, the workspace default applies; if neither is set, sessions default to "medium". The legacy "think" value from older configs is silently migrated to "medium". Backends gracefully cap unsupported values (e.g. Anthropic auto-degrades xhigh → high on models that don’t support it; Pi caps max at xhigh).
Matchers
Section titled “Matchers”Regex Matching
Section titled “Regex Matching”Most events use regex matching against the event’s match value:
{ "matcher": "^urgent$", "actions": [ { "type": "command", "command": "notify-send 'Urgent!'" } ]}Omit the matcher field to match all events of that type.
Examples:
| Pattern | Matches |
|---|---|
^urgent$ | Exact match for “urgent” |
bug|feature | Either “bug” or “feature” |
^prod- | Any value starting with “prod-” |
| (omitted) | All events of that type |
Cron Matching
Section titled “Cron Matching”For SchedulerTick events, use cron expressions instead of regex:
{ "cron": "0 9 * * 1-5", "timezone": "Europe/Budapest", "actions": [ { "type": "prompt", "prompt": "Give me a morning briefing" } ]}Cron format: minute hour day-of-month month day-of-week
| Field | Range | Examples |
|---|---|---|
| Minute | 0–59 | 0, */15 |
| Hour | 0–23 | 9, 14 |
| Day of month | 1–31 | 1, 15 |
| Month | 1–12 | 1, */3 |
| Day of week | 0–6 (0 = Sunday) | 1-5, 0,6 |
Common patterns:
| Cron | Schedule |
|---|---|
*/15 * * * * | Every 15 minutes |
0 9 * * * | Daily at 9:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
30 14 1 * * | 1st of each month at 2:30 PM |
0 */6 * * * | Every 6 hours |
Timezone: Uses IANA timezone names (e.g., Europe/Budapest, America/New_York). Defaults to system timezone if not specified. Verify with crontab.guru.
Conditions
Section titled “Conditions”Conditions are optional filters that run after the matcher/cron matches but before actions fire. All conditions in the array must pass (implicit AND). If the array is empty or omitted, actions fire unconditionally.
{ "cron": "0 9 * * *", "timezone": "Europe/Budapest", "conditions": [ { "condition": "time", "weekday": ["mon", "tue", "wed", "thu", "fri"] } ], "actions": [ { "type": "prompt", "prompt": "Good morning! Here's your daily briefing." } ]}Time Conditions
Section titled “Time Conditions”Check time-of-day and day-of-week in a given timezone.
{ "condition": "time", "after": "09:00", "before": "17:00", "weekday": ["mon", "tue", "wed", "thu", "fri"], "timezone": "Europe/Budapest"}| Property | Type | Description |
|---|---|---|
after | "HH:MM" | Start of time window (inclusive) |
before | "HH:MM" | End of time window (exclusive) |
weekday | string[] | Allowed days: mon, tue, wed, thu, fri, sat, sun |
timezone | string | IANA timezone. Falls back to matcher timezone, then system local |
State Conditions
Section titled “State Conditions”Check fields from the event payload. Useful for filtering on specific transitions or values.
{ "condition": "state", "field": "permissionMode", "from": "safe", "to": "allow-all"}| Property | Type | Description |
|---|---|---|
field | string | Payload field name (e.g., permissionMode, sessionStatus, labels, isFlagged) |
value | any | Exact match |
from | any | Previous value (for transition events) |
to | any | New value (for transition events) |
contains | string | Array membership check (e.g., check if a label is present) |
not_value | any | Matches anything except this value |
Logical Composition
Section titled “Logical Composition”Combine conditions with and, or, and not:
{ "condition": "and", "conditions": [ { "condition": "time", "weekday": ["mon", "tue", "wed", "thu", "fri"] }, { "condition": "time", "after": "09:00", "before": "17:00" } ]}{ "condition": "or", "conditions": [ { "condition": "state", "field": "permissionMode", "value": "allow-all" }, { "condition": "state", "field": "isFlagged", "value": true } ]}{ "condition": "not", "conditions": [ { "condition": "time", "weekday": ["sat", "sun"] } ]}| Type | Behaviour |
|---|---|
and | All sub-conditions must pass |
or | At least one sub-condition must pass |
not | None of the sub-conditions may pass |
Matcher Options
Section titled “Matcher Options”Each matcher entry supports these optional fields:
| Property | Type | Default | Description |
|---|---|---|---|
matcher | string | Match all | Regex pattern for event filtering |
cron | string | — | Cron expression (SchedulerTick only) |
timezone | string | System TZ | IANA timezone for cron |
conditions | array | [] | Conditions that must all pass before actions fire (see Conditions) |
permissionMode | string | "safe" | Security mode for commands |
labels | string[] | [] | Labels applied to prompt-created sessions |
telegramTopic | string | — | Routes spawned sessions to a Telegram forum topic — see Telegram Topic Routing |
enabled | boolean | true | Set to false to disable without deleting |
actions | array | Required | Array of command/prompt actions |
Telegram Topic Routing
Section titled “Telegram Topic Routing”When you have a Telegram supergroup paired in Settings → Messaging → Telegram,
you can route the sessions a matcher spawns into a dedicated forum topic by
setting the telegramTopic field.
{ "automations": { "LabelAdd": [ { "matcher": "^urgent$", "telegramTopic": "Urgent Alerts", "permissionMode": "ask", "actions": [ { "type": "prompt", "prompt": "Look at the urgent issue: $LABEL" } ] } ] }}When the matcher fires, the spawned session is bound to a forum topic of the
given name. The topic is created on first use and reused thereafter, so
subsequent runs of the same matcher (and any other matcher that uses the same
telegramTopic value) post into the same topic.
Activation requirements
Section titled “Activation requirements”All of these must hold; otherwise the field is silently ignored and the session runs without a Telegram binding (same as if the field were absent):
- A Telegram supergroup is paired at Settings → Messaging → Telegram
- The Telegram bot is connected
- The bot has the Manage Topics admin permission in the supergroup
Setting up the Telegram supergroup
Section titled “Setting up the Telegram supergroup”If you haven’t paired a supergroup yet, follow these steps. The whole flow takes about a minute.
1. Create a supergroup with topics enabled
Section titled “1. Create a supergroup with topics enabled”A regular Telegram group can’t host topics — you need a supergroup with “Topics” mode enabled.
- New group: Telegram → New Group → add anyone (or yourself for a solo workspace) → set a name. Telegram converts groups to supergroups automatically once members exceed a threshold or you flip Topics on.
- Enable Topics: Open the group → tap the group name → Edit (pencil icon on mobile, ⋯ on desktop) → toggle Topics on → Save. The group is now a forum supergroup; you’ll see a “General” topic and a ”+” button to create more.
2. Add the bot to the supergroup
Section titled “2. Add the bot to the supergroup”Open the supergroup → tap the group name → Add members → search for your
bot’s username (e.g. @CraftAgentsBot) → add it.
3. Make the bot an admin with “Manage Topics”
Section titled “3. Make the bot an admin with “Manage Topics””This is the step most people miss — the bot needs explicit permission to
create new topics, otherwise topic-creation calls return
400: Bad Request: not enough rights to create a topic.
- In the supergroup, tap the group name → Edit → Administrators.
- Tap Add Administrator → pick the bot.
- In the permissions list, toggle on Manage Topics (other permissions like Delete Messages or Pin Messages are optional and not required by the topic-routing feature).
- Tap Save / Done.
4. Pair the supergroup with the workspace
Section titled “4. Pair the supergroup with the workspace”- In the Craft Agent app: Settings → Messaging → Telegram → Pair Supergroup.
- The dialog shows a 6-digit code, a deep link to the bot, and a 5-minute countdown.
- In Telegram, in any topic of your supergroup, type
/pair <code>(replace<code>with the digits from the dialog). - The bot replies with a confirmation. The dialog closes automatically and the Settings row updates with the supergroup’s title and chat ID.
That’s it — automations with telegramTopic set will now create topics in
this supergroup.
- Topic names are 1–128 characters and case-sensitive (
"Reports"and"reports"are different topics). - Two matchers using the same
telegramTopicvalue share one topic — useful for grouping related automations. - If you change a matcher’s
telegramTopicvalue, the next run uses (or creates) the new topic; the old topic remains in Telegram with its history intact. - Renaming the topic in Telegram doesn’t sync back — the binding follows the topic ID, not the display name.
Environment Variables
Section titled “Environment Variables”Common Variables
Section titled “Common Variables”Available to all command actions:
| Variable | Description |
|---|---|
CRAFT_EVENT | Event name (e.g., LabelAdd, PreToolUse) |
CRAFT_EVENT_DATA | Full event payload as JSON |
CRAFT_SESSION_ID | Current session ID |
CRAFT_SESSION_NAME | Current session name |
CRAFT_WORKSPACE_ID | Current workspace ID |
App Event Variables
Section titled “App Event Variables”Dynamically generated from event payloads:
Label events (LabelAdd, LabelRemove):
CRAFT_LABEL— Label ID being added/removed
PermissionModeChange:
CRAFT_OLD_MODE— Previous permission modeCRAFT_NEW_MODE— New permission mode
FlagChange:
CRAFT_IS_FLAGGED—trueorfalse
SessionStatusChange:
CRAFT_OLD_STATE— Previous session statusCRAFT_NEW_STATE— New session status
SchedulerTick:
CRAFT_LOCAL_TIME— Current time (HH:MM)CRAFT_LOCAL_DATE— Current date (YYYY-MM-DD)
Agent Event Variables
Section titled “Agent Event Variables”Tool events (PreToolUse, PostToolUse, PostToolUseFailure):
CRAFT_TOOL_NAME— Tool being usedCRAFT_TOOL_INPUT— Tool parameters as JSONCRAFT_TOOL_RESPONSE— Tool output (PostToolUse only)CRAFT_ERROR— Error message (PostToolUseFailure only)
Session events (SessionStart):
CRAFT_SOURCE— Session source (e.g.,startup,resume)CRAFT_MODEL— Model name
Subagent events (SubagentStart, SubagentStop):
CRAFT_AGENT_ID— Subagent IDCRAFT_AGENT_TYPE— Subagent type
Permission Modes
Section titled “Permission Modes”Command actions run with security checks by default. You can adjust this per matcher:
| Mode | Behaviour | Use Case |
|---|---|---|
safe | Commands checked against allowlist | Default, recommended |
allow-all | Bypass security checks | Trusted automation only |
{ "matcher": "^urgent$", "permissionMode": "allow-all", "actions": [ { "type": "command", "command": "osascript -e 'display notification \"Urgent!\" with title \"Craft Agent\"'" } ]}Labels for Prompt Actions
Section titled “Labels for Prompt Actions”Prompt actions can attach labels to the sessions they create. This makes it easy to filter and organise scheduled sessions:
{ "cron": "0 9 * * *", "labels": ["Scheduled", "morning-briefing"], "actions": [ { "type": "prompt", "prompt": "Give me today's priorities" } ]}Labels also support environment variable expansion: "priority::${CRAFT_LABEL}".
Rate Limits
Section titled “Rate Limits”To prevent runaway loops (e.g., an automation that indirectly triggers itself), the event bus enforces rate limits:
| Event | Max fires / minute |
|---|---|
SchedulerTick | 60 (1/sec) |
| All other events | 10 |
Excess events are silently dropped for the remainder of the 60-second window.
Examples
Section titled “Examples”Daily Morning Briefing
Section titled “Daily Morning Briefing”Schedule a prompt every weekday at 9 AM:
{ "version": 2, "automations": { "SchedulerTick": [ { "cron": "0 9 * * 1-5", "timezone": "Europe/Budapest", "labels": ["Scheduled", "briefing"], "actions": [ { "type": "prompt", "prompt": "Run the @daily-standup skill" } ] } ] }}Weekday-Only AI News (with Conditions)
Section titled “Weekday-Only AI News (with Conditions)”Use a time condition to restrict a daily schedule to weekdays:
{ "version": 2, "automations": { "SchedulerTick": [ { "name": "Morning AI news", "cron": "0 9 * * *", "timezone": "Europe/Budapest", "conditions": [ { "condition": "time", "weekday": ["mon", "tue", "wed", "thu", "fri"], "timezone": "Europe/Budapest" } ], "labels": ["Scheduled", "ai-news"], "actions": [ { "type": "prompt", "prompt": "Run the @ai-news skill and summarize today's AI developments" } ] } ] }}Permission Mode Gate (with Conditions)
Section titled “Permission Mode Gate (with Conditions)”Only notify when permission mode changes specifically from safe to allow-all:
{ "version": 2, "automations": { "PermissionModeChange": [ { "conditions": [ { "condition": "state", "field": "permissionMode", "from": "safe", "to": "allow-all" } ], "actions": [ { "type": "command", "command": "osascript -e 'display notification \"Permission escalated to allow-all\" with title \"Craft Agent\"'" } ] } ] }}Log Label Changes
Section titled “Log Label Changes”Track when labels are added or removed:
{ "version": 2, "automations": { "LabelAdd": [ { "permissionMode": "allow-all", "actions": [ { "type": "command", "command": "echo \"[$(date)] Added: $CRAFT_LABEL\" >> ~/label-log.txt" } ] } ], "LabelRemove": [ { "permissionMode": "allow-all", "actions": [ { "type": "command", "command": "echo \"[$(date)] Removed: $CRAFT_LABEL\" >> ~/label-log.txt" } ] } ] }}macOS Notification on Urgent Label
Section titled “macOS Notification on Urgent Label”{ "version": 2, "automations": { "LabelAdd": [ { "matcher": "^urgent$", "permissionMode": "allow-all", "actions": [ { "type": "command", "command": "osascript -e 'display notification \"Urgent session flagged\" with title \"Craft Agent\"'" } ] } ] }}Audit Permission Mode Changes
Section titled “Audit Permission Mode Changes”{ "version": 2, "automations": { "PermissionModeChange": [ { "permissionMode": "allow-all", "actions": [ { "type": "command", "command": "echo \"$(date): $CRAFT_OLD_MODE -> $CRAFT_NEW_MODE\" >> ~/mode-audit.log" } ] } ] }}Multiple Schedules with Enable/Disable
Section titled “Multiple Schedules with Enable/Disable”Run different automations at different times, and disable some without deleting:
{ "version": 2, "automations": { "SchedulerTick": [ { "cron": "0 9 * * 1-5", "timezone": "Europe/Budapest", "labels": ["Scheduled"], "actions": [ { "type": "prompt", "prompt": "Give me a morning briefing" } ] }, { "cron": "0 17 * * 5", "timezone": "Europe/Budapest", "enabled": false, "labels": ["Scheduled"], "actions": [ { "type": "prompt", "prompt": "Summarise this week's completed tasks" } ] } ] }}The second automation (Friday summary) is disabled and won’t run until "enabled" is set to true or removed.
Prompt with Custom Connection, Model, and Thinking Level
Section titled “Prompt with Custom Connection, Model, and Thinking Level”Override provider, model, and thinking level for a specific automation:
{ "version": 2, "automations": { "SchedulerTick": [ { "cron": "0 8 * * 1-5", "timezone": "Europe/Budapest", "labels": ["Scheduled"], "actions": [ { "type": "prompt", "prompt": "Review overnight @github pull requests", "llmConnection": "openrouter", "model": "anthropic/claude-sonnet-4.6", "thinkingLevel": "high" } ] } ] }}Validation
Section titled “Validation”Ask Craft Agent to validate your automations:
Validate my automations configurationOr use the config_validate tool with target: "all".
The validator checks for:
- Invalid JSON syntax
- Unknown event names
- Empty actions arrays
- Invalid cron expressions or timezones
- Invalid or unsafe regex patterns (ReDoS prevention)
- References to non-existent labels
- Invalid condition types, field names, or weekday values
- Condition nesting depth exceeding limits
- Missing
llmConnectionslugs (error — will fail at runtime) - Model/provider mismatches when both
llmConnectionandmodelare specified (warning) - Invalid
thinkingLevelvalues (legacy"think"is silently migrated to"medium")
Security
Section titled “Security”Automations include several built-in safety measures:
- Shell injection prevention — User-controlled values in environment variables (session names, labels, prompts) are automatically escaped
- ReDoS protection — Regex patterns are limited to 500 characters and checked for catastrophic backtracking patterns
- Rate limiting — Prevents infinite loops from automations that trigger other automations
- Permission modes — Commands are checked against an allowlist by default
- Timeouts — Commands are killed after their timeout expires (with SIGKILL fallback)
Best Practices
- Start simple — Test with
echocommands before writing complex scripts - Use labels — Tag scheduled sessions for easy filtering
- Set timeouts — Prevent runaway commands with the
timeoutfield - Log failures — Redirect stderr to track issues:
command 2>> ~/automation-errors.log - Be specific — Use matchers to avoid triggering on every event
- Test cron — Use crontab.guru to verify expressions
- Use
enabled: false— Temporarily disable automations instead of deleting them during debugging
Troubleshooting: Automation not firing
- Check event name — Must be exact (e.g.,
LabelAddnotlabeladd) - Check matcher — Regex must match the event’s match value
- Check cron — For SchedulerTick, verify your cron expression
- Check enabled — Ensure the matcher doesn’t have
"enabled": false - Check logs — Look for
[automations]in the application logs
Troubleshooting: Command blocked
If you see “Bash command blocked” errors:
- Add
"permissionMode": "allow-all"to the matcher - Or simplify the command to avoid shell constructs like
$()
Troubleshooting: Prompt not creating session
- Ensure the event is an App event (prompt actions don’t work with Agent events)
- Check that the prompt text is not empty
- Verify
@mentionsreference valid sources or skills
Troubleshooting: Condition not matching
- Check weekday spelling — Must be 3-letter lowercase:
mon,tue,wed,thu,fri,sat,sun - Check timezone — Use IANA names (e.g.,
Europe/Budapest). Invalid timezones silently fall back to system local - Check time format — Must be
HH:MMin 24-hour format (e.g.,09:00, not9:00 AM) - Check state field names — Use
permissionMode,sessionStatus,labels,isFlagged - Check nesting depth — Conditions nested deeper than 8 levels always evaluate to false
- Check logs — Look for
[automations]entries that mention condition evaluation