diff --git a/AGENTS.md b/AGENTS.md
index 1fac69b96..f80a06cc3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -47,7 +47,7 @@ Do not commit code generated by `GenerateTools` — generated code is regenerate
## Critical rules (read this every session)
1. **At PR-creation time, follow the [PR-creation flow](docs/agents/release-and-versioning.md#pr-creation-flow) in `docs/agents/release-and-versioning.md`.** Every PR gets a `target/YYYY` label (`target/2026`; legacy v10 work uses `target/v10`). Breaking changes additionally get a `breaking-change` label, a `⚠️ Breaking change` callout, and a `CHANGELOG.md` entry — recorded under the **next yearly major** (breaking changes are batched to the year cut, not shipped mid-year). A breaking-change PR may target **`main` only** — never a `release/YYYY` stable train. This is non-negotiable — review will block.
-2. **Default to backwards compatibility.** Prefer additive over breaking changes. Before changing a public signature, removing an API, renaming a package, or altering an on-disk format, ask: can this be additive instead? `[Obsolete]` markers, transition shims (see `src/Shims/` + `Fallout.SourceGenerators.TransitionShimGenerator`), the `[Experimental("FALLOUT0xx")]` opt-in escape hatch for not-yet-stable surface, feature flags, and overload-based extension are all preferred to a hard break. When a breaking change is genuinely unavoidable, it lands on `main`, is held for the next yearly major, and follows rule #1's flow — the break must be deliberate, named, and migration-pathed in the CHANGELOG. See [#262](https://github.com/ChrisonSimtian/Fallout/issues/262) for the broader discussion.
+2. **Default to backwards compatibility.** Prefer additive over breaking changes. Before changing a public signature, removing an API, renaming a package, or altering an on-disk format, ask: can this be additive instead? `[Obsolete]` markers, transition shims (see `src/Shims/` + `Fallout.SourceGenerators.TransitionShimGenerator`), the `[Experimental("FALLOUT0xx")]` opt-in escape hatch for not-yet-stable surface, feature flags, and overload-based extension are all preferred to a hard break. When a breaking change is genuinely unavoidable, it lands on `main`, is held for the next yearly major, and follows rule #1's flow — the break must be deliberate, named, and migration-pathed in the CHANGELOG. See [#262](https://github.com/ChrisonSimtian/Fallout/issues/262) for the broader discussion. The `[Experimental]` convention (diagnostic-ID scheme + registry) is documented in [docs/agents/conventions.md](docs/agents/conventions.md#experimental-for-opt-in-unstable-apis) and [docs/experimental-apis.md](docs/experimental-apis.md).
3. **Central package versions only** — add to `Directory.Packages.props`, never `Version=` inline.
4. **Tests next to code** — every `src/Foo` has a `tests/Foo.Tests` sibling. Mirror namespaces.
5. **Stay on xUnit + FluentAssertions + Verify.** Don't introduce new test frameworks.
diff --git a/docs/agents/conventions.md b/docs/agents/conventions.md
index 59b972c4c..d817f4a9f 100644
--- a/docs/agents/conventions.md
+++ b/docs/agents/conventions.md
@@ -10,6 +10,29 @@ Three groups: conventions to respect, things never to do, and the tool-wrapper r
- **No IDE-specific style files committed.** `.editorconfig` and `*.DotSettings` were removed during the takeover — relying on `dotnet format` defaults and review.
- **Telemetry opt-out is set in test runs** (`FALLOUT_TELEMETRY_OPTOUT=true`). Keep it that way.
- **No per-file license headers.** The MIT notice lives in [`LICENSE`](../../LICENSE) at the repo root, and NuGet packages declare MIT via `PackageLicenseExpression`. Per-file headers were stripped in v11 (one source of truth + the header URL would have rotted on the repo-org transfer). Vendored third-party code keeps its own copyright headers — don't touch those (e.g. files under `src/Persistence/Fallout.Persistence.Solution/` retain Microsoft's MIT notice).
+- **`[Experimental]` for opt-in unstable public APIs.** Not-yet-stable public surface is marked with `[Experimental("FALLOUT0xx")]` rather than held back or shipped silently. See [the `[Experimental]` convention](#experimental-for-opt-in-unstable-apis) below and the [diagnostic-ID registry](../experimental-apis.md).
+
+## `[Experimental]` for opt-in unstable APIs
+
+Per [ADR-0004 §5](../adr/0004-calendar-versioning-and-dual-pace-channels.md), public APIs that aren't ready to commit to a stability guarantee are marked with [`System.Diagnostics.CodeAnalysis.ExperimentalAttribute`](https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.experimentalattribute) instead of being held back or shipped silently. The attribute ships in the .NET 8+ BCL — **no package reference needed** (the repo targets .NET 10).
+
+```csharp
+using System.Diagnostics.CodeAnalysis;
+
+[Experimental("FALLOUT001")]
+public sealed class NewPluginHost
+{
+ // ...
+}
+```
+
+**Rules:**
+
+- **Diagnostic-ID scheme: `FALLOUT0xx`.** Each experimental surface gets its own ID (e.g. `FALLOUT001`), allocated **sequentially and never reused** — a retired ID stays retired. Register every allocation in the [diagnostic-ID registry](../experimental-apis.md) in the same PR that introduces it.
+- **Consumers must explicitly opt in.** `ExperimentalAttribute` is an *error-by-default* diagnostic: code that touches the API fails to compile until the consumer suppresses the exact ID — `#pragma warning disable FALLOUT001` around the call site, or `$(NoWarn);FALLOUT001` in their project. Opting into instability is therefore a conscious, per-API choice — which is right for a *framework* (a product devs build on), not an app.
+- **Promoting to stable = removing the attribute.** Because the feature already rode the trunk (`main`/edge), there is no cross-branch cherry-pick — deleting the `[Experimental]` line is the whole promotion. This is what lets stabilised work feed back into the regular channels without a divergent fork. Adding *or* removing `[Experimental]` is **not** a breaking change.
+- **Channel discipline differs.** On `main`/edge, experimental churn is expected and the attribute is a courtesy. On a `release/YYYY` **stable train**, any risky-but-shipped public surface **must** wear `[Experimental]` — that contract is what keeps the stable line trustworthy while still carrying new work.
+- **Don't apply it speculatively.** Because the diagnostic is error-by-default, marking an API that's already used internally breaks the build everywhere it's referenced. Only add `[Experimental]` to a genuinely not-yet-stable API, and suppress every internal usage in the same change so the build stays green.
## What not to do
diff --git a/docs/experimental-apis.md b/docs/experimental-apis.md
new file mode 100644
index 000000000..b56aa5fa9
--- /dev/null
+++ b/docs/experimental-apis.md
@@ -0,0 +1,57 @@
+# Experimental APIs
+
+Fallout marks not-yet-stable public APIs with [`System.Diagnostics.CodeAnalysis.ExperimentalAttribute`](https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.experimentalattribute) so consumers opt into instability deliberately. This page is the **canonical registry of allocated `FALLOUT0xx` diagnostic IDs**.
+
+See [ADR-0004 §5](adr/0004-calendar-versioning-and-dual-pace-channels.md#5-experimental-for-opt-in-unstable-apis) for the decision and [the agent conventions](agents/conventions.md#experimental-for-opt-in-unstable-apis) for the contributor rules.
+
+## How it works
+
+An experimental API carries a diagnostic ID:
+
+```csharp
+using System.Diagnostics.CodeAnalysis;
+
+[Experimental("FALLOUT001")]
+public sealed class NewPluginHost
+{
+ // ...
+}
+```
+
+`ExperimentalAttribute` is an **error-by-default** diagnostic. Code that touches the API will not compile until you explicitly suppress the exact ID, which is your conscious opt-in to an API that may change without notice:
+
+```csharp
+#pragma warning disable FALLOUT001 // opting into an experimental API
+var host = new NewPluginHost();
+#pragma warning restore FALLOUT001
+```
+
+or, project-wide, in your `.csproj`:
+
+```xml
+
+ $(NoWarn);FALLOUT001
+
+```
+
+## Diagnostic-ID scheme
+
+- IDs use the form `FALLOUT0xx` and are allocated **sequentially**.
+- An ID is **never reused** — once retired it stays retired, so a suppression can never silently re-bind to a different API.
+- Every allocation is recorded in the registry table below, in the same PR that introduces the attribute.
+- **Promoting an API to stable means deleting the `[Experimental]` attribute** (the feature already rode the trunk — no cross-branch cherry-pick). The ID's row moves to **Promoted** status and the ID is retired, not recycled. Adding or removing `[Experimental]` is not a breaking change.
+- **Channel discipline:** on `main`/edge the attribute is a courtesy; on a `release/YYYY` stable train any risky-but-shipped public surface **must** wear it.
+
+## Registry
+
+Status values: **Experimental** (live, opt-in), **Promoted** (attribute removed, now stable — ID retired), **Withdrawn** (API removed before promotion — ID retired).
+
+| ID | Surface | Introduced | Status | Notes |
+|----|---------|------------|--------|-------|
+| _none allocated yet_ | — | — | — | First experimental API to land claims `FALLOUT001`. |
+
+