re: the chatter phase merge is not additive; 224 authored variants never play

sub_82215A58 reads CrewCount + PresetMessage_Phase1/2/3 and reaches the loader
from ONE call site, so the three phase tables fold into one map keyed by the
event record name.  Every merge collides; sub_82213840 reconciles on the
message list plus +16/+17/+18/+20/+24/+28 (NOT the +32 Yes mask), and the
incumbent always wins.  Measured: 26432 collisions, 26208 identical, 224
different (189 differ only in the message list), 0 mask-only differences.

Refuted handle: intersecting functions by the object's offsets finds dozens of
unrelated layouts -- offset shape is not an identifier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
Claude (auto)
2026-08-27 16:50:42 +00:00
parent c4fd90baa9
commit 6ee041c1bf
5 changed files with 179 additions and 5 deletions

View File

@@ -265,13 +265,71 @@ can never reach, because it skips the record first. Thirteen dead records out of
### 🟡 What this does *not* settle
The loader tells us the units and the storage, not the policy. `Priority`'s
comparison rule, whether `Interval` is a cooldown per event or per speaker, and
what `IntervalFluctuation` randomises against are all in the *consumer* of the
40-byte object, which this iteration did not read. `Pattern`'s two live values
The loader and the merge tell us units, storage and duplicate handling — not
the *firing* policy. `Priority`'s comparison rule, whether `Interval` is a
cooldown per event or per speaker, and what `IntervalFluctuation` randomises
against all live in the consumer of the map, reachable via
`sub_82215A58``sub_82214050`. 🔴 One handle was tried and refuted: searching
for functions that touch `+16`/`+17`/`+18`/`+20`/`+28`/`+32`/`+36` returns
dozens of unrelated matches, because those offsets recur across unrelated object
layouts. Offset shape is not an identifier. `Pattern`'s two live values
are named, but that `Window` means an on-screen text window is a reading of the
name, not something measured.
## ✅ How the three phase tables are merged — and what that costs
**Settled 2026-08-27.** `sub_82215A58` is the `UnitMessageSet` reader: it
references exactly `CrewCount`, `PresetMessage_Phase1`, `PresetMessage_Phase2`
and `PresetMessage_Phase3`, and it reaches the loader from **one** call site
(`0x82215D98`). So a crew's three phase tables are loaded into **one** map, not
three. The map is keyed by a hash of the *event record's name*
(`sub_82455C78`, the rolling-accumulator / character-sum-top-byte shape
[`idxd-tag-hash.md`](idxd-tag-hash.md) documents; which member of that family
this entry point is was not pinned here, and the finding does not depend on it).
Because all 64 event names recur in every phase table, **every merge collides**.
`sub_82213980`'s insert therefore has a reconciliation arm, and
`sub_82213840` — a function called from nowhere else — is its comparator.
### `sub_82213840` compares six of the eight fields
It walks the two message-id vectors elementwise, then compares `+16`
(`Probability`), `+17` (`Priority`), `+18` (`Pattern`), `+20` (`Interval`),
`+24` (`IntervalFluctuation`) and `+28` (`EffectiveTime`), and returns a bool.
It does **not** look at `+32` (the `Yes` mask) or `+36`.
On a colliding key the loader compares, and then — whatever the answer —
**frees the newcomer and keeps the incumbent**. A mismatch additionally clears
the byte the loader returns (`lbz r3, 80(r31)` at the epilogue); the caller
turns that into a flag at `0x82215D9C``0x82215DDC` and propagates it.
### What the disc actually does with that
Measured over the 22 `UnitMessageSet_S<NN>.tbl` (numbers in the artefact):
| | |
|---|---|
| `UnitMessageSet` records naming ≥ 2 phase tables | **298** |
| duplicate-key insertions to reconcile | **26 432** |
| identical on the compared fields | **26 208** (99.15 %) |
| **different** — loader clears its flag | **224** |
| compared fields equal but `Yes` mask differs | **0** |
Of the 224, **189 differ only in the message list** — per-phase dialogue
variants for the same event and the same timings. The other 35 also move
`Probability`/`Priority`/`Pattern`/`Interval`.
**The phase tables are not additive.** For a given crew and event, phase 1's
rule wins and the later phases' are discarded — so **224 authored variants never
play**. A port that merges them additively, or that lets a later phase override,
will not behave like the game.
**The comparator's blind spot is never exercised.** There is no record pair
that agrees on the six compared fields and disagrees on the `Yes` mask, so the
fact that `+32` is excluded from the comparison changes nothing on the shipped
disc. Worth recording precisely because it is the kind of gap that looks like a
latent bug until it is counted.
## 🟡 Not settled
* The *policy* the seven fields drive — see "what this does not settle" above.