rig forgejo-runner status says nothing about 'activating' — the one systemd state that always means a crash loop #143

Open
opened 2026-08-01 19:05:36 +00:00 by claude-bot-andresmgsl · 0 comments

Context

rig forgejo-runner status prints the systemd state and comments on exactly one value. From @andres's ci-box while the runner was crash-looping, 2026-08-01:

rig-forgejo-runner: service:  forgejo-runner.service (activating)

That was the whole output on the subject. No note, no hint, nothing — while the unit had restarted 76 times and the runner had been Offline since install.

The gap

#133 added a liveness note, and it is gated on active (commands/forgejo-runner-status.sh:33):

forgejo_runner_liveness_note() {
  [ "${1:-}" = active ] || return 0}

That gate is correct for what it was written for — an idle-but-healthy runner is indistinguishable from a stalled one, so the note says what active does not prove. But it means the one state that is never ambiguous gets no comment at all.

On this unit, activating can only mean a crash loop. Type=simple marks a unit active the instant it forks, so it can only sit in activating as auto-restart — the daemon exited non-zero and systemd is counting down RestartSec. There is no benign reading.

So the states are covered exactly backwards: active, which is genuinely ambiguous, carries three lines of guidance; activating, which is unambiguously broken, carries none.

Spec

Say the unambiguous thing where it is unambiguous.

  • When the state is activating, emit a note — and unlike the active one, this is a detected condition, so it is a warn, not a log. warn in this file means drift actually measured, which this is.
  • Name what it means (the daemon is exiting and being restarted, so the runner is not connected) and the command that shows why (journalctl -u forgejo-runner, plus the restart counter).
  • When the state is failed, the same reasoning applies — cover it in the same pass.
  • Leave the active note exactly as it is: its reasoning is unchanged and its tests pin the boundary.

Tasks

  • Extend the note function to cover activating and failed, as warn
  • Keep the active path and its log/no-warning semantics untouched
  • test/cli.sh: drive the function on activating, failed, active, inactive, unset — the existing extracted-function idiom already added for #133
  • changelog.d/<n>.md

Acceptance criteria

  • activating produces a warning naming the crash loop and the journal
  • failed likewise
  • active output is byte-unchanged, still log, still no WARNING:
  • inactive and unset still produce nothing
  • bash test/cli.sh passes; CI's shellcheck -x step is clean

Test plan

Drive the extracted function across all five states. The existing #133 tests already pin active / inactive / unset, so this extends a harness rather than inventing one.

  • Must fail: remove the activating arm and its check goes red.
  • Must not regress: the active note must not become a warn — that check exists and must keep passing.

Dependencies

Follow-up to #133 / !134. Independent of #141 and #142, though the same ci-box incident surfaced all three: #142 is why the daemon died, #141 is why install called it a success, and this is why status afterwards did not say so either.


@andres — filed, not fixed, per your instruction. This one is mine twice over: I wrote the note that stops short of this state.

## Context `rig forgejo-runner status` prints the systemd state and comments on exactly one value. From @andres's ci-box while the runner was crash-looping, 2026-08-01: ``` rig-forgejo-runner: service: forgejo-runner.service (activating) ``` That was the whole output on the subject. No note, no hint, nothing — while the unit had restarted 76 times and the runner had been Offline since install. ## The gap #133 added a liveness note, and it is gated on `active` (`commands/forgejo-runner-status.sh:33`): ```bash forgejo_runner_liveness_note() { [ "${1:-}" = active ] || return 0 … } ``` That gate is correct for what it was written for — an idle-but-healthy runner is indistinguishable from a stalled one, so the note says what `active` does not prove. But it means the one state that is **never** ambiguous gets no comment at all. On this unit, `activating` can only mean a crash loop. `Type=simple` marks a unit active the instant it forks, so it can only sit in `activating` as `auto-restart` — the daemon exited non-zero and systemd is counting down `RestartSec`. There is no benign reading. So the states are covered exactly backwards: `active`, which is genuinely ambiguous, carries three lines of guidance; `activating`, which is unambiguously broken, carries none. ## Spec Say the unambiguous thing where it is unambiguous. - When the state is `activating`, emit a note — and unlike the `active` one, this **is** a detected condition, so it is a `warn`, not a `log`. `warn` in this file means drift actually measured, which this is. - Name what it means (the daemon is exiting and being restarted, so the runner is not connected) and the command that shows why (`journalctl -u forgejo-runner`, plus the restart counter). - When the state is `failed`, the same reasoning applies — cover it in the same pass. - Leave the `active` note exactly as it is: its reasoning is unchanged and its tests pin the boundary. ## Tasks - [ ] Extend the note function to cover `activating` and `failed`, as `warn` - [ ] Keep the `active` path and its `log`/no-warning semantics untouched - [ ] `test/cli.sh`: drive the function on `activating`, `failed`, `active`, `inactive`, unset — the existing extracted-function idiom already added for #133 - [ ] `changelog.d/<n>.md` ## Acceptance criteria - [ ] `activating` produces a warning naming the crash loop and the journal - [ ] `failed` likewise - [ ] `active` output is byte-unchanged, still `log`, still no `WARNING:` - [ ] `inactive` and unset still produce nothing - [ ] `bash test/cli.sh` passes; CI's `shellcheck -x` step is clean ## Test plan Drive the extracted function across all five states. The existing #133 tests already pin `active` / `inactive` / unset, so this extends a harness rather than inventing one. - **Must fail:** remove the `activating` arm and its check goes red. - **Must not regress:** the `active` note must not become a `warn` — that check exists and must keep passing. ## Dependencies Follow-up to #133 / !134. Independent of #141 and #142, though the same ci-box incident surfaced all three: #142 is why the daemon died, #141 is why install called it a success, and this is why `status` afterwards did not say so either. --- @andres — filed, not fixed, per your instruction. This one is mine twice over: I wrote the note that stops short of this state.
claude-bot-andresmgsl added the
bug
scope:runner
labels 2026-08-01 19:05:37 +00:00
claude-bot-andresmgsl added the
ready
label 2026-08-17 23:23:46 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/rig#143
No description provided.