Designing reliable automation: When to use rules, scripts, and agents
Not every task needs an agent. How to choose between automation rules, Runner scripts, and AI agents in Plane, with worked examples for each.
Not every task needs an agent. How to choose between automation rules, Runner scripts, and AI agents in Plane, with worked examples for each.


Every new layer of tooling arrives sold as a replacement for the last one. The project tool was going to end the status spreadsheet. Templates were going to end copy-and-paste. Automation rules were going to end the manual chasing. All three shipped, and all three are still running next to the thing they replaced. Someone on your team has a spreadsheet open right now that the project tool was supposed to make unnecessary, and they are right to keep it.
What happens instead is duller than the pitch. The new layer takes the work the old one could not reach, the boundary between them settles, and teams run both for years without thinking about it again.
Agents are the current arrival, and the pattern is holding. Automation is not a binary choice between workflows and AI. Different tasks need different levels of determinism and autonomy, and what decides the layer is how much interpretation a task requires and how much responsibility you are willing to hand to something that might get it wrong.
You feel the boundary the first time a request arrives that the automation builder has no shape for. "Flag anything in this cycle that looks likely to slip." There is no condition that means likely.
The spectrum of automation in Plane
Requests like that one do not fail because the tool is missing a feature. They fail because they were sent to the wrong layer.
Plane gives you three layers of automation. They differ by one thing: how much of the decision the system makes on its own.
Custom automations → Runner scripts → Agents
All three run inside a boundary a person draws. You decide what each layer may do before it runs, and you pick up whatever it hands back. For a custom automation, that boundary is the condition you wrote. For a Runner script, it is the code. For an agent, it is the playbook, which you write in prose, and which leaves no boundary if you skip it.
This is not a maturity ladder. Nobody graduates from custom automations. A team running two hundred rules and no agents is not behind a team running five agents. Each step along the list gives the system more room to decide, and more room to get it wrong too.
What separates the layers is what you have to specify.
Approach | System is told | System decides |
Custom automations | Exact condition and action | Almost nothing |
Runner Scripts | Procedure and logic | How to execute the procedure |
Agent | Instructions, triggers, tools | How to accomplish the goal |
More autonomy does not mean better automation. It means more responsibility is being handed to the system.
These layers solve different classes of problems. A custom automation cannot interpret. An agent should not be trusted to flip the same boolean two hundred times a day and get it right every time. Neither of those is a defect.
1. Event-driven custom automation rules
Plane's trigger-based automations follow one shape: when a trigger fires, if conditions are met, perform actions. Five event triggers cover work item created, work item updated, state changed, assignee changed, and comment added. A scheduled trigger accepts daily, weekly, monthly or custom cron expressions with a timezone. Conditions filter on state, priority, assignees, labels, work item type, and created by, with AND logic across conditions and optional OR groups.
A worked example - Your support team files bugs through Intake, and urgent ones sit unacknowledged over weekends:
- Trigger: Work item created
- Conditions: Work item type is Bug, AND Priority is Urgent
- Actions: Change property to set Assignees to the on-call engineer, and add a comment with a template variable naming the reporter and the SLA clock
That automation will do the same thing on its four thousandth run as it did on its first.
What you can use custom automations for:
- Status transitions- A merged pull request moves the work item to In Review without anyone remembering to do it.
- Notifications- The on-call engineer hears about an urgent bug, and the rest of the channel does not.
- Field synchronization- A priority change on the parent pushes down to every child that inherited it.
- Ownership changes- reassignment on escalation, or on a state that belongs to another team.
- SLA reminders- a comment lands before the clock runs out, not in the report afterwards.
Those tasks share a property. The correct answer is known before the trigger fires. Nothing is being figured out.
Characteristics:
- Stateless - The rule holds nothing between runs.
- Predictable - The same input produces the same action every time.
- Low maintenance - A rule that has run quietly for eighteen months is doing what it did on day one.
- No interpretation - The rule cannot weigh two signals against each other, which is exactly why it never surprises you.
Change property and add comment work on all plans of Plane. Send webhook and run script are Enterprise Grid actions, and Business plans get project-level automations only, so workspace-level scope also means Enterprise Grid. Check that before you design around this layer.
A custom automation matches a condition on one work item and acts on that same item. The moment the decision depends on a second one, you are in Runner territory. "Close the parent when the last child finishes" is the wall most teams hit first, because the rule fires on the child while the answer lives with its siblings.
2. Runner scripts for deterministic workflows
Plane Runner is programmable automation: a sandboxed environment that runs JavaScript and TypeScript in response to workspace events, on a schedule, or during a workflow transition.
Every script is one exported function. Event scripts receive the full event payload. Scheduled scripts receive variables only, and the scheduler checks roughly every five minutes.
export async function main(input: AutomationEventInput, variables: Record<string, string>) {
const projectId = input.event.project_id;
const workItemId = input.event.entity_id;
return { success: true, message: "Done!" };
}Inside that function you get an injected Plane SDK, a library of reusable functions, environment variables, and fetch. The SDK retrieves and updates work items, lists and creates states and labels, adds comments, creates relations, and runs filtered search. Built-in functions include getSiblings, getChildren, addComment, addLabel, postToSlack and httpRequest. Outbound fetch is restricted to an allowlist you configure per script.
Example use cases, all four of which ship as documented scripts:
- Close the parent when the last child is done
The script runs on work item updated. It retrieves the item, exits if there is no parent, lists states to map state IDs to their groups, confirms the item just landed in a completed group, calls Functions.getSiblings(), and updates the parent only when every sibling is also complete. Seven steps, one correct answer, no judgment anywhere in it.
- Create dependent work across projects - Take three variables,
sourceProjectId,sourceProjectStateNameset to something like "Ready for QA", anddestinationProjectId. When an item in the source project reaches that state, the script copies the name, description, and priority into a new work item in the QA project and links the two with arelates_torelation. The QA lead stops chasing engineers for handoffs, and the trail between the two items survives in the relation. - Sync Git metadata and external services into work items -
Functions.httpRequestandfetchboth work, withENV.API_TOKENfor credentials, so a nightly script can pull build or deployment data from an external API and write it into structured fields. - Nightly backlog hygiene and recurring operational work - A cron-triggered script uses advanced search to find items untouched for thirty days, labels them, comments, and reports the count. Nobody runs a Friday cleanup meeting for this.
- Enforce a precondition on a transition - Attach a script as a precondition on the To Do to In Progress transition. It retrieves the item, checks for a non-empty assignees array and an
estimate_point, collects the missing fields, and throws an error naming all of them. The throw blocks the transition and shows the message to the person who tried it.
// return { success: true } allows the transition
// return { success: false } blocks it
// throw new Error("reason") blocks it and shows the reason to the userWhy scripts instead of custom automations?
Because the workflow is a procedure. Every step in it has one correct outcome. "Close the parent when every child is done" has exactly one correct answer at any moment. Reaching it takes a query, a loop, and a conditional. It takes no interpretation, which is why handing it to an agent would be a downgrade.
Runner is deliberately constrained, and the constraints tell you what it is for. Execution times out at 10 seconds, initialization at 5, memory caps at 128 MB. requireModule imports, Node APIs like fs and child_process, eval and prototype manipulation are all blocked. Runner is built for bounded procedures. A data pipeline belongs outside it.
One availability note, because it changes who this section is for. Plane Runner and scheduled automations are Enterprise Grid features. On Pro or Business, this layer is a purchase decision before it is a design decision. Check plans
Where deterministic automation breaks down
Take a request every release manager has made:
"Identify the five work items most likely to delay the release."
You can encode this deterministically, and teams do. Weight staleness, add points for unestimated scope, subtract for an active assignee. It works, right up to the point where the criteria move.
When automation rules fail:
- Multiple signals - Stale updates, blocker relations, unestimated scope, a reassignment three days ago. None is decisive alone.
- Conflicting priorities - A P1 with an active owner is safer than a P2 nobody has touched.
- Historical context - This team's In Review has meant four days for the last six cycles.
- Natural language updates - The most informative content sits in comments, not fields.
- Dependency interpretation - A blocker relation pointing at an item that was itself descoped is not a blocker.
You could encode a scoring function for this, and teams do. Weight staleness, add points for unestimated scope, subtract for an active assignee. It works until the shape of the project changes, and then you have a script that confidently returns the wrong five items. That is worse than returning nothing, because someone will act on it.
3. Reasoning agents
Give agents a job, not just a prompt.
A prompt gets answered once. A job has an owner, a definition of done, boundaries, and a trigger that fires without anyone remembering to ask. That difference is what separates this layer from a chat window.
A reasoning agent takes structured project data and unstructured context, plans intermediate steps, and produces a decision, a recommendation, or a generated artifact.
That is the layer, not a product SKU. Plane does not ship one fixed agent. You start from a template or build your own, and either way you define four things: what it owns, the playbook it follows, when it starts, and what it can reach.
The five templates show what the layer is for, and each one names the tools it needs:
Agent | What it does | Connected tools |
Standup | Collects progress, blockers, and next steps, and turns them into one update | Slack, Gmail, Projects |
Delivery Risk | Watches for stalls, blockers, dependencies, and slipping timelines | GitHub, Slack, Projects |
Spec | Turns rough requests into structured requirements and finds the gaps | Figma, Wiki |
Request Triage | Reviews incoming work, spots duplicates, adds context, routes it | Slack, Projects, Intake |
Customer Feedback | Groups feedback into themes and surfaces what keeps recurring | Slack, Projects, Wiki |
Build your own | Whatever handoff your team currently does by hand | Any MCP server you connect |
Custom agents cover the handoffs the templates do not. One example, triggered on assignment:
A root cause agent handles incident enrichment. Sentry's integration files a work item when an alert fires, carrying the error context with it. The agent is assigned to that item, reads the Sentry error, queries Datadog over MCP for logs around the same window, and comments its findings back on the work item. Its only action is the comment, so an engineer opens the item with the investigation already done.
Plane's Sentry integration creates the work item when an alert fires. The agent picks it up on assignment, pulls the matching window from Datadog over MCP, and writes what it found back as a comment. Four lines of playbook, one external tool, one permitted action, no state changes.
You can build multiple agents as per the use cases -
Use case | What the agent does |
Release risk | Weighs stale updates, blockers, and slipping dates against each other and flags what is likely to slip |
Root cause | Reads an alert, pulls matching logs from a connected tool, and comments the findings |
Cycle summary | Reads the cycle's activity and writes what actually happened |
Dependency check | Walks the relation graph and separates real blockers from stale ones |
Duplicate detection | Spots that two differently worded reports describe one defect |
Status reporting | Turns a project's activity into an update written for a specific audience |
Each of these ends in something a person reads and acts on.
Work with a known answer belongs one layer down.
Task | Layer that owns it |
Updating a field | Custom automations |
Closing completed work | Runner script |
Assigning labels from explicit rules | Custom automations |
An agent can update a field. A custom automation already does it correctly every time, with a log you can read.
What the agent layer asks of you
The next request is always "do that every Monday, and fix what you find." An agent will take it. The second half of that sentence is the part that needs writing carefully.
Probabilistic systems can answer the same question two defensible ways, so you sample quality instead of running a test that passes. A goal without a boundary invites action nobody asked for.
Permissions compound too. An agent that can read your knowledge base, create work items, and post to Slack has more reach than any one of those permissions suggests, and that is how something confidential ends up in a channel it should not be in.
So the playbook and the access scope are the two fields worth real time. The good version of "fix what you find" reads "summarize what you find, comment it on the cycle, change nothing else."
A decision matrix for choosing the right layer
Design automation around the decision, not the technology.
Work down these in order. Stop at the first one you answer yes to.
- Is the action fully determined by the trigger plus a few field conditions? Custom automations
- Does deciding require reading something the trigger did not hand you? Runner
- Does it need loops, external APIs, or data transformation? Runner
- Is the correct output unknowable before the task runs? Agent
- Does being wrong cost more than being slow? Human, with an agent preparing the input.
If your task requires... | Use | Looks like |
One trigger, one action | Custom automations | Urgent bug filed, assign the on-call engineer, comment the SLA clock |
Conditional logic | Runner | Close the parent when the last child reaches a completed state |
Loops, APIs, data transformation | Runner | Nightly sync of build metadata from an external API into work items |
Contextual interpretation | Agent | Root cause enrichment from an alert plus logs in another system |
Organizational judgment | Human + Agent | Ship or slip the release |
Signs you picked the wrong layer
Automation drifts. The symptoms are easy to spot once you know what they look like.
Symptom | What it means | Move to |
The rule has four OR groups and still misses cases | The decision needs data the trigger does not carry | Runner |
The script hand-tunes weights to rank things | You are encoding judgment as arithmetic | Agent |
Someone edits the agent's output every time before using it | The task had a knowable answer | Runner or Custom automation |
The agent needs another tool connection every few weeks | Its scope is drifting past what it owns | Split it into two agents |
The automation runs, and nobody opens the result | The task did not need automating | Delete it |
Composing automation pipelines
The interesting systems are not built on one layer. They hand off.
Example pipeline:
- A pull request merges. The GitHub integration moves the linked work item to Merged.
- An automation rule triggers on that state change.
- A Runner script validates the linked work items, creates the QA item in the QA project, and updates dependent items across projects.
- The Delivery Risk Agent reviews release health across the cycle and drafts a summary.
- A human approves the release.
Steps two and three stay deterministic. Step four is interpretation and could not be a script. Step five carries organizational consequence and should not be delegated.
The skill worth developing is knowing when to switch from deterministic execution to probabilistic reasoning, and when to switch back. Most badly designed automation gets the second switch wrong. It lets the agent that wrote the summary also close the items.
Guardrails for reliable automation
Automation is only reliable when you control what it can do, what happens when it fails, and how its actions can be reviewed or undone. Rules need this as much as agents do, because a rule with the wrong condition executes its mistake four thousand times.
Permissions and scope
- Give automation only the permissions it needs
- Restrict agents and runners to the projects, work items, and actions they actually require
- Apply the same access controls to automated actions as to human actions
Plane's agent configuration supports this. You pick the projects, trusted web sources, and connected tools each agent can reach, and you decide whether it acts through a person's account or a team-managed service account. The Spec Agent gets Figma and Wiki. It has no reason to hold the Slack token the Standup Agent uses.
Runner has one real scoping control, and it is worth using well. Each script carries its own allowed-domains list, so the Slack notification script gets hooks.slack.com and nothing else. A script that reaches a payments API cannot quietly start posting to a webhook you did not authorize.
The gap is on the Plane side of the boundary. Activating and managing scripts requires Workspace Admin, and the documented access scope is "read and write access to all workspace resources and your user profile." A script that only needs to read one project gets the whole workspace. Until that is scoped per script, least privilege here is something you enforce by code review and convention, not something the platform enforces for you.
Retries and failure handling
- Retry transient failures automatically
- Set limits so failed workflows do not loop indefinitely
- Make failures visible instead of silently dropping work
- Define what happens when a downstream service is unavailable
What Plane documents today is narrower than that list. For workflow transition scripts: "If they fail, the transition still stands, and the error is logged." No retry policy is described.
In practice, that means retry logic and the partial-failure decision live inside your script. A nightly sync that touches 300 work items and dies at item 180 on a 502 needs to know where it stopped, and the 10-second execution ceiling means it cannot simply start over from item 1. Write it to record progress, skip what it already did, and be safe to run twice. Functions.httpRequest Throws on any non-2xx response, so a try/catch around the external call is the difference between a script that logs "third-party API returned 503, 120 items remaining" and one that just says errored.
Auditability
- Record what triggered the automation
- Capture actions taken and their outcomes
- Make agent-generated decisions or artifacts distinguishable from deterministic changes
- Preserve enough history to understand why a change happened
Runner execution records capture trigger type, timing, errors, and return values, which is why the return { success: true, message: "..." } at the end of a script is worth filling in properly. message: "Closed parent PROJ-441, 6 of 6 children complete" reads usefully in the log six months from now. message: "Done" does not. Test runs are tagged trigger_type: "test" and reviewed separately from production runs. Agent activity is visible as it starts, returns and asks for input. At the workspace level, API-enabled audit logs are an Enterprise Grid feature.
The third bullet is the one teams skip. "Who changed this" and "was this a judgment call" are different questions, and a timestamp only answers the first.
Approval gates
Not every automated action should execute immediately.
Use approval gates when automation can:
- Change critical project state
- Affect multiple teams or projects
- Trigger external actions
- Make consequential recommendations
- Move work across a defined control boundary
Plane's workflows support approvals with designated approvers per transition. A single workflow comes with Business, and multiple workflows with approvals are Enterprise Grid.
Approvals and pre-condition scripts are complementary controls. A pre-condition script checks whether defined conditions are met, so nobody moves a work item into In Progress without an assignee and an estimate, and it names the field that is missing. An approval gate captures a person's sign-off, which is a judgment no condition can express. Use the script to keep incomplete work out of a state, and the gate where someone has to own the call.
Choose the least autonomy that solves the problem
Every automation you build will eventually raise the same question. Someone will ask why it did what it did.
An automation rule answers in one line: this trigger, this condition, this action. A Runner script answers with an execution log and the source that produced it. An agent answers with an account of its reasoning that may not be the reasoning. A person answers by explaining the call they made and why.
All four are acceptable answers. They are not equally cheap, and the difference only shows up on the day something goes wrong.
That is the case for picking the least autonomous layer that can do the job. It is also the one that can explain itself in the fewest steps. Rules where the answer is known, Runner where the procedure is known, agents where the path needs interpretation, humans where the decision carries consequence.
Before you build, decide which of those four answers you want to be giving. It usually settles the layer faster than the requirements do.
Pick the automation your team complains about most this week, and check whether it needs more than a trigger and an action. Most of them don't.
Trigger-based automations ship on every Plane plan. Start free or read the automation docs.
Already past automations? Plane Runner and Agents are on Enterprise Grid. Talk to us about your workflow.
Recommended for you


