Commit Graph
2352 Commits
Author SHA1 Message Date
Jonas Volman 4f4c85021d Reconcile Lane C with canonical house-rule audit
Integrate verified source and data progress; exclude unproven COMMON ownership, held UB/asm forms, and address-named data credit. Stable sweep: 23 exact functions gained, zero lost.
2026-09-22 23:12:40 -07:00
Jonas Volman a01115aaf5 Integrate verified Claude Lane B campaign
# Conflicts:
#	config/parked.json
2026-09-21 20:20:30 -07:00
Jonas VolmanandClaude Fable 5.1 ccf3b4125d Lane B wave 3: path.c complete - three strict-exact functions (+2,783 meaningful / +2,800 padded)
New strict exact (function board for ai/path is now 32/32):
  ai/path :: _path_state_traverse             2096
  ai/path :: _path_state_estimated_distance    496
  ai/path :: _closest_point_to_attractor       208

This is the owner's all-inlined COMDAT admission (2026-09-21: "admit the class
... genuine shared-header __inline, byte-identical COMDAT, strict-exact caller,
full ownership/build sweep, zero regressions. No hand expansions.") doing what
it was for: path.c HAND-EXPANDED point_from_line3d at three sites because the
old emission guard forbade the real helper, and those sites were the whole
residue. The three hand expansions are replaced by the REAL
point_from_line3d(...) call. path.obj therefore newly emits _point_from_line3d
(48 B, math/real_math.h, byte-identical to January's single folded copy credited
to ai/action_charge); all three callers are strict exact. admit_check.py: PASS.

Other source changes, each read from the first-party /Od build first and then
confirmed in January's bytes:
- _path_state_estimated_distance: a genuine reconstruction BUG. After the first
  walk /Od does `current_node_index = child_node_index;` (0x4c657e) and the
  second walk tests and advances current_node_index, as does the `?:`. We
  iterated child_node_index. With the copy, January's spilled result byte web and
  its EDI/ESI/EBX assignment appear - every row of the previous ledger. Exact on
  the first compile.
- _path_state_traverse: `closest_distance < 4.0f` (was distance_to_destination;
  /Od 0x4c76ee compares closest_distance); the second collision_bsp pointer is a
  block-scoped local; and the local below.

DISCLOSURE for owner awareness - a local that is written once and never read:
    struct path_node previous_node_values;  ...  previous_node_values = *new_node;
It is a first-party debugging snapshot, NOT an invention: the /Od RTC descriptor
NAMES it (`previous_node_values`, size 68 = sizeof(struct path_node), [ebp-0x8ec])
and /Od does the `rep movsd` right after path_get_node. VC7 deletes the copy in
the optimised build (no rep movs, frame 0x85c unchanged), but the extra IL local
is what yields January's x87 ordering at 0x2cc-0x2d8; January also shows a
constant +1 assert-line drift across this span, consistent with extra source
lines here. Strip test on the final file: without it the function is [sha].
Same class as `jet_marker_count` in 01e16607. Revert on request.

