Repository navigation
Description
RFC #1 — Plugin contract shape
Self-RFC. Comment with agreement, disagreement, or alternatives. The proposal below is what we'll ship in v12 unless this discussion produces a better answer.
Context
v12 (milestone #7) ships Fallout.Plugin.Sdk 1.0 — the public abstractions package contributors compile plugins against. This RFC pins the contract: how a plugin declares itself and how the host discovers it. IFalloutPlugin's shape is the most consequential SDK decision — it can't break without a major SDK bump, and every plugin author touches it first.
Proposal
A plugin is a NuGet package referenced by the build project. No JSON manifest, no preprocessor directives, no separate plugin folder — same convention as Roslyn analyzers, MSBuild SDKs, source generators.
The entry point is a single assembly attribute pointing at a registration class:
[assembly: FalloutPlugin(typeof(TerraformPlugin))]
public sealed class TerraformPlugin : IFalloutPlugin
{
public PluginApiVersion ApiVersion => PluginApiVersion.V1;
public void Configure(IFalloutPluginBuilder builder)
{
builder.Services.AddSingleton<ITerraformWorkspaceLocator, DefaultTerraformWorkspaceLocator>();
builder.AddToolWrapper<TerraformTasks>();
builder.AddBuildMiddleware<TerraformWorkspaceDetectionMiddleware>();
builder.AddHost<TerraformCloudHost>(detect: env => env.Has("TFC_RUN_ID"));
}
}At startup the orchestrator scans referenced assemblies for FalloutPluginAttribute, instantiates each IFalloutPlugin, calls Configure, and merges into the composition root.
Why this shape
- Opt-in discovery via attribute — only assemblies declaring it pay any cost. No type-walking every referenced DLL.
- DI as the registration vocabulary — plugins compose against
IServiceCollection+ a smallIFalloutPluginBuilder. .NET devs already know this. - One entry point per plugin — single class, single method. Easy to find, read, test.
- API-versioned —
PluginApiVersionlets the host detect a too-new plugin and fail clearly, not at random later.
Alternatives rejected
- JSON manifest (
fallout-plugin.json) — more ceremony than[assembly: FalloutPlugin], no clearer signal, still needs reflection for design-time types. - Convention-based discovery (any
IFalloutPluginis registered) — forces a full assembly scan, risks surprise activations from transitive deps, harder to debug "why is this loading?". #addindirectives (Cake's model) — couples the plugin model to source-file pragmas; poor fit with .NET tooling (IntelliSense, refactoring, restore); wrong audience.
Questions for discussion
- Allow
IFalloutPluginto declare deps on other plugins (builder.RequirePlugin<DotNetPlugin>())? Default: no — discover capabilities via DI, not plugin-to-plugin references. Disagree? - Give
ConfigureaCancellationToken? Default: no — registration is fast and synchronous; async setup belongs in a middleware. - Minimum SDK target framework? Default:
net10.0(matchesglobal.json). Ship anetstandard2.0flavour for older runtimes? Probably not — the build host itself needsnet10.
Out of scope (separate RFCs)
- Extension-point catalogue — RFC [RFC #2] Extension-point catalogue for v12 SDK #98.
- SDK versioning policy — RFC [RFC #3] Fallout.Plugin.Sdk versioning policy #99.
- Discovery/load mechanics — RFC [RFC #4] Plugin discovery and load model #100.
- Conflict resolution — RFC [RFC #5] Conflict resolution semantics between plugins #101.
How to engage
React or comment. Strong opinions backed by use cases are most useful — "this won't work because [my plugin needs X]" is gold; "what about a manifest file?" without a use case isn't.
Decision lands in
When this RFC locks (2026-08-31), its shape becomes the spec for:
- [SDK-2] Plugin contract: [FalloutPlugin] attribute + IFalloutPlugin #147 — [SDK-2] Plugin contract: [FalloutPlugin] attribute + IFalloutPlugin
Activity
docs: publish v11/v12 plugin-architecture roadmap
ChrisonSimtian commentedon May 25, 2026
Self-decision lock-in date: 2026-08-31
This is a self-RFC (per the roadmap) — it ships an opinionated proposal and asks "tell me where I'm wrong." Currently zero comments.
If no substantive counter-proposal lands by 2026-08-31, the proposed shape locks, gets handed to the implementing SDK issue (#147 —
[FalloutPlugin]attribute +IFalloutPlugin), and this RFC closes as decided.The most useful counter-example shape is: "I'd want to build a plugin that does X, and the proposed contract breaks because of Y." Rough form is fine — that's the kind of comment that reshapes proposals.
docs(roadmap): publish 2026-08-31 RFC self-decision deadline (#186)
dennisdoomen commentedon Oct 10, 2026
Do we have an example of something that would benefit from this? Defining a plug-in architecture is hard, and usually you don't do that until you have a particular request that you use to design the contract.
ChrisonSimtian commentedon Oct 13, 2026
I was thinking shrinking the core down and throwing all the tooling etc into plugins, i.e. github actions.
But not urgent, i reckon we focus on the statics cleanup and then look into this again from a different, cleaner lense
-> on hold