Skip to content

Emit session_end when a console session exits normally - #5

Merged
grantcox merged 1 commit into
mainfrom
stagility/session-end-at-exit
Aug 27, 2026
Merged

grantcox merged 1 commit into
mainfrom
stagility/session-end-at-exit

Conversation

@grantcox

Copy link
Copy Markdown
Collaborator

The bug

session_end is effectively never emitted. In console1984 0.2.4 the only path to finish_session is:

CommandExecutor#execute
  rescue Console1984::Errors::ForbiddenCommandExecuted
    Console1984.supervisor.exit_irb    # command_executor.rb:31
      → Supervisor#stop                # supervisor.rb:29
        → session_logger.finish_session # supervisor.rb:65

There is no at_exit, no END block, no trap, and nothing hooks IRB's normal exit. So a session ended with exit — which is every ordinary session — is never closed, and command_count / duration_seconds are populated for effectively no real session.

Upstream doesn't hit this because Console1984::SessionsLogger::Database#finish_session just nils out two ivars. If it never runs, nothing is lost: the session row and its commands are already in the database. We hung a record emission off the same hook, which does depend on it firing.

Found while writing the manual acceptance plan for v1.0.0 — the plan asserted a session_end for a normal console session and it wasn't there.

The fix

start_session arms the close with at_exit. Registering inside start_session rather than in the Railtie keeps it self-limiting: it only arms for a real interactive session, and never for rake / rails runner / web / worker.

The two paths are safe together. Whichever runs first clears @session_id, and emit's existing nil-session guard drops any later event without one — so the forbidden-command path still emits exactly one session_end, and the at_exit is then a no-op.

Why the specs didn't catch it

spec/console_audit/sessions_logger_spec.rb called logger.finish_session directly. The unit behaviour was correct; the integration never happened. The new specs drive the registered handler instead, and one pins the duck-type name console1984 calls on exit, so an upstream rename fails here rather than silently taking out both paths.

Confirmed the three behavioural specs fail without the lib/ change.

Limits

  • A killed dyno (heroku ps:stop, or the dyno being reaped) still produces no session_end. Nothing in-process can fix that; the session_start and command records are already durable, which is the property that matters.
  • at_exit handlers run LIFO. This one is registered at console start, well after Rails' boot-time handlers, so it runs before them and the queue backend should still be connected. If an enqueue does fail there it's fail-open and warns, which is the pre-existing behaviour for every other record.

Replaces #3, which was merged into add-ci after add-ci had already merged to main, so the commit never landed. Same single commit (81fb292), retargeted at main.

@grantcox
grantcox marked this pull request as ready for review August 27, 2026 03:55
@grantcox
grantcox requested a review from becky-ynab August 27, 2026 03:55
@grantcox
grantcox enabled auto-merge August 27, 2026 03:58
console1984 reaches finish_session only through Supervisor#stop, which
only exit_irb calls, which CommandExecutor only calls after a forbidden
command has executed. A session ended with a plain `exit` was therefore
never closed: no session_end record, and command_count and
duration_seconds existed for effectively no real session.

Upstream does not notice because its own Database logger treats
finish_session as in-memory cleanup -- the session row and its commands
are already persisted, so nothing is lost if it never runs. We hung a
record emission off the same hook, which does need it to fire.

start_session now arms the close with at_exit. The two paths are safe
together: whichever runs first clears @session_id and emit drops any
later event without one.

The existing spec called finish_session directly, so the unit behaviour
was correct while the integration never happened. The new specs drive
the registered handler instead, and one pins the duck-type name that
console1984 calls on exit so a rename fails here rather than silently
taking out both paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@grantcox
grantcox force-pushed the stagility/session-end-at-exit branch from 81fb292 to 71a3226 Compare August 27, 2026 07:44

@becky-ynab becky-ynab left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still good!

@grantcox
grantcox merged commit e53bbdc into main Aug 27, 2026
9 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