Skip to content

fix(lifecycle): preserve normal owner shutdown reasons - #367

Merged
mikehostetler merged 1 commit into
agentjido:mainfrom
icostan:fix/owner-shutdown-reasons
Sep 13, 2026
Merged

mikehostetler merged 1 commit into
agentjido:mainfrom
icostan:fix/owner-shutdown-reasons

Conversation

@icostan

@icostan icostan commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Description

Jido.Scheduler.Job and Jido.Sensor.Runtime monitor an owner process and stop when it exits, but both wrapped every reason as {:owner_down, reason}. A clean owner shutdown therefore produced an exit reason OTP classifies as abnormal:

  • every sensor and cron job logged a GenServer terminating crash report on the normal shutdown path (Jido.AgentServer.SensorLifecycle passes owner_pid: self(), so each sensor monitors the agent server);
  • a :transient child would be restarted after its owner was already gone.

This mirrors :normal, :shutdown and {:shutdown, term} as-is and keeps wrapping anything else.

The runtime already had this exact classification in two other places — AgentServer.normal_cron_exit?/1 and Lifecycle.Keyed.clean_shutdown?/1 — so the fix would have made four copies. All four now route through a single Jido.Util.clean_exit_reason?/1.

Notes:

  • No caller depends on the old shape (grep owner_down lib/ returns only the definitions). Abnormal exits still yield {:owner_down, reason}.
  • Sensors implementing terminate/2 now receive the owner's own reason instead of {:owner_down, _}. @callback terminate(reason :: term(), ...) documents no shape, so this is not a contract break, and it makes standard OTP reason matching work in user sensors. Documented in a new Owner Monitoring section of the Sensor.Runtime moduledoc.
  • Scheduler.Job now logs the owner exit at debug, for parity with Sensor.Runtime — a clean owner exit otherwise leaves no trace at all.

Type of Change

  • Bug fix (non-breaking change fixing an issue)
  • New feature (non-breaking change adding functionality)
  • Breaking change (fix or feature causing existing functionality to change)
  • Documentation update

Breaking Changes

None. The only observable change on the abnormal path is unchanged; on the clean path the exit reason becomes the owner's own reason rather than a wrapper.

Testing

  • Tests pass (mix test) — 2283 passed, 187 excluded
  • Quality checks pass (mix quality) — format, --warnings-as-errors, credo, dialyzer (0 errors)

Each preserved reason is its own test rather than a loop, so one failing reason cannot mask the others. Verified by reverting the fix locally: all six preserves … tests fail, and the two wraps an abnormal owner exit regression guards still pass.

Checklist

  • My code follows the project's style guidelines
  • I have updated the documentation accordingly
  • I have added tests that prove my fix/feature works
  • All new and existing tests pass
  • My commits follow conventional commit format
  • I have NOT edited CHANGELOG.md (it is auto-generated by git_ops)

Related Issues

N/A

🤖 Generated with Claude Code

https://claude.ai/code/session_011SvMs4VNrCVzWi6RNo4Xrv


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Jido.Scheduler.Job and Jido.Sensor.Runtime stop when the owner process
they monitor exits, but wrapped every reason as {:owner_down, reason}.
A clean owner shutdown therefore produced a reason OTP classifies as
abnormal: every sensor and cron job logged a "GenServer terminating"
crash report on the normal shutdown path, and a :transient child would
be restarted after its owner was already gone.

Mirror :normal, :shutdown and {:shutdown, term} as-is and keep wrapping
anything else. Extract the classification into a single
Jido.Util.clean_exit_reason?/1, replacing the duplicate predicates in
AgentServer and Lifecycle.Keyed so the four copies cannot drift.

- no caller depends on the old shape; abnormal exits still yield
  {:owner_down, reason}
- sensors implementing terminate/2 now receive the owner's own reason,
  documented in the Sensor.Runtime moduledoc
- Scheduler.Job logs the owner exit for parity with Sensor.Runtime,
  which otherwise leaves a clean owner exit with no trace

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SvMs4VNrCVzWi6RNo4Xrv
@mikehostetler
mikehostetler merged commit 47df8c3 into agentjido:main Sep 13, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants