Skip to content

tower-cron: conditions can't see exitCode (ReferenceError every run) and failure runs can never deliver alerts #1142

Description

@waleedkadous

Symptom

~/.agent-farm/tower.log fills with a recurring WARN (every 15 min for months, survives Tower restarts):

[WARN] Condition evaluation failed for 'Service Health Check': ReferenceError: exitCode is not defined

The triggering task is a user health-check cron (.af-cron/*.yaml) with:

condition: "exitCode != 0"message: "Service Health Alert:\n${output}"target: architect

Root cause (two layers, both in packages/codev/src/agent-farm/servers/tower-cron.ts)

  1. evaluateCondition() (line ~293) builds new Function('output', 'return ' + condition) — only output is in scope. Any condition referencing exitCode throws ReferenceError, which is caught at line ~251, logged as WARN, and forces shouldNotify = false. The docs/UX imply exit-code awareness; the evaluator doesn't provide it.

  2. Delivery gate (line ~256) is if (shouldNotify && result === 'success'). A monitoring script that detects a problem and exits non-zero produces result = 'failure', whose branch only logs a WARN ("infrastructure noise"). Consequence: "alert me when this command fails" is inexpressible — the alert is suppressed on exactly the runs that matter. (In the wild, task authors work around it by pushing notifications from inside the script.)

Prescribed fix (design decided — carry it in BUGFIX)

  1. Capture the exit code in runCommand(): in the exec callback, error.code (number) is the exit code when the process ran and exited non-zero. Resolve with { output, exitCode } (0 on success) instead of rejecting for plain non-zero exits. Keep rejecting/flagging genuinely infrastructural errors (spawn failure, timeout/kill — error.killed/error.signal) as today's failure class.

  2. Expose exitCode to conditions: new Function('output', 'exitCode', 'return ' + condition), invoked with both. Conditions using only output are unchanged (backward compatible).

  3. Rework the delivery gate: deliver when the condition (now able to see exitCode) evaluates true, regardless of the old success/failure split:

    • Task with a condition: delivery is decided by the condition alone (a non-zero exit is data, not noise).
    • Task without a condition: keep today's behavior (deliver only on exit 0) so existing no-condition tasks don't start alert-spamming on flaky commands.
    • True infrastructure errors (spawn fail/timeout) keep the existing WARN-only path, with exitCode reported as non-zero (e.g. 124/-1) so exitCode != 0 conditions still fire if the author wants them to.
  4. State row semantics: last_result in cron_tasks should record success/failure by exit code as before — only the delivery logic changes.

  5. Docs: document the condition environment (output: string, exitCode: number) wherever .af-cron YAML is documented (afx skill + agent-farm.md), BOTH trees.

Acceptance

  • A task with condition: "exitCode != 0" produces no ReferenceError; delivers its message exactly when the command exits non-zero.
  • A task with condition: "output.includes('FAIL')" behaves exactly as today.
  • A task with no condition delivers only on exit 0 (unchanged).
  • Unit tests cover: exitCode condition true/false, output-only condition regression, no-condition regression, timeout path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/towerArea: Tower server / agent farm CLI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions