Skip to content

feat(plugin-devenv): ship devenv as a cdylib plugin, not a builtin - #419

Open
raphaelvigee wants to merge 3 commits into
raphaelvigee/exec-runners-genericfrom
raphaelvigee/exec-runners-devenv-cdylib
Open

feat(plugin-devenv): ship devenv as a cdylib plugin, not a builtin#419
raphaelvigee wants to merge 3 commits into
raphaelvigee/exec-runners-genericfrom
raphaelvigee/exec-runners-devenv-cdylib

Conversation

@raphaelvigee

@raphaelvigee raphaelvigee commented Aug 23, 2026

Copy link
Copy Markdown
Member

On top of the generic-runner PR. devenv is a real cdylib plugin now, not a
builtin.

It was asked for as a loadable plugin during design and shipped as a builtin:
crates/plugin-devenv had no [lib] crate-type, and bootstrap.rs registered
both halves unconditionally into the host binary. My miss.

What changed

crates/plugin-devenv-cdylibcrate-type = ["cdylib"], loaded from a
heph-devenv-plugin.json manifest, exactly like go/gha/oci. The host binary no
longer links plugin-devenv at all.

Two components, both named devenv: a driver that builds the environment
artifact, and an exec runner (#418) that serves sessions from it. Separate
components, because a runner is not a driver — it has no schema, parses nothing,
builds nothing.

Being the runner is what makes this plugin the party that decides how the
process starts: ExecSession::prepare is per target, so one devenv shell is
opened once inside the plugin and every target's exec is routed into it.

Since #418 hands the host a live session object rather than an id, that shell —
its socket, its pid — simply lives on the session. Runner is a single field
again: the Mutex<HashMap<String, Arc<dyn ExecSession>>> and the AtomicU64
that minted ids existed only to answer the host by a name it had invented, and
both are deleted here.

SessionSupport moves into the plugin, where it belongs: current_exe() in a
cdylib is the host binary, which is exactly the agent and per-target client a
session needs, and home/root come from CreateConfig. The host no longer
assembles it.

Packaging

Ships as a release artifact on all three targets, same flow as the others:

  • one more package in the single cargo build — splitting it would serialize a
    fourth LTO tail
  • staged into dist/, macos-portable.sh'd, version-slot patched, manifest
    generated by tools/pluginmanifest, published
  • pattern: "heph*" and files: dist/* pick it up with no further change
  • install-devenv-plugin for local use; gen-example runs it, since
    example/exec_runner/BUILD uses driver = "devenv"
  • qualityCrates gains the crate — a new crate missing there lints green
    locally and red in CI, which has already cost time once on this stack

Tests

Two in bin-e2e, which is the only place this seam exists — an in-process test
constructs the driver through generics and never crosses it:

the shipped cdylib dlopens and its devenv driver answers across the ABI PASS
devenv no longer resolves with no plugin configured PASS

The second is the one that matters. Without it the conversion half-lands: the
builtin keeps answering, every fixture passes, and nobody notices the cdylib is
dead weight that is never loaded.

Verified against real staged release artifacts — plugin_dylib 12/12.
exec-runner 37, plugin-devenv 10, engine 545, heph 128, e2e/exec_runner 12.
lint clean.

Note for reviewers

e2e is a devenv script and is baked at shell start, so an existing shell
still stages the old three-artifact list and this suite fails on a missing
heph-devenv-plugin.dylib. Re-enter the shell (or direnv reload) before
running e2e locally. CI is unaffected — it starts a fresh shell.

Stack created with GitHub Stacks CLIGive Feedback 💬

@raphaelvigee raphaelvigee changed the title raphaelvigee/exec runners devenv cdylib feat(plugin-devenv): ship devenv as a cdylib plugin, not a builtin Aug 23, 2026
@raphaelvigee
raphaelvigee marked this pull request as ready for review August 23, 2026 13:23
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from a136555 to f0a7755 Compare August 23, 2026 14:27
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from f0a7755 to 2f71585 Compare August 23, 2026 15:56
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from 2f71585 to f408839 Compare August 23, 2026 16:40
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from f408839 to 4248073 Compare August 23, 2026 17:55
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from 4248073 to c1eb51b Compare August 23, 2026 22:46
raphaelvigee and others added 3 commits August 24, 2026 10:18
`devenv` was asked for as a real loadable plugin and shipped as a builtin:
`crates/plugin-devenv` had no `[lib] crate-type`, and `bootstrap.rs`
registered both halves unconditionally into the host binary.

It is now a cdylib like go, gha and oci — `crates/plugin-devenv-cdylib`,
loaded from a `heph-devenv-plugin.json` manifest, built and published for all
three targets. Nothing about devenv is compiled into heph any more.

One driver serves both halves. It builds the environment artifact, and it
answers the exec-runner lane that opens a session from it — so the runner is
the party that starts the process: `prepare_spec` is per target, so one
`devenv shell` is opened once inside the plugin and every target's exec is
routed into it. The lane rides new `DriverMethod` ids on the existing
`drivers` export, so no `PluginComponents` layout change and no ABI_SEMVER
bump.

`SessionSupport` moves into the plugin, where it belongs: `current_exe()` in a
cdylib is the *host* binary, which is exactly the agent and per-target client
a session needs, and `home`/`root` come from `CreateConfig`. The host no
longer assembles it.

- `install-devenv-plugin`, mirroring `install-gha-plugin`.
- Release pipeline: one more package in the single `cargo build` (splitting it
  would serialize a fourth LTO pass), staged into `dist/`, version-slot
  patched, manifest generated, published. `pattern: "heph*"` and `files:
  dist/*` pick it up with no further change.
- `qualityCrates` gains the crate — a new crate missing there lints as green
  locally and red in CI.

Two bin-e2e tests, which is the only place this seam exists: the shipped
cdylib is `dlopen`ed and its `devenv` driver answers across the ABI, and
`devenv` no longer resolves with no plugin configured. The second is the one
that matters — without it the conversion half-lands, the builtin keeps
answering, every fixture passes and the cdylib is never actually loaded.

plugin-devenv 10, engine 545, heph 128, plugin-sdk 36. lint clean.
`example/exec_runner/BUILD` uses `driver = "devenv"`, which stopped resolving
the moment the driver left the binary. `gen-example` installs the plugin and
`.hephconfig2` loads it by manifest, the same way it already does for go.
Follows the ABI change below: a runner's session now crosses as an object, so
`Runner` implements `ExecRunner` and nothing else. The 55-line `ExecRunnerPlugin`
adapter, the `Mutex<HashMap<String, Arc<dyn ExecSession>>>` it kept sessions in,
and the `AtomicU64` that minted ids all existed to answer the host by id. The
host holds the session itself now, so none of them have a caller.

`Runner` is back to a single field. The devenv shell, its socket and its pid are
where they always should have been — inside the session object, invisible to the
host.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RC5ykWRMPjRKGk1Tz1KTja
@raphaelvigee
raphaelvigee force-pushed the raphaelvigee/exec-runners-devenv-cdylib branch from c1eb51b to decb5b2 Compare August 24, 2026 08:21
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.

1 participant