When things break

A red run in the Actions tab tells you which thing broke without you opening the log, because the exit codes are distinct.

CodeMeaningWhat was changed
0Success, including "nothing tagged".Whatever the run decided.
1Configuration error, caught before IBM is contacted.Nothing.
2IBM could not be reached.Nothing.
3A required notification channel failed.Nothing.
4A job's tags can never be written. The log names it.Everything else in the run.

The guarantee behind codes 2 and 3

When Qoffee cannot reach IBM, or cannot deliver a message to a required channel, it changes nothing at all. Every tag is left exactly as it was and the same news is reported again on the next run.

This is the property the whole tool rests on. A job is never untagged unless the notification about it was confirmed delivered. A transient network problem therefore costs you a red run and nothing else, and self corrects fifteen minutes later.

Code 4 is the one that needs you

Everything else retries on its own. Code 4 does not, and cannot.

It means a tag write will never succeed no matter how many times it is tried. In practice this is always the same thing: the job already carries five tags of your own, and IBM allows five, so there is no room for Qoffee's state tag.

Since there is no database, Qoffee cannot record that it gave up. Left as a generic error it would be swallowed and retried forever, which reaches you as endless duplicate notifications on a run that looks green. So it is surfaced instead: the run goes red, the log names the job, and it stays that way until you act.

Remove one tag from that job, or remove qoffee from it, and the next run proceeds normally. The other jobs in the same run are unaffected. One job's tag write failing does not abandon the rest.

Reading the log

Before anything is applied, the whole plan is written to the log, one line per job, each with the reason.

plan: notify=True actions=3
  setstate  job#3beed3 - state changed to RUNNING
  resolve   job#a71fe4 - completed successfully
  leave     job#c02b8a - failure held; batch still active

When something behaves in a way you did not expect, "why is this job still tracked" is the question, and that is where the answer is. The three verbs are the only three things Qoffee can decide to do.

VerbMeaning
setstateRecord a new status on the job. Applied only after a confirmed send.
resolveStop tracking this job. Applied only after a confirmed send.
leaveDo nothing. Never touches a tag.

Job IDs appear as short hashes because the log on a public repository is world readable. The same job produces the same hash within a run, so the lines stay correlatable. See what the public log shows.

Exit codes are defined at the top of qoffee/core/engine.py and documented again in qoffee/cli.py.