feat(mod-api): expose developer mode

This commit is contained in:
MaxTomahawk
2026-08-24 10:37:10 +02:00
parent e88f2ef060
commit 58681fe62e
6 changed files with 278 additions and 2 deletions
+4 -2
View File
@@ -469,8 +469,10 @@ is warned once per name and the rest of the list still runs. The engine's own
Gen 1 verbs are **not** seeded on Gold: a row-list verb handed Gold's ctx would
find no runner on it, so `data.commands` under Gen 2 is the mod verbs alone.
**`mod.save`, `mod.options`, `mod.log`, `mod.assets`, `mod.find`, exports.**
Generation-agnostic; nothing to adapt.
**`mod.save`, `mod.options`, `mod.log`, `mod.assets`, `mod.find`,
`mod.developer`, exports.** Generation-agnostic; nothing to adapt.
`mod.developer` is the same fixed boot-time boolean on both generations and is
available while the entry chunk runs.
**`mod.world`.** Same method set, resolved against Gold's world
(`src/world/gen2/WorldAPI.lua`). Two differences show through and are
+19
View File
@@ -695,6 +695,25 @@ hotkeys. Either set `POKEPORT_DEV=1` in the environment or pass
love . --developer
```
Every sandboxed entry chunk receives the same boot decision as the boolean
`mod.developer`. It is available while the entry file is loading, so a mod can
keep diagnostic commands, screens, and verbose tracing out of player builds:
```lua
if mod.developer then
mod.commands:register("my_mod:diagnostics", function(ctx)
-- open or print this mod's diagnostic view
end)
end
```
`mod.developer` is a plain boolean snapshot for this boot. It grants no
permission and exposes neither the process environment nor the loader. In a
normal player boot it is `false`; `POKEPORT_DEV=1` and `--developer` make it
`true` through the same engine-owned decision that enables the console and hot
reload. Use a mod option for player-facing feature toggles rather than treating
developer mode as configuration.
While developer mode is active:
- `` ` `` (backtick) opens the console overlay — a Lua REPL with `game`,
+99
View File
@@ -0,0 +1,99 @@
# RFC 0017: Public mod developer-mode signal
## Status
Proposed.
## Motivation
The loader already has one boot-time developer-mode decision. It enables the
console and hot reload and can be forced in headless tests, but a sandboxed mod
cannot read it. `mod.commands` can register a diagnostic command but cannot say
whether the current boot is a developer boot. `mod.exports` only publishes
values to other mods. `game.ready` fires after entry registration and carries
only the game. `mod.options` is player configuration, not engine mode, and
`mod.log` logs unconditionally. The pre-sandbox compatibility
`os.getenv("POKEPORT_DEV")` deliberately returns `nil`, because the process
environment is hidden from mods.
The concrete consumer is **Adaptive Trainers**. Its approved Chapter 30 and
Phase H require trainer, boss, Rival, and League diagnostic views plus
seed-label tracing to exist only when `POKEPORT_DEV` is active. Without a
public signal, the mod must either ship those registrations in production,
misuse a player option, or import loader/Logger internals. All three violate
the approved observability boundary or the sandbox/public-API policy.
## Decision and plan extended
This implements **D-AT-005: diagnostics and seed tracing are admitted only by
the engine's developer-mode decision**. The consuming design is tracked in the
Adaptive Trainers implementation plan,
[`docs/superpowers/plans/2026-08-14-adaptive-trainers.md`](https://github.com/MaxTomahawk/gen1recomp-adaptive-trainers/blob/main/docs/superpowers/plans/2026-08-14-adaptive-trainers.md),
Task 9. The engine delta is generic and contains no trainer, balancing,
diagnostic-layout, seed-label, or Adaptive Trainers policy.
## Exact API delta
Every sandboxed mod object adds one field:
```lua
mod.developer -- boolean
```
The loader copies its existing `dev` decision into this field before invoking
the mod's entry chunk. It is therefore available for load-time registration:
```lua
if mod.developer then
mod.commands:register("my_mod:diagnostics", diagnostics_command)
end
```
The value is a plain boolean snapshot, not a loader reference or environment
facade. `false` is the normal player-build answer. `POKEPORT_DEV=1`, the
`--developer` command-line path, and the loader's existing injected `opts.dev`
test seam produce `true` through the same decision that already controls the
developer console, hot reload, and loader diagnostics. It grants no permission
and does not expose environment variables. The answer is fixed for the life of
that loader; changing a field on a mod's own table cannot change engine mode.
The field is generation-independent and has identical semantics on Red, Blue,
Yellow, Gold, and Silver.
## Migration and compatibility
Existing mods change nothing. `mod.developer` is additive, requires no
permission, and does not bump the integer mod API. Existing API-v1 and API-v2
entry chunks receive one extra scalar field and retain all prior fields and
methods unchanged. No name is removed or shadowed.
With no mods installed, `Loader:_api` is never called, so the delta allocates no
mod object and changes no data, save, options, event, hook, command, or file.
With mods installed in a normal boot, the new field is `false` unless an author
explicitly reads it. Existing registration and logging behavior is unchanged.
An adopting mod should gate developer-only registrations and verbose logging
directly on `mod.developer`. Player-facing behavior belongs behind
`mod.options`, not this signal.
## Verification
- `tests/engine/mod_developer_mode_test.lua` loads a real sandboxed mod through
the public SDK with developer mode both on and off. It proves the boolean is
available during entry execution and that the same source registers its
diagnostic command only for the developer load. It also covers the
command-line global path and a Gen 2 load.
- `tests/engine/mod_developer_mode_parity_test.lua` is the separate no-mod
parity suite. It proves both developer answers discover no mods, create no
files, and leave injected vanilla data unchanged. It also loads an unchanged
API-v1 probe and verifies identity, `mod:read`, exports, and options behavior.
- The full ROM-free engine and modkit tiers remain the compatibility proof for
`content.X:register/override/get`, `events:on`, `hooks:wrap`, `mod.log`,
`mod:read`, manifest v1 fields, and `pokemon.before_give`.
No registry or schema changes are involved, so generated registry documentation
is unaffected.
## Deprecation etiquette
Nothing is removed, renamed, superseded, or deprecated.