R18's two tiers: a form generated from a config file's own values, and raw JSON for what a form cannot express. Admin → Rust mod config, one live round trip per action, nothing cached between a browser and a game host's disk. `configEdit.js` is the part that could not be done naively. JavaScript cannot tell `1` from `1.0`, and both mod frameworks deserialize a config into typed C# classes — so a read-modify-write silently rewrites every whole-numbered float as an integer on fields nobody touched, and a plugin that then throws at load does not come back. It never parses, mutates and re-serialises: it records the SOURCE SPAN of every value and splices literals into them, so an untouched `1.0` is still `1.0` and a number an admin types travels as text the whole way (D35/D36). The bridge's own config is editable with `Host`, `Port` and `ServerId` locked, in the form and in the raw tier, because either would cut the link carrying the edit or strand every row this site holds (D38). Credentials render masked with a reveal; the raw tier shows them (D37) and the audit trail never does. `rust_config_writes` records every save including the refused and the rolled back — an operator asking why a setting is not what they set needs to see that somebody tried. Three defects a browser walk found that 179 green tests did not: * every save of the bridge's own config was refused while the page said the opposite — a `<select>` whose value matches no `<option>` shows the first one, so the reload guess `RunicGateway` was on the wire and "nothing" was on the screen; * `btn ghost` is not a class this platform defines (`.btn-ghost` is), so every secondary button in this module has rendered as a primary one since phase 7 — here it made the open file and the active tier indistinguishable; * a save's refusal rendered at the top of a long form, far from the button. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
52 lines
2.3 KiB
JSON
52 lines
2.3 KiB
JSON
{
|
|
"$comment": [
|
|
"What a release copies into the bundle, declared ONCE. Read by .gitea/workflows/release.yml when",
|
|
"it assembles the tarball, and by server/scripts/checkBundle.js when CI asks whether that list",
|
|
"still covers everything the module's entry point can reach.",
|
|
"",
|
|
"This is an INCLUDE list on purpose. An exclude list ships whatever it forgot: the day someone",
|
|
"adds server/tools/ with a scratch credential in it, an exclude list packs it and nobody finds",
|
|
"out. The cost of that choice is that a new top-level directory silently drops OUT of every",
|
|
"release instead — which is exactly what happened to Module-uo between v0.3.0 and v1.0.0, where",
|
|
"server/commands/ arrived with a cutover, the list did not learn about it, and the module",
|
|
"installed and then died at the register stage on the operator's box. checkBundle.js exists so",
|
|
"that cannot happen twice, and it runs on the PR that adds the directory.",
|
|
"",
|
|
"server[] entries are paths under server/; root[] and generated[] are paths under the module",
|
|
"root.",
|
|
"",
|
|
"node_modules is NOT here, and its absence is asserted rather than assumed: this module declares",
|
|
"no runtime dependencies (everything the shipped half needs arrives on ctx), so the release runs",
|
|
"no npm ci and packs no dependency tree. checkBundle.js fails the PR that adds a `dependencies`",
|
|
"entry to server/package.json without also teaching the release to pack it — because a module",
|
|
"whose bundle silently lacks its own dependency fails the same way the missing directory did.",
|
|
"",
|
|
"generated[] ships but is not copied — release.yml writes module.json through jq to stamp the",
|
|
"released version into it, since the committed one is a floor rather than a record of the last",
|
|
"release. It is listed because server/index.js requires it, and a check that did not know it",
|
|
"ships would report the module's own manifest as missing from the bundle."
|
|
],
|
|
"server": [
|
|
"boot.js",
|
|
"catalogue.js",
|
|
"configEdit.js",
|
|
"core.js",
|
|
"db",
|
|
"index.js",
|
|
"ingest.js",
|
|
"model",
|
|
"package.json",
|
|
"permSync.js",
|
|
"router",
|
|
"sidecarClient.js"
|
|
],
|
|
"root": [
|
|
"swagger-fragment.json",
|
|
"LICENSE.md",
|
|
"README.md"
|
|
],
|
|
"generated": [
|
|
"module.json"
|
|
]
|
|
}
|