No new representation-punning casts (castcheck real=1 landing=1). Byte-inert
/Od restructurings the worker found were deliberately NOT applied (not
confirmable in January's bytes).

Config: config/parked.json - the three path entries removed (now exact).
Parks 168 / 0 / 0.

Verification: full ninja all_source progress semantic_progress pass (Code
1456768 -> 1459551, 7312 -> 7315). stable_verdicts vs the FROZEN baseline:
gained 24 / 15744 bytes, regressions 0. All 11,060 function and 1,706 section
report rows: 0 lost 100%, 0 missing. Fake scan 0 leads, line endings preserved,
git diff --check clean.
Reviewer: the wave was stopped on the owner's token budget before a reviewer
agent ran, so the ORCHESTRATOR reviewed this packet itself: re-gated from
scratch, read the full 22-line diff, re-derived the RTC attestation from its own
/Od dump, and ran admit_check / castcheck / fake scan / unitcheck.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 12:17:02 -07:00
Jonas VolmanandClaude Fable 5.1 3ba2eb9106 Lane B: three exact functions under the owner's per-site rule-24 admission (+2,802 meaningful / +2,816 padded)
New strict exact:
  ai/path_structure_bsp :: _structure_test_line2d     1104  (object now 6/6)
  ai/action_obey        :: _action_obey_control         992  (object now 27/27)
  ai/actions            :: _actor_action_try_to_dive    720

OWNER ADMISSION (2026-09-21), quoted: "Admit only with per-site /Od attestation,
byte-inert cast, compatible prefix layout, strict exactness, and ledger
disclosure. Land the three; keep the fighter held."

DISCLOSURE - every NEW 3D->2D view cast in this commit, with the first-party
/Od + /RTC build (halo_cache_symbols.exe) instructions that attest the SAME
punned object at the SAME site. The punning is Bungie's: C cannot pass or copy a
3D object as a 2D one without it.

1. _actor_action_try_to_dive - ONE cast:
     dot_product2d(&left_animation_alignment, (real_vector2d const *)&actor->input.facing_vector)
   /Od fn 0x42c1f0:  0042c3e6 mov edx,[ebp-8]      ; actor
                     0042c3e9 add edx,0x140        ; &actor->input.facing_vector (real_vector3d)
                     0042c3ef push edx
                     0042c3f0 call -> fn 0x42dd30
   and fn 0x42dd30 is dot_product2d: a[0]*b[0] + a[1]*b[1], two components only.
   Byte-inert: with the cast deleted the TU compiles (C4133) and the function is
   STILL strict exact 720.
2. _structure_test_line2d - FOUR casts, (real_point2d const *)&vertexN->point:
     vector_from_points2d(v0, v1, &e0e1); (v0, p1, &e0p1); (p0, v0, &p0e0); (p0, v1, &p0e1)
   /Od fn 0x4cee00, call sites 0x4cefb6 / 0x4cefca / 0x4cefe1 / 0x4ceff8: the
   vertex pointer locals [ebp-0x5c] and [ebp-0x60] (results of
   tag_block_get_element; the 3D point is at offset 0) are pushed DIRECTLY as the
   2D point arguments, in exactly that argument order, to fn 0x42e350 =
   vector_from_points2d (two 2-component subtractions).
   Byte-inert: with the five casts deleted (operands kept) the function is STILL
   strict exact 1104.
3. _action_obey_control - ONE cast, an aggregate COPY rather than an argument:
     alignment_vector = *(real_vector2d const *)&actor->input.facing_vector;
   /Od fn 0x43c2d0:  0043c7c7 mov eax,[ebp-8]        ; actor
                     0043c7ca mov ecx,[eax+0x140]
                     0043c7d0 mov edx,[eax+0x144]
                     0043c7d6 mov [ebp-0x2c],ecx
                     0043c7d9 mov [ebp-0x28],edx
                     0043c7dc lea eax,[ebp-0x2c] / push eax / call normalize2d
   Integer mov pairs = an 8-byte STRUCT ASSIGNMENT out of the 3D vector straight
   into the one 2D local that is then normalised (member-wise real copies compile
   to movss at /Od, and there is no intermediate temp). This replaces our
   member-wise copy through a `facing` temporary.
   Byte-inertness caveat, stated plainly: this cast cannot be strip-tested - C
   rejects an aggregate assignment across types outright - so the showing here is
   the /Od aggregate copy plus the fact that a pointer cast emits no code. The
   reviewer measured every undecorated alternative (.i/.j, .j/.i,
   set_real_vector2d, bound 3D pointer member-wise) as residual.
Compatible prefix layout (math/real_math.h): real_vector2d is `real n[2]`,
real_vector3d `real n[3]`; real_point2d `real n[2]`, real_point3d `real n[3]` -
each 2D union is exactly the first 8 bytes of its 3D counterpart.
The idiom already appears 72 times in 21 canonical files, incl. math/real_math.c.

Other source evidence:
- _structure_test_line2d: both loop heads are `mov 1 / test / je` in /Od
  (while (TRUE)); the inner break lands on the outer back-jump so the tail sits
  inside the inner loop; two blocked copies. Depends on the assert-spacing
  relocation identity taken from the data lane in 7583df5d.
- _actor_action_try_to_dive: the /Od RTC descriptor lists an extra 8-byte
  aggregate `left_animation_alignment` built by set_real_vector2d(-facing.j,
  facing.i) and fed to a real dot_product2d call; without it the body is residual.

STILL HELD: _update_alien_fighter_physics_new (1,088, exact in scratch): its
cross_product2d view-cast site has not been /Od-attested; owner: keep it held.

Config: config/parked.json - _structure_test_line2d and
_actor_action_try_to_dive entries removed (now exact). Parks 171 / 0 / 0.

Verification: full ninja all_source progress semantic_progress pass (Code
1453966 -> 1456768, 7309 -> 7312). stable_verdicts vs the FROZEN baseline:
gained 21 / 12944 bytes, regressions 0. All 11,060 function and 1,706 section
report rows: 0 lost 100%, 0 missing. Guard, ownership, fake scan, line endings
clean. git diff --check clean.
Reviewer: independent adversarial agents confirmed all three EXACT and credible
and held them ONLY on rule 24, which the owner has now ruled on per site; the
orchestrator re-derived every /Od attestation above from its own dumps.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 11:33:18 -07:00
Jonas VolmanandClaude Fable 5.1 f0b191bc68 Lane B: three exact functions under the owner's all-inlined COMDAT admission (+2,545 meaningful / +2,560 padded)
New strict exact:
  ai/actor_type_flood :: _actor_type_flood_desire_shamble  1296  (object now 2/2)
  units/vehicles      :: _update_human_plane_physics        928
  ai/path_smoothing   :: _find_tangent_point                336  (object now 6/6)

OWNER ADMISSION (2026-09-21), quoted: "Sufficient: admit the class. Require a
genuine shared-header __inline, byte-identical COMDAT, strict-exact caller, full
ownership/build sweep, and zero regressions. No hand expansions."
Each landing newly emits ONE header-inline COMDAT that January's split credits
to another object. Conditions verified per landing by scratch/orch/admit_check.py:
  _real_random_range   32 B  math/real_math.h  January owner ai/action_alert      byte-identical
  _interpolate_scalar  64 B  math/real_math.h  January owner game/player_control  byte-identical
  _set_real_point2d    32 B  math/real_math.h  January owner ai/path_obstacles    byte-identical
All three callers are strict exact; no hand expansion is used anywhere in them.

The evidence the owner accepted:
 (1) Compiler (scratch/orch/inline_lab.py): VC7 13.00.9254 with the campaign's
     real flags (/O2 /Oy- /DDEBUG /Dxbox) emits the out-of-line COMDAT for a
     non-static __inline whose EVERY call site inlined, and emits nothing when
     the inline is unused - so emission implies use in that TU.
 (2) January's own image (scratch/orch/inline_owner_census.py): of 104
     header-inline COMDATs, 65 are owned - per January's PDB section
     contributions - by an object that holds ZERO references to them (e.g.
     _cross_product2d in actor_combat.obj). Owners cluster at the earliest
     link-order TUs, as "folded survivor = first emitter" predicts. The split's
     single definition is therefore a link-fold artifact, not evidence that the
     other TUs did not emit the COMDAT.

Source evidence (all read from the first-party /Od build first, then confirmed
in January's bytes):
- _actor_type_flood_desire_shamble: result/switching flags zeroed at the top, all
  six `result = FALSE` arms, chain-assigned counters, `--timer == 0`, per-arm
  two-argument helper calls, MAX with double evaluation; `< 15.0f` confirmed by
  January's `test ah,5`. real_random_range is byte-REQUIRED (a hand expansion
  measures 1280 != 1296; January shows the inlined parameter temporaries).
- _update_human_plane_physics: uses the real interpolate_scalar helper.
- _find_tangent_point: every call is a real real_math.h helper including
  set_real_point2d; store-by-store spellings are residual and worse than baseline.

Held alongside (NOT landed): _update_alien_fighter_physics_new (1,088, exact in
scratch) - needs a NEW 3D->2D view cast whose site is not /Od-attested; owner:
"keep the fighter held".

Config: config/parked.json - _actor_type_flood_desire_shamble entry removed
(now exact). Parks 173 / 0 / 0. No header, symbols.json, flag or tool change.

Verification: full ninja all_source progress semantic_progress pass (Code
1451421 -> 1453966, 7306 -> 7309). stable_verdicts vs the FROZEN baseline:
gained 18 / 10128 bytes, regressions 0. All 11,060 function and 1,706 section
report rows: 0 lost 100%, 0 missing. Guard, fake scan, line endings and the
new-punning-cast screen clean. git diff --check clean.
Reviewer: independent adversarial agents confirmed each body EXACT and credible
and held them ONLY on the ownership question the owner has now ruled on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 11:27:15 -07:00
Jonas VolmanandClaude Fable 5.1 74c9cb2a02 Lane B wave 2: zero-credit fuzzy source corrections (path, collisions, action_charge)
ZERO exact credit. Better-evidenced bodies retained per house rule 11. No new
representation-punning casts (screened), no ownership change, no new
fake_match_scan lead, line endings preserved.

- physics/collisions.c _collision_move_point (4,752 B, not parked): reviewer byte
  compare with relocation sites zeroed - differing bytes 3,847 -> 6. Size
  4752 == 4752 and relocations 226 == 226. The remaining six bytes (0x595-0x597,
  0x59b-0x59d) are ONE transposition of two independent instructions, so the body
  is semantically January on every path. Each hunk was strip-tested alone and is
  byte-bearing (position block scope, post-increment placement, ...). The owner's
  collisions[-1] ban is respected; line 1572 is untouched.
- ai/path.c (both PARKED, entries refreshed in this commit):
  _path_state_traverse (2,096 B): rewritten statement-for-statement from the /Od
  build (fn 0x4c6a70) with each fact confirmed in January's bytes. Frame and
  return count are now January's; differing regions 132 -> 61; objdiff
  64.6% -> 98.6%. Padded size is now 2112 vs 2096. Residue is confined to
  0x10b-0x4cc, at the hand-expanded point_from_line3d sites forced by the
  emission guard (path.obj is in the owner's still-guarded all-inlined class).
  _path_state_estimated_distance (496 B): size now equal (was 528), 191 == 191
  instructions, objdiff 77.5% -> 95.5%; residue is a three-register cycle.
- ai/action_charge.c _action_charge_perform (3,248 B, not parked): three hunks
  accepted - melee arm first, the `!ranged || underwater` arm, and the two-store
  unreachable test - each a pure block move or an equivalent rewrite with the
  same evaluation order. NOT landed: the worker's `__inline` added to eleven
  baseline-exact local math helpers (flips their COMDAT selection for zero
  credit; two earlier lanes declined the same lever; needs a helper-linkage
  ruling). action_charge still emits the January-owned _point_from_line3d.

Config: config/parked.json - the two path entries re-measured in place by line
surgery (base size / sha / objdiff percent) with a dated evidence note each.
Parks 174 active / 0 stale / 0 invalid.

Verification: see the preceding strict commit (same build): 0 exact regressions,
0 report rows lost. Reviewer: independent adversarial agent per unit,
ACCEPT_FUZZY_ZERO_CREDIT; partial files built by the reviewers and re-gated by
the orchestrator.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 11:10:49 -07:00
Jonas VolmanandClaude Fable 5.1 d57ba30c0d Lane B wave 2: nine strict-exact functions (+4,715 meaningful / +4,784 padded)
New strict exact (unit :: function, padded bytes):
  game/player_control     :: _player_control_modify_desired_angles  1584
  ai/actor_combat         :: _actor_aim_projectile                   720
  memory/stack_memory_pool:: _stack_memory_pool_allocate             640
  memory/stack_memory_pool:: _stack_memory_pool_resize_block         192
  ai/action_vehicle       :: _action_vehicle_setup_impromptu         464
  scenario/wind           :: _wind_variance_initialize               384
  ai/actions              :: _actors_searching_same_position         320
  ai/action_converse      :: _action_converse_perform                256
  game/game_engine_race   :: _race_engine_get_score                  224
Function boards now complete: action_converse 9/9, wind 9/9, stack_memory_pool
33/33, game_engine_race 36/36, player_control 49/49 (objects NOT marked Matching:
whole-object data/ownership audit not done; player_control .rdata is owner-held).

Method. Every closure started from the SOURCE SHAPE read out of the first-party
unoptimised /Od + /RTC build (halo_cache_symbols.exe, scratch/orch/odbuild.py)
and was then confirmed against January's own bytes before it was relied on
(double attestation); none is a spelling search.
- _player_control_modify_desired_angles: both autolevel products are
  `error * K * magnitude3d(v)`; three lines, no local, cast or parenthesis.
- _actor_aim_projectile: weapon_vector is scoped to its own inner block, the dot
  product is tested directly (no `alignment` local), and the three debug-info
  stores follow January's order (flag first). Reviewer strip tests: block alone,
  order alone and alignment-drop alone are each residual; only the combination
  is exact.
- _stack_memory_pool_allocate: result zeroed before the 0x342 assert, ONE return,
  positive guard with the assert in the else, in-place size arithmetic. The
  worker's unattested `last_block` pointer local was REMOVED by the reviewer; the
  /Od arm spelling lands instead.
- _stack_memory_pool_resize_block: body untouched. The static helper
  stack_memory_pool_dispose_block takes (pool, reference), as /Od pushes them and
  as the callee forwards them to valid_block(pool, reference).
- _action_vehicle_setup_impromptu: passes state_data->attempt_distance /
  ->continue_distance (the fields just stored) instead of the parameters.
- _wind_variance_initialize: block-local `word control_point_indices[4]` assigned
  inside the innermost axis loop; name and size from the /Od RTC descriptor.
- _actors_searching_same_position: four pointer locals, result = FALSE after the
  four calls, if / else-if arms assigning result, one return. Requires
  actor_get_pursuit_location (baseline exact, STILL exact 64) to read
  uncover.pursuit_location through its own union member in a second arm, as /Od
  has it. The evade-direction enum gains its two real middle constants; values
  are unchanged (_actor_evade_random is still 4).
- _action_converse_perform: REMOVES a pre-existing `true_value` dummy local from
  production. `in_range = TRUE` is assigned in two arms (if / else-if): /Od has two
  distinct stores, impossible from one `||` statement, and January has
  `mov ebx,1` with two bl stores each with two in-edges. One return.
- _race_engine_get_score: a `score` accumulator assigned on both arms with ONE
  return (single-exit law); team_index/laps/flags_touched are block locals of
  the non-team arm.

Orchestrator overrides of reviewer-approved text (not landed, zero credit lost
beyond what is listed under HELD):
- action_vehicle: the reviewer's landing.c also rewrote _find_destination with 16
  NEW 3D->2D view casts. Owner ruling 2026-09-21 keeps rule 24 strict for new
  representation-punning casts, so only the two-line exact closure lands.
- actions: _actor_action_try_to_dive (720, exact in scratch) needs ONE new view
  cast -> HELD. _actor_action_handle_vehicle_entry's fuzzy rewrite introduced an
  empty `if` arm holding only a `//` comment (new fake_match_scan lead the
  reviewer missed) -> reverted to production.

Config: config/parked.json - six entries REMOVED (now exact):
_action_converse_perform, _wind_variance_initialize, _actor_aim_projectile,
_actors_searching_same_position, _player_control_modify_desired_angles,
_race_engine_get_score. No header, symbols.json, flag or tool change.

Verification (tip of the following fuzzy commit): full ninja all_source progress
semantic_progress pass, Code 1446706 -> 1451421, 7297 -> 7306 functions.
stable_verdicts vs the FROZEN baseline: gained 15 / 7568 bytes, regressions 0.
All 11,060 function and 1,706 section report rows: 0 lost 100%, 0 missing.
Parks 174 active / 0 stale / 0 invalid. Admission audit 23 / 0 / 8 / 0.
Guard, ownership, fake scan, line endings, new-punning-cast screen: clean on
every landed file. git diff --check clean.
Reviewer: independent adversarial agent per unit, ACCEPT_EXACT for all nine;
orchestrator re-gated every file through scratch/orch/land.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 11:10:49 -07:00
Jonas VolmanandClaude Fable 5.1 479341043f Lane B wave 1: zero-credit fuzzy source corrections (infection, bipeds, physics)
ZERO exact credit. Better-evidenced source retained per house rule 11; none of
these functions is parked, so no park entry moves.

- ai/actor_type_infection.c _infection_swarm_control (3,616 B): partial rewrite
  from January's bytes. Baseline was 256 bytes short with a wrong frame and a
  wrong constant multiset (missing 1.0f, -1/15, (double)1e-4f, +/-pi/2; an extra
  _normalize3d call). Now residual [sha] ONLY: size 3616 == 3616 and relocations
  104 == 104. Side effect: the unit's .rdata section (228 B) now matches January
  (data +228, not counted as Lane B code credit). Adds the include of
  ai_scenario_definitions.h for the _ai_atom_move_facing_* enum the recovered
  obey-facing switch needs (no header edited).
- units/bipeds.c _biped_update_moving (4,080 B): four in-function corrections,
  each read from January: `0.01f` not the double `0.01` (January's constant is
  __real@3f847ae140000000 = (double)0.01f); `!(fabs(dyaw) < _real_epsilon)`;
  the two ground_tangential_* stores in January's order; no (short) cast on
  actor_index. The reviewer measured the worker's file-scope `realcmp` macro as
  byte-inert and it was DROPPED (strip test). Still residual [size, sha].
- physics/physics.c _physics_update_old (5,168 B): rotation_axis is assigned
  immediately before normalize3d, after object_translate - a store cannot cross
  a call, so its position relative to the call IS January's statement order.
  The reviewer REJECTED the rest of the physics candidate (21
  PHYSICS_POINT_FROM_LINE3D macro sites and body rewrites) pending an owner
  ruling; only this two-line change lands.

Verification: same full build and sweeps as the preceding strict commit -
0 exact regressions, 0 report rows lost, parks 180 / 0 / 0.
Reviewer: independent adversarial agent, ACCEPT_FUZZY_ZERO_CREDIT for each;
partial-landing files produced by the reviewers and re-verified by the
orchestrator (unitcheck NO LOSS, guard, ownership, fake scan, line endings).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 07:26:50 -07:00
Jonas VolmanandClaude Fable 5.1 01e16607f0 Lane B wave 1: six strict-exact functions (+2,727 meaningful / +2,784 padded)
New strict exact (unit :: function, padded bytes, relocations):
  game/aim_assist        :: _local_player_aim_assist                        368  12
  game/game_engine_king  :: _king_calculate_hill_state                      448  32
  units/unit_dialogue    :: _unit_describe_speech                           272
  units/unit_dialogue    :: _unit_make_damage_sound                         784
  units/biped_limp_noodle:: _biped_limp_noodle_relax_nodes_onto_environment 256
  units/vehicles         :: _create_ghost_effect                            656
game_engine_king is now 29/29 on the function board (object NOT admitted: its
.bss naming is owner-held and its .data is blocked, see below).

Evidence per function:
- _local_player_aim_assist: the later Win32 /Od build (halo_cache_symbols.exe
  fn 0x589420) and its RTC descriptor attest, independently of any compile, the
  result local initialised first with ONE return, the nested
  player_control_get_zoom_level argument, the real subtract_vectors3d call with
  RTC names velocity/target_velocity/magnetism_velocity, and two STORED pointer
  locals. /Od neither invents locals nor inlines. Closed on the first compile
  after the evidence; every element strip-tested alone -> residual.
- _king_calculate_hill_state: the LAW Z negative-set member. January has ONE
  hill_controlled_count == HILL_CONTROL_TIME test at the bottom, not three; the
  team arm counts red/blue with the uncontrolled arm in the middle; each arm owns
  its data_iterator. That restores the two missing _king_globals references and
  January's pinned zero register with no zero-store steering.
- _unit_describe_speech: the one board function whose loop differs by pure shape
  (January top-tested). A done-flag `while (!done)` loop, the Marathon idiom.
- _unit_make_damage_sound: single-exit law (January 1 ret, ours had 2); the body
  is guarded by dialogue_index != NONE instead of an early return.
- _biped_limp_noodle_relax_nodes_onto_environment: accessor-binding law - the
  object_get() result is bound to a local (non-destructive lea), as January does.
- _create_ghost_effect: the marker count is TWO shorts summed in the loop test
  (hover_marker_count from the call, jet_marker_count = 0). First-party
  attestation: the later unoptimised debug build (halo_cache_symbols.exe fn
  0x8f5530) stores the call result to one short, zero-initialises a SECOND
  short, then marker_index, and its loop test is movsx idx / movsx a / movsx b /
  add / cmp; the sibling create_pelican_effect (0x8f5c90) has the same three
  slots with the second one counting "jet thrusters". January's store order
  (`mov [ebp-4],0` then the call result) is exactly that. The zero-valued local
  is therefore an attested source variable, not a steering dummy; without it the
  body is the old residual. Flagged in the handoff for owner awareness.

Zero-credit riders in the same files (no credit claimed):
- vehicles.c _update_alien_fighter_physics_new: credible restructure with scoped
  blocks and real names; still residual [relocs 33!=32, sha]. The reviewer
  measured `real const` thrust locals as byte-neutral, so the plain literals land.
- game_engine_king.c: adds the MISSING `struct game_engine king_engine` table
  (declared in the header, referenced from game_engine_list.c, defined nowhere).
  136 bytes, 0 content bytes differ from January, 23/23 relocations identical.
  objdiff still scores the section <100%: January leaves the "king" literal
  undefined in this object and `$`-literal relocations match only by combined
  address (same cause as race_engine). Correct source, zero data credit.

Config: config/parked.json - four entries REMOVED (now exact):
_king_calculate_hill_state, _biped_limp_noodle_relax_nodes_onto_environment,
_unit_describe_speech, _unit_make_damage_sound. Parks 184 -> 180, 0 stale, 0
invalid. No header, symbols.json, flag, tool or semantic-override change.

Verification: full ninja all_source progress semantic_progress pass (Code
1443979 -> 1446706, 7291 -> 7297 functions). stable_verdicts vs the frozen
baseline: gained 6 / 2784 bytes, regressions 0. Row diff of all 11,060 function
and 1,706 section report rows: 0 rows lost 100%. Admission audit 19/0/8/0.
_point_from_line3d guard, ownership (newly defined symbols vs January) and
fake_match_scan: clean on every changed file. git diff --check clean.
Reviewer: an independent adversarial agent re-gated every candidate from
scratch and returned ACCEPT_EXACT for all six; orchestrator re-verified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 07:26:50 -07:00
Jonas VolmanandClaude Fable 5.1 7583df5d62 Lane B prerequisite: take the data lane's hunks in six Lane B files
Identical text to claude/data-section-credit-20260920 for these six files
(git diff against that branch is empty), so the two branches merge cleanly.
ZERO Lane B credit is claimed: the five data sections belong to the data lane.

Taken deliberately because three of the hunks change relocation IDENTITIES
that Lane B code residuals need before they can ever gate exact:
  action_vehicle.c        0.1225f -> 0.35f*0.35f  (__real@3dfae147, January's
                          folded product) - _action_vehicle_find_destination
  collision_debug.c       assert text count<=14   - _collision_debug_render
  path_structure_bsp.c    assert spacing          - _structure_test_line2d
and three are the .bss layout law (= 0 / = FALSE on debug globals):
  player_control.c, collisions.c, bipeds.c

Units/functions: none change verdict. Strict meaningful +0, padded +0.
Fuzzy: none. Data: +489 B (116+300+43+26+4), credited to the data lane.
Stable regression: scratch/lane_b_before.json -> lane_b_w0.json, 0 regressions,
0 gains. Lane inventory row-diff: 0 losses, 0 moves. Parks 0 stale / 0 invalid.
Full ninja all_source progress semantic_progress: pass
(Code 1443979 / 1770166, 7291 / 7574; Data 2360994 -> 2361483).
Config/header changes: none. Reviewer: data lane's own verification; re-verified
here by full build and row diff.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 02:27:48 -07:00
Jonas VolmanandClaude Opus 5 f910bd2754 encounters.obj CLOSED 61/61: _encounter_create EXACT (+752), switch at one site
Owner ruling 2026-09-20: admit at the single byte-proven site only; document the
threading-law evidence; leave the byte-invisible site natural.

The random-leader case now tests the marine type with a single-case switch,
switch (actor_type) { case _actor_marine: ... break; }, instead of an if. The
normal-leader site keeps its natural if. No source comment is added; the
evidence is recorded here and in the lane ledger.

WHY THIS SITE. January re-tests actor_type on the fall-through from the normal
case into the random case - mov ecx,[ebp-0x14] at 0x223, cmp cx,7 at 0x226 -
and that test is semantically redundant, because the dominating normal-case test
has already established actor_type == _actor_marine on that path. The redundant
test physically survives in January's bytes at the random-case site, which makes
that site the byte-proven one.

THE THREADING LAW (decoded, VC7 13.00.9254). The jump threader carries branch
knowledge from if compares but not from switch case edges, and it does not
thread an edge into a switch-dispatch test. With an if at both sites our build
threads the fall-through past the random test (jmp over it); about 25 if-based
spellings all stay threaded. A single-case switch at the random site is exactly
the shape that keeps January's re-test. The same law reproduces in a standalone
repro TU. The normal site's form is invisible in the bytes - a switch there also
matches - so it stays an if, per the ruling.

Measurement note: objdiff's fuzzy figure reads 94.19%, not 100, because of
recorded trap #2/#4 - January's jump table relocates onto _encounter_create+0x2c0
and ours onto an internal $L label, the same seven bytes spelled differently. The
strict comparator reports 38/38 relocations with 0 differing rows and the build
credits the function.

Verified: full ninja green; code 1,463,135 -> 1,463,880 (7,305 -> 7,306
functions); encounters 60/1 -> 61/0; 11,060 function rows and 1,706 section rows
diffed board-wide, 0 worse; no symbol added or removed; parks 179 / 0 / 0; forbid
guard exit 0; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 02:06:29 -07:00
Jonas VolmanandClaude Opus 5 3ad3622623 props.obj CLOSED 17/17: _prop_get_active_by_unit_index EXACT (+144)
Owner ruling 2026-09-20: admit as proposed - the iterator and single-exit result
are natural; document the load-bearing declaration order in the ledger, not with
an intrusive source comment. No source comment is added.

The loop is rewritten with prop_iterator, this file's own idiom: both props.c
siblings that walk props use it (prop_get_base_by_unit_index,
prop_new_unacknowledged), January inlines it in both - there are no
_prop_iterator_* relocations - and January's prop_get_base bytes show the same
inlined header, cmp ecx,-1 then index := next then je, as this function. The
match predicate and the single-exit result follow the sibling. No new symbol is
defined.

LEDGER - THE LOAD-BEARING DECLARATION ORDER. The one fact that decides the match
is not visible in January's bytes: the single-exit initialiser long result =
NONE must EXECUTE AFTER the target-actor selection, so it is the last
declaration. Its web and the -1 constant then sit in the pre-loop join block that
initialises the cursor, lifting the cursor web's priority from 36 to 40 - a tie
with the state web that the [W+0x44] position tie-break awards to the cursor, so
it takes ecx, which is January's colouring. Declaring result first, as the
sibling does, is measured residual. Both the result web and the constant vanish
from the final code, which is why more than fifty earlier spellings - all without
a result variable, or with it initialised first - never reached it. The decoded
allocator's force-test confirms the threshold exactly (state <= 36 or cursor >= 40).

This reverses recorded conclusions R16 ("closed class-C colouring tie"), the
wave-I oracle's "UNREACHABLE by natural source", and props_obj.md's "do not
repeat" on the combined predicate, which does fail inside the plain loop but is
exact inside the iterator loop.

Verified: full ninja green; code 1,462,991 -> 1,463,135 (7,304 -> 7,305
functions); props 16/1 -> 17/0 with .text at 100%; 11,060 function rows and
1,706 section rows diffed board-wide, 0 worse; parks 179 / 0 / 0; forbid guard
exit 0; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 02:05:12 -07:00
Jonas VolmanandClaude Opus 5 ba612bffb7 _actor_customize_unit EXACT (+640) under the narrow _random_range exception
Owner ruling 2026-09-20: allow the narrow _random_range COMDAT exception and land
+640. Do not extend it to _point_from_line3d; its explicit guard remains.

One line. The grenade count is computed with the header's __inline helper,
random_range(lower, upper + 1), instead of spelling out
seed_random_range(get_global_random_seed_address(), lower, upper + 1). This is
exactly how the already-exact _ai_scripting_renew in ai_script.c computes the
same grenade count from the same fields.

The decoded allocator predicts why it matters: the helper's two inlined
parameters raise the grenade block's pressure from 3 to 5, lifting
actor_variant_definition's web from 53 to 65 - one point above change_colors at
64 - so it takes ebx and change_colors spills, which is January's allocation. The
same form also fixes the push order at 0x18e. One source fact resolves both
halves of what had been classed as "allocator decision plus a separate
difference".

THE EXCEPTION, and its limits. The change newly defines exactly one symbol,
_random_range, a COMDAT byte-identical to January's single copy, which the split
credits to action_obey.obj. That emission is admitted by the ruling above and by
nothing broader; ai_script.obj and encounters.obj already emit the same COMDAT.
The _point_from_line3d guard still exits 0 on this unit.

The function was parked; its entry is removed by brace-depth line surgery.

Verified: full ninja green; code 1,462,353 -> 1,462,991 (7,303 -> 7,304
functions); actors 73/3 -> 74/2; 11,060 function rows and 1,706 section rows
diffed board-wide, 0 worse; newly defined symbols: _random_range only; parks
180 -> 179 active, 0 stale, 0 invalid; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 02:04:23 -07:00
Jonas VolmanandClaude Opus 5 a0343465fc _actor_create_for_unit EXACT (+624): one return statement instead of five
The residual was one source fact. January's actor_create_for_unit has a single
exit - long actor_index = NONE; if (unit_index != NONE &&
actor_variant_definition_index != NONE) { ... } return actor_index; - with no
inner returns. Ours had five return statements.

This is the recorded single-exit law, and the shape is attested in this very
file: actor_new opens with the same long actor_index = NONE guard and returns
once. With one return VC7 splits the bottom return per incoming edge, which gives
the NONE initialiser its own web coloured esi; the -1 constant is then rescued
into esi by copy preference - January's pin - and the guard-fail exit becomes a
separate epilogue that does not pop ebx, so push ebx sinks below the guards.
Every piece of the recorded "-1 pin plus fifth epilogue" residual follows.

The biped rejection now clears a boolean valid flag, the attested carrier for
this idiom (actor_firing_position.c, ai_communication.c and action_charge.c use
the same spelling). It is not what buys the bytes: VC7 threads it away entirely,
leaving no register, slot or instruction, and four different spellings of it
compile to identical bytes. Restoring any single early return breaks the match.

The function was parked (class register-allocation); its entry is removed by
brace-depth line surgery, never re-serialized, and parks go 181 -> 180.

Verified: full ninja green; code 1,461,731 -> 1,462,353 (7,302 -> 7,303
functions); actors 72/4 -> 73/3; 11,060 function rows and 1,706 section rows
diffed board-wide, 0 worse; parks 180 active / 0 stale / 0 invalid; forbid guard
exit 0; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 01:54:10 -07:00
Jonas VolmanandClaude Opus 5 3fdad47e67 _ai_communication_event: evidenced corrections, relocations now equal - ZERO bytes
Owner ruling 2026-09-20: land the package minus the invented flag and the
unproven actor-arm polarity change; keep all evidenced corrections, document it
as fuzzy, grant zero credit. The function stays residual and this is not
scoreboard movement.

Result: relocations 335 -> 336, equal to January with an identical multiset;
the frame now matches January's 0x126c (was 0x1268); REAL regions 339 -> 253.
Padded size moves from 16 short to 64 over - accepted because relocation
identity outranks padded size in the lane's ordered key, and on this function
size is dominated by the non-local branch-width noise wave H measured.

THE BEHAVIOUR FIX, proven by a controlled experiment rather than taken on
trust. The encounter arm of enemy_status[5] read !subject_encounter->enemy_alive.
Flipping ONLY that negation, with nothing else changed, turns exactly one
instruction - the je after the byte load at [esi+0x44] - into jne, and January
has jne at that site. So January's source tested enemy_alive; ours was wrong.

WHAT WAS DELIBERATELY LEFT OUT, per the ruling:
- the invented negative-sense protagonist_invalid flag. It moved padded size
  only (the frame matches January without it), the same profile as the
  steering rejected in _actor_look_update.
- the matching flip in the ACTOR arm. On the floor that flip is byte-inert, so
  January's bytes cannot decide it; it stays !target_really_alive. For the
  record, within this package it is not inert - 8,096 bytes with it against
  8,128 without, but REAL regions favour the other way (265 against 253) - so
  the signals conflict and nothing proves either polarity.

Other evidenced corrections kept: the 2.0f literal at the player-rating
assignment, value-identical to the real const it replaces, where January
references that global once and we did twice; char locals for the two %c debug
codes, which January computes at 8 bits; statement orders recovered from store
positions relative to calls. Disclosed per the verifier: play_type now lives in
candidate scope, so a reply candidate stores _unit_play_speech_none instead of a
stale value - dead data, since play_type is read only on the non-reply path.

Verified: full ninja green; 11,060 function rows and 1,706 section rows diffed
board-wide, 0 worse; ai_communication 45/3 unchanged; its .rdata stays 100%; no
symbol added or removed; parks 181 active / 0 stale / 0 invalid; forbid guard
exit 0; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:47:02 -07:00
Jonas VolmanandClaude Opus 5 0269a75997 _ai_debug_render_actor: fix seven January-proven defects - ZERO bytes banked
A source-correctness landing under the standing "correct source beats a
misleading byte score" ruling. The function stays residual and this is not
scoreboard movement - though here every key improves as well: padded size
25024 -> 25008 against 24976, REAL regions 529 -> 340, and the frame now
matches January exactly, sub esp,0x80c -> 0x810.

Each defect was confirmed by its own January byte window becoming register-blind
identical, and by leave-one-out (dropping any one raises the blind region count).

V1  a real UNINITIALISED READ. January passes lea [ebp-8] and lea [ebp-4] as the
    full and partial vision-distance out-pointers and reads [ebp+edx-8] in the
    second loop nest: it is ONE real[2] array. Our full_distance_reference[1] was
    never written. Fixing it is what brings the frame to 0x810.
P2  an UNREACHABLE ARM. January's je 0x5c5f from both the render_paths_current
    test and the has-path test lands on the "following path" status text, so it
    sits outside that block; our "no current path" else could never run.
N1  normalize2d writes straight into desired_facing's own cells before the > 0
    test, spelled as at actor_combat.c:2304 and eight other AI sites.
X   four cross blocks read p0.x, not p1.x (fld [ebp-0x18]): p0.x = p0.x - 0.2f.
P1  the paths block indexes steps by the short local, not the char field.
SR1 the avoidance-objects loop addresses through an element pointer, as at
    actor_moving.c:903.
FA2 the flying-axes blocks use the existing __inline helpers scale_vector3d and
    point_from_line3d in January's term-major order. These are ordinary calls to
    existing helpers, which house rule 13 permits; nothing is written out.

Disclosure the verifier required: FA2 adds twelve (real_point3d *) casts on the
point_from_line3d arguments. All twelve are strip-test inert - the section is
byte-identical with them removed - and the cast is the tree-wide idiom.

Verified: full ninja green; 11,060 function rows and 1,706 section rows diffed
board-wide, 0 worse; ai_debug 59/1 unchanged; no symbol added or removed; the
pre-existing _point_from_line3d guard finding stays at exactly one; parks 181
active / 0 stale / 0 invalid; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:39:00 -07:00
Jonas VolmanandClaude Opus 5 fd57245a6e _actor_move_vector_avoidance: 96 bytes short -> 16 - ZERO bytes banked
The function stays residual; this is not scoreboard movement. Padded size goes
4048 -> 4128 against January's 4144, relocations stay at 135/135, and REAL
regions go 180 -> 170. This recovers most of the size an earlier landing traded
away to get the relocation count exact.

THE PERPENDICULAR SITE is written as a two-axis accumulation - rotation starts
from *global_zero_vector3d and takes only the left and up terms - instead of a
call to actor_move_transform_avoidance_vector. This is a reconstruction of
January's own code, not the helper body written out: January's bytes at
0xe73-0xf02 contain no forward-axis term at all, no load of avoidance_data.forward
and no multiply by __real@00000000. VC7 13.00.9254 never folds x*0.0f; with the
helper genuinely inlined at this very site it leaves three fmul by zero. So
January's source computed only two axes, in its own text. A comment at the site
says so.

The remaining hunks are statement reorders fixed by store positions relative to
calls, and a declaration moved to where January's frame puts it.

The verifier found a dead store, perpendicular.i = 0.f, never read once the
forward term is gone. It is byte-inert - the function's bytes are identical with
and without it - so by the strip test the plain spelling is the admissible one,
and it is removed. No diagnostic-only dummy locals are present.

Verified: full ninja green; 11,060 function rows and 1,706 section rows diffed
board-wide, 0 worse; actor_moving 31/5 unchanged; newly defined symbols versus
the floor: none; parks 181 active / 0 stale / 0 invalid; forbid guard exit 0;
fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:37:11 -07:00
Jonas VolmanandClaude Opus 5 2ee8bfefe8 _actor_move_update: fix a proven allow_jump defect - ZERO bytes banked
The function stays residual at [sha]; this is not scoreboard movement. REAL
regions 57 -> 53 with size, relocations and real code end all unchanged.

THE DEFECT, verified from January's bytes before landing. Our source set
allow_jump only when BOTH vehicle->vehicle.hover < 0.7f AND up.k < 0.8f.
January tests hover (fcomp __real@3f333333 at 0x54e, jp 0x5fa), then executes
mov byte ptr [ebp-3], 1 at 0x562 - allow_jump = TRUE - and only then tests
up.k (fcomp __real@3f4ccccd at 0x566, jp 0x5fa). So January sets allow_jump
whenever hover < 0.7, and falls through when up.k >= 0.8. Our source had a
real behaviour difference; the if is now nested accordingly. The escape
direction, crouch and break stay inside the up.k test.

Also: rotation is scaled with the existing scale_vector3d helper instead of
three *= statements.

WHAT WAS NOT LANDED. The wave's primary variant PA also rewrote a negation with
negate_vector3d, which makes actor_moving.obj newly define a _negate_vector3d
COMDAT. January's actor_moving.obj does not define that symbol, so under the
2026-09-20 ownership ruling it cannot be emitted; PA_noNG, landed here, adds no
new symbol. A further variant PB is byte-worse on padded size (+16) and its extra
levers are neutral respellings rather than fixes, so it stays held.

Verified: full ninja green; 11,060 function rows and 1,706 section rows diffed
board-wide, 0 worse; actor_moving 31/5 unchanged; newly defined symbols versus
the floor: none; parks 181 active / 0 stale / 0 invalid; forbid guard exit 0;
fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:36:04 -07:00
Jonas VolmanandClaude Opus 5 5dddede52e _ai_test_line_of_sight: take HCEX-attested local names - byte-identical, ZERO bytes
Renames the function's parameters and locals to the names HCEX.pdb records:
point0 -> p0, cluster0 -> p0_cluster_index, point1 -> p1, cluster1 ->
p1_cluster_index, collision_fraction -> collision_t, and the expand-source /
expand-target corners to p0a/p0b and p1a/p1b/p1c. Per the house rule on real
names. All 128 non-debug sections of ai.obj are byte-identical before and
after, so the parked entry stays valid and nothing is scoreboard movement.

WHAT WAVE H ESTABLISHED ABOUT THIS FUNCTION - recorded because it overturns a
committed "proof". R11, R25 and res7 said no shape closes its 12-byte frame gap.
Every probe behind that kept four separate objects and only changed scopes.

The ledger now closes to the byte: 34 real bytes = the 7-byte collision_t
initialiser (the correct behaviour we keep, since January's bytes prove it reads
an uninitialised slot on the heavy-fog path) + 27 bytes from ONE surplus 12-byte
frame cell - 24 bytes of disp8 -> disp32 lea widening because our frame crosses
0x80, and 3 bytes for a cross-jump January can take and we cannot.

The mechanism: ai.c's TU-private REAL_MATH_EXTERNAL_NORMALIZE3D and
REAL_MATH_EXTERNAL_POINT_FROM_LINE3D defines make perpendicular address-exposed,
which blocks liveness packing and forces the interleaved struct copy. With the
helper bodies visible, the separate-locals source HCEX attests reproduces
January's frame, slot census and copy order exactly; size, 50 relocations and
331 instructions all match, and 37 ModRM bytes plus one esi/edi decision remain.

That route is dead under the 2026-09-20 ownership ruling: visible bodies make
ai.obj emit a _point_from_line3d COMDAT that January's split does not attribute
to it. So the function stays residual, now for a stated reason rather than a
false impossibility proof.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:34:45 -07:00
Jonas VolmanandClaude Opus 5 f5698ba245 _actor_look_update: land owner-admitted (a)+(b) only - ZERO bytes banked
Owner ruling 2026-09-20: admit (a) and (b) only; reject (c) as semantically
inert steering; keep the function fuzzy at zero credit and audit the helper's
COMDAT ownership. It stays residual at [size 4704!=4720, sha] and this is not
scoreboard movement.

(a) valid_real_normal2d's definition takes the __inline specifier. Its sibling
    valid_real_normal3d is __inline in real_math.h, and four January objects
    reference this function. The call stays out of line.
(b) the aiming-speed tail is two separate tests rather than one disjunction;
    HCEX's line table puts them on separate lines, 1740 and 1745.
Both carry in-file disclosure comments, which were checked to leave the
function's bytes identical to the measured candidate.

What was NOT landed, and why:
- (c) can_look = TRUE on the asleep path - rejected by the ruling.
- the copy of desired_aiming into desired_looking moved after update_facing.
  It was paired with (c). Without it, the asleep path has already set both
  vectors from desired_facing, so the move adds only a redundant store of an
  equal value - the same inert steering (c) was rejected as.
- the plain-source edits (the minor_valid flag, the 2D struct copies and dot
  order). Ablated one subset at a time: without (c) they push padded size from
  4704 to 4688 and instructions away from January's 1,439, so under the lane's
  ordered key they are WORSE and stay out.
(a)+(b) alone ties every higher key - size 4704, relocations, 1,439
instructions, real code end 4690 - and improves REAL regions 120 -> 119.

COMDAT OWNERSHIP AUDIT, as directed: CLEAN. In both builds actor_looking.obj is
the only definer of _valid_real_normal2d and actors, actor_moving and units are
its only referencers. The one difference is the COMDAT selection flag, now
select-any against the split's no-duplicates - but csplit writes no-duplicates
for all 8,223 function COMDATs and never records select-any, even for helpers
that were certainly __inline in January such as valid_real_normal3d and
dot_product3d. The flag carries no evidence, and our build already differs this
way on 162 functions including that sibling.

Verified: full ninja green; 11,060 function rows and 1,706 section rows diffed
board-wide, 0 worse; actor_moving 31/5, actors 72/4 and units 189/189
unchanged; parks 181 active / 0 stale / 0 invalid; forbid guard exit 0;
fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:32:09 -07:00
Jonas VolmanandClaude Opus 5 923940b71b _actor_emotion_unopposable_retreat EXACT (+1,264): reuse the admitted square macro
Owner ruling 2026-09-20: admit the macro reuse, and update its evidence comment
to disclose that the hand-written parenthesised form also matches here.

The function closes strictly exact from plain source plus one reuse of the
already-admitted TU-private actor_perception_distance_squared macro, at the
friend-target distance square.

The plain-source parts, each shown load-bearing by reverting it alone:
- actor_emotion_assess_unopposable_danger spells its priority as
  if (facing <= 1) priority = 3; else priority = 2; rather than (facing<=1)+2.
  Both constants then thread through the inlined helper before if-conversion,
  which removes the join test that had exiled the visibility arm out of line.
  The helper itself stays byte-identical and EXACT at 112. This answers what
  suppresses a hoist: remove the reason for the join test, not the placement.
- a redundant if (target_count > 0) guard around the priority-adjust loop is
  deleted; that IL-changing, code-preserving guard had tipped a whole-function
  allocator state to the wrong side.
- block-scope iterator, one function-scope target_index, and named
  upper_bound / lower_bound reals at both random-range sites (the action_guard.c
  idiom).

The macro comment now carries the honest ladder, re-derived independently
before the ruling: d*d and (d*d) fail at size 1280, while (d)*(d) and the
macro's ((d)*(d)) both match. Unlike the first site, the single-parenthesis
hand spelling also matches here, so this site alone does not prove a macro
stood there; the comment says so, and that the macro is used here by ruling.

Verified: full ninja green; code 1,460,468 -> 1,461,731 (7,301 -> 7,302
functions); 11,060 function rows and 1,706 section rows diffed board-wide with
0 worse, the only movement being the target and actor_perception .text;
actor_perception 39/5 -> 40/4; parks 181 active / 0 stale / 0 invalid; forbid
guard exit 0; fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:28:19 -07:00
Jonas VolmanandClaude Opus 5 b423909b1d Name ai_sound_volume_enum_strings from HCEX: +1,560 data bytes
ai.obj's 1,560-byte .data section sat at 97.8% with ZERO differing content
bytes. January's split has no name for the five-entry sound-volume table, so it
spelled the enum's reference to it as _ai_update_section + 0x5f8, while ours
spelled it _ai_sound_volume_names + 0 - the same address. objdiff credits a data
section all-or-nothing, so that one relocation spelling held back all of it.

AUTHENTICATED PROVENANCE, as the lane brief requires for symbols.json. The HCEA
SHIP PDB (HCEX.pdb), read with DIA2Dump -compiland on
..\..\..\build\x360\SHIP\halo\ai.obj, records:

    Data : static, File Static, Type: char *[0x5], ai_sound_volume_enum_strings

A five-pointer array is exactly the 20 bytes between +1528 and +1548, and it is
file-static. That also shows our name was WRONG: Bungie's is
ai_sound_volume_enum_strings, not ai_sound_volume_names. Per the house rule on
real names for private globals it is renamed in ai.c - two lines, and since it
is static nothing outside the file references it.

symbols.json gains one line, by line surgery with its CRLF endings preserved,
at a file offset where nothing existed before, so there is no alias-precedence
risk at a shared offset:

    { "file_offset": 2847464, "flags": 0,
      "name": "_ai_sound_volume_enum_strings", "static": true }

The form matches the 273 existing data statics.

Verified:
  re-split    every one of the 833 split objects hashed before and after;
              exactly ONE changed, source/ai/ai.obj; none new or vanished
  board       11,060 function rows: 0 worse, 0 better, 0 vanished, 0 new
              1,706 section rows: 0 worse, 1 better (ai .data 97.814 -> 100.000)
  metric      Data 2,376,514 -> 2,378,074 (+1,560); code unchanged
  lane        all 11 units unchanged at 523 exact / 24 residual
  gates       parks 181 active / 0 stale / 0 invalid; ai forbid guard exit 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 21:10:48 -07:00
Jonas VolmanandClaude Opus 5 264ddd8a3a Declare game_allegiance_get_incidents and restore its caller: +11,600 data bytes
Owner ruling 2026-09-20: approve with full sweep - land the prototype and the
decoded block only after all 12 consumers, ownership, guard, full build and
zero-regression checks pass. All passed.

game_allegiance_get_incidents is defined in game_allegiance.c and called from
nowhere, and was declared in no header - a genuine omission, since every sibling
function of that file is declared in game_allegiance.h in exactly this style. The
declaration is placed between game_team_is_ally and game_allegiance_provoke,
matching the definition order in the .c.

The caller is the block decoded from January's _ai_communication_event at
0x3c1-0x409: when ai_debug.print_allegiance is set and the allegiance still
holds, it prints the incident count against the threshold, substituting 999 for
a NONE threshold. It sits inside if (observed) where broken is in scope; the
argument order is fixed by January's reuse of the cause_team slot at 0x40c.

WHY IT PAYS. objdiff credits a data section all-or-nothing, and
ai_communication's 11,600-byte .rdata sat at 99.5% purely because this block's
two string literals were never emitted. Data moves 2,364,914 -> 2,376,514,
exactly the +11,600 measured beforehand on a scratch candidate.
_ai_communication_event stays residual - 16 bytes short instead of 96, 335 of
336 relocations - so the code metric is unchanged at 1,460,468 / 7,301.

THE SWEEP, as required:
  full board  11,060 function rows and 1,706 section rows diffed before/after:
              0 worse, 0 vanished, 0 new; 3 better (the target function, and
              ai_communication .rdata 99.51 -> 100.00 and .text 94.90 -> 95.24)
  consumers   strict gate rows for all 12 files that include the header:
              exactly one row moved, the target, and it improved; no EXACT lost
  guard       --forbid-emitted-symbol _point_from_line3d identical on all 12
  ownership   the change newly defines exactly two symbols, both string literals
              that January's ai_communication.obj also defines, and newly
              references one, which January's object also references - CLEAN
  build       full ninja green; parks 181 active / 0 stale / 0 invalid;
              fake_match_scan 0 findings; no C4013 implicit declarations

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 21:07:11 -07:00
Jonas VolmanandClaude Opus 5 94c3273708 Define and use ai_script_squad_separator: +3,920 data bytes
January's ai_script.obj carries a one-byte external .rdata object,
_ai_script_squad_separator = '/', which our build did not emit at all. Our
source used the literal '/' at the one site that splits an "encounter/squad"
name with strrchr.

Now defined as a file-scope char const and used at that site. VC7 folds the
const into the immediate, so the function's code is byte-identical and
ai_script stays 116/116 exact - and because the object keeps external linkage it
is still emitted, matching January exactly: .rdata, one byte, class external,
0x2f, and zero code relocations to it on either side.

The defining-only variant also matches the bytes but was rejected: a global that
nothing uses exists only to emit a byte, which is what the no-fake-matching rule
forbids. Using it at the split site is what explains why January had it.

The data metric moved far more than one byte, from 2,360,994 to 2,364,914
(+3,920), which suggests the build credits a unit's data only once all of it
matches. That is being investigated separately.

Verified: full ninja green, code unchanged at 1,460,468 / 7,301, parks 181
active / 0 stale / 0 invalid, forbid guard exit 0, fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 20:58:17 -07:00
Jonas VolmanandClaude Opus 5 67a1117544 Fix swapped colour arguments in _ai_debug_render_actor and re-baseline it
Owner ruling 2026-09-20: land it and re-baseline the fuzzy floor; correct source
outweighs a misleading byte score; grant zero exact credit and document the
backend artifact.

ZERO exact credit. The function stays residual at 24,976 padded bytes and this
is not scoreboard movement.

ai_debug.c:1100 and :1110 had their colour arguments swapped relative to
January. Verified from January's bytes before landing: January pins the color
local in EBX for the whole loop body, selecting white, yellow or aqua at 0x5a7,
0x5c5 and 0x5cd, then pushes EBX at the first site and _global_real_argb_blue at
the second - the latter immediately followed by push 0x3f800000, the 1.f
argument of render_debug_vector. Our source had them exactly reversed. The fix
takes a 480-byte window from 431 differing bytes to 13.

The floor is numerically worse: padded size goes from 24976 to 25024 and REAL
regions from 495 to 529. That previous size equality was never a match - it was
the padding coincidence recorded in wave C, whose "the code length comes back"
claim was already retracted. The new number is an honest floor measured against
correct source. No sibling moved; ai_debug is 59/60 before and after.

The 48 bytes are NOT caused by the colour fix. They are an independent
branch-shortening FIXPOINT, and that is the reusable finding: of the eight
branches where January emits SHORT and we emit NEAR, five have final spans
comfortably inside +/-127, and for one of them January's span and ours are the
identical 123 bytes. Three seed branches one byte over the line block the other
five. It is the largest byte family in the function, has no known source lever,
and is invisible to every region metric - trap #6 at scale. It also explains the
non-additive sizes earlier waves measured.

Corollary that changes the plan here: the 4-byte-short frame is currently paying
for the branch tax, so correcting it in isolation makes the function +39 rather
than +12. No future wave should attack the frame on its own.

Verified: full ninja green, halobetacache 1,460,468 / 7,301 unchanged by this
commit, parks 181 active / 0 stale / 0 invalid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 20:24:20 -07:00
Jonas VolmanandClaude Opus 5 8e4309dd16 Admit January's out-of-bounds threat scan: _actor_emotion_update EXACT (+1,664)
Owner ruling 2026-09-20: admit as a target-proven BUG with a precise source
comment, a layout assertion, and independent verification.

January scans the threat array from NUMBER_OF_ACTOR_THREAT_TYPES rather than
from NUMBER_OF_ACTOR_THREAT_TYPES-1, so its first iteration reads
specific_threats[9] - one element past the end. Proven from raw bytes: mov eax,9
at +0xf5, a six-byte alignment nop touching neither EAX nor memory, the loop head
at +0x100, the load at +0x103, and dec eax only at +0x10d, after the load.

The same read was refused earlier because it was being landed on a function that
would stay fuzzy. That premise no longer holds - the function is now STRICT
EXACT, so January's own bytes prove the access rather than a reconstruction
guessing at it.

Per the ruling the layout is now ASSERTED rather than assumed. Two compile-time
assertions in this file's existing assertion block state that specific_threats
holds exactly NUMBER_OF_ACTOR_THREAT_TYPES elements and that cumulative_threats
begins immediately after it, so the stray index is
cumulative_threats[_actor_threat_none] and nothing else. If either stops
holding, the file stops compiling.

Independent verification, done before the landing rather than taken on trust:
every use of cumulative_threats in the whole tree is index 1 through 8 or the
literal 5; index 0 is never written anywhere; and the single variable-index use
is a read-only csprintf argument in the actor_debug_print_threat debug macro.
The stray byte is therefore always zero, the early-out never fires on it, and
observable behaviour is unchanged. The comment now records all of that.

The byte ledger closes exactly at 19 differing bytes and was verified additively
- closing either half alone leaves the function residual at 13 or 18 bytes, so
the crouch restructure could not have been landed alone. The second half is a
semantics-preserving if/else-if restructure; all four input combinations traced.

Verified: full ninja green, halobetacache 1,460,468 / 1,770,166 (7,301 / 7,574),
actor_perception 38/6 to 39/5 with no sibling lost, relocations 38/38 and zero
differing rows, parks 181 active / 0 stale / 0 invalid, forbid-guard exit 0,
fake_match_scan 0 findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 20:22:42 -07:00
Jonas VolmanandClaude Opus 5 b6c4100427 Reorder two statement pairs in _actor_move_update - ZERO bytes banked
The function does not close, so this banks zero bytes and is not scoreboard
movement. REAL regions 63 to 57; size 3136 and relocations 72 already matched
and still do.

Both hunks are pure statement reorders with identical semantics. crouch = FALSE
moves after the vector_from_points3d call, and move_in_3d = TRUE moves ahead of
two independent local assignments. Neither value is read by anything it crosses.
The positions are January's: a store's position relative to a CALL is source
statement order.

Verified: full ninja green, parks 181 active / 0 stale / 0 invalid, no gate row
moved anywhere in the unit, forbid-guard exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 20:14:04 -07:00
Jonas VolmanandClaude Opus 5 c23d0f5386 Wave F fidelity landings: three improvements, ZERO bytes banked
None of these three close a function. The board metric is unchanged at
1,458,814 / 7,300 and none of this is scoreboard movement.

_ai_debug_render_actor - binds actor->input.position.head_position to a named
local, which is the recorded accessor-binding law: the inline field reference
folds the offset destructively, while binding it forces the non-destructive
form. It emits January's fsubr [ebx+4] / fsubr [ebx+8] for the first time and
recovers 6 real bytes. Wave E's refutation of the "this family needs a header
prerequisite" claim stands - point_from_line3d and scale_vector3d are already
__inline in real_math.h and already called this way in this file.

_actor_move_vector_avoidance - relocation COUNT goes 140 to 135, which is
January's exactly, and relocation identity goes 5 differing rows to 2. REAL
regions 200 to 180, register-and-frame-blind 100 to 84, frame 0x60ec to 0x60e4
against January's 0x60e0. Key 3 moved AWAY and by more than the padded figure
shows - real-code distance went from +47 to -87 - which is acceptable only
because relocation identity outranks padded size in the lane's ordered key. The
verifier required that disclosure survive into any summary, and it does.

_ai_communication_event - closes 80 of the 176 missing bytes, from 7888 to 7968
against 8064, by writing the actor_get plus actor_type_get_race sequence we were
missing entirely. The separate 72-byte block remains header-blocked on
game_allegiance_get_incidents and was left strictly alone: no header edit and no
consumer-local prototype.

Verified: full ninja green, parks 181 active / 0 stale / 0 invalid, exactly one
gate row moved per unit with none lost, forbid-guards exit 0 on all three units,
fake_match_scan 0 findings on all three files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 19:21:50 -07:00
Jonas VolmanandClaude Opus 5 ea507d5bc7 Wave E fidelity landings: four improvements, ZERO bytes banked
None of these four close a function, so none of them bank a byte and the record
says so plainly. The board metric is unchanged at 1,458,814 / 7,300.

_actor_perception_aiming_vector_test_blockage - the only one that moves a HIGHER
ranked key. The 16-byte size gap is closed: both builds are 400 padded bytes and
the gate row goes [size 384!=400, sha] to [sha], with REAL regions 9 to 7.
January does not use a result/blockage PAIR but ONE short carrier whose live
range VC7 splits three ways. That was forced by running the return-width law
BACKWARDS: in a short function a literal return 1; emits mov ax,1, but January
emits mov eax,1, so January's constants are assignments to a short local whose
register home is eax. The decode independently corroborates the cascade I read
from January's bytes earlier in this session.

_actor_emotion_unopposable_retreat  47 -> 44 REAL regions.
_actor_destination_update           24 -> 20 REAL regions.
_actor_move_update                  ties every ranked key; one instruction moves
from differing to matching. Its one-line change is spelling
vehicle->vehicle.airborne_ticks as > 0. I verified the load-bearing claim myself
and it is stronger than stated: the field is byte at vehicle_datum.h:26, so the
two forms are provably identical on an unsigned type; vehicles.c:2309 spells
this exact expression on this exact field; and EVERY other use of the field in
the tree is an explicit comparison, so the bare truth test was the anomaly.

The blockage landing stales a second parked entry, whose evidence explicitly
said the size gap was 'not attributed to a proved compiler mechanism'. It now
is, so the measurements are refreshed and the evidence records the decode.
objdiff falls 56.21528 to 54.791668 there, which is diagnostic only and never
credit - every authoritative key improved or held. Both functions stay parked
because neither is exact. parked.json was edited by line surgery.

Verified: full ninja green, parks 181 active / 0 stale / 0 invalid, all 11 lane
units gated per row with no row lost, forbid-guards exit 0, fake_match_scan 0
findings on both touched files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 18:39:59 -07:00
Jonas VolmanandClaude Opus 5 b3223f36e8 Narrow-return package LANDED: +8,400 strict bytes - my 'proven impossible' was wrong
I committed in 4410f35e that part (c) of this package was PROVEN IMPOSSIBLE,
on a lab showing that a short-returning function always emits 'mov ax,3' for a
returned constant. That conclusion was wrong because the lab only ever tested
'return <constant>;'.

A board-wide scan settles it: 122 narrow-returning functions in our own build
emit 'mov eax,imm' immediately before ret, including _actor_visibility_at_point
in this very object, which is EXACT. The corrected law, stated as the verifier
required rather than as the decoding agent first wrote it:

  In a narrow-returning function, 'return <constant>;' materialises the constant
  at RETURN-SLOT width (mov ax,K), while a constant assigned to a single-exit
  accumulator materialises it at REGISTER width (mov eax,K). The accumulator's
  own width is INERT - short and long accumulators both reach 38/6. It is the
  declared RETURN TYPE that flips callers to cleanup-first; the body rewrite
  alone buys nothing.

So January's 'mov eax,3' never meant a wide return type. It meant the single-exit
idiom this campaign already knows January uses, and which this file uses nine
times over.

actor_get_perception_knowledge becomes short in both definition and declaration,
with its body rewritten to the file's own goto done idiom. All three functions
are now EXACT: the callee holds at 240, _actor_perception_update banks 4,720 and
_prop_status_refresh banks 3,680.

The third file is why this is not the two-file change that was proposed. The
landing also improves a PARKED function's bytes - ai_communication's
_ai_communication_actor_talk_weight goes 99.31293 to 99.993195 percent - which
stales its recorded evidence and fails ninja's PROGRESS gate outright. Its
measurements are refreshed and the two sentences this landing falsifies are
rewritten: the entry asserted that the long return type was definition-correct
and that a narrow declaration contradicted the owner definition, and both are
now untrue. The function is still not exact, so it stays parked, and its
residual is down to a single real region. parked.json was edited by line
surgery, never re-serialized.

Verified: full ninja green, halobetacache 1,458,814 / 1,770,166 (7,300 / 7,574),
all 11 lane units gated per row, exactly two rows gained and none lost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 18:37:04 -07:00
Jonas VolmanandClaude Opus 5 73c0519846 Land the owner-approved parenthesised accessor: _ai_test_line_of_fire EXACT (+336)
Owner ruling 2026-09-20: admit the named TU-private accessor macro with
disclosure - it is semantically meaningful and hygienically parenthesized - and
keep the equivalent collisions.c precedent.

    #define LINE_OF_FIRE_PILL_SPHERE_RADIUS(pill)  ((pill).width)

The function's own park entry named the reopening criterion as 'an attested or
owner-approved parenthesising spelling'. That criterion is now met, so the park
is removed; parks go 182 to 181, and the validator reports 181 active, 0 stale,
0 invalid.

The result is byte-identical to January over all 336 bytes including every
relocation addend and all padding - not merely normalized-equal - with 10/10
relocations at identical addresses and an identical 419-record symbol table.
Exactly one gate row moved across all 46 functions of ai.obj; the unit goes
43/3 to 44/2 and both survivors are pre-existing parks with unchanged tags. The
forbid-emitted-symbol guard exits 0 against a floor of 0, and fake_match_scan
reports the same 2 pre-existing #pragma findings before and after.

The comment now cites the first-party evidence for the naming that the original
packet missed: real_math.h:409 declares fast_vector_intersects_sphere's fourth
parameter as radius, while vector_intersects_pill3d's fifth at real_math.h:536
is pill_width. The asymmetric reading across the two arms is exactly the
asymmetry Bungie's own header draws.

parked.json was edited by line surgery, never re-serialized.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 18:13:07 -07:00
Jonas VolmanandClaude Opus 5 2c9223514e Collapse _prop_get_active_by_unit_index from 7 real regions to 2 - ZERO bytes banked
The function does not close, so this banks zero bytes. It is landed as a
fidelity improvement and because the resulting source is the more natural
spelling.

The whole 144-byte residual is a two-register swap - next_prop_index and
prop->state are ECX/EDX in January and EDX/ECX in ours - plus its one
consequence, that January can load _prop_data into EDX before pushing the index
while we must push EDX before overwriting it. Size, relocations and instruction
count were already equal; the gate row is [sha] alone.

Fourteen shapes locate the lever precisely: read-count changes are inert and
statement ORDER is the whole story. Moving the NONE test above the copy flips
the colouring to January's ECX and takes the function from 7 REAL regions to 2.
Scope, declaration order and three loop spellings are then all byte-identical,
confirming the recorded law that declaration order is inert.

What remains is one instruction: January speculates 'mov esi,ecx' into the
flag-latency slot above the branch, we sink it into the call setup. The two
REAL regions are the same instruction at its two positions. The bind is that
assign-before-test puts the copy above the branch but colours EDX, while
test-before-assign colours ECX and sinks the copy; no shape produced both, and
the for-header carry that comes closest emits the increment twice for +16 bytes.

Guards: no gate row moved anywhere in the unit, forbid-emitted-symbol exit 0
against a floor of 0, fake_match_scan 0 findings before and after, and the
change is function-local in a single TU with no header touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 18:03:34 -07:00
Jonas VolmanandClaude Opus 5 edd8b386bb Wave D: two more EXACT, +1,671 meaningful bytes, 0 regressions
ai_debug          _code_00039990                       EXACT   848
  ai_communication  _ai_communication_get_player_rating  EXACT   832

source/ai/ai_debug goes 58/60 to 59/60; source/ai/ai_communication 44/48 to
45/48. halobetacache 1,448,422 -> 1,450,093 (7,295 -> 7,297).

Both land as DISCLOSED INFERRED constructs under the owner's standing rule -
TU-private, descriptively named, carrying a comment that says plainly the shape
is inferred from January's bytes and not attested, with the measurement table
that forced it - and both reach STRICT EXACT, which the owner set as the bar.

_code_00039990. The residual was one parenthesised group at a call argument.
I did NOT land the proposer's bare `(point_height+0.15f)`: its adversarial
verifier surveyed all 477 non-vendored .c files and found a whole-argument
parenthesised SUM occurs ZERO times in the tree, and corrected the proposer's
claim that it was "attested 11 times" - the cited ai_debug.c:1722-1724 sites
parenthesise the OPERANDS and leave the top-level sum bare, which is the shape
that does not match. Nine spellings reach exact; I took the one the owner's
ruling names, a TU-private macro `ai_debug_attractor_label_height(height)`,
which also gives the bare 0.15f a name it did not have.

_ai_communication_get_player_rating. Binding the two real_point3d endpoints to
named const pointers before the vector is taken fixes the order in which the
compiler squares the components: an address-of expression and a pointer value
are different operand forms here. CORRECTED BEFORE LANDING - the verifier found
a real error in the comment text that would have shipped: its first row claimed
the untouched spelling already squares k,i,j when it measures k,j,i, which would
have told a future reader the change was unnecessary. Table fixed and
re-verified EXACT.

A park retired: _ai_communication_get_player_rating was parked
`instruction-scheduling`. parked.json 183 -> 182, no other entry touched, never
re-serialized.

Guard note: `--forbid-emitted-symbol _point_from_line3d` exits 1 on ai_debug
both BEFORE and AFTER this change - that file genuinely calls the helper, so the
COMDAT is the known systemic extra-COMDAT gap and not a regression. On
ai_communication it exits 0 before and after. Checked both ways rather than
assumed.

Verified: ninja exit 0; stable_verdicts gained 5 / 5,920 padded bytes with
REGRESSIONS 0; parked 182 active / 0 stale / 0 invalid; audit 0 contradicted /
0 revoked; fake_match_scan 0 findings on both files; diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 17:27:07 -07:00
Jonas VolmanandClaude Opus 5 f1299316c8 Wave C part 2: the flagship and the two actor_moving rows, 0 regressions
Three more zero-credit fidelity landings. All three functions remain residual,
so this banks ZERO strict bytes.

  ai_debug  _ai_debug_render_actor  (24,976 B, a third of the portfolio)
      FOUR target-proven reconstruction defects plus the NULL-deref fix:
        - a wrong constant, 1.6f where January pushes 1.f (immediate census
          T14/O13 and T1/O2, the one surviving 1.6f at a site that matches)
        - alignment_vector_3d built from control.current_fire_target_aim_vector
          instead of output.animation.alignment_vector promoted to 3D with k=0
          (+0x6ec is the impulse BOTH builds test, so +0x6f0/+0x6f4 is the 2D
          alignment vector, and the local NAME corroborates it)
        - p1 taken from control.desired_aiming_vector instead of burst_origin
          (+0x64c burst_initial_position is lea'd by both builds for the
          preceding line; +0x658 is the adjacent member. Plotting a unit
          DIRECTION as a world point in a Burst Geometry block was just wrong)
        - an if/else with an inner ternary that made VC7 cross-jump two copies
          of the same five-argument render_debug_vector call; one call with a
          nested ternary reproduces January three-arm colour block and REMOVES
          a duplicated call from our source
      frame sub esp 0x81c to 0x80c against January 0x810, i.e. +12 to -4;
      relocations 1905 = target; regions 252 to 206; census 58/2 with the whole
      per-function gate table byte-identical to pristine.

  actor_moving  _actor_move_update       size 3120 to 3136 = target, relocation
      TYPE sequence from about 20 mismatched rows to zero, byte-LCS unmatched
      target bytes 455 to 232, regions 142 to 103.
  actor_moving  _actor_move_vector_avoidance   hand-rolled cross-product math
      replaced by the existing cross_product3d helper; regions 236 to 227.

TWO CORRECTIONS TO MY OWN RECORD, both from the adversarial verifiers:

1. I wrote that the four fixes make "the code length come back" so the section
   is 24,976 again. It does not. January code ends at 0x5ffd with 12 bytes of
   trailing pad; pristine and the landing end at 0x6009 with none. Both sections
   are 24,976 only because January padding absorbs the difference, which is an
   arithmetic coincidence and exactly the padding trap this lane keeps warning
   about. The landing stands on the defect fixes and the region and instruction
   evidence, NOT on the padded-size argument. Real code is still 12 bytes long.

2. The seed I wrote for that agent was wrong: "January has 31 dword slots and
   the candidate has 30, diff the SETS". Both frames are DENSE, so a set diff
   localises nothing; January simply has one more granule. The agent said so and
   worked from invariant multiset censuses instead.

Verified: ninja exit 0; halobetacache 1,448,422 / 1,770,166 (7,295 / 7,574)
unchanged as expected; stable_verdicts REGRESSIONS 0; parked 183 / 0 / 0; the
--forbid-emitted-symbol _point_from_line3d guard exits 0; diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 16:23:30 -07:00
Jonas VolmanandClaude Opus 5 694396011e Wave C part 1: three zero-credit fidelity landings, 0 regressions
All three functions remain residual, so this banks ZERO strict bytes. A
residual's padded size is its potential, not a realized gain, and the record
says so throughout.

  ai_communication  _ai_communication_event
      restores a re-fetched actor_get(subject_actor_index) January has in the
      _comm_look_direction_danger case. size 7856 -> 7888 (target 8064),
      relocations 328 -> 330 (target 336). The identifier is not invented:
      January's assert macro baked `subject_actor->danger_zone.object_index
      != NONE` into the object, so any other name swaps a string COMDAT.

  actor_perception  _prop_status_refresh
      three boolean stores put in props.h declaration order (noncombat,
      in_combat, fighting - we had fighting first), plus the owner-approved
      macro at its squared-distance site. REAL regions 52 -> 4.

  actor_perception  _actor_perception_update
      the owner-approved macro at its two remaining sites.
      REAL regions 14 -> 2.

The _prop_status_refresh decode is the best mechanism work of the wave. A
register-blind structural diff (normalise every register name, then re-diff)
showed the instruction multiset identical in every cluster but one: January
`xor edx,edx; mov dx,[eax+0x182]` against our `movsx eax,[eax+0x182]`. That
single site was the whole deficit, and the renaming FORCES it - ours landed the
destination on EAX which also held the definition pointer, making
`xor eax,eax; mov ax,[eax+...]` impossible. Control: the sibling site in
_actor_perception_refresh_danger_zone compiles to `xor ecx,ecx; mov cx,[eax+0x182]`
in BOTH builds from identical source, proving it is not a type or cast issue.

Verified: ninja exit 0, halobetacache 1,448,422 / 1,770,166 (7,295 / 7,574)
unchanged as expected; stable_verdicts REGRESSIONS 0; parked 183 active / 0
stale / 0 invalid; the mandatory
`gate.py source/ai/actor_perception --forbid-emitted-symbol _point_from_line3d`
guard still exits 0; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 16:15:51 -07:00
Jonas VolmanandClaude Opus 5 5186c5bd2a Land _actor_perception_refresh EXACT on the owner's ruling (+2,416 bytes)
OWNER RULING: "Land _actor_perception_refresh (+2,416) with a TU-private,
descriptively named inferred macro and documented evidence. Do not land
_ai_test_line_of_sight uninitialized read."

Both parts honoured. _ai_test_line_of_sight stays held and unlanded; its diff
and evidence remain in scratch only.

The macro is translation-unit private - a #define in the .c, no header touched -
named actor_perception_distance_squared to match its sibling
actor_perception_distance_squared2d already in the same macro block, and it
carries a comment that says plainly it is INFERRED FROM JANUARY'S BYTES and not
attested in any surviving source. The comment reproduces the measurement that
justifies it, with the rest of the function held constant:

    prop->distance * prop->distance             residual
    (prop->distance) * (prop->distance)         residual
    (prop->distance * prop->distance)           residual
    bind a local first, (distance * distance)   residual
    ((prop->distance) * (prop->distance))       EXACT

Only the canonical macro expansion reaches January. A hand-written expression
does not produce the doubly-parenthesised form; a correctly written macro
produces it inevitably, which is why the byte evidence is itself the argument
that a macro stood here. The second hunk - an explicit if/else giving that
else-if arm its own cross-jump resolution - carries its own comment recording
that the two are required jointly; I measured each alone and both are residual.

Verified: EXACT 2416, relocdiff `sha equal` with 0 differing rows, 86/86
relocations, unit census 35/9 -> 36/8, fake_match_scan 0 findings, no sibling
lost, comments did not shift any match_assert line literal.

Wave B totals after this landing:

  halobetacache  1,448,422 / 1,770,166   (7,295 / 7,574)
  overall        1,469,654               (7,562)
  stable_verdicts  gained 3, 4,240 padded bytes, REGRESSIONS 0
  parked           183 active / 0 stale / 0 invalid

Net for the wave: +4,229 meaningful bytes, +3 functions, 0 regressions, one
whole object closed and one park retired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:15:45 -07:00
Jonas VolmanandClaude Opus 5 7e83d31bce Lane A wave B: two objects closer, one whole object closed
Ten residuals decoded in parallel, each on its own scratch copy, every proposed
landing re-measured by an adversarial verifier and a third time by the
orchestrator against the pristine tree. Targets were picked by the board
censuses added earlier this session.

EXACT, +1,817 meaningful bytes / +2 functions / 0 regressions:

  source/ai/actor_firing_position  _pre_evaluator_attack   1120 B
      26/27 -> 27/27, THE WHOLE OBJECT CLOSES
  source/ai/ai_debug               _code_0003a910           704 B
      57/60 -> 58/60

_pre_evaluator_attack was the +12 frame class, and the census called it: the
function homed twelve bytes more than January with [ebp-0x40] never referenced
at all - the elided-aggregate-member signature. The cause was a hand-expanded
library idiom, scale_vector3d + add_vectors3d + a real_vector3d temporary,
where January writes point_from_line3d in place. That spelling, casts included,
is verbatim real_math.c:2536 inside point_to_line_distance_squared3d, and the
change is a net deletion. Frames now agree at sub esp,0x34; relocdiff 39/39,
0 differing rows. The verifier confirmed the patched function's RAW bytes are
identical to January's, not merely equal under the normalized hash.

_code_0003a910 is target-proven: January emits ONE fdivr against 1.0f and THREE
fmul, so `scale = 1.0f/vertex_count` then three `*= scale` is its source, not a
rounding liberty; three `/=` divides would emit three fdivs. vehicle_stuck in
units/vehicles.c writes the same centroid the same way.

Landed at explicitly ZERO credit, because both functions remain residual and
both verifiers were emphatic that a residual's potential is not a realized
gain:

  ai_debug        _ai_debug_render_actor     a real off-by-one CORRECTED
  actor_perception _actor_perception_update  an invented macro replaced by
                                             real_math.h's genuine helper

The render_actor fix is a defect, not byte-steering: `switch (target_type-1)`
with case labels that are the enumerators and _actor_target_none == 0 dispatches
every type onto the previous colour and sends _actor_target_damaging_enemy into
the asserting default arm. January proves the compiler subtracts 1 itself
(`dec eax` where we emitted `add eax,-2`).

A park retired. _pre_evaluator_attack was parked `unclassified`; the frame
census contradicted that class and was right. parked.json 184 -> 183 entries,
no other entry touched, never re-serialized. First park retired by the census;
it says 35 more carry the same kind of evidence.

HELD, not landed, and both are deliberate:

  _ai_test_line_of_sight       drops an initialiser January lacks, which is
      byte-proven, but lands an uninitialised read on a function that stays
      residual - the same class as the owner's standing hold on
      _actor_emotion_update's out-of-bounds read, and worth zero credit.
  _actor_perception_refresh    gates strictly EXACT at 2,416 bytes, the biggest
      win this wave found, but needs a macro we would be inventing. A five-way
      strip test shows ONLY the full canonical macro expansion ((a) * (a))
      reaches January - four simpler spellings all stay residual - and neither
      of its two hunks works alone. That cuts both ways and the ruling is the
      owner's, not a lane orchestrator's. Full evidence in the wave report.

Negatives worth more than some landings: _encounter_create, 23 shapes, no
landing - its -4 frame is not a missing local but which pointer wins EBX, and
the previous wave's "spill slot" reading is refuted by `count` at [ebp-8].
_prop_get_active_by_unit_index, 60 gated builds mapping the entire reachable
image of a 144-byte function to three hashes, January's not among them - a
whole-function ECX/EDX two-colouring. Both agents also caught metric traps
where a shape matches January's instruction count by coincidence while
diverging earlier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:08:45 -07:00
Jonas VolmanandClaude Opus 5 8fd76228b9 Apply independent-review corrections to the ai_script BUG disclosure
The owner's ruling was to accept the 214-byte ai_script match only with a
documented target-proven BUG and an independent review. The review returned
CONFIRMED WITH CORRECTIONS; this commit lands every correction it required.

Code (comment only, zero bytes changed):
- Evidence now leads with January's 34-byte .rdata literal
  "%s: ai_migrate_and_speak %s %s %s". The 0x14 cleanup proves three VALUES;
  only the literal proves four CONVERSIONS, so the literal is the primary
  evidence and the cleanup is corroboration.
- Struck "so no shipping configuration runs it". Both gate flags are exposed
  as the HaloScript external globals ai_print_migration / ai_print_scripting
  (source/hs/hs_globals_external.c:1468,1470) and are therefore settable at
  runtime by a scenario script or the debug console in the very build being
  matched. The disclosure now says the path is reachable.

Documentation:
- Removed the dangling "house rule 24" citation; docs/campaign_house_rules.md
  has 22 rules. The governing text is the original-bug policy in
  docs/matching_methodology.md, now cited directly with a correction note.
- Withdrew the "always-executed paths" distinction from the two donors the
  owner previously excluded; reachability is not the owner's stated reason.
- Recorded the review itself, including the two checks it added that the
  author had not made (the literal has exactly one relocation in the whole
  object; the 2020 cross-build defect verified first-hand at VA 0x00948864)
  and its finding that fake_match_scan cannot clear a variadic-arity case.
- Manifest: split source_commit into per-row landing_commit and
  lane_tip_commit, which were previously conflated.

Re-verified after the edit: EXACT 224, source/ai/ai_script 116/116,
relocdiff sha equal with 0 differing rows, 16/16 relocations; full rebuild
reproduces 1,444,193 / 1,770,166 (7,292 / 7,574); parks 184 active / 0 stale
/ 0 invalid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 13:56:46 -07:00
Jonas VolmanandClaude Opus 5 3e44bc7829 Lane A wave A6 and closeout: house-rule 9 cleanup and deliverables
Zero strict bytes, zero regressions, zero exact credit.

ai_communication.c drops three consumer-local prototypes - game_team_is_enemy,
game_team_is_ally and game_allegiance_incident - that were hiding missing owner
declarations in a consumer C file. Wave A5 added the genuine owner header
game/game_allegiance.h, which declares all three at lines 23, 26 and 38; a /W3
compile reports no C4013, and the change is byte-inert with all 44 exact
siblings preserved.

Deliverables:
  claude_lane_a_ai_core_HANDOFF_20260920.md            - full handoff, including a
      complete disposition table for all 35 owners totalling exactly 79,657
      meaningful bytes, each with its live gate row and a concrete reopening
      condition
  claude_lane_a_ai_core_admission_manifest_20260920.json - machine-readable, every
      measurement read live from build/split, build/base and the semantic ledger
      at generation time; 1 exact row, 8 fuzzy rows at explicit zero credit,
      0 regressions
  claude_lane_a_ai_core_rejected_hypotheses_20260920.md - 18 numbered negatives

Final verification at this tip: halobetacache 1,444,193 / 1,770,166 (7,292 /
7,574); stable_verdicts diff vs the frozen baseline gained 1 / 224 bytes / 0
regressions (exit 0); accepted ledger +214 meaningful / 0 regressions; parks 184
active / 0 stale / 0 invalid; admission audit 19/0/8/0; tools tests 1,152 passed
/ 5 skipped / 26 subtests; source/ai fake scan 2 findings, both the inherited
ai.c pragma pair; no production file outside the 11-unit allowlist changed; no
header, comparator, target object or build tool changed; ai_debug's inherited
_point_from_line3d COMDAT is byte-identical to a rebuild from the baseline
source and the other ten units emit no copy; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 13:16:48 -07:00
Jonas VolmanandClaude Opus 5 7356576d32 Lane A wave A5: restore the print_allegiance incident report
Zero strict bytes, zero regressions, zero exact credit.

_ai_communication_event goes from 7776 to 7856 bytes against January 8064 (gap
288 -> 208) and from 319 to 328 relocations against 336 (gap 17 -> 8). All 44
exact siblings preserved.

The first of January's two print_allegiance blocks is restored - a console_printf
of "incident between teams %s and %s: %s, %s" with the two team names and the
betrayal/accident and observed/unobserved selections - placed where January's
bytes put it, after the betrayal/hostility resolution and before the
if (observed) allegiance-incident call. It was identified by the same
string-relocation audit that drove wave A3.

ai_debug.print_allegiance is a real named field of the existing
struct ai_debug_state. The change adds #include "game/game_allegiance.h", the
genuine owner header of global_game_team_names - house rule 9 satisfied, not
circumvented. No header was edited and no consumer-local prototype was written.

January's second print_allegiance block calls game_allegiance_get_incidents,
which is declared in no header in the tree; that half stays a deferred header
prerequisite for source/game/game_allegiance.h and was not attempted.

Verification: build unchanged at 1,444,193/1,770,166 (7,292/7,574);
stable_verdicts diff vs the frozen baseline gained 1 / 224 bytes / 0 regressions;
parks 184/0/0 (this function is not parked); admission 19/0/8/0; fake scan 0
findings in the changed file; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 13:14:03 -07:00
Jonas VolmanandClaude Opus 5 e99467da09 Lane A wave A4: actor_move_update structural corrections
Zero strict bytes, zero regressions, zero exact credit.

_actor_move_update goes from 3088 to 3120 bytes against January 3136 (size gap
48 -> 16) and from 869 to 882 instructions against 888 (gap 19 -> 6). Three
changes, each read off January: the initialized-declaration order that the
twelve-store prologue emits; the moving_into_fire_timer branch polarity with its
arms swapped; and normalize3d(...) != 0.f in place of > 0.f.

Two shapes from the worker candidate were rejected and the rejection improved the
result. (1) Hand-expanding actor_move_transform_avoidance_vector at two call
sites is manual inlining of an ordinary helper, forbidden by house rule 13 and
named explicitly on this unit's do-not-repeat list; its measurements are kept as
evidence (it does fix test_avoidance_vector's relocation count, 17 -> 16, and
shrinks that size gap 208 -> 16, but overshoots). (2) Rewriting
TEST_FLAG(flags, crouch ? A : B) as (crouch && TEST_FLAG(A)) || (!crouch &&
TEST_FLAG(B)) was measured and dropped: restoring the natural ternary gives 3120
bytes where the verbose form gives 3104, so the concise source is also the
closer one.

Verification: build unchanged at 1,444,193/1,770,166 (7,292/7,574);
stable_verdicts diff vs the frozen baseline gained 1 / 224 bytes / 0 regressions;
parks 184/0/0 (actor_moving has none); admission 19/0/8/0; fake scan 0 findings
in the changed file; point guard passes; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:38:11 -07:00
Jonas VolmanandClaude Opus 5 4fa32b2c80 Lane A wave A3: restore the missing communication diagnostic graph
Zero strict bytes, zero regressions; the largest structural recovery in the lane.

_ai_communication_finished goes from 912 bytes / 29 relocations / 305
instructions to 1568 / 86 / 482 against January 1584 / 87 / 484, and objdiff from
42.12 to 91.44 percent. The remaining gap is 16 bytes and ONE relocation, down
from 672 bytes and 58 relocations. All 44 exact siblings preserved.

Found by a per-function string-literal relocation diff over all 34 Lane A
residuals: this function references zero string literals where January references
sixteen, no Lane A unit emits a string absent from the January image, and 32 of
34 residuals have exactly matching string sets. January .rdata supplied the exact
texts, the prologue supplied the 0x650 frame and the [ebp-0x450] message buffer,
and the call sites supplied the asymmetric sprintf-then-csstrcat idiom. Every
debug guard resolved to a real named field of the existing struct ai_debug_state,
so no new type, header, name or declaration was needed.

Also lands ai_debug.c:2281 "has_approach_point>0" -> "has_approach_point" (the
field is a boolean); section size stays at January exact 24976 and differing
regions go 1452 -> 1451.

Rejected from the same candidate: writing the ai_communication_event is-reply
player_rating as a 2.0f literal moves relocations 319 -> 318 against 336 and
instructions 2280 -> 2278 against 2353, i.e. structurally away on both counts.
The rejected-hypotheses file had recorded that probe as never compiled; it is now
compiled and negative.

config/parked.json: one entry re-baselined with refreshed measurements and a new
reopening condition. No park retired, none unrelated touched.

Verification: build unchanged at 1,444,193/1,770,166 (7,292/7,574);
stable_verdicts diff vs the frozen baseline gained 1 / 224 bytes / 0 regressions;
parks 184/0/0; admission 19/0/8/0; fake scan 0 findings in the changed files;
point guard passes; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:35:20 -07:00
Jonas VolmanandClaude Opus 5 8110e554dc Lane A wave A2: three zero-credit source corrections
No new strict bytes and no regressions. All three changes are source-authenticity
corrections retained at zero exact credit.

actor_firing_position/_pre_evaluator_attack recovers the MISSING RELOCATION
(38 -> 39, matching January), differing relocation rows 24 -> 9, objdiff
93.62 -> 96.11. Four byte-decided levers: 20.0f * (d / 2.0f) stops VC7 folding
0.5f*20.0f into 10.0f; two initializers become statements because the zero store
sits on the opposite side of call _actor_get_weapon_definition and a stack store
cannot be sunk across a call; an explicit strict < replaces MIN(), which expands
to ((a)>(b)?(b):(a)) and can only emit test ah,0x41 where January emits test ah,5.

ai/_ai_test_ballistic_line_of_fire rebuilds the recorded w3c b5 shape: instruction
counts become 296/296 (were 292/296), real differing regions 28 -> 11, objdiff
94.5 -> 98.996. It reproduces January's fsubr / fst-to-dead-parameter-home /
post-call fld structure for segment_time exactly.

ai/_ai_test_line_of_fire lands only the store-order half of the old lof2
candidate; the rejected bare call-site parentheses are NOT landed and that prior
ruling stands. objdiff 88.17 -> 89.20.

actors.c drops three "= NULL" initializers that were forcing a 12-byte .bss
January does not have. January defines all three in linker_common.obj and
references them as undefined, i.e. the original used tentative definitions. These
were the only "struct data_array *x = NULL;" definitions in the whole tree; the
sibling encounters.c already uses the tentative form. .bss is now absent and the
three symbols are COMMON, matching January.

config/parked.json: three entries re-baselined (measurements refreshed, evidence
extended with the lever and a reopening condition), none retired, none unrelated
touched. Parks remain 184 active / 0 stale / 0 invalid.

Verification: full ninja build unchanged at 1,444,193/1,770,166 (7,292/7,574);
stable_verdicts diff vs the frozen baseline gained 1 / 224 bytes / 0 regressions;
accepted ledger +214 meaningful / 0 regressions; admission audit 19/0/8/0; fake
scan 0 new findings (source/ai stays at the 2 inherited pragma leads); point
guard passes on all three units; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:26:41 -07:00
Jonas VolmanandClaude Opus 5 9eca0524de Close ai_script.obj at 116/116 (+214 meaningful bytes)
_ai_scripting_migrate_and_speak becomes strict exact: 224/224 padded bytes,
16/16 relocations, relocdiff reports sha equal and zero differing rows.

January's call site cleans 0x14 bytes (five dwords) and loads speech_type only
afterwards at +0x7d, while the format literal read out of January's .rdata is
"%s: ai_migrate_and_speak %s %s %s" - four %s conversions for three values. Our
source was passing speech_type as a fourth value and cleaning 0x18.

Landed under the original-bug policy with a BUG (preserved for exact matching)
comment that records the evidence, the runtime consequence (the fourth
conversion's slot is the saved-EDI home, so vsprintf dereferences the caller's
entry EDI as a char *) and the corrected form. The branch is reachable only
under ai_debug.print_migration / print_scripting.

Flagged for owner ratification in the wave report; reverting is a single token.

Verification: full ninja build 1,444,193/1,770,166 Halo bytes (7,292/7,574
functions); stable_verdicts diff gained 1 / 224 bytes / 0 regressions; accepted
ledger +214 meaningful / 0 regressions; parks 184 active / 0 stale / 0 invalid;
admission audit 19/0/8/0; fake scan 0 findings in the changed file and the same
2 inherited findings across source/ai; git diff --check clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:14:50 -07:00
Jonas Volman 6130661c95 Reconcile final Astra campaign (+4073 bytes) 2026-09-20 10:52:50 -07:00
Jonas Volman 5361eeae52 Reconcile Astra exact-match campaign (+8314 bytes)
Integrate six accepted exact functions, two whole-object admissions, evidence-backed zero-credit source corrections, owner-header cleanup, and curated campaign records. Preserve zero regressions and exclude generated scratch archives from canonical history.
2026-09-20 02:31:53 -07:00
Jonas Volman 37d513310b Reconcile evidence-backed 10K exact batch 2026-09-19 23:12:42 -07:00
Jonas Volman 9d155f1d60 Admit ten verified exact objects 2026-09-19 15:56:43 -07:00
Jonas Volman 389126d5a7 Reconcile Claude two-block exact-match campaign 2026-09-19 13:44:32 -07:00
Jonas VolmanandClaude Opus 5 9b6ad2c75b Wave 21: _structure_visibility_traverse_surface_lists strict exact (park reopen)
- surface_index_buffer is declared before the frustum selection (January
  loads the index buffer before the pvs test)
- material looked up through nested typed tag-block gets; the buffer walk
  skips the two index words and post-increments through the count, matching
  January's two separate pointer advances
- block-scoped vertex0/1/2 pointers read the compressed vertex data per
  surface, matching January's left-to-right index loads before the call

Wave 21 (3 bundle implementers + 1 reviewer, 14 TUs, 22,880 target bytes)
closed only this function. Stable diff vs wave 20: 1 gained, 0 regressions;
active parks 191 -> 190.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 23:26:16 -07:00