Skip to content

crates/sessions: new crate for session primitives + lifecycle hooks primitive - #230

Merged
evanspearman merged 1 commit into
mainfrom
evan/lifecyclehooks
May 26, 2026
Merged

crates/sessions: new crate for session primitives + lifecycle hooks primitive#230
evanspearman merged 1 commit into
mainfrom
evan/lifecyclehooks

Conversation

@evanspearman

@evanspearman evanspearman commented May 25, 2026

Copy link
Copy Markdown
Member

Adds a sessions library crate with two types:

  • LifecycleHook — three optional script slots (on_activate, on_destroy, on_failure), at least one required. Invariant enforced in the builder and reused on the serde path via try_from/into.
  • HookScript — adjacently tagged: { type = "inline" | "external", value = "..." }.
  • Loadout — stub for now; will grow as minimal2/minimald need it.

Pedantic clippy on; tests + doctests cover the invariant and serde round-trip.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added sessions crate to workspace.
    • Introduced lifecycle hooks for sessions with activation, destruction, and failure event handlers.
    • Added support for inline and external scripts in lifecycle hooks.
    • Introduced session loadouts with environment variable management.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2123ab0-9c45-4a45-ace8-f7224972d61e

📥 Commits

Reviewing files that changed from the base of the PR and between c0b592b and 3f54735.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • Cargo.toml
  • crates/sessions/Cargo.toml
  • crates/sessions/src/lib.rs
  • crates/sessions/src/lifecyclehook.rs
  • crates/sessions/src/loadout.rs

📝 Walkthrough

Walkthrough

This PR introduces a new sessions crate to the minimal workspace, defining session runtime primitives: LifecycleHook for configuring scripts at activation, destruction, and failure transitions, and Loadout for bundling environment variables with lifecycle hooks. Lifecycle hooks enforce validation that at least one script must be configured.

Changes

Session Primitives

Layer / File(s) Summary
Workspace and crate initialization
Cargo.toml, crates/sessions/Cargo.toml, crates/sessions/src/lib.rs
Added sessions crate to workspace members, created the new crate manifest with serde and toml dependencies, and exported lifecyclehook and loadout submodules.
Lifecycle hook types and validation
crates/sessions/src/lifecyclehook.rs
Introduced HookScript enum (inline String or external PathBuf), Error for validation, and LifecycleHook struct holding optional scripts for activation, destruction, and failure. Implemented builder pattern with build() validation rejecting all-empty hooks; serde deserialization enforces the same invariant via try_from/into. Added bidirectional conversions between hook and builder. Comprehensive unit tests cover builder validation, partial/full hook acceptance, TOML round-tripping, empty hook rejection, and serialization behavior.
Loadout session configuration
crates/sessions/src/loadout.rs
Defined Loadout struct aggregating a HashMap<String, String> for environment variables and a vector of LifecycleHook. Lifecycle hooks default to empty and are omitted from serialized output when not present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • msample

Poem

🐰 A new crate hops into the workspace so neat,
With lifecycle hooks dancing through activation's beat,
Hooks that validate they're never left bare,
Sessions and loadouts configured with care,
Building the future, one script at a time! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introduction of a new crate for session primitives with lifecycle hooks functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

/// Script body stored inline.
Inline(String),
/// Path to a script file on disk.
External(PathBuf),

@twitchyliquid64 twitchyliquid64 May 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Over the time of building minimal Ive started to dislike PathBuf, because it leads to confusion/invariants/bugs based on:

  1. Is this a path relative to the user system, the sandbox fs, or the minimald (in the future) ?
  2. Is this meant to be interpreted relative to the cwd, if so which of the above contexts?
  3. More of a grumble, but if you join an absolute path with cwd, it overwrites with the absolute path. I can feel a bug coming from this one day (i.e. if a user specifies an absolute path in the config).

Not sure what the fix is here, but we may want some newtype around paths. Food for thought.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, newtypes sound like the right way to go here. I'm using them for paths in the patches stuff I'm working on, where it becomes even more important to keep track of this stuff.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually, they're probably not strictly newtypes as they'll probably need to contain a bit more context in some cases.


impl std::error::Error for Error {}

/// A script executed by a lifecycle hook.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Whats the constraints aroud what a valid script is? Do we want to just say 'bash v4 or greater compatible?' (I would say POSIX shell except everyone ignores when thats the constraint and uses modern features lol)

@evanspearman evanspearman May 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think it depends on exactly when these run. If it's something like:

Install packages -> Activate Hook -> ... -> Destroy Hook -> Packages are gone
                                     !!! -> Failure Hook -> Destroy Hook -> Packages are Gone

Then we don't really need to constrain it. It could be fish, python, ruby, whatever you want as long as you have the package to run it. Just put in the right shebang.

If they're run when you don't have your packages available then I think some specific version of bash (as long as we can grantee that's available) makes sense.

@twitchyliquid64 twitchyliquid64 May 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmmm, at this stage i realize im not fully sure what the context is for lifecycle hooks. Are these basically just activation scripts for a session?

If theres one activation script then it makes sense to have a configurable shell like bash/fish etc. But if we are composing multiple scripts we need the env vars that get exported in one + the background tasks/jobs that get run to stay alive as all the activation scripts run (i.e. Karl's use case of launching postgres, we dont want postgres to die when the activation script finishes), in which case what shell is chosen for interpreting all those scripts?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, right. In that case I think we just do bash unless we have a good reason not to. I don't think that needs to be encoded here though.

@evanspearman

Copy link
Copy Markdown
Member Author

Going to merge this now. I've been playing around with more contextful path types a bit, but I don't see much harm in just switching this to them later.

@evanspearman
evanspearman merged commit 9483790 into main May 26, 2026
8 checks passed
@evanspearman
evanspearman deleted the evan/lifecyclehooks branch May 26, 2026 20:54
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