Tasks

Auto-resolve

← Back to Tasks

Auto-resolve is how most tasks complete themselves. A task with an auto-complete checklist item carries a scheduled fire time and a list of conditions; at fire time, the cron evaluates the conditions and — if all are met — runs the task's action and marks it done. If any condition is not met, the operator can optionally be notified.

The data model

An auto-resolve task has at least one checklist item like this:

{
  "label": "[Auto-complete] Complete at 19:00 (when 1 condition met)",
  "automaticType": "scheduled_time",
  "automaticConfig": {
    "time": "19:00",
    "conditions": [
      {
        "property": "messageThread.hasUnansweredGuestMessage",
        "operator": "equals",
        "value": "false",
        "notifyOnNotMet": true
      }
    ]
  }
}
  • time — the wall-clock time the cron evaluates this item (in the tenant's local timezone).
  • conditions — boolean checks against the reservation + guest + message state.
  • notifyOnNotMet — if true, when fire time arrives and the condition fails, the operator gets a WhatsApp ping.

How the cron evaluates

Two cron jobs in cronJobs.ts:

  • morningCron (06:00 CET)
  • afternoonCron (15:00 CET)

…both call dailyTaskSeeder, which:

  1. Iterates every reservation in the ±1 day window from today.
  2. For each task template that fires on check-in / check-out / after_checkout, re-evaluates whether a task should exist for that reservation.
  3. If yes and no live task exists yet → seed one.
  4. For existing auto-resolve tasks whose scheduled time falls in the window → evaluate conditions, fire or skip.

The ±1 day window is documented in server/services/reservationEventFiring.ts.

Common conditions

  • reservation.nukiAccessCode is_not_null — Send Check-in precondition (PIN exists).
  • messageThread.hasUnansweredGuestMessage equals false — pre-checkout reminder precondition (guest's last message was answered).
  • task.dependencies.allDone equals true — fire only after dependent tasks complete.
  • reservation.guestMood notIn ['angry', 'disputed'] — thank-you message precondition.
  • reservation.checkoutTime is_not_null — for time-relative tasks.

The full property catalogue lives in platformCapabilities.ts and is autodiscovered into the task template editor condition picker.

Why a task gets blocked

When fire time arrives and a condition is false, the cron:

  1. Logs the failure to app_errors.
  2. If notifyOnNotMet: true, sends the operator a WhatsApp message naming the task, reservation, and failing condition.
  3. Leaves the task in scheduled status (next morning cron will re-evaluate).

Example operator-facing message:

Task 216874 (Castle&River Luxe-Vydrica) Auto-resolve was BLOCKED because the condition is not met: reservation.nukiAccessCode is_not_null. Please resolve this task manually.

What to do — see Send Check-in for the classic case.

Silent-skip is no longer possible

Before #180, an action that internally bailed out (e.g. WhatsApp sendReply skipped because the guest was opted out) would still mark the task done. The operator had no signal anything had been skipped. Post-#180:

  • Auto-resolve runs the action.
  • If the action returns skipped: true with a reason, the task moves to failed with the reason.
  • The reason appears in the Activity Log and on the task card.

The same change applies to assign_cleaner (#171) — silent skips when the cleaner was unassigned are now visible.


What an operator should know

  • Every auto-resolve task has a schedule (when it'll fire) and conditions (what must be true for it to fire).
  • You can see both on the task card.
  • If a condition is currently failing, you have until the fire time to fix it — usually by replying to the guest or generating the missing PIN.
  • If you miss the window, the operator-notification will arrive at fire time and you'll have to manually trigger.

  • #214 — surfaced the scheduled time + condition state on cards.
  • #180 — silent-skip eliminated.
  • #171assign_cleaner skip-visibility.
  • #260 — JOIN disambiguation for the nukiAccessCode is_not_null condition.
Source: the FlatsBratislava operator manual.