# Roy-3 — 0.9.1 annotation-pattern validation (Roy-3a original holdout + Roy-3b engineered overlap)

**Date:** 2026-05-23 (Roy-3a ran 11:37→11:53 local, Roy-3b ran 12:25→12:39 local)
**Plan:** [release_0_9_1.md § Stage 5](../plans/release_0_9_1.md) · [persona_convergence_crucible.md § "Iteration log"](../plans/persona_convergence_crucible.md)
**Companion:** [18_roy_2.md](18_roy_2.md) (pre-annotation A/B partner on original holdout) · [19_roy_2pc.md](19_roy_2pc.md) + [20_roy_2c.md](20_roy_2c.md) (pre-annotation A/B partners on engineered overlap) · [22_roy_5a.md](22_roy_5a.md)
**Specs:** [scenarios/roy/roy_3a_iteration.yaml](../../scenarios/roy/roy_3a_iteration.yaml) · [scenarios/roy/roy_3b_iteration.yaml](../../scenarios/roy/roy_3b_iteration.yaml)
**Fixtures:** [scenarios/roy/roy_1_holdout.yaml](../../scenarios/roy/roy_1_holdout.yaml) (Roy-3a) · [scenarios/roy/roy_2pc_holdout.yaml](../../scenarios/roy/roy_2pc_holdout.yaml) (Roy-3b)
**Reproduction:** [protocols/23_roy_3_reproduction.md](protocols/23_roy_3_reproduction.md)

## Status

0.9.1 Stage 5 validation iteration. Two sub-iterations measure whether Wires A+1+2+3 (shipped via PRs #253 / #254 / #255 / #256 / #257 between 2026-05-13 and 2026-05-22) produced the cross-arm behavioral divergence that the bare `cluster_reward_bias` path could not under Roy-1a / Roy-2 / Roy-2pc / Roy-2c.

| Sub-iteration | Test AUT mode | Fixture | Question |
|---|---|---|---|
| **Roy-3a** | llm-primary | `roy_1_holdout.yaml` (original) | Do annotations augment Roy-2's salience-mediated A-vs-C tool-family divergence on general percepts? |
| **Roy-3b** | llm-primary | `roy_2pc_holdout.yaml` (engineered overlap) | Does Wire-A's cluster-bias annotation drive arm A toward `sense_food_source` on food-themed percepts (Wire-A's specific design test)? |

Pre-registered diagnostic outcomes (from [release_0_9_1.md Stage 5](../plans/release_0_9_1.md)):

