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.