| name | config-and-migration |
| description | Use when working on the config schema/YAML, the DSL and ParamSpec, the compiler (resolve/typecheck/lower/erase/snapshot), diagnostics, transactional reload, catalog validation, or the EE/EA/AE and legacy-config migrator. |
Config + migration: content is a compiled program
Authored YAML+DSL is compiled, not interpreted (§10). se-compile turns the
whole library into one immutable Snapshot, published by a single AtomicReference
(§0). The hot path never sees a string; see effect-engine for what runs the
compiled Ability[].
Use when / not
Use for the schema, the DSL grammar + ParamSpec, any compiler phase,
diagnostics, /se reload, catalog validation, /se problems, or the migrator.
Not for the runtime engine (effect-engine), PDC layout
(item-data-model), or per-hit cost (performance-hot-paths).
The pipeline (se-schema → se-compile, both PURE, zero Bukkit)
| Phase | Does | Source |
|---|
| grammar | tokenize → untyped AST (effect lines, condition expr && || (), selectors, inline tags) | §2 |
| resolve | names + cross-version handles via an injected PlatformResolvers facade (tests pass a fake) | §2, §9 |
| typecheck | ParamSpec-driven arity/type/range/enum/cross-ref → Diagnostics; NEVER throws | §7, §10 |
| lower | AST → CompiledEffect/CompiledCondition (typed args, pre-built AST); WAIT → cumulative waitTicks | §3.2 |
| erase | SOURCE ERASURE: every source → ONE Ability[]; assigns dense ids + the stable-key↔id map | §4.1, §5.3 |
| snapshot | immutable Snapshot: Ability[], per-trigger index, suppression + damage plans, interners | §2 |
Hard rules
- Cross-version resolution is a COMPILE phase (§9): legacy tokens
(
CONFUSION→nausea, GENERIC_MAX_HEALTH→max_health) become resolved handles
interned into the effect; the runtime can never touch a renamed constant.
Unknown token → file/line diagnostic, warn-and-skip that one op, load never
crashes. See cross-version-item-api for the alias maps.
- The hardened SnakeYAML factory (dup-key last-wins + 64-alias cap + a
LinkageError fallback for 1.8-era servers) is duplicated-by-spec in
compile.load.YamlNode.newYaml and migrate.LegacyYaml.newYaml (migrate cannot
reach compile), locked in lockstep by testfx.YamlAcceptance. The migrator
tolerates duplicate keys deliberately — it parses other plugins' sloppy configs.
items/*.yml sound fields accept EITHER the bare string OR the
{ sound: NAME, volume: V, pitch: P } bracket map (SoundCue.fromField); a
bare string plays at volume/pitch 1.0.
- Diagnostics carry
Source(file,line,col) from SnakeYAML Marks through
compile (§2 diag, §10). Surfaced at load, on /se reload, via /se problems.
Never an exception. Codes come from the closed schema.diag.DiagCode enum —
Diagnostics/Diagnostic have no String overloads (ADR-0042). Numbers/bools
parse only through ContentParse.intOr/doubleOr/boolOr: content numbers block;
config/item/menu numbers warn-and-fall-back.
- Transactional, all-or-nothing reload (§10): build off-thread; fatal
diagnostics keep the OLD
Snapshot live — a bad edit never takes the server
down. Clean → swap the AtomicReference on the global thread.
/se reload --dry-run compiles + reports without swapping.
- Catalog validation runs inside
./gradlew build — CatalogValidationTest
/ CosmicPackValidationTest (se-bootstrap) compile the whole bundled library
against a permissive fake PlatformResolvers and fail on any blocking
diagnostic, so hundreds of enchants are audited as a reviewed diff, not a
live-server gamble (§10). Version-specific handle-token existence is validated
live by the tester's CatalogSuite (matrix-gate), not here.
- Migrator emits commented, reviewable YAML with inline TODOs (not opaque
transforms), reusing the same
ParamSpec + alias maps (§10). Migration is
config-only: it reads another plugin's authored config (EE/EA/AE YAML) and
re-expresses it in our DSL. It does not touch live item NBT — there is no
lazy in-place item rewrite (DESCOPED; see item-data-model and ADR-0005).
- Packs are gated artifacts (ADR-0046): export stamps a registry fingerprint
(
engine.boot.RegistryFingerprint, canonical text committed at
docs/reference/authoring-surface.txt + drift test); /se pack apply dry-run compiles the
whole pack surface through the REAL loaders before touching disk — extend the fingerprint's
canonical serialization (and bump its 1: version) only when the authoring surface's
serialization format itself changes; regen goldens (./gradlew regenDocs) after adding any
kind/param/trigger/var.
ParamSpec — one declaration, four uses (§7)
One declaration drives typecheck/range, tab-completion, /se docs, and migration:
.param("chance", D.DOUBLE.min(0).max(100))
.param("cooldown", D.INT.min(0).def(0))
.affinity(Affinity.AOE)
See effect-engine for the full EffectKind class shape. A malformed line is a
file:line:col diagnostic at load, never a NumberFormatException mid-combat (§7).
Registration is an explicit, greppable, checked-in registry — not
annotation-processor codegen (§7, §13 #2).
Boundaries (§2)
se-schema = the DSL as a typed language. se-compile = the compiler (Bukkit-free
via the injected facade). se-migrate = EE/EA/AE + legacy-YAML config importer (config-only; no item NBT).
PDC stores stable string keys; dense Ability.id is a per-run accelerator only —
items resolve by stable key after any reorder (§5.3).