Skip to content

runtime: let a preset carry extensions and pre-built backends#444

Merged
mfw78 merged 1 commit into
dev/m1from
feat/m2-preset-launch-surface
Jul 20, 2026
Merged

runtime: let a preset carry extensions and pre-built backends#444
mfw78 merged 1 commit into
dev/m1from
feat/m2-preset-launch-surface

Conversation

@mfw78

@mfw78 mfw78 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Closes #273

What

Widens the Runtime preset trait: components/add_ons take self/&self instead of being static, and a new extensions() hook (default empty) lets a preset supply linker extensions. RuntimeBuilder::with_runtime binds a preset by value; runtime::<R>() stays as sugar over it for R: Default. PresetBuilder::launch gathers the presets extensions ahead of any appended via with_extensions, then builds components from the (now consuming) preset.

Why

The preset trait could not express a domain preset that also needs a linker Extension, or hand back an already-built instance (e.g. mock backends), so both paths bypassed runtime::<R>() and wired assembly by hand. This closes the trait-surface gap without a marker-vs-value split beyond runtime/with_runtime.

Testing

  • preset_extensions_and_appended_extensions_both_link: a value-bound preset extension and an appended extension both reach the linker exactly once.
  • preset_hands_over_a_prebuilt_backend: components(self) carries a pre-built LogPipeline through unchanged.

AI Assistance

Implemented with Claude Code assistance.

Closes #273

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Checked whether this closes the preset-unreachability gap flagged on #442 (with_logs/with_chain/with_store unreachable through preset-based callers). It's only half-closed:

  • A preset author can now pre-load a Prebuilt backend inside their own Runtime::components(self) impl (the new PrebuiltLogsPreset test demonstrates this well). But an external caller holding a stock preset (e.g. CoreRuntime) still has no hook to override that preset's chain/store/logs before .launch()PresetBuilder::launch (builder.rs:109) still does self.preset.components().build(...) inline with nothing exposed in between to intercept. The test harness still bypasses PresetBuilder entirely via the manual TypedBuilder chain, for exactly this reason, and this PR doesn't change that. Might be worth scoping the PR body to "preset-authored pre-built backends" so it doesn't read as fully closing #442's gap.

  • builder.rs:98 — the doc comment promises the preset's extensions are gathered with appended ones "after," but there's no test for the case where the preset and an appended extension register the same namespace (only distinct namespaces alpha/beta are tested). If a caller appends an extension meaning to override a preset's built-in one for the same namespace, the resulting behavior (silent double-link, last-wins, or error) is undefined and untested.

Everything else — ownership of self.preset.components()/extensions()/add_ons(), the runtime::<R>() sugar over with_runtime — looks clean, no bugs found there.

@mfw78
mfw78 force-pushed the feat/m2-registry-service branch from a3f76a6 to c2d821b Compare July 20, 2026 13:03
Base automatically changed from feat/m2-registry-service to dev/m1 July 20, 2026 13:09
@mfw78
mfw78 force-pushed the feat/m2-preset-launch-surface branch from a3e6162 to 14ad51b Compare July 20, 2026 13:10
@mfw78

mfw78 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Reviewed both points against the end-of-train state (feat/m5-git-tag-pins-umbrella-patch).

Point A (preset override hook) — still valid. PresetBuilder::launch (builder.rs:456) still does self.preset.components().build(...) inline, so an external caller holding a stock preset still cannot override its chain/store/logs before .launch(). This is exactly the gap already tracked in #509 (filed off #442's review), so leaving it there rather than duplicating. Agreed the effect this PR delivers is preset-authored pre-built backends, not a full close of #442's gap; the frozen PR body is moot post-merge.

Point B (same-namespace extension) — resolved; the behaviour is defined and tested. It is not undefined. The combined set (self.preset.extensions(config) then the appended ones, builder.rs:450-451) flows into AssembledRuntime.extensions and on through Supervisor::boot* to HostServices::from_extensions (supervisor.rs:399 / 493), which refuses a duplicate namespace with bail!("duplicate extension service namespace {namespace}") (extension.rs:255). So an appended extension sharing a preset's namespace fails boot loudly rather than silently double-linking or last-winning. Covered by duplicate_namespace_is_refused (extension.rs:359). The only thing not exercised is that path specifically through PresetBuilder, but the combined vec provably reaches the single dedup chokepoint, so such a test would only re-prove the plumbing; not tracking it.

@mfw78
mfw78 merged commit 97e0292 into dev/m1 Jul 20, 2026
5 of 6 checks passed
@mfw78
mfw78 deleted the feat/m2-preset-launch-surface branch July 20, 2026 13:16
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