Skip to content

fix(runtime): whole-train hardening for the builder and launch stack#196

Open
mfw78 wants to merge 4 commits into
fix/m0-train-followupsfrom
fix/m0-train-hardening
Open

fix(runtime): whole-train hardening for the builder and launch stack#196
mfw78 wants to merge 4 commits into
fix/m0-train-followupsfrom
fix/m0-train-hardening

Conversation

@mfw78

@mfw78 mfw78 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR carries the whole-train red-team fixes for the epic #79 M0 train (feat/m0-runtime-builder, feat/m0-component-builders, feat/m0-runtime-preset, feat/m0-addons, feat/m0-task-executor), stacked at the end so it lands after the full train merges.

The composed train meets the epic design target: the type-state chain (RuntimeBuilder -> TypedBuilder -> ComponentsStage -> ReadyBuilder, plus the Runtime preset shortcut) cleanly separates declaration from the imperative LaunchRuntime::launch sequence, which returns a RuntimeHandle owning the event-loop task, a programmatic shutdown trigger, and the add-on handles. Component builders are pluggable with correct dependency direction (ReferenceExtBuilder lives in shepherd-cow-host, the core knows nothing of cow). Add-ons install before engine boot (locked by test). TaskSet::shutdown preserves old JoinSet::shutdown semantics exactly (abort then join, verified against the base ref). No double construction, no boot-order regression of consequence, and the binary is a thin one-liner consumer. bootstrap::run survives as a compat wrapper with no in-repo callers.

Changes

  1. Fix the tip's own just ci gate: cargo doc -D warnings broke on a broken intra-doc link to RuntimeHandle plus a redundant explicit link in crates/nexum-runtime/src/bootstrap.rs.
  2. Expose the log read side through the builder: both launch paths built Components internally and swallowed it, so RuntimeHandle exposed nothing and the embed example had to bypass the builder to keep the log handle. RuntimeHandle now carries the shared LogPipeline (logs() accessor); the embed example is updated accordingly.
  3. Fix a spurious instant shutdown: in the event-loop task, a failed signal-handler registration resolved the shutdown select arm immediately, stopping a healthy runtime at launch. The signal leg now parks on registration failure so the programmatic trigger stays the only stop.
  4. Drop CLI assumptions from library wording: "positional " language in the launcher's warn/bail messages and builder rustdoc is replaced with neutral module-source wording (the "no modules to run" test assertion prefix is preserved).
  5. Make the injectable executor reachable from the builder: the openers and launcher take &dyn TaskExecutor, but both launch stages hard-wired TokioExecutor, forcing custom-executor embedders to hand-assemble AssembledRuntime. with_executor is now available on TypedBuilder and PresetBuilder (the default remains ambient tokio), locked by a new builder-path e2e test that launches the real example module through a counting executor and exercises the shutdown-to-wait handshake.

Test plan

  • just ci green on this branch, zero e2e skips.
  • Embed example run manually: after wait(), logs.list_runs("example") returns the module init run with 1 record, confirming the log pipeline accessor works end to end.
  • New builder-path e2e test launches the real example module through a counting executor and exercises the shutdown-to-wait handshake, covering with_executor on both the typed and preset paths.
  • Verified TaskSet::shutdown preserves old JoinSet::shutdown semantics (abort then join) against the base ref.

Notes

Follow-ups identified but not implemented in this PR:

  • OS-signal shutdown is unconditional in the library launcher. An embedder with its own signal policy cannot opt out; this should become a composition-root choice (a builder flag or an add-on).
  • PrometheusAddOn install is process-global and non-idempotent. A launch that fails after install leaks the recorder, and a second in-process launch then fails. AddOnHandle.guard is unused; consider recorder handles.
  • PresetBuilder cannot amend the preset's add-on set. An override or extend seam on the preset path would let CoreRuntime run without Prometheus.
  • LaunchContext.data_dir is advisory and never read by the launcher. Use it or drop it before embedders depend on it.
  • PresetBuilder::launch and ReadyBuilder::launch duplicate the assemble-and-launch tail. Factor a shared private helper.
  • RuntimeAddOns is a plural-named trait implemented per single add-on; a naming pass may be worth it if churn is acceptable.

Deferred documentation

  • docs/00-overview.md and docs/design/linker-extension-seam.md still describe the pre-builder launch flow and need updating for the type-state chain.
  • The new RuntimeHandle::logs and with_executor surfaces are undocumented outside rustdoc.
  • bootstrap::run's status (compat wrapper, no in-repo callers) should be stated explicitly in the docs.

AI Assistance: Claude Code (Fable 5 red-team and fixes, Sonnet 5 PR authoring) used for the full change.

mfw78 added 4 commits July 5, 2026 16:25
A failed shutdown-signal registration previously resolved the shutdown
future at once, stopping a healthy event loop the moment it started.
Park the signal leg on failure so the programmatic trigger stays the
only stop.
The openers and launcher already take an injectable TaskExecutor, but
both builder launch stages hard-wired TokioExecutor, so an embedder
supplying its own executor had to bypass the builder and assemble the
runtime by hand. Add with_executor to the typed and preset chains,
defaulting to the ambient tokio runtime, and lock the seam with a
builder-path e2e launch of the example module.
@mfw78 mfw78 force-pushed the fix/m0-train-followups branch from bae8eab to 077f605 Compare July 5, 2026 16:25
@mfw78 mfw78 force-pushed the fix/m0-train-hardening branch from f7c3d5b to bc49020 Compare July 5, 2026 16:25
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