| Outcome | Diagnosis |
|---|---|
| Arm A `sense_food_source` count > B AND > C in Roy-3b | Wire-A reached LLM proposer's decision pathway on engineered overlap. |
| Arm A > C on tool-family divergence in Roy-3a (richer than Roy-2's 17/3/2 vs 21/5/1/1) | Wire 1 + Wire 2 compound the salience-mediated signal Roy-2 surfaced. |
| **A ≈ B ≈ C across both fixtures** | **Annotation pattern alone is insufficient. Pre-filter ranker (post-1.0) becomes load-bearing; investigate prompt rendering / priming-side regressions.** |

## What shipped

- [`scenarios/roy/roy_3a_iteration.yaml`](../../scenarios/roy/roy_3a_iteration.yaml) — llm-primary + multi-arc priming + original holdout fixture.
- [`scenarios/roy/roy_3b_iteration.yaml`](../../scenarios/roy/roy_3b_iteration.yaml) — llm-primary + multi-arc priming + engineered overlap fixture.
- No new analyzer scripts (Roy-3 reads `result.json`, NAc dumps, and `MAXIM_LOG_FILE` JSONL directly).
- No new tests (the pre-Roy-1 attribution scale stress test covers Roy-3's load).

## Result

Both sub-iterations completed against the same healthy leader without re-tries; pre-flight `outcome: ok` in both result.json.

| Iteration | Wall | Preflight latency_ms | Priming session | Arms |
|---|---|---|---|---|
| Roy-3a | **952.5s (~15.9 min)** | 353.6 | `20260523_114722` | a/b/c |
| Roy-3b | **879.9s (~14.7 min)** | 293.0 | `20260523_123334` | a/b/c |

Roy-3a and Roy-3b walls are 5-10% under Roy-2's 883s. Both faster than Roy-2pc's 1502s (substrate-primary) because llm-primary test-time pacing is shorter.

### Per-arm

| Iteration | Arm | Substrate | system_prompt | session_id | turns | duration_s | finish |
|---|---|---|---|---|---|---|---|
| Roy-3a | a | from_priming | neutral | `20260523_114915` | 10 | 119.5 | cancel |
| Roy-3a | b | blank | "You are a hungry infant" | `20260523_115114` | 10 | 90.7 | cancel |
| Roy-3a | c | blank | neutral | `20260523_115245` | 10 | 97.5 | cancel |
| Roy-3b | a | from_priming | neutral | `20260523_123525` | 10 | 108.5 | cancel |
| Roy-3b | b | blank | "You are a hungry infant" | `20260523_123713` | 10 | 82.6 | cancel |
| Roy-3b | c | blank | neutral | `20260523_123836` | 10 | 82.9 | cancel |

### Headline pairwise diffs

#### Roy-3a (original holdout)

| Pair | `reward_bias_l2` | `cluster_reward_bias_l2` | (keys) | `causal_link_Δ` | `episodes_Δ` | `valence_KS` (p) | ATL jaccard |
|---|---|---|---|---|---|---|---|
| a_vs_b | 0.0 | **0.0370** | 2 | +340 | +702 | 0.294 (0.077) | 0.047 |
| a_vs_c | 0.0 | **0.0370** | 2 | +330 | +694 | 0.294 (0.020) | 0.054 |
| b_vs_c | 0.0 | 0.0 | 0 | −10 | −8 | 0.000 (1.000) | 0.652 |

#### Roy-3b (engineered overlap)

| Pair | `reward_bias_l2` | `cluster_reward_bias_l2` | (keys) | `causal_link_Δ` | `episodes_Δ` | `valence_KS` (p) | ATL jaccard |
|---|---|---|---|---|---|---|---|
| a_vs_b | 0.0 | **0.1002** | 2 | +302 | +685 | 0.117 (0.998) | 0.047 |
| a_vs_c | 0.0 | **0.1002** | 2 | +303 | +685 | 0.168 (0.928) | 0.043 |
| b_vs_c | 0.0 | 0.0 | 0 | +1 | +0 | 0.111 (1.000) | 0.667 |

### Priming-side substrate (load-bearing finding — addresses both sub-iterations)

The post-priming NAc `cluster_reward_bias` map is structurally different from Roy-2 / Roy-2c era:

| Iteration | priming `cluster_reward_bias` entries | values |
|---|---|---|
| Roy-2 (2026-05-12) | 6 | all `sense_food_source` × +1.0000 |
| Roy-2c (2026-05-13) | 6 | all `sense_food_source` × +1.0000 |
| Roy-4 (2026-05-13) | 6 | all `sense_food_source` × +1.0000 |
| Roy-5a (2026-05-13) | 6 | all `sense_food_source` × +1.0000 |
| **Roy-3a (2026-05-23)** | **2** | `sense_food_source` × {+0.18, +0.98} |
| **Roy-3b (2026-05-23)** | **2** | `sense_food_source` × {+0.21, +0.98} |

Identical multi-arc priming (5 stages × 10 turns each, `cradle_prelinguistic` × 2 / `cradle` × 2 / `cradle_prelinguistic` × 1) under identical substrate-primary AUT mode produced **6 saturated +1.0 clusters across four iterations** through 2026-05-13 then **2 partially-saturated clusters across both Roy-3 sub-iterations** post-2026-05-22. Six wires merged into main between those dates (PRs #253-#257), all targeting 0.9.1's annotation surface. The substrate-primary priming path was not the explicit target of any of these wires; the priming-side regression is a side effect, surfaced by Roy-3.

### Wire-A annotation read at arm-A test time

`get_agent_tool_biases(agent_id="default_agent")` aggregates max(|bias|) across all clusters per tool. After the test-phase tick decay in the arm-A session, the renderable values are:

| Iteration | arm A cluster 1 | arm A cluster 2 | max(|bias|) | Wire-A render |
|---|---|---|---|---|
| Roy-3a | 0.0068 | 0.0364 | **0.036** | `sense_food_source  [neutral / mixed]` |
| Roy-3b | 0.0211 | 0.0979 | **0.098** | `sense_food_source  [neutral / mixed]` |

Per Wire-A's bias-string mapping in [release_0_9_1.md Stage 2](../plans/release_0_9_1.md):
- `bias ≥ 0.5` → "strongly rewarding"
- `0.1 ≤ bias < 0.5` → "mildly rewarding"
- `−0.1 ≤ bias < 0.1` → "neutral / mixed"
- ...

Both arms A render under the "neutral / mixed" band — a structurally-null signal. Arm A is annotated, but the annotation conveys no actionable bias to the LLM proposer. The "strongly rewarding" annotation Wire-A was designed to deliver never materializes because the cluster bias decays to below the 0.1 floor during the test-phase ticks. Arms B and C have empty `cluster_reward_bias` entries (blank substrate) so the annotation section is skipped entirely.

### Test-phase tool distribution (the headline Roy-3 behavioral signal)

#### Roy-3a (original holdout)

```
Arm A (substrate-primed, neutral):
  46× respond
   4× _llm_unavailable
   2× infant_humanoid_pick_up
   1× sense
   1× sense_tools
   1× infant_humanoid_listen
   1× <no-tool>

Arm B (blank, "hungry infant"):
  11× respond
   3× sense
   2× infant_humanoid_pick_up
   1× examine
   1× say
   1× sense_presence
   1× <no-tool>

Arm C (blank, neutral):
  21× respond
   3× sense
   2× infant_humanoid_pick_up
   2× _llm_unavailable
   1× sense_tools
   1× <no-tool>
```

#### Roy-3b (engineered overlap)

```
Arm A (substrate-primed, neutral):
  28× respond
   3× use
   2× infant_humanoid_pick_up
   2× _llm_unavailable
   1× infant_humanoid_use
   1× sense_presence
   1× <no-tool>

Arm B (blank, "hungry infant"):
   4× infant_humanoid_pick_up
   2× use
   2× infant_humanoid_use
   1× sense_tools
   1× respond
   1× _llm_unavailable
   1× <no-tool>

Arm C (blank, neutral):
   4× examine
   3× infant_humanoid_pick_up
   2× eat
   1× respond
   1× _llm_unavailable
   1× <no-tool>
```

**Zero `sense_food_source` calls in any arm of either sub-iteration.** Both pre-registered "annotation reached the LLM proposer's decision pathway" outcomes (Roy-3b: Arm A > B AND > C on `sense_food_source`; Roy-3a: A > C on richer tool-family divergence than Roy-2) **DID NOT happen.**

### JSONL emission summary

Per-iteration `MAXIM_LOG_FILE` JSONL captures the Stage 0c + Wire 3 instrumentation:

| Iteration | NAc_RECOMMEND total | NAc_RECOMMEND passed_gate=True | best_tool of passes | WIRE_3_FILTER events | EC_TRACE linguistic | EC_TRACE drive |
|---|---|---|---|---|---|---|
| Roy-3a | 974 | 811 | all `sense_food_source` | 0 | 292 | 83 |
| Roy-3b | 963 | 800 | all `sense_food_source` | 0 | 186 | 81 |

`WIRE_3_FILTER` count is 0 in both as expected — `bodies/infant_humanoid` has no components below integrity threshold at any point in either run, so Wire 3 has nothing to filter. The instrumentation surface is present and emitting on the priming path (where substrate-primary runs `recommend_action`); the absence of `sense_food_source` at test reflects llm-primary not consuming `recommend_action` proposals, not a missing instrumentation.

The 811 / 800 priming-phase passed_gate hits land on `sense_food_source` correctly — this confirms the substrate-primary AUT proposer was generating viable `sense_food_source` proposals during priming, and the cluster wire was reinforced as designed (just with fewer distinct EC cluster IDs than Roy-2 era).

### Cross-iteration A/B comparison (Roy-2 → Roy-3a; Roy-2pc → Roy-3b)

#### Roy-2 → Roy-3a (post-annotation overhead on the original holdout)

Single-variable target: 0.9.1 annotation wires shipped between the two iterations. Priming and fixture and test-AUT-mode are identical.

| Metric | Roy-2 (pre-annotation) | Roy-3a (post-annotation) | Interpretation |
|---|---|---|---|
| Wall time | 883s | 953s | +8%; annotation rendering adds modest per-prompt overhead |
| Priming `cluster_reward_bias` keys | 6 × +1.0 | **2 × {+0.18, +0.98}** | **Priming-side structural regression.** Same priming, but fewer + weaker cluster bias entries. |
| `cluster_reward_bias_l2` (a_vs_b) | 2.4495 (6 keys) | **0.0370 (2 keys)** | Priming-side regression propagates to pairwise diff. |
| `valence_KS` (a_vs_b, p) | **0.291 (0.023)** | 0.294 (0.077) | Effect size same; p-value drifts above α=0.05 (sample asymmetry similar — episodes Δ +702 vs +659). |
| `valence_KS` (a_vs_c, p) | 0.291 (0.019) | **0.294 (0.020)** | a_vs_c p-value cleanly reproduces below α=0.05. The valence carryover signal Roy-2 first established remains observable. |
| Arm A test-phase tool diversity (excluding respond / _llm_unavailable) | 5 distinct tools (sense / pick_up × 2 variants / say) | 5 distinct tools (sense / sense_tools / pick_up / listen / <no-tool>) | Roughly comparable diversity. Roy-2's "sense + pick_up family" pattern weakened — A now uses sense_tools + listen variants more like Roy-2's arm C. |
| Arm A `respond` count | 17 | **46** | **Arm A shifted toward narrative-verbal responses.** Roy-2's substrate-primed arm used action verbs; Roy-3a's substrate-primed arm defaults to talking. |
| Arm A `sense` family count | 3 sense | 1 sense + 1 sense_tools | Action-verb dominance weakened. |
| Arm C tool family | 5× look + 1× listen + 1× sense_tools | 3× sense + 2× pick_up + 1× sense_tools | Arm C unchanged in shape but the *distinguishing* `look`/`listen` family shrunk to `sense`/`pick_up` — the same family Roy-2 arm A used. **The A-vs-C divergence Roy-2 surfaced is weakened: both arms now run on the same family.** |

#### Roy-2pc → Roy-3b (post-annotation overhead on engineered overlap, plus the test-AUT-mode flip llm-primary)

Two-variable diff: 0.9.1 annotation wires shipped + test AUT mode flipped from substrate-primary (Roy-2pc) to llm-primary (Roy-3b). The cleaner single-variable comparison is Roy-2 → Roy-3a above; Roy-2pc → Roy-3b carries the AUT-mode flip too.

| Metric | Roy-2pc (substrate-primary, pre-annotation) | Roy-3b (llm-primary, post-annotation) |
|---|---|---|
| Wall time | 1502s | 880s |
| Priming `cluster_reward_bias` keys | 6 × +1.0 | 2 × {+0.21, +0.98} |
| Arm A `sense_food_source` count | 0 | **0** |
| Arm A `infant_humanoid_pick_up` count | 2 (both FAILED) | 2 |
| Arm A action verbs other than pick_up | 0 (substrate-primary's hardcoded fallback) | 3× use + 1× infant_humanoid_use + 1× sense_presence |
| Cross-arm convergence on `sense_food_source` | A ≡ B ≡ C (all 0) | **A ≡ B ≡ C (all 0)** |

Arm A's behavior under llm-primary + annotation produces a richer action mix than Roy-2pc's substrate-primary hardcoded fallback, but the engineered-overlap fixture's food-themed percepts do not drive `sense_food_source` even once. **The Wire-A specific test outcome reproduces Roy-2pc's null behavioral signal on the same fixture under llm-primary + annotation.**

## What this proves

The pre-registered **"A ≈ B ≈ C across both fixtures"** outcome reproduces. Zero `sense_food_source` calls across all six arms × two fixtures × three priming-substrate configurations confirms the annotation pattern alone is insufficient to drive arm A's LLM proposer toward the priming-acquired tool.

The disambiguating diagnostic (does the annotation reach the LLM at all?) decodes by three independent observables:

1. **Wire-A's annotation IS attached to the StructuredContext** at LLM-submission time (the agent-loop producer site at [runtime/agent_loop.py:2864](../../src/maxim/runtime/agent_loop.py) runs unconditionally when `_loop_nac` exists and the env-var disable is unset). Both Roy-3a and Roy-3b ran with the env var unset, so the wire is active.
2. **The annotation's signal value is `[neutral / mixed]`** in both arms A — max(|bias|) post-decay is 0.036 (Roy-3a) and 0.098 (Roy-3b), both below the 0.1 "mildly rewarding" floor. The LLM sees an annotation but the annotation says "no notable substrate signal".
3. **The annotation's signal value is null at test time because the priming-side cluster bias is weaker than Roy-2 era.** Same priming arc + same AUT mode + same turn budget produced 2 cluster keys at non-saturated bias in Roy-3 vs 6 cluster keys at +1.0 in Roy-2 / Roy-2c / Roy-4 / Roy-5a. The change correlates with 0.9.1's wire merges; the priming-side regression is not directly caused by Wire-A (which is read-only), so the candidate causes are Wire 1 (variance accumulation in `record_outcome`), Wire 2 (PainBus subscriber + decay path), or some interaction between them.

The annotation-pattern *thesis* (substrate-annotates-LLM-context surfaces signal the cluster-wire consumer cannot) is **not directly falsified** by Roy-3 — the test conditions never gave the LLM a meaningfully-non-null annotation to bias on. The thesis is **untested under the conditions that would actually demonstrate it**. The pre-registered diagnostic correctly identified this as the "investigate prompt rendering / priming-side regressions" branch.

### Definitively proved

- The Wire-A annotation surface is wired end-to-end at the agent-loop producer + prompt-builder composer level.
- The Stage 0c `sim_recommend_action` JSONL emissions fire on every `recommend_action` call (974 / 963 events), including the sub-threshold path (best_tool=<none>, passed_gate=False).
- Wire 3's `WIRE_3_FILTER` emission path is present and silent under an intact body (0 events in both iterations, as designed — `bodies/infant_humanoid` has no damaged components in either Roy-3 run).
- Identical multi-arc priming + identical substrate-primary AUT mode + identical turn budget across Roy-2 → Roy-3 produces structurally different `cluster_reward_bias` outputs (6 saturated keys → 2 partially-saturated keys). The single-seed-iteration cluster wire output was stable across four iterations (Roy-2 / Roy-2c / Roy-4 / Roy-5a, all 6 × +1.0) and changed only after 2026-05-22 wire merges.

### Cleanly refuted

- **"Annotation pattern alone produces cross-arm behavioral divergence on the engineered overlap fixture"** (Roy-3b's pre-registered positive outcome). Zero `sense_food_source` in all three arms.
- **"Annotation pattern compounds Roy-2's salience-mediated signal on the original holdout"** (Roy-3a's pre-registered "richer than Roy-2's tool-family divergence" outcome). Arm A's shift toward `respond` and convergence with arm C on `sense` / `pick_up` family suggests the salience-mediated A-vs-C divergence Roy-2 cleanly surfaced is *weakened*, not augmented, under the annotation overhead.

### Still unfalsified

- **The annotation-pattern thesis itself.** The test conditions did not give the LLM a non-null annotation. A Roy-3 retry where Wire-A renders "strongly rewarding" (priming substrate that saturates cluster bias above 0.5, OR a shorter test phase so decay hasn't crushed the bias, OR a Wire-A bias-strength floor that defers to the raw `cluster_reward_bias` value rather than the post-decay snapshot) would actually test what Wire-A was designed to deliver.
- **Whether the priming-side regression is caused by Wire 1, Wire 2, both, or an interaction.** Determining this requires bisecting the 5/13→5/22 PR window with a Roy-2-shaped iteration on each intermediate commit. Out of Roy-3 scope; tracked as a 1.0 closing item.

## What this means for 0.9.1

0.9.1 still ships per [release_0_9_1.md § "What 0.9.1 does NOT do"](../plans/release_0_9_1.md) framing: the wires close architectural gaps (per-tool risk annotation, Pavlovian valence accumulator, embodiment-state tool filter, agent-wide cluster-bias prompt surface) regardless of persona behavior. The four wires are now load-bearing surface area for 1.1+ work that depends on substrate-annotates-LLM-context infrastructure.

What does NOT ship as part of 0.9.1 disposition:

- **The annotation-pattern as a validated cross-arm behavioral signal.** Roy-3's null result on both fixtures cannot be read as a Wire-A success or failure — only as "the conditions where Wire-A would be expressive were not constructed in this iteration".
- **Closing the persona-convergence research question.** Per the [persona_convergence_crucible.md framing rule](../plans/persona_convergence_crucible.md): "each Roy is an attempt, evaluated honestly on its own terms. An attempt where nothing took is not a failure — it is a finding about which wires or which crucible mechanics need refinement." Roy-3 finds that (a) the priming-side cluster fan-out regressed, (b) the test-phase bias decay floor undercuts Wire-A's rendering window, (c) Roy-2's salience-mediated signal weakened under annotation overhead.

## Implications for v1_refinement.md

Two follow-up items for the 1.0 closing list:

1. **Bisect the priming-side cluster_reward_bias regression** (Wire 1 vs Wire 2 vs interaction). One Roy-2-shaped iteration per intermediate commit between 5/13 (Roy-5a baseline) and 5/22 (Wire 1 merge). ~30-50 min per bisect step at substrate-primary priming wall.
2. **Decide whether Wire-A's render needs a "raw priming snapshot" floor** that defers to the cluster bias at session-end rather than the post-decay snapshot. This would change the wire's semantics from "current substrate-acquired signal" to "ever-acquired substrate signal", with the trade-off that learned aversions could also persist past appropriate extinction. Trade-off worth deciding before 1.0 ships.

Neither blocks 1.0; both go on the 1.0 → 1.1+ trajectory per the foundations doc framing rule.

## What this still does NOT prove

- **Persona convergence at any level.** Roy-3 is a structural-readiness validation; no Roy iteration to date has produced clean cross-session persona persistence (the third question on [persona_convergence_crucible.md § Methodology](../plans/persona_convergence_crucible.md)).
- **That a future Roy iteration with a meaningfully-non-null Wire-A annotation would behave differently.** Pre-registering the next iteration's expected outcomes is the right discipline; reading Roy-3's null result as predictive of failure would be wrong.
- **That the priming-side regression is harmful in production.** The two saturated-vs-partially-saturated clusters still carry the +0.98 / +0.21 bias that drives substrate-primary action selection during priming. The regression's harm is specifically on Wire-A's read window when llm-primary is the test-time mode.

## Reproduction

See [protocols/23_roy_3_reproduction.md](protocols/23_roy_3_reproduction.md).

## Recommendation

**0.9.1 ships unchanged.** Roy-3's diagnostic decodes as "the test conditions did not exercise the conditions the annotation pattern requires", not "the annotation pattern is wrong". The four wires are correctly wired into the agent-loop / prompt-builder surface.

**Next iteration recommendation: Roy-3c-bisect** — one Roy-2-shaped iteration per intermediate PR between 5/13 (Roy-5a) and 5/22 (Wire 1 merge) to root-cause which wire merge introduced the priming-side cluster_reward_bias regression. This is required diagnostic work before 1.0 ships; without root-causing, the regression silently confounds future Roy iterations.

**Secondary:** revisit Wire-A's design point — does the render benefit from a "session-end raw priming snapshot" floor that defers to the cluster bias at priming session end rather than the post-decay arm-A snapshot? The trade-off (Pavlovian aversion / persistent learning vs cleanly extinguishing biases) needs a deliberate decision before 1.1 work.

## Artifacts

- [`~/.maxim/roy/roy-3a/result.json`](/Users/dennyschaedig/.maxim/roy/roy-3a/result.json), [`summary.md`](/Users/dennyschaedig/.maxim/roy/roy-3a/summary.md)
- [`~/.maxim/roy/roy-3b/result.json`](/Users/dennyschaedig/.maxim/roy/roy-3b/result.json), [`summary.md`](/Users/dennyschaedig/.maxim/roy/roy-3b/summary.md)
- JSONL traces: `/tmp/roy_3a_ec_trace.jsonl` (7.9 MB), `/tmp/roy_3b_ec_trace.jsonl` (7.4 MB). Run logs: `/tmp/roy_3a_run.log`, `/tmp/roy_3b_run.log`.
- Specs: [`roy_3a_iteration.yaml`](../../scenarios/roy/roy_3a_iteration.yaml) · [`roy_3b_iteration.yaml`](../../scenarios/roy/roy_3b_iteration.yaml).
- Fixtures: [`roy_1_holdout.yaml`](../../scenarios/roy/roy_1_holdout.yaml) (reused unchanged from Roy-1a) · [`roy_2pc_holdout.yaml`](../../scenarios/roy/roy_2pc_holdout.yaml) (reused unchanged from Roy-2pc / Roy-2c / Roy-4 / Roy-5a).

## Retroactive correction (2026-05-27, [32_wire_a_post_w1_w2.md](32_wire_a_post_w1_w2.md))

The W1+W2 integration test surfaced a Roy cross-session agent_id mismatch (exp 32 Bug A) that was already in effect during Roy-3a + Roy-3b. Priming MemoryHub defaulted to `agent_id="default_agent"`; the test-arm AUT used `agent_id="sim_aut"` per [orchestrator.py:534](../../src/maxim/simulation/orchestrator.py); `NAc.get_agent_tool_biases(agent_id="sim_aut")` returned `[]` because the strict-equality filter at [nac.py:1903](../../src/maxim/decisions/nac.py) didn't cross the priming/test agent_id boundary.

**The claims "Arm A is annotated, but the annotation conveys no actionable bias to the LLM proposer" and "Wire-A's annotation IS attached to the StructuredContext at LLM-submission time" need to be re-read with the bug in scope.** The agent-loop producer DID run unconditionally (`_loop_nac is not None`, env-var not set); however the *content* the producer attached was `context.cluster_bias_annotations = []` (Wire-A's strict-equality lookup returned empty), and the prompt-builder composer at [agent_loop.py:2864](../../src/maxim/runtime/agent_loop.py) renders an empty section to `""`. The LLM proposer never saw a `[neutral / mixed]` annotation — it saw NO annotation section at all. The max\|bias\| of 0.036 / 0.098 reported in the original verdict is independently valid (calculated post-hoc from the persisted `aut_nac.json` dump, not from `get_agent_tool_biases`), but conflating "bias below the rendering floor" with "annotation rendered as neutral" was an inference error caused by the wiring bug.

What stays valid from this experiment:
- **Zero `sense_food_source` calls across all arms** — behavioral measurement, valid.
- **Priming-side cluster_reward_bias regression** (6 saturated → 2 partial) — independent of Bug A, regression-mechanism still applies.
- **Roy-2 → Roy-3 valence_KS collapse** (p=0.023 → p=0.998) — independent.
- **Stage 0c `sim_recommend_action` JSONL emission count and shape** — independent (recommend_action runs in priming, not in the bug-affected test-arm path).

What needs re-validation after Fix A (exp 32 Bug A fix) lands:
- Whether Wire-A's annotation EVER reaches the LLM in the test arm with non-empty content.
- Whether the "test conditions did not exercise the conditions the annotation pattern requires" recommendation still holds, OR whether the conditions were never tested because the instrument was broken.
