Skip to content

Extension Surface

[KEY for revisiting ADR-0002] — this document exists for one reason: to make ADR-0002's second revisit trigger — "the extension interfaces have been stable across two milestones" — answerable by grep instead of git archaeology. It is not a new mechanism, a new approval gate, or a change to how modules are built; it is a name for something that already exists.


1. What counts as the extension surface

Everything a third party (or a first-party module playing by the same rules — dogfooding, per 00_CONSTITUTION.md) can extend through, per 02_MODULE_MODEL.md §6:

Framework-wide (this list rarely grows): - GameModule and its five-stage lifecycle (Configure, RegisterServices, Initialize, Ready, Shutdown). - IServiceRegistry / IServiceResolver, including AddSingleton and Replace (see 03_SERVICE_REGISTRY.md §3.1). - IEventBus, IEvent, CancellableEvent (see 04_EVENT_BUS.md). - The UI registration surface (IHudRegistry and panel registration — see 11_UI_ARCHITECTURE.md §3). - IItemStateRegistry / IItemStateSerializer / ItemStateSerializer<TState> — persisting a new per-instance item state type without modifying Inventories (see 09_ITEM_FRAMEWORK.md §2.1). - Plugin, PluginManifest, IPluginCatalog (Applejack.Core.Plugins) — the surface a third party writes against, not a new extension mechanism itself: a plugin uses the four points above exactly as a first-party module does. See 23_PLUGIN_SYSTEM.md and ADR-0010.

Per module (this list grows with every module): - Its public service interfaces — the "Public API" section of that module's Code/<Module>/README.md. - Its published events — the "Events published" section of the same README.md. - Its GameResource asset schemas (items, jobs, recipes, and module configuration types) — no code required to extend, per Guides/03_EXTENDING_THE_FRAMEWORK.md §1–2.

2. What is not on the surface

Anything internal — concrete service implementations (InventoryStore, PropertyStore, ...), persistence document shapes (DoorDocument, ...), and private module wiring. These can change freely, as often as needed, without touching the extension surface, exactly because 02_MODULE_MODEL.md §5 already keeps them off it. A change here is never the kind of change this document tracks.

3. The convention

A breaking change to anything in §1 — a removed or renamed member, a changed signature, a service interface split or merged — gets an explicit Extension surface: line in its commit body, naming what changed and why. An additive change (a new interface, a new event, a new optional member) doesn't need one; it can't have broken anything by definition.

This is not a new review gate. It's a marker, so the question in §4 below has an answer instead of requiring someone to re-read every commit since M1.

4. Using this when ADR-0002's trigger comes up

git log --grep="Extension surface:" <last-two-milestone-tags>..HEAD

No hits (or only additive, non-breaking ones) across two milestone tags is the condition ADR- 0002's Notes section names as a reason to revisit "modules, plus a runtime plugin loader later." Hits naming real breaking changes mean the surface is still moving, and the loader would be built against a target that keeps changing under it — not yet time.