# Round-A1..A4 findings — canary tid=6 spawn chain & divergence frontier

## Anchor reframe (round-37 misread corrected)

The "factory/registry layer divergence at [0x828E1F08]" framing is falsified.
Both engines install the SAME static-XEX `.rdata` vtable `0x820A183C` at the
singleton's `[+0]`. The instance VAs differ only because of ε-class allocator
divergence (audit-043).

| Probe                      | Canary               | Ours                 |
|----------------------------|----------------------|----------------------|
| `[0x828E1F08]`             | 0xBC22C910 (heap)    | 0x40111910 (heap)    |
| `[[0x828E1F08]+0]` vtable  | 0x820A183C           | 0x820A183C (SAME)    |
| `vtable[+0]` thunk         | 0x82175330           | 0x82175330 (SAME)    |
| `vtable[+8]` thunk         | 0x82175340 → b sub_821741C8 | SAME (vtable bytes from XEX `.rdata`) |

The thunks at 0x82175330+ are 8-byte `lwz r3, 8(r3); b <real_method>`
trampolines. Slot 2 (`+0x08`) is the worker dispatch entry that round 33
identified as 471× in canary tid=6 / 0× in ours.

## A.1 — Canary dispatcher loop is in sub_822F1AA8 on tid=6

Probe `--audit_jit_prolog_pc=0x821741C8 --audit_jit_prolog_r3_bytes=256` on
canary (35 s):

