Skip to content

Commit a35dd42

Browse files
pruukclaude
andcommitted
Merge M4 polish: room-scoped refinement, bespoke buffs, content, CI
All four M4 streams (per-room weather refinement, buffs 59001+ with BuffOverrides, biome content + builder-guide docs, CI + ExcludeZonePatterns + admin polish), plus boot self-heal for the engine overlay-clobber bug found by the OOBE smoke. Smoke 8/8 PASS; final review: ready to merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 parents 5fb74ac + c308488 commit a35dd42

51 files changed

Lines changed: 3232 additions & 430 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# CI for the GoMud weather module — two tiers, mirroring the development
2+
# workflow in CONTRIBUTING.md:
3+
#
4+
# standalone the four pure packages (sim, crawler, content, seasons)
5+
# build and test in this repo alone, with the race detector.
6+
# engine-coupled the whole module is synced into a fresh clone of upstream
7+
# GoMud master and built/vetted/tested there (the root
8+
# package and engine/ only compile inside a checkout).
9+
10+
name: CI
11+
12+
on:
13+
push:
14+
branches: [main]
15+
pull_request:
16+
17+
jobs:
18+
standalone:
19+
name: Standalone (pure packages)
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Check out module
23+
uses: actions/checkout@v4
24+
25+
- name: Set up Go (from go.mod)
26+
uses: actions/setup-go@v5
27+
with:
28+
go-version-file: go.mod
29+
30+
- name: gofmt check
31+
run: |
32+
gofmt -l .
33+
test -z "$(gofmt -l .)"
34+
35+
- name: go vet (pure packages)
36+
run: go vet ./sim/... ./crawler/... ./content/... ./seasons/...
37+
38+
- name: go test -race (pure packages)
39+
run: go test -race -count=1 ./sim/... ./crawler/... ./content/... ./seasons/...
40+
41+
# This job tracks UPSTREAM GoMud master as an early warning for engine API
42+
# drift. A failure here while `standalone` is green usually means upstream
43+
# moved (an engine API the adapter binds to changed) — that's signal, not
44+
# noise: patch engine/ (see CONTRIBUTING.md "DOGMud backport delta" for the
45+
# pattern), don't ignore the red.
46+
engine-coupled:
47+
name: Engine-coupled (upstream GoMud master)
48+
runs-on: ubuntu-latest
49+
steps:
50+
- name: Check out module
51+
uses: actions/checkout@v4
52+
with:
53+
path: module
54+
55+
- name: Clone upstream GoMud master (shallow)
56+
run: git clone --depth 1 --branch master https://github.com/GoMudEngine/GoMud.git gomud
57+
58+
- name: Set up Go (from GoMud's go.mod)
59+
uses: actions/setup-go@v5
60+
with:
61+
go-version-file: gomud/go.mod
62+
cache-dependency-path: gomud/go.sum
63+
64+
# Mirrors scripts/sync-to-checkout.ps1 exactly: same exclusion list.
65+
# go.mod and go.sum MUST NOT travel — in-checkout modules have no go.mod
66+
# (they are part of the engine module).
67+
- name: Sync module into checkout (modules/weather)
68+
run: |
69+
mkdir -p gomud/modules/weather
70+
rsync -a --delete \
71+
--exclude '.git' \
72+
--exclude '.github' \
73+
--exclude '.claude' \
74+
--exclude '.worktrees' \
75+
--exclude 'docs' \
76+
--exclude 'scripts' \
77+
--exclude 'go.mod' \
78+
--exclude 'go.sum' \
79+
--exclude '*.png' \
80+
--exclude 'Screenshot*' \
81+
module/ gomud/modules/weather/
82+
83+
# Regenerates modules/all-modules.go so the weather module is actually
84+
# imported (linked into the binary) — the same step operators run on
85+
# install. Without it the module would compile but never register.
86+
- name: go generate (wire module imports)
87+
working-directory: gomud
88+
run: go generate ./...
89+
90+
- name: go build (whole engine)
91+
working-directory: gomud
92+
run: go build ./...
93+
94+
- name: go vet (module in checkout)
95+
working-directory: gomud
96+
run: go vet ./modules/weather/...
97+
98+
- name: go test -race (module in checkout)
99+
working-directory: gomud
100+
run: go test -race -count=1 ./modules/weather/...
101+
102+
- name: Triage hint (read on failure)
103+
if: failure()
104+
run: |
105+
echo "If 'standalone' passed and this job failed, upstream GoMud likely moved an API the module binds to."
106+
echo "Patch engine/ against the new upstream — see CONTRIBUTING.md. The red is signal, not noise."

README.md

Lines changed: 296 additions & 68 deletions
Large diffs are not rendered by default.

content/context.md

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,56 @@ dependency is `gopkg.in/yaml.v2`, which the GoMud engine itself uses.
4242
- **arch_test.go**: purity guardrail — fails if any file imports a
4343
`GoMudEngine/GoMud/internal` path.
4444
- **moduledata_test.go**: validates the SHIPPED YAML files under
45-
`files/datafiles/`. For emote tables: parseable, `filename == weather+".yaml"`,
46-
at least one outdoor-default and one indoor-default line. For mutator specs:
47-
parseable, `mutatorid` is **`weather-` or `season-`** namespaced (both
48-
namespaces validated under the same rules), filename matches
49-
`fileNameFor(mutatorid)` (mirrors `util.ConvertForFilename`), `respawnrate`
50-
forbidden (would fight the orchestrator), `decayintoid` forbidden (upstream
51-
`MutatorList.Remove` resets `SpawnedRound` and runs `Update` whose decay
52-
branch has no liveness guard — the decay target is instantly resurrected),
53-
`decayrate` required (self-heal safety net). A count assertion requires at
54-
least **14** shipped mutator specs (8 weather + 6 season) so a sync/copy
55-
mistake is loud. For emote tables it also validates `seasonal:` variant keys
56-
(each must be a season of a shipped track and carry at least one line), and a
57-
separate check (`TestShippedSeasonalAmbience`) loads `emotes/seasons/`,
58-
asserts the six (track, season) ambience tables are present with outdoor +
59-
indoor default lines, and that each file obeys the `<track>_<season>.yaml`
60-
filename rule.
45+
`files/datafiles/`. Helpers: `fileNameFor` mirrors the engine's
46+
`util.ConvertForFilename` **byte-exactly** (lowercase, drop `'`, keep
47+
`[a-z0-9]`, everything else → `_`; byte-level, so names must stay ASCII) —
48+
used for both mutator and buff filename checks; `knownBiomes()` is the
49+
canonical biome-key set — the keys of `sim.DefaultClimate()` plus
50+
`"default"` — and `checkBiomeKeys` fails any emote-table biome key outside
51+
it (a key no room ever reports is unreachable prose) or with zero lines.
52+
- **Emote tables** (`TestShippedEmoteTables`): parseable,
53+
`filename == weather+".yaml"`, at least one outdoor-default and one
54+
indoor-default line, every biome key (base sections AND inside `seasonal:`
55+
blocks) validated via `checkBiomeKeys`, and `seasonal:` variant keys must
56+
each be a season of a shipped track and carry at least one line. A separate
57+
check (`TestShippedSeasonalAmbience`) loads `emotes/seasons/`, asserts the
58+
six (track, season) ambience tables are present with outdoor + indoor
59+
default lines (biome keys validated too), and that each file obeys the
60+
`<track>_<season>.yaml` filename rule.
61+
- **Mutator specs** (`TestShippedMutatorSpecs`): parseable, `mutatorid` is
62+
**`weather-` or `season-`** namespaced (both namespaces validated under the
63+
same rules), filename matches `fileNameFor(mutatorid)`, `respawnrate`
64+
forbidden (would fight the orchestrator), `decayintoid` forbidden (upstream
65+
`MutatorList.Remove` resets `SpawnedRound` and runs `Update` whose decay
66+
branch has no liveness guard — the decay target is instantly resurrected),
67+
`decayrate` required (self-heal safety net — it is also what lazily heals
68+
stale per-room mutators). **Indoor-variant rules (M4)**: every outdoor
69+
`weather-<type>` must have a `weather-<type>-indoor` twin (pairing
70+
completeness — a 9th type can't ship half-finished), and indoor variants
71+
are sheltered by construction: `lightmod`, `playerbuffids`, `mobbuffids`,
72+
and `alertmodifier` are all forbidden on them. A **bidirectional type-list
73+
drift guard** requires the shipped outdoor `weather-<type>` id set to equal
74+
`sim.KnownWeatherTypes` minus `clear` — shipped-but-unlisted and
75+
listed-but-unshipped both fail (the list is what `BuffOverrides.<type>`
76+
config enumeration keys off). A count assertion requires at least **22**
77+
shipped mutator specs (8 weather + 8 indoor + 6 season) so a sync/copy
78+
mistake is loud.
79+
- **Buff specs** (`TestShippedBuffSpecs`, M4): validates
80+
`files/datafiles/buffs/` against what the engine's plugin buff loader
81+
(`internal/buffs/plugin.go`) will accept — parseable under
82+
`yaml.UnmarshalStrict` against the full engine `BuffSpec` schema (typos in
83+
field names fail), ids inside the module's documented **59001–59099**
84+
range with no duplicates, filename exactly the engine's computed
85+
`<BuffId>-<ConvertForFilename(Name)>.yaml` (via `fileNameFor`; the loader
86+
rejects any other name), a player-visible description, `triggerrate` set
87+
and `triggercount` in 1..5 (the mutator re-applies every round the weather
88+
holds, so a short count means the buff fades shortly after shelter), and
89+
the **gentleness policy**: every statmod must be in [-10, 0] — a small
90+
penalty or zero; no damage, no scripts, no bonuses (a positive mod is
91+
rejected as a design change needing explicit review). A **cross-check** then walks every shipped
92+
mutator spec: each `playerbuffids` entry must be one of the shipped buffs
93+
(no borrowing engine ids like 31/33, which vary by world and are harsher
94+
than weather warrants). At least 3 buff specs must ship.
6195
- **doc.go**: package-level comment.
6296

6397
### Key Types

0 commit comments

Comments
 (0)