diff --git a/docs/research/2026-07-30-265-capture-bisect.md b/docs/research/2026-07-30-265-capture-bisect.md new file mode 100644 index 00000000..819e7a1a --- /dev/null +++ b/docs/research/2026-07-30-265-capture-bisect.md @@ -0,0 +1,437 @@ +# #265 capture-driven bisection — steep-slope response family + +**Status: verdict reached, S1 and S2 both CLEARED for the two concrete +mined events; real mechanism identified as a pre-existing (frozen-phase) +architecture, not a Campaign P regression.** This is a research/bisection +pass; no production code was changed. The harness (committed, +`tests/AcDream.Core.Tests/Physics/Issue265SteepSlopeCaptureBisectTests.cs`) +and mining tool (`tools/analyze_265_steep_slope_capture.py`) are permanent; +the A/B code toggles described below were applied and reverted locally and +never committed. + +## 0. Scope recap + +Issue #265 (`docs/ISSUES.md`): after the TS-4-removal-then-revert +(`2e27d066`+`a8a7d64b`), the live matrix gate (2026-07-30, scenarios 4/5) +found three symptoms: (a) jumping INTO an uphill slope bounces (retail does +not), (b) house-roof slides no longer happen, (c) occasional +stuck-sliding-on-an-edge. Two remaining Campaign-P suspects were named: + +- **S1** — `db2889af` ("#116 shape-1"): `BSPQuery.cs` Path-6's `hasSphere1` + (head-sphere-only hit while airborne, foot sphere clear) branch changed + from a steepness-gated dual path (steep → slide-tangent-then-`Slid`; + shallow → `SetCollide`+`Adjusted`) to an unconditional + `SetCollisionNormal` + `return Collided`. +- **S2** — the AP-7 `calc_friction` threshold rewrite (merge `26e0334a`): + `0.0` → `0.25`, unconditional into-plane velocity subtraction past the + threshold. + +## 1. Segment mining + +Captures used: `artifacts/matrix-session2-resolve.jsonl` (15,726 records, +copied from the coordinator worktree's `artifacts/matrix-session2-resolve.jsonl`) +and `artifacts/matrix-session3-resolve.jsonl` (12,145 records at copy time). + +**Session3 is not usable.** Every one of its 12,145 records shows the +identical position `(60.372223, 9.071998, 79.344925)`, zero velocity, and +`transientState=3` (Contact|OnWalkable) from tick 0 to tick 12144 — the +player was standing perfectly still (likely AFK / alt-tabbed) for the +entire ~7.2-minute capture window. It contains no motion at all and was +excluded from further analysis. + +### 1.1 First pass — strict signature scan (`tools/analyze_265_steep_slope_capture.py`) + +Two signatures were scanned for directly on the JSONL fields: + +- **Signature A (uphill-jump bounce)**: an airborne record (`bodyBefore` + Contact bit clear) with `result.collisionNormalValid=true` and a "steep, + non-floor, non-wall" normal (`0.02 < normal.Z < FloorZ=0.6642`), followed + by a next-tick upward jump in `bodyBefore.velocity.z` while still + airborne. +- **Signature B (lost-slide / edge-wedge)**: ≥6 consecutive ticks with + Contact set but OnWalkable clear (resting against a non-walkable steep + surface), a non-trivial requested move each tick, and near-zero net + advance. + +**Result: 0 hits for both signatures, in both files.** Session2 has only +38 `collisionNormalValid=true` records total (out of 15,726), and every one +of them has `normal.Z` in the `[0.85, 1.0]` bucket — i.e. every reported +collision normal in this capture is CLOSE TO FLAT/floor-like, never in the +"genuinely steep" `< FloorZ` band my first-pass signature targeted. This is +an honest negative result for the specific "steep" heuristic; the actual +symptom-bearing frames, mined below, are moderate-angle (Z≈0.86–0.95, +above `FloorZ` — walkable BY THE THRESHOLD) and are found by a different +signature. + +### 1.2 Second pass — velocity-annihilation scan + +Widened the signature to "a tick where `|horizontal velocity|` before is +`>2 m/s` and after (next tick) is `<0.05 m/s`, then how long the position +stays frozen afterward." This found exactly **two events**, both in +session2: + +| idx (0-based) | tick | `|v_horiz|` before | frozen for | cell | +|---|---|---|---|---| +| 3153 | 3153 | 8.90 m/s | 46+ ticks (session2 continues past it; not EOF) | `0xAAB30007` | +| **3434** | **3434** | **18.00 m/s** | **12,292 ticks — to EOF** | `0xAAB40011` | + +**Event at idx 3433/3434 (the primary oracle for this pass), full trace** +(`records[3415..3434+41]`, printed via ad-hoc Python — see +`tools/analyze_265_steep_slope_capture.py` for the reusable scanner): + +- Ticks 3415–3432: clean ballistic fall. `vBefore = (11.15, 14.13, vz)` + with `vz` accumulating from ‑16.72 to ‑23.14 (pure gravity, no further + horizontal drive — a jump/leap with residual momentum, exactly the kind + of trajectory the oracle plan's §1.3 predicted would NOT hit the + TS-4 degenerate case). `Z` falls from 92.79 to 79.90. +- **Tick 3433 (the landing):** `result.collisionNormalValid=true`, + `result.collisionNormal=(0.2857143, 0.42857143, 0.85714287)` — exactly + `(2,3,6)/7`, a REAL polygon normal (not the `UnitZ` degenerate default). + `result.isOnGround=true`. `bodyAfter.contactPlaneValid=true`, + `bodyAfter.walkablePolygonValid=true`, `bodyAfter.walkableVertices` = the + triangle `(240,0,88), (264,0,80), (264,24,68)` — `normal.Z=0.857`, well + ABOVE `PhysicsGlobals.FloorZ` (0.6642): **a legitimately walkable roof + slope, not the "steep, non-walkable" case either S1 or the original TS-4 + shortcut ever targeted.** `bodyAfter.velocity` is UNCHANGED + `(11.15,14.13,‑23.14)` — confirms (see `PhysicsEngine.cs:1489`, capture + fires inside `ResolveWithTransition`, before any caller-side velocity + response) that neither `calc_friction` nor `HandleAllCollisions` ran yet. +- **Tick 3434 (the very next resolve call):** `input.currentPos == + input.targetPos` (ZERO requested motion this tick — the previous frame's + integration already produced zero displacement). `bodyBefore.velocity = + (0, 0, 0)` — **already fully zeroed by the time THIS resolve call even + starts.** `transientState=7` (Contact|OnWalkable|Sliding). Every + subsequent record (12,292 of them, to the literal end of the file) is + byte-identical: same position, same zero velocity, same + `transientState=7`. + +**Event at idx 3152/3153** is the same shape at a shallower ~18° roof edge +(`normal≈(0,0.32,0.95)`): the player glides/climbs cleanly along the edge +for ~140 ticks (idx 3016–3152, gaining ~10 m of Z — this portion is +healthy behavior), then at idx 3153 horizontal velocity is forced to +exactly zero in one tick and the position freezes for the rest of the +examined window. + +**Both events are the SAME shape**: a real, correct, non-default collision +normal is recorded on the landing tick; on the very next tick the mover's +full horizontal velocity has already vanished and the position never +changes again. This is what the user experiences as "roof slides no +longer happen" (symptom b) and "occasional stuck-sliding-on-an-edge" +(symptom c). Neither event's landing surface is steep by `FloorZ` — both +are moderate, walkable-by-threshold roof pitches. + +## 2. Replay harness + +`Issue265SteepSlopeCaptureBisectTests.cs` builds a synthetic +`PhysicsEngine` containing ONE polygon — the exact real triangle recovered +from record 3433's `bodyAfter.walkableVertices` — registered via +`ShadowObjectRegistry`, then replays the EXACT real captured ballistic +state (position + velocity, record index 3415) forward with real gravity +at 30 Hz, calling `PhysicsEngine.ResolveWithTransition` every tick exactly +like `PlayerMovementController` does at the Core boundary. Once the mover +reports `IsOnGround`, the harness keeps REQUESTING the same forward +velocity every tick (simulating held input) — this is deliberate: it turns +the harness from "replay what the live game did" (which trivially +reproduces the freeze, since the live game's own subsequent inputs were +already zero — see §4) into "does the physics engine itself allow +continued advance across this surface," which is the actual question S1 +and S2 bear on. + +### 2.1 Harness commissioning (three real bugs found and fixed while building it — kept as code comments) + +1. `ShadowObjectRegistry.Register`'s broad-phase culls by distance from + `worldPos`. Registering at the literal real-world coordinates (X≈256) + while querying at world origin put the polygon ~264 units away — the + very first run found **zero collisions at all**. Fixed by re-anchoring + the whole synthetic scene (triangle + approach trajectory) at the + triangle's centroid. +2. `CellTransit.BuildShadowCellSet`'s outdoor flood + (`CellTransit.AddAllOutsideCells`) treats world position as + landblock-local (an anchor-frame convention shared with + `Ts4SteepRoofWedgeCaptureTests`/`DoorBugTrajectoryReplayTests`, active + whenever `CellGraph.TryGetTerrainOrigin` has no real terrain to + consult). The real-world coordinates (X≈256) are outside the valid + `[0,192)` per-landblock range even after centroid re-anchoring picked a + bad cell id — still zero collisions. +3. `LandDefs.AdjustToOutside` (inside the flood) **silently re-derives** + the actual `(lx,ly)` grid cell from the sphere's real position and + corrects a mismatched seed rather than honoring the literal + `seedCellId` passed to `Register` — an arbitrary chosen cell id + (`0x00000011`) registered successfully (`TotalRegistered=1`) but + `GetObjectsInCell(0x00000011)` came back empty; the entity had actually + landed in cell `0x00000001` (the canonical grid-(0,0) cell, matching + the re-anchored centroid). Switching the harness's cell id to + `0x00000001` fixed it. + +These are documented in the test file's code comments in case another +harness hits the same three traps. + +## 3. A/B outcomes + +### (i) HEAD vs (ii) S1-reverted + +The S1 revert (`BSPQuery.cs`'s `hasSphere1` branch restored to the +pre-`db2889af` steepness-gated dual path, mirroring the still-current +`sphere0` branch — applied locally, verified `git diff --stat` clean +before and after, never committed) produced **byte-identical output** to +HEAD for the full 80-tick replay: same landing tick (19), same clean +44-tick glide (ticks 20–62, `adv=0.5149` every tick, `cnv=false`), same +freeze at tick 63 (`adv=0.0000` for 16+ consecutive ticks, capped by the +harness's tick budget — it would continue indefinitely), same recorded +`collisionNormal=(-0.958,0.128,0.256)` from that point on. + +**Why they're identical — confirmed by diagnostic instrumentation** +(`PhysicsDiagnostics.ProbeIndoorBspEnabled`/`ProbeBuildingEnabled`, the +`[path-dispatch]`/`[path5-diag]` probes `db2889af` itself added): across +the entire 80-tick replay, **`hit1=True` never appears once.** The two +`[path-dispatch] ... collide=True ... contact=False ...` lines (Path 6 +firing during the airborne approach) are followed by +`insertType=Placement` (Phase 3's walkable-landing retry succeeding) — +this is the STILL-UNCHANGED `sphere0` (foot) branch's graceful +`SetCollide`→`Adjusted`→Phase-3-Placement chain, not the `hasSphere1` +branch S1 touched. Once grounded, every subsequent Path-5 dispatch reports +`hit0=False hitPoly0=False` then `hit1=False hitPoly1=False` — a genuinely +clean glide with no collision at all, which is why the S1 edit (which only +fires inside `if (hit1 || hitPoly1 is not null)`) never executes for this +trajectory. **S1's site is provably unreached by the real mined +trajectory that produced the freeze.** Reverting code that never runs +cannot change the outcome — this is not a coincidence, it's the direct +mechanical explanation. + +### (iii) S2 toggle + +**Not run as a harness A/B — proven inert by static analysis instead.** +`grep -rn "\.calc_friction(" src/` returns **zero production call sites** — +the only callers of `PhysicsBody.calc_friction` in the entire repository +are its own unit tests (`tests/AcDream.Core.Tests/Physics/PhysicsBodyTests.cs`). +`PlayerMovementController.cs` mentions it only in a code comment +(line ~2021, "friction next frame") — it is never invoked. Neither +`ResolveWithTransition` nor `PlayerMovementController`'s tick loop calls +`calc_friction` anywhere. **S2's threshold value (0.0 vs 0.25) cannot +affect any live or replayed behavior, full stop** — there is no toggle to +run because there is no live code path to toggle. + +### (iv) Both reverted + +Follows immediately from (ii) and (iii): with S1 reverted producing +byte-identical output to HEAD, and S2 provably inert, the "both" variant +is mathematically identical to (ii), which is identical to (i). No +separate run was needed. + +### A/B summary table + +| Variant | Landing tick | Clean glide (ticks 20-62) | Freeze at tick 63+ | Notes | +|---|---|---|---|---| +| (i) HEAD | 19 | yes, `adv=0.5149`/tick | yes, frozen forever | `hit1` never true | +| (ii) S1 reverted | 19 (identical) | yes (identical) | yes (identical) | S1's branch unreached | +| (iii) S2 toggle | n/a | n/a | n/a | dead code, no call sites | +| (iv) both | 19 (identical) | yes (identical) | yes (identical) | follows from (ii)+(iii) | + +## 4. The actual mechanism (found by hand-tracing the live capture against `PlayerMovementController.cs`, independently confirming it explains BOTH mined freeze events exactly) + +Neither S1 nor S2 touch velocity. The full-zero-in-one-tick signature +(§1.2) is produced by two pre-existing, Campaign-P-independent pieces +working in sequence: + +1. **The landing tick** (`PlayerMovementController.cs`, the + `if (resolveResult.IsOnGround && _body.Velocity.Z <= 0f)` block): + Contact+OnWalkable are set, and — because `Velocity.Z < 0` — ONLY the + Z component is hand-zeroed: velocity becomes `(11.15, 14.13, 0)`. + `PhysicsObjUpdate.HandleAllCollisions` then runs with `shouldReflect = + true` (the mover was airborne the frame before: `prevOnWalkable=false` + makes `shouldReflect` unconditionally true regardless of the new + grounded state — see `PhysicsObjUpdate.cs:163-164`). But + `dot(velocity, collisionNormal) = dot((11.15,14.13,0), + (0.286,0.429,0.857)) ≈ +9.25` — POSITIVE (moving away from, not into, + the surface, because the Z component that would have made it negative + was just zeroed) — so the `if (dot < 0f)` reflection guard + (`PhysicsObjUpdate.cs:177`) never fires. Velocity survives this tick as + `(11.15, 14.13, 0)`. +2. **The very next tick** (`PlayerMovementController.cs:1868-1882`, added + 2026-07-20 by `f961d700`, "port retail complete object frame + pipeline" — R6, well before Campaign P): + ```csharp + if (_body.OnWalkable) + { + float savedWorldVz = _body.Velocity.Z; + if (hasAnimationRootMotion) + { + _body.Velocity = new Vector3(0f, 0f, savedWorldVz); + } + ... + } + ``` + `OnWalkable` is now true (set last tick), so this runs UNCONDITIONALLY, + EVERY tick, for as long as the mover stays grounded: it zeros + `Velocity.X/Y` to exactly zero (`savedWorldVz` is already 0 from step + 1), replacing physics-integrated horizontal velocity with + animation-root-motion-driven displacement (`pmDelta.Origin`, populated + from `_advanceAnimationRootMotion`, which only produces nonzero + displacement when a movement key is actually held). **With no key held + at the instant of landing, `pmDelta.Origin` stays `Vector3.Zero` forever, + and the mover never advances again.** This reproduces `bodyBefore.velocity + = (0,0,0)` at record 3434 exactly, and the permanent freeze that follows. + +This is the R6 "local player animation-owned grounded movement" +architecture: once grounded, walking is driven entirely by held-input + +animation root motion, not by integrating `Velocity`. It has been in +place since 2026-07-20 — **ten days before Campaign P and the TS-4 +removal/revert (2026-07-29/30)** — and is explicitly a frozen-phase +architecture per the milestones doc (R6 shipped; the freeze list bars +rework without a dedicated brainstorm). It is retail-DIVERGENT in one +specific way that matters here: retail does not need a held key to carry +residual momentum across a landing — a fast fall onto a walkable-but- +sloped surface should glide/sled per `docs/ISSUES.md` #166 ("Slope-landing +glide + bounce absent... acdream lands clean and dead"), which is filed, +open, and explicitly OUT OF SCOPE for this pass (the `Sledding` +`PhysicsStateFlags` bit that would let `calc_friction`'s Sledding-gated +overrides engage is never set anywhere in the codebase — a separate, +already-tracked gap). + +`git log --oneline -3 -- src/AcDream.Core/Physics/PhysicsObjUpdate.cs` +confirms `HandleAllCollisions` itself was also last touched by an +unrelated water fix (AP-10, `cc8d57a2`) — Campaign P did not modify it +either. + +## 5. Re-reading the oracle plan's S1 claim against the mined evidence + +The task asked specifically: if S1's port is faithful but its SCOPE is +wrong, say exactly that. Re-checked against +`docs/research/2026-07-30-ts4-116-oracle-plan.md` §2.3-§2.4 and §3, plus +this pass's own finding: + +- **S1's port IS faithful in isolation.** Its cited sources + (`acclient_2013_pseudo_c.txt:323824-323834`, ACE `BSPTree.cs:221-230`) + are an exact structural match — not a BN misdecompile, not a citation + error. This was independently re-verified by reading the current + `BSPQuery.cs:2259-2302` against the same two sources again this pass; no + discrepancy found. +- **S1's scope is narrower than any symptom this pass could reproduce — + not wider.** The oracle plan's own Addendum 2 (§"implementation + session") already found this exact pattern once, for the door + tick-22760 capture: the hypothesis assumed the "not-yet-in-Contact" + branch would fire, but the mover was actually GROUNDED (`Contact` set), + so dispatch went to Path 5 instead and S1's site was never reached. This + pass finds the SAME pattern a second, independent time, for a + DIFFERENT capture (a genuine airborne fall, not a grounded door-push): + the foot sphere (`sphere0`) reaches the rising/sloped polygon at the + same moment as or before the head sphere, so the `if (hit0 || + hitPoly0 is not null)` branch above `hasSphere1`'s check fires first and + RETURNS before `hasSphere1`'s block is ever entered + (`BSPQuery.cs:2188` gates the whole `hasSphere1` block behind falling + through that first `if`). `hit1=True` never appears once across the + entire 80-tick replay, confirming this mechanically, not just by + inference. +- **Two independent capture families (a grounded door-push, and now an + airborne fall-and-land) both show S1's site going unreached.** This + strongly suggests S1's real-world reach is much narrower than its + authors worried — for it to matter, a trajectory would need the FOOT + sphere to stay clear while the HEAD sphere alone grazes a polygon + during an airborne (not-yet-grounded) frame — e.g. jumping up under an + overhang, or clipping a roof's underside while airborne with the feet + still below the eave line. **Neither of #265's two concrete mined + freeze events is that geometry.** S1 remains a real, citable, retail- + faithful port-accuracy improvement and should NOT be reverted on this + evidence (it fixes a genuine, if narrow, divergence for whenever its + exact geometry does occur) — but it is not implicated in the symptoms + #265 was filed against. + +## 6. Named culprit + +**Neither S1 nor S2. This is S3 — but not a NEW regression: it is the +pre-existing, frozen-phase R6 "grounded movement is animation-root-motion- +owned" architecture (`PlayerMovementController.cs:1868-1882`, landed +2026-07-20 via `f961d700`, ten days before Campaign P), which +unconditionally zeros the mover's horizontal `Velocity` every tick once +`OnWalkable` is true, with no gate on approach speed, surface steepness, +or how the mover became grounded.** It was mechanically traced, tick by +tick, against BOTH of #265's concrete mined freeze events and reproduces +the observed `(0,0,0)` velocity and permanent position-freeze exactly. + +This explains symptom (b) (roof slides don't continue — there is no +"continue," walking requires a held key that landing doesn't supply) and +symptom (c) (stuck at the landing spot indefinitely) completely, for both +mined events. It does **not**, by itself, explain symptom (a) (the +"bounce" on jumping into an uphill slope) — that is a property of +`PhysicsObjUpdate.HandleAllCollisions`'s elastic reflection (`shouldReflect += true` whenever the mover was NOT already on walkable ground before AND +after the resolve — `PhysicsObjUpdate.cs:163-164`), which is ALSO +pre-existing (from the #182 rebuild, well before Campaign P) and fires for +ANY valid `CollisionNormal` reported while airborne, regardless of which +BSPQuery branch produced it. This pass did not find or replay a concrete +"bounce" event in the captures (the closest analogue — the tick-63 +edge-freeze in the replay harness — shows a suspicious secondary normal, +`(-0.958,0.128,0.256)`, unrelated to the registered polygon's own plane +normal, with Path-5 diagnostics showing no fresh BSP hit during the frozen +ticks; this smells like stale `ContactPlane`/`CollisionNormal` persistence +at a polygon boundary rather than a fresh reflection, and — like the S1 +revert — was unaffected by reverting S1. It is flagged as a genuine open +question, not resolved this pass, and may be an artifact of this +harness's single small (24-unit) synthetic triangle rather than a general +production bug; a real roof's continuous mesh would not present a "run off +the edge of a 24-unit patch" boundary at all. See §7). + +## 7. What's still open (do not guess, per CLAUDE.md) + +1. **Why does the user perceive this as a NEW regression coinciding with + Campaign P**, if the freeze mechanism (§4) predates it by ten days and + is unaffected by S1/S2? Two honest hypotheses, neither confirmed: + (a) the roof-jump/fall scenario was specifically exercised for the + FIRST time as part of the Campaign P visual matrix (scenarios 4/5), + surfacing a pre-existing bug rather than a new one; (b) a genuinely + separate, not-yet-isolated interaction exists. Resolving this needs + either a live retail-vs-acdream side-by-side of the EXACT same + fall-and-land-with-no-input scenario pre-Campaign-P (to confirm the + freeze is not new), or a fresh capture of the user's ACTUAL "roof + slide" repro (holding a movement key throughout, not a passive fall) to + see whether the animation-root-motion path (which DOES produce + displacement while a key is held) also fails. +2. **The tick-63 edge freeze** in this pass's own harness (§6, closing + parenthetical) — a `CollisionNormal` unrelated to the registered + polygon's plane, reported while Path-5 diagnostics show no fresh hit. + Candidate next step: extend the harness's synthetic roof to several + contiguous polygons (removing the small-triangle-edge artifact) and + re-run; if the freeze persists on a much larger interior region, it is + a real, separate, third mechanism worth its own root-cause pass + (possibly `SpherePath.PrecipiceSlide`'s edge-crossing test, or stale + `LastKnownContactPlane` persistence — NOT yet confirmed, do not guess + further). +3. **Symptom (a)'s bounce** was analyzed only by static code reading + (`HandleAllCollisions`'s reflection math), not independently reproduced + against a live-captured bounce event — none of the 38 + `collisionNormalValid=true` records in session2 showed the "airborne, + then a large upward `Velocity.Z` jump next tick" signature this pass's + Signature-A scanner looked for. A fresh capture specifically of a + jump-into-an-upward-slope repro (ideally with `ACDREAM_PROBE_RESOLVE=1` + or `ACDREAM_CAPTURE_RESOLVE` active for the WHOLE approach, not just + the moment of impact) would let Signature A actually fire and give a + concrete oracle the way records 3433/3434 did for the freeze. + +## 8. Recommended fix direction + +**Do not touch S1** (`BSPQuery.cs`'s `hasSphere1` branch) — it is a real, +narrow, retail-faithful improvement unrelated to #265's two concrete mined +events; reverting it would only reopen the #116 shape-1 door-collision gap +it was written to close, for zero benefit here. + +**Do not spend further effort on S2** (`calc_friction`'s threshold) until +it is actually wired into a live code path — right now changing it changes +nothing observable, in either direction. If/when `calc_friction` IS wired +into `PlayerMovementController` (a legitimate future piece of closing #166, +the downhill-sled issue), the 0.25 threshold becomes live and worth +re-testing at that point, not before. + +**The real target is #166 + the grounded-movement architecture (§4/§6), +which is a frozen-phase design question, not a quick fix.** Per CLAUDE.md's +"the roadmap and the observed bug disagree → brainstorm before writing +code" rule, this needs `superpowers:brainstorming` before any +implementation: does acdream want a genuine physics-driven momentum carry +across a landing (porting the retail `Sledding` state + a real +`calc_friction` wiring), or a narrower "if IsOnGround at high incoming +speed, force a minimum coast distance regardless of held input" patch? The +former is retail-faithful and already has a filed target (#166); the +latter would be a new, unfiled design decision. Either way, this is +explicitly NOT an S1/S2 code change — it is new work against +`PlayerMovementController.cs`'s grounded-movement block and +`PhysicsBody.calc_friction`'s wiring, gated on a design conversation, not a +revert.