一键导入
legacy-1-8-9
Use when touching the 1.8.9 legacy overlay, -Pse.target=legacy builds, JvmDowngrader, the mega-jar, or the legacy/mega smoke gates.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when touching the 1.8.9 legacy overlay, -Pse.target=legacy builds, JvmDowngrader, the mega-jar, or the legacy/mega smoke gates.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Use when running or verifying StarEnchants' test gate — which commands to run, the Paper+Folia version matrix, concurrency rules for booting many servers, and how to read results honestly instead of trusting a success banner.
Use when implementing or modifying the runtime engine — stateless systems, the activation pipeline and gate order, effect/condition/trigger/selector kinds, the Ability record, the Sink, declared Affinity, the dispatch plan, or how one activation executes.
Use when working on item state, the PDC codec, the ItemView content-hash cache, the stable-key to dense-id map, component stores, the WornState resolver, lore/name rendering, or legacy item migration.
Use when writing or reviewing ANY code in the StarEnchants repo — the design principles, engine boundaries, threading/Folia rules, config snapshot pattern, extensibility pattern, and the invariants every change must preserve.
Use when writing or reviewing code on the combat or item hot path, declaring an effect Affinity, touching the Sink/Dispatcher, the ItemView cache, interning, or anything the ArchUnit/CI lint or the JMH bench guards.
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.
基于 SOC 职业分类
| name | legacy-1.8.9 |
| description | Use when touching the 1.8.9 legacy overlay, -Pse.target=legacy builds, JvmDowngrader, the mega-jar, or the legacy/mega smoke gates. |
1.8.9 is a separately-compiled tree of the same modules, not a second
codebase (docs/legacy-1.8.9-codeshare-design.md). The modern floor stays
1.17.1; the shipped StarEnchants-<ver>.jar is a Multi-Release jar carrying both
the modern v61 tree and this downgraded v52 tree, the JVM selecting one at load.
Each forking module keeps its shared, 1.8-safe logic in flat src/ and adds two
trees under se/<module>/overlay/{modern,legacy}/. The active one is added as a
main srcDir by -Pse.target (default modern):
sourceSets["main"].java.srcDir(if (legacy) "overlay/legacy" else "overlay/modern").
The unit of era variation is the seam, not the file — the same-FQN whole-file
swap of ADR-0036 is superseded (except the two bindings twins below):
src/ (Hands, ParticleFx, ItemStateStore, EquipSource, ActorProbe,
DropControl, Projectiles, VanillaStats, AnvilRename, …) with two
era-exclusive impls under era-unique FQNs (ModernHands/LegacyHands,
ModernDispatchSink/LegacyDispatchSink, ModernParticleFx/LegacyParticleFx,
PdcItemStateStore/NbtItemStateStore, …). Parity is a per-era javac fact
(implements), not a convention, and drift can't slip past the merge gate. The
impls are constructor-injected from the composition root — shared consumers
name only the interface, never an era class (the EraTreeGateTest G1-c check
enforces this).engine.sink.DispatchSinkBase + ModernDispatchSink/LegacyDispatchSink;
feature.combat.EquipListener shared + the era armour-change feeders). The base
is in src/, so it lands in both class sets automatically.VanillaStats.NONE / AnvilRename.UNSUPPORTED in src/, the real
ModernVanillaStats / ModernAnvilRename in overlay/modern.bootstrap.compat.EraBindings (implements the shared EraServices — the whole
era wiring manifest, absorbing the former Wiring/Bridges/Targets/Commands) and
platform.resolve.HandleLookups. Content rule (gated): construction and
one-line delegation only — no if/for/while/switch/try, no mutable fields.
Real per-era logic is extracted to era-exclusive classes (LegacyTargets,
LegacyEnchantResolver, LegacyGearPoll), NOT inlined into a binding.src/ — Sink, the kinds,
WornState/ItemView, the pure core. A same-FQN class lives in src/ or an
overlay, never both (a seam interface and its Modern*/Legacy* impls have
different names, so this holds).-Pse.target=legacy → a disjoint buildDirLegacy compiles redirect buildDir to build-legacy/ (root build.gradle.kts),
so the modern build/ jar is never clobbered and Gradle's incremental compiler
can't mix classes across the overlay swap. The merge (build-mega-jar.sh) reads
the modern jar from build/ and the downgraded legacy jar from build-legacy/
in any order. build-legacy/ is gitignored.
The base tree targets Java 8 (class v52) — a 1.8.x server runs only on JDK 8.
scripts/build-legacy-jar.sh compiles at 17 then lowers the assembled jar
61→52 with JvmDowngrader (pinned 1.3.6), shading its stdlib-API + util
runtime helpers under the se_jdg root so the jar is self-contained. Records,
sealed types, and switch-expressions lower cleanly; a JDG version bump is a gated
change requiring a fresh live smoke.
On a modern JVM the classloader serves META-INF/versions/17/ for any class
present there and the base (v52) copy otherwise. A class that exists in only
one era's tree, but is reachable from the other era's bytecode, would load
with the wrong-era signature → NoSuchMethodError. scripts/tools/MegaJarGate.java
(an ad-hoc-compiled ASM tool inside build-mega-jar.sh, like Jdk8ApiGate) proves
soundness derived from the tree, replacing the old hand
ALLOW_ERA_EXCLUSIVE(_PREFIX) allowlists:
overlay/<era>/ is legitimately one-era); a divergent class with no such
source fails.:integrate) is derived from the module set, not
a hard-coded prefix.EraBindings, HandleLookups, declared in
build-mega-jar.sh's SE_MEGA_BINDINGS) must be present in both trees, and the
base EraBindings must reference v1_8_R3 while its versions/17 copy must not
(the era-direction check, generalising the old Commands grep).The fast companion EraTreeGateTest runs in every ./gradlew build (G1-a twin
set = bindings; G1-b bindings composition-only; G1-c no src names an era class;
G1-d EraBindings implements EraServices), so a broken seam fails locally. The
tester is excluded by design: its era-specific suites live in src/ includes
(not overlay dirs), so P1 rejects it exactly as before — run-matrix.sh boots the
modern tester, legacy-smoke.sh the downgraded legacy tester, mega-smoke.sh
boot-smokes the shipped plugin on both eras.
| Gate | Script | Proves |
|---|---|---|
| Legacy compile (1 + 1b) | build-legacy-jar.sh (-Pse.target=legacy) | overlay and shared main link against the real Spigot 1.8.8 + v1_8_R3 jar — a 1.8-absent symbol is a javac error, not a runtime crash |
| JDK-8 API gate (2) | jdk8-api-gate.sh (embedded in build-legacy-jar.sh) | closed-world ASM scan of the downgraded v52 jar against a real JDK 8 baseline — an un-shimmable JDK-9+ java.*/javax.* reference fails here, not at runtime |
| Live legacy smoke (4) | legacy-smoke.sh | the downgraded tester boots on a real craftbukkit-1.8.8 under JDK 8 and the reduced in-server suite fresh-PASSes |
| Mega smoke | mega-smoke.sh | the merged mega-jar ENABLES on both eras (base v52 on 1.8, versions/17 v61 on Paper) with no wrong-era leak |
jdk8-api-gate.sh is the single chokepoint for the language wall, so it gates
every legacy-producing lane (legacy-smoke.sh on PR+push, build-mega-jar.sh on
release). SE_SKIP_JDK8_GATE=1 is a loud, local-only escape hatch.
Material.CLOCK-class constants break the 1.8 compile. Flattening renamed
hundreds of Materials; a hard-referenced modern constant is absent on the 1.8
jar. Resolve by name through the resolver, never a compile-time constant.getItemInHand() on an empty hand can return null
(modern returns AIR); the LegacyActorProbe / LegacyHands impls must guard it.PlayerArmorChangeEvent /
PlayerItemDamageEvent, so equip-refresh, ITEM_DAMAGE, and heroic-durability are
all driven by the single feature.trigger.LegacyGearPoll (one repeating task, one
5-slot scan). Its fixed per-slot subscriber order is load-bearing — ITEM_DAMAGE
fires before any heroic restore, and the poll records the post-restore durability so
next tick sees no phantom delta. Do not re-split it into per-concern polls.build-legacy/libs can retain an old-version
-legacy.jar a bare find | head -1 would grab by directory order — a false
PASS on old code. The scripts pin the exact current-version filename.ARCH_PREFIX under set -u. The Apple-Silicon arch -x86_64 prefix array
is often empty on x86/CI; expand it as ${ARCH_PREFIX[@]+"${ARCH_PREFIX[@]}"}
so bash 3.2 doesn't error on an unbound empty array.ThreadLocalRandom.nextDouble(double) unstubbable in the legacy smoke — the roll
must come from an injected supplier (the pipeline already reads act.chanceRoll()),
or every proc throws on the real 1.8 jar.See paper-cross-version for the modern range, matrix-gate for the full gate,
and docs/legacy-1.8.9-codeshare-design.md for the derivation.