Fallout-build / Fallout-build/Fallout

[RFC #1] Plugin contract shape — IFalloutPlugin discovery & registration

Open
#97 1 comment 1 reaction 0 assignees View on GitHub
RFC target/vNext
Dominant language
C#
Stars
154
Forks
19
Avg merge
1d 22h
Merged PRs (30d)
15

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:
```csharp
[assembly: FalloutPlugin(typeof(TerraformPlugin))]

public sealed class TerraformPlugin : IFalloutPlugin
{
public PluginApiVersion ApiVersion => PluginApiVersion.V1;

public void Configure(IFalloutPluginBuilder builder)
{
builder.Services.AddSingleton();
builder.AddToolWrapper();
builder.AddBuildMiddleware();
builder.AddHost(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 small `IFalloutPluginBuilder`. .NET devs already know this.
- **One entry point per plugin** — single class, single method. Easy to find, read, test.
- **API-versioned** — `PluginApiVersion` lets 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 `IFalloutPlugin` is registered) — forces a full assembly scan, risks surprise activations from transitive deps, harder to debug "why is this loading?".
- **`#addin` directives** (Cake's model) — couples the plugin model to source-file pragmas; poor fit with .NET tooling (IntelliSense, refactoring, restore); wrong audience.

## Questions for discussion
1. Allow `IFalloutPlugin` to declare deps on other plugins (`builder.RequirePlugin()`)? Default: no — discover capabilities via DI, not plugin-to-plugin references. Disagree?
2. Give `Configure` a `CancellationToken`? Default: no — registration is fast and synchronous; async setup belongs in a middleware.
3. Minimum SDK target framework? Default: `net10.0` (matches `global.json`). Ship a `netstandard2.0` flavour for older runtimes? Probably not — the build host itself needs `net10`.

## Out of scope (separate RFCs)
- Extension-point catalogue — RFC #98.
- SDK versioning policy — RFC #99.
- Discovery/load mechanics — RFC #100.
- Conflict resolution — RFC #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:
- #147 — [SDK-2] Plugin contract: [FalloutPlugin] attribute + IFalloutPlugin

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.