- ~1678 fires of sub_821741C8 on **tid=6**
- r3 at entry = `0xBCCC4A80` (the inner sub-object of the silph::UImpl
  singleton — extracted via the thunk's `lwz r3, 8(r3)`)
- LR at entry = `0x822F1D5C` (return PC after the `bctrl` at 0x822F1D58 inside
  sub_822F1AA8)
- Singleton's `[+C0..+D0]` UTF-16 spells "HF Frequency" (a UI label)

The dispatch site in canary (the `bctrl`) is at PC 0x822F1D58 inside
sub_822F1AA8:
```
0x822F1D40:  lwz     r3, 7944(r25)        ; r3 = [r25+0x1F08] = [0x828E1F08]
0x822F1D4C:  lwz     r11, 0(r3)           ; vtable
0x822F1D50:  lwz     r11, 8(r11)          ; vtable[+8] = thunk 0x82175340
0x822F1D54:  mtctr   r11
0x822F1D58:  bctrl                         ; → 0x82175340 → b 0x821741C8
```

## A.2 — Canary tid=6 spawn site is sub_821746B0 at PC 0x82174824

Enumeration of `ExCreateThread` calls in canary (35 s, 21 unique tuples):

```
entry=821748F0 start_ctx=BC365700 lr=824AC5F0 guest_lr=82174828  ← silph dispatcher #1
entry=821748F0 start_ctx=BC366DA0 lr=824AC5F0 guest_lr=82174828  ← silph dispatcher #2
```

PC `0x82174824` is the `bl 0x82172370` (the `ExCreateThread` thunk) inside
`sub_821746B0`. The setup is:
```
0x8217480C:  lis     r11, 0x8217
0x82174810:  li      r7, 0
0x82174814:  li      r6, 4               ; priority
0x82174818:  mr      r5, r29             ; start_ctx
0x8217481C:  addi    r4, r11, 18672      ; r4 = 0x821748F0 (entry)
0x82174820:  li      r3, 0
0x82174824:  bl      0x82172370          ; ExCreateThread
```

The entry `0x821748F0` is a thread main that calls `bl 0x821749C0` (the
inner dispatch).

## A.3 — sub_822F1AA8 spawns a SECOND thread at 0x822F1B08

The dispatch-loop function `sub_822F1AA8` itself ALSO spawns a thread at
PC 0x822F1B08 with entry=`sub_822F1EE0` and `start_ctx=BCE24A40`:
```
0x822F1AEC:  lis     r11, 0x822F
0x822F1AFC:  addi    r4, r11, 7904        ; r4 = 0x822F1EE0
0x822F1B08:  bl      0x82172370           ; ExCreateThread
```

sub_822F1EE0 → sub_822F1F20 contains its own atomic state-machine + wait loop.

## A.3' — sub_822F1AA8 has exactly 2 callers, both in sub_8216EA68

```
source=0x8216ECCC source_func=0x8216EA68 kind=call
source=0x8216EE10 source_func=0x8216EA68 kind=call
```

So sub_8216EA68 is the only function that drives sub_822F1AA8.

## A.4 — Ours' divergence is INSIDE the spawned thread, NOT at the spawn

Mirror-probed ours at `sub_821746B0` body BB heads (parallel mode, 50M
instructions, XENIA_CACHE_PERSIST=1):

| PC          | Fires | Notes                                          |
|-------------|-------|------------------------------------------------|
| 0x821746B0  | 1     | Entry. r3=0x40ba9a80                           |
| 0x821746E0  | 1     | After `bl 0x8284DCFC` (critical-section)       |
| 0x82174798  | 1     | After the early `beq` (r28==0 branch)          |
| 0x821747B8  | 1     | **Past the gate**: `[0x828E2B14]=0x40105000` non-NULL; `bl 0x82150EF8` returned r3=0x4024a840 (NON-NULL) |
| 0x821747D8  | 1     | After the inner `bl 0x821723F0`                |
| 0x8217480C  | 1     | Enters the spawn block                         |
| 0x82174828  | 1     | **Post-`bl ExCreateThread`**, r3=0x1070 = thread handle |

**OURS DOES SPAWN THE THREAD VIA THIS SITE.** The returned handle 0x1070 is
**tid=13's thread handle** (per round 37 final state). So **ours' tid=13 IS
the same logical thread as canary's tid=6** — spawned by the identical call
site with the same entry (0x821748F0).

## A.4 — Divergence is INSIDE the spawned thread's body

Round 37's frame trail for ours' tid=13 wedge:
`0x821CB1E0 → 0x821CBAE0 → 0x821CC454 → 0x821C4F18 → 0x82174A80`

The LAST frame `0x82174A80` is **inside sub_821749C0** (= the inner dispatch
called from sub_821748F0). It's right after the vtable dispatch at
0x82174A78 (`bctrl` on `[r30+vtable][+16]`):

```
0x82174a64:  mr      r3, r30              ; r3 = some object
0x82174a68:  lwz     r11, 0(r30)
0x82174a6c:  lwz     r4, 4(r29)
0x82174a70:  lwz     r5, 8(r31)
0x82174a74:  lwz     r11, 16(r11)         ; r11 = vtable[+0x10]
0x82174a78:  mtctr   r11
0x82174a7c:  bctrl                         ; dispatch
0x82174a80:  lwz     r3, 0(r29)           ; ← wedge frame top (LR after bctrl)
```

So `sub_821749C0`'s vtable[+0x10] dispatch on tid=13/tid=6's `r30` object
lands at audit-049 territory in ours (chain through sub_821CB030+0x128 that
ends waiting forever on handle 0x1078). In canary, the same dispatch on the
same object SHOULD land somewhere that ultimately reaches sub_822F1AA8's
dispatch loop and runs sub_821741C8 1678× via vtable[+8].

**The object `r30` is the result of `bl 0x821CF3F0`** at PC 0x821749DC. So
sub_821CF3F0 returns a registry-lookup object; the vtable on this object's
slot +0x10 method's body determines whether the thread wedges or runs.

## Phase B classification

Class 3 — **Missing init-time precondition**. Ours reaches the spawn site,
ours' tid=13 enters the chain, ours' tid=13 enters sub_821749C0, but the
vtable[+0x10] dispatch at PC 0x82174A78 in ours lands in audit-049 territory
(wait forever on 0x1078) rather than continuing through the canonical chain
toward sub_822F1AA8's outer dispatch loop.

Possible classes to refine in next round:
- **3a**: same vtable but state-dependent — `r30`'s field at a specific offset
  differs in ours vs canary, causing the method body to take a different
  branch.
- **3b**: the vtable in `r30` is DIFFERENT in ours vs canary (e.g., ours has
  a base-class vtable but canary has a derived-class vtable).
- **4**: synthesis fallback — spawn a SECOND thread that runs sub_822F1AA8's
  dispatch loop directly, bypassing the wedged sub_821749C0 chain.

## Next probe (A.4.5)

Probe both engines at sub_821749C0 entry filtering tid=13 (ours) / tid=6
(canary), capturing:
- `r3` and `r4` at entry (the factory-output object and the ctx)
- After the `bl 0x821CF3F0` at 0x821749DC: capture r30 (= sub_821CF3F0
  return — the object whose vtable is dispatched at 0x82174A78)
- At PC 0x82174A78 (the divergent bctrl): r30 + r30+0 (vtable) + vtable[+0x10]
  (the dispatch target)

If ours and canary have IDENTICAL `vtable[+0x10]` targets but the method
body's behavior differs → class 3a (state divergence). If targets differ →
class 3b (vtable identity divergence).
