Building & Running Automations
Everything you do after the module is switched on: build a rule in the wizard, watch it decide against real records, publish it so it runs on a schedule, and — only once it has proved itself on your data — let it act. This page follows that order, because the product does.
The page: five tabs, five questions
Automation Center is one page (Sidebar → Administration). Switching tabs never reloads it; each tab is fetched the first time you open it and then cached, and the tab you were last on is remembered. Three of the five appear only if you hold their permission, so a colleague's screen may legitimately show fewer than yours.
| Tab | The question it answers | Needs |
|---|---|---|
| Overview | Is the engine healthy, and does anything need me? Counters for draft / active / paused / needs review, the registered types and whether each is available, the registry checksum, and the last five runs. | automations |
| Automations | What rules exist, in what mode, on what schedule? One row each, with last run, next run, status and the action menu. | automations |
| Runs & Logs | What has actually happened? Every occurrence of every automation, newest first, each opening into the run detail. | automations_logs |
| Analytics | What is the pattern? Totals across recent runs, the completed / exceptions / failed split, and reason codes ranked by frequency. | automations_analytics |
| Settings | Where are the controls? The dispatch job name, the registry checksum and the type table — plus a pointer to Cron Manager, which owns scheduling. | automations_settings |
The New automation button sits on the tab bar and shows only while Overview or Automations is active — it belongs to those two tabs, not to the page.
skipped_overlap, cancelled or
timed_out — none of which has a checkbox. Deselect every option to drop the
filter entirely and see all of them.
The builder: five steps
Everything about an automation is set in one modal wizard whose stepper you can click through in any order. The steps are guidance, not security: every value is revalidated server-side by the owning type's own handler, so skipping ahead cannot smuggle a bad configuration past a rule.
Step 1 — Type, title, description
The type dropdown lists only types that are currently available: one whose owning module
is switched off does not appear, though Overview still lists it as registered-but-inactive. Today the
one shipped type is attendance.auto_decision, contributed by HumanResource.
Title is required and is what every list, run row and audit entry will call this rule — write it so a colleague reading the log next quarter knows what it does. Description is optional free text. Changing the type clears your conditions, because condition fields come from the type's own catalogue and a rule written against attendance fields is meaningless under another type.
Step 2 — Action: what the rule does, and how carefully
Phase 1 is approve-only. There is no Reject anywhere in this module, because the attendance handler refuses any other action — an automation can move work forward, never backwards.
| Field | Default | What it means |
|---|---|---|
| Look back (days) | 7 1–365 |
Only records this many days back are considered — what keeps a routine daily run away from a historical backlog. Set it wide and the first run meets years of records. |
| Settle delay (hours) | 12 0–720 |
Wait this long after a record completes, so corrections and late edits have time to land. A
younger record is skipped as NOT_YET_SETTLED and reconsidered later. |
| Overtime policy | skip |
The one setting here with real consequences — see below. |
The builder rewrites the help text as you change the overtime policy:
Safest, and why it is the default. Any record carrying overtime is left for a person, with
reason code OVERTIME_PRESENT. The automation clears the uncontroversial majority and
hands you back a short queue of the ones that genuinely needed a human.
Approve base hours, reject the overtime. The measured hours are still stored — no policy here destroys a number. What changes is the decision recorded against them, so unapproved overtime is withheld at read time and stops reaching payroll until a reviewer says otherwise. The choice is reversible, and the preview warns you by number before you can go live.
Approve both. The record is approved with its computed overtime intact — nothing is reset, but the overtime is accepted without review. Use this only where overtime is already controlled upstream (a hard cap, a separate approval step, a scheduling rule).
Step 3 — Scope and rules
Employee scope is one picker holding teams and individual members, grouped with Teams first — a flat list buries a handful of teams under every employee in the company. Search matches inside both groups. A token that no longer resolves (a deleted member, a removed team) is kept in raw form rather than quietly disappearing, because silently dropping it would rewrite your scope on the next save without you ever seeing it go.
Conditions are rows of field · operator · value, and a record must satisfy every
one — they are ANDed. Anything that fails is left pending with RULE_NOT_MET, never
silently ignored. Fields come from the selected type; for attendance they include log type, worked
minutes, record age in hours, whether a schedule exists, whether existing overtime is present, whether
the payroll date is locked, whether a schedule-change request is pending, and department /
establishment membership. Choosing a field narrows the operators to the ones it supports, and the unary
IS_TRUE / IS_FALSE hide the value box because they take none.
Step 4 — Schedule and safety
| Field | Default | What it controls |
|---|---|---|
| Schedule expression | */15 * * * * |
A five-field cron expression, validated by the platform's single cron parser — an invalid one is refused on save, not discovered later. |
| Timezone | Asia/Manila |
The expression is matched in this zone, so "07:00" means seven in the morning where the work happens, not UTC. |
| If the previous run is still active | Skip this occurrence | Overlap policy. Skip drops the occurrence; Queue the latest holds it for after. Skip is the safe default — a run overlapping itself is a signal, not something to pile onto. |
| Records per batch | 250 |
How many records the engine asks for at a time. It also sets the preview's sample size. |
| Maximum records per run | 1000 |
The hard ceiling for one occurrence. Anything beyond it waits for the next run rather than turning one tick into an unbounded job. |
| First live run limit | 50 |
The cap on the first run allowed to change records — see Promoting to Live. |
Step 5 — Review and preview
The Review pane reads the configuration back before anything is written: title, type, lookback, settle delay, overtime policy, schedule with its timezone, scope as labels (not raw tokens), and the number of conditions. It exists so you can catch the scope you forgot to narrow.
Previewing against real data
Run preview evaluates your unsaved configuration against your real records and reports what it would do. It changes nothing, needs no publish, and runs the same handler, candidate provider and rule evaluator a real run does — there is deliberately no second code path, because a preview that could disagree with the run it predicts would be worse than no preview at all.
340 matching record(s). In this sample of 250: 214 would be acted on, 36 left for review.
12 record(s) already have overtime recorded. With this overtime setting,
a live run would reset those hours to zero.
NOT_YET_SETTLED: 21
OVERTIME_PRESENT: 12
RULE_NOT_MET: 3? means the
type cannot count cheaply — judge by the sample instead.Saving — always a draft
Save writes a draft in Observe mode, whatever else you did in the wizard. It never schedules anything and never grants the rule the ability to act; each save of an existing automation increments its revision.
Publishing in Observe
Publish (Observe) in the row's action menu turns a draft into a scheduled rule: status
active, next occurrence computed from your expression and timezone, and you recorded as
the publisher — the authority its runs are checked against from then on. Needs
automations_publish.
From this moment the automation runs on its schedule and changes nothing. It evaluates records, records an outcome and a reason for each, and files a run you can read. That is the whole point of the mode.
The Observe period — what to compare
There is no fixed length. Observe answers "would I have made these same decisions?", so run it long enough to cover the variation your data actually has — a payroll cutoff, a holiday, a week with a night shift in it. For each run:
| Compare | Against | A disagreement means |
|---|---|---|
| Would act count | What you approved by hand that day | Far lower: too strict, or a settle delay longer than your working rhythm. Far higher: scope or conditions too loose. |
| The reason breakdown | Your own reasons for leaving things pending | An unexpected dominant code is the fastest way to find the assumption you got wrong. |
| Individual record outcomes | A handful you know the answer to | Spot-check both directions: one it would approve that you would not, and one it skipped that you would have cleared. |
| Run status | Plain completed | Completed with exceptions or failed — not ready to be trusted unattended. |
Promoting to Live
Switch to Live mode appears on an active automation still in Observe, and it is the only action in this module that asks you to confirm. Four conditions must all hold, and the refusal names the one that failed:
automations_go_live. Separate from
automations_publish on purpose: being allowed to schedule a rule that only reports is
not the same as letting it change payroll inputs unattended.The first live run is capped
An Observe run proves the rule selects the right records. It cannot prove the approvals are right, because nothing was approved. So the first run that actually changes records — the one nobody has seen the results of — is bounded, and a mistake that survived Observe costs a reviewable handful instead of a thousand records. Once one live run has completed, the normal ceiling applies.
Switching back to Observe
Switch back to Observe is always available on a live automation, needs no confirmation, and
needs no go-live permission — only automations_update. Demotion is always safe, so it
requires no evidence. Publishing and changing mode are separate actions because they answer separate
questions: publish asks should this run on a schedule?, mode asks should it be allowed to
change records? — and the second must stay reversible, so an operator watching a live run behave
badly can stop it acting without archiving the rule and losing its configuration. The two directions
are two menu entries, never a toggle, so the menu always states what will happen rather than what is
currently true.
Pause, resume, archive
Status becomes paused; nothing is due, so the dispatcher will not pick it up.
Configuration, publisher, mode and run history are untouched. This is also the prerequisite for
editing an active automation. Needs automations_update.
Recomputes the next occurrence from the current expression and timezone and sets the status back
to active. Offered for both paused and needs review, so it is also how
you clear a rule the engine parked once you have fixed the cause. An invalid schedule blocks the
resume rather than leaving a rule that can never fire. Needs automations_update.
Status becomes archived. Archived rules are filtered out of the Automations tab by default
(tick Archived to see them) and their runs stay in Runs & Logs — history is not
rewritten. Archive is not a dead end either: Clone is offered on archived rows precisely so
an old configuration can be resurrected. Needs automations_delete.
Clone — and exactly what it resets
Cloning builds a variant — same rules, different department; same schedule, different cutoff — without
retyping a five-step wizard. It is offered on every status, archived included, and gated on
automations_create, because creating is what it does. The copy carries the configuration
and gets a title with a Copy suffix. What it deliberately does not inherit is the whole
design — each reset is a way this could otherwise go wrong:
| Reset | To | Why it must be |
|---|---|---|
| Identifier | a fresh one | It is unique and seeds a run's idempotency key. Two automations sharing it would collide on the same due occurrence, so one would silently never run. |
| Status | draft | Cloning a live rule into a second live rule that starts acting the moment it is saved is precisely the accident to prevent. |
| Mode | Observe | A copy of a live rule starts by watching, like everything else. |
| Next run | none | Nothing is due, so the dispatcher cannot pick the copy up before you have looked at it. |
| Publisher | cleared | This is the authority anchor a run is revalidated against. Inheriting it would let a copy act with the original publisher's authority without anyone having published it. |
| Revision | 1 | A copy is a new object, not the next revision of the source. |
| Last-run fields | cleared | Those are snapshots of the source's runs. The copy has no history. |
Runs and run items are not copied — they belong to the source. Because the copy has no completed run of its own, it must serve its own Observe period before it can be promoted, exactly like a rule built from scratch. The audit trail records the clone as a creation and names the source, so the log answers "where did this rule come from?".
Run Now
Run now queues an occurrence outside the schedule. It is offered on active automations
and needs automations_run_now. It is not a shortcut past anything: the run is created as
queued, marked as manually triggered, and goes through the normal executor path in the
automation's current mode — an Observe automation still observes. The confirmation says so plainly: it
"starts on the next dispatcher tick", within a minute, not instantly.
Reading a run
Open any row in Runs & Logs with the magnifier. The detail runs top-down, from the summary to the individual records.
Records evaluated · Would act · Skipped · Failed. Skipped is the designed outcome — records deliberately left for a person — so it never degrades the run's status the way a failure does.
Automation, run identifier, correlation id, scheduled time, status, duration and mode. Mode is read from the run's own record, not the snapshot, because it says what the run actually did — including a demotion applied after it was queued.
Lookback, settle delay, overtime policy and schedule expression, frozen when the run started. Later edits do not change what a past run did, so this is the honest answer to "what rule was in force?".
The reason breakdown counts every code across the run; below it the first records are listed as subject, outcome and reason. Every record has a reason — nothing is skipped in silence.
APPROVED line in the reason breakdown for the number a live run
actually changed. The proper operations view is Milestone 4 work — see
Research & Known Gaps.
The reason codes you will see
These come from the attendance type's handler, in the order it applies them. The first four are its own mandatory baseline, checked before your conditions.
| Code | Outcome | Meaning |
|---|---|---|
PAYROLL_LOCKED | skipped | The payroll date is locked. |
SCHEDULE_CHANGE_PENDING | skipped | A schedule-change request is outstanding, so the record is still moving. |
NOT_YET_SETTLED | skipped | Younger than your settle delay; reconsidered on a later run. |
OVERTIME_PRESENT | skipped | Overtime is recorded and your policy is skip. |
RULE_NOT_MET | skipped | Your own conditions did not all pass. |
ELIGIBLE | would approve | Observe: one the rule would have acted on. |
APPROVED | approved | Live: the record was actually approved. |
TRANSITION_ERROR | failed | The approval itself threw. One bad record does not abandon the rest of the batch. |
| a service code | skipped | The record stopped being eligible between evaluation and approval — the shared transition service refused, and its own reason is recorded rather than a bare "skipped". |
What a healthy run looks like
| Signal | Healthy | Look into it |
|---|---|---|
| Status | completed | completed with exceptions — records failed. failed — the run itself stopped. |
| Failed count | 0 |
Anything above zero. Open the record outcomes and read the TRANSITION_ERROR
rows. |
| Records evaluated | Steady run to run, well under your maximum | Pinned exactly at Maximum records per run — the run is truncated and there is a backlog it never reaches. |
| Would act / approved | A plausible fraction of records evaluated | Zero on every run (never fires) or 100% (filtering nothing). |
| Skipped | Present, with a reason mix you recognise | One code suddenly dominating — usually an upstream change, not an automation change. |
| Duration | Short and stable | Climbing run to run, or long enough that occurrences overlap and get skipped. |
| Automation status | active | needs review — the engine parked it; it will not run until the cause is resolved. |
Next: who may do each of these, whose authority a live run acts with, and how the schedule actually reaches the engine — Permissions, Authority & Scheduling.