One armourAgainst, in the authority, shared with the harness (R-257)

Left open by R-256. Two rules decided how a hit meets armour — armourAgainst (a critical
ignores it, a special halves it, rounded down) and damageAfterArmour (what is left never
goes below zero). Both lived in ringbrp.mjs, which is the game. Neither lived in
rules.mjs, which is the authority, so the guard that forbids redefining a rule had
nothing to forbid. tools/simulate.mjs applied armour from its own inline copies of both,
as it had since it was written.

They agreed, which is the whole point. Nothing published was wrong and no guard could
have said they were two things rather than one. But every number in BESTIARY.md, every
row of the lethality baseline and every measurement quoted from R-251 onward comes out of
that harness: change how a special hit meets armour and the game changes, the numbers
describing the game do not, and all nine guards still pass. Same shape as ddc4f99, with
no tolerance to blame and no reason it would ever have surfaced.

Both rules now live in rules.mjs. ringbrp.mjs imports and re-exports them because they
are public API at game.ringbrp. The simulator imports them. Verified live in the world
that game.ringbrp.armourAgainst and the rules.mjs export are the same function object,
not two that agree.

Proof it changed nothing: check-lethality replays 47 creatures over 2000 fights each and
every one fights exactly as recorded — no tolerance, no drift — across roughly four
million resolved attacks, all of which now go through the moved rule.

check-rules fails any file outside rules.mjs that halves armour or subtracts it inline,
naming file and line. Negative-tested by restoring the harness's original three lines
verbatim (both patterns fire) and by redefining armourAgainst in ringbrp.mjs, which the
name-based guard now catches because the rule finally lives somewhere it belongs to.
Arriving in rules.mjs also tripped the spot-check requirement immediately: eight new
spot-checks, and what a special does to hide 9 is now written down once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-09-12 23:33:58 +01:00
co-authored by Claude Opus 5
parent bb10613992
commit c3a337314f
7 changed files with 108 additions and 30 deletions
+41
View File
@@ -5395,3 +5395,44 @@ hit meets armour, the game changes and the measurements do not, and the guards w
still pass. That is R-251 with a longer fuse. Not fixed here: moving a rule into
`rules.mjs` and re-pointing the simulator at it is a change to the measurement pipeline
and wants its own before-and-after.
## R-257 — the harness had its own copy of how armour works
Left open by R-256 and fixed here. Two rules governed how a hit meets armour:
- `armourAgainst` — a critical ignores armour, a special halves it, rounded down
- `damageAfterArmour` — what is left never goes below zero
Both lived in `ringbrp.mjs`, which is the game, and neither was in `rules.mjs`, which is
the authority — so the guard that forbids redefining a rule had nothing to forbid. And
`tools/simulate.mjs` applied armour from its own inline copies of both, as it had since
it was written.
**They agreed.** That is the whole point. Nothing published was ever wrong, and no guard
could have told you they were two things rather than one. But every number in
BESTIARY.md, every row of the lethality baseline and every measurement quoted in this log
from R-251 onward comes out of that harness. Change how a special hit meets armour and
the game changes; the numbers that describe the game do not; all nine guards pass. That
is exactly the shape of ddc4f99, which changed the game and left the measurements behind
for four commits — except that this one had no six-point tolerance to blame, and would
have hidden indefinitely.
**The fix.** Both rules moved into `rules.mjs`. `ringbrp.mjs` imports and re-exports them,
because they are part of the public API at `game.ringbrp` and macros call them. The
simulator imports them. Verified live in the world that `game.ringbrp.armourAgainst` and
`rules.mjs`'s export are the same function object, not two that agree.
**Proof it changed nothing.** check-lethality compares 47 creatures over 2000 fights each
against the recorded baseline, and every one of them fights *exactly* as recorded — no
tolerance, no drift. A refactor of the rule the harness applies to every single hit,
across roughly four million resolved attacks, moved nothing. That is what makes it a
refactor rather than a change.
**The guard.** check-rules now fails any file outside `rules.mjs` that halves armour or
subtracts it inline, naming file and line; check files are exempt. Negative-tested by
restoring the harness's original three lines verbatim — both fire — and by redefining
`armourAgainst` in `ringbrp.mjs`, which the older name-based guard now catches because
the rule finally lives somewhere it can be said to belong to. The two new exports also
tripped the spot-check requirement the moment they arrived, which is the authority
working as designed: eight new spot-checks, and the numbers for a special hit against
hide 9 are now stated in exactly